A tutorial covering seven omni-dev workflows. Switch tabs to navigate.
omni-dev exposes JIRA issues through the JFM (JIRA-Flavoured Markdown) format β YAML frontmatter plus a markdown body that round-trips losslessly to and from Atlassian Document Format (ADF). You edit issues like normal markdown files; omni-dev handles the ADF translation on push.
export ATLASSIAN_INSTANCE_URL="https://myorg.atlassian.net" export ATLASSIAN_EMAIL="me@example.com" export ATLASSIAN_API_TOKEN="atlassian_api_token_here" omni-dev atlassian auth status
.md file.
omni-dev atlassian jira read PROJ-1234 > PROJ-1234.md
omni-dev atlassian jira write PROJ-1234.md
For a one-shot fetch/edit/push cycle in $EDITOR, use the
edit subcommand:
omni-dev atlassian jira edit PROJ-1234
The example below exercises most of the unusual ADF nodes JFM supports
β panels, expanders, nested expanders, layout sections with columns,
directive tables with merged cells and nested expands inside table
cells, decision and task lists, status pills, mentions, dates,
emoji, inline cards, annotations, subscript/superscript, colored
spans, and the adf-unsupported escape hatch.
---
type: jira
instance: https://myorg.atlassian.net
key: PROJ-1234
summary: "Add diff/patch coverage analysis for PR comments"
status: In Progress
issue_type: Story
assignee: Alice Smith
priority: High
labels:
- coverage
- ci
- tooling
---
# Overview
The PR coverage comment reports whole-project coverage on every PR,
which hides whether the *changed* lines are tested. This issue tracks
adding patch-coverage attribution β see :mention[Alice]{id=712020:abc} for the design.
Status today: :status[Implementation]{color=blue}, target ship
:date[2026-06-30]{timestamp=1782777600000}.
:::panel{type=info}
**Context.** This is the third attempt at patch attribution. Read the
:card[https://myorg.atlassian.net/browse/PROJ-1100]{localId=card-1} retrospective before
proposing new architecture changes.
:::
:::panel{type=warning}
**Heads up.** The 12k-line lcov fixture under
`tests/fixtures/coverage/big_project.lcov` is licensed CC0 but is
2 MB β do **not** add more like it without a justification.
:::
## Layout: requirements + non-goals side by side
::::layout
:::column{width=66.66}
### Requirements
- First :status[Partial]{color=yellow} patch-coverage number within
:span[300 ms]{color=#cf222e} of report parse.
- Uncovered-new-line list exact against the diff, to within
≤ 1 line on the reference report set.
- Memory ceiling: 512 MB resident on the i7-1185G7 reference
laptop.
:::
:::column{width=33.33}
### Non-goals
- Branch coverage (line coverage only; deferred to PROJ-1500).
- Mutation testing.
- Report formats other than lcov / llvm-cov JSON / Cobertura at v1.
:::
::::
## Decisions
:::decisions
- <> Use line coverage only; ignore branch data (PR #952).
- <> Default `DiffScope` to `DiffOnly` so run-to-run variance on
untouched files does not surface as phantom deltas (PR #973).
- <> Auto-detect the report format from file contents.
:::
## Open tasks
- [x] Pin the lcov grammar to the documented subset.
- [x] Verify the metric shape against :card[https://github.com/rust-works/omni-dev/issues/949]{localId=task-card}.
- [ ] Land the `CoberturaReport` parser β see expander below.
- [ ] Snapshot the markdown-renderer output.
## Implementation detail (collapsed)
:::expand{title="Patch-attribution pseudocode (click to expand)" localId=exp-pseudo}
The analyzer intersects the diff's added lines with the report's
covered lines and emits a `PatchCoverage { covered, total }` summary.
```rust
state:
added: BTreeSet<(PathBuf, u32)> // new lines from the diff
covered: BTreeSet<(PathBuf, u32)> // hit lines from the report
uncovered: Vec<(PathBuf, u32)> // added β§ Β¬covered
patch_pct: f64
```
:::nested-expand{title="Why a BTreeSet?"}
Deterministic ordering so the uncovered-line list and the rendered
snapshots stay stable across runs β set iteration order can otherwise
churn the comment on every CI run.
:::
:::
## Risks (directive table β exercises colspan, borders, and nested-expand inside cells)
::::table{layout=default}
:::tr
:::th
Risk
:::
:::th
Likelihood
:::
:::th
Mitigation
:::
:::
:::tr
:::td{border-color=#091e42 border-size=2}
**Phantom per-file deltas** from run-to-run coverage variance on files
the PR never touched.
:::
:::td
:status[High]{color=red}
:::
:::td
:::nested-expand{title="DiffScope=DiffOnly strategy"}
Default the project-delta and indirect-change sections to files the
diff touches. Surface real cross-file effects only via a
magnitude-gated note (≥ 10 net covered lines). `--all-files`
restores the noisier report.
:::
:::
:::
:::tr
:::td
Baseline lcov missing at the merge-base (cold start / expired artifact).
:::
:::td
:status[Medium]{color=yellow}
:::
:::td
Recompute coverage at the merge-base in a worktree, rewriting absolute
`SF` paths to the workspace prefix so both reports strip one prefix.
:::
:::
:::tr
:::td{colspan=3}
**Cross-cutting:** huge reports blow the parse budget β see
[the design](https://example.com/design){annotation-id=anno-7 annotation-type=inlineComment}.
:::
:::
::::
## Inline rich content
H2O is water; E = mc2. The candidate
:span[attribution algorithm]{bg=#fff8c5} is named after :emoji{shortName=:dolphin: text="π¬"} β
no idea why.
This paragraph contains :placeholder[fill in parse time once
measured] and a :card[https://github.com/rust-works/omni-dev/issues/952]{localId=card-952}.
[This sentence carries an inline comment thread.]{annotation-id=anno-3 annotation-type=inlineComment}
## Unsupported-node escape hatch
ADF nodes JFM doesn't yet model round-trip through a fenced block:
```adf-unsupported
{"type":"customAtlassianNode","attrs":{"key":"value"}}
```
omni-dev preserves these byte-for-byte across read/write.
Implementation target ship 2026-06-30
tests/fixtures/coverage/big_project.lcov
is licensed CC0 but is 2 MB β do not add more like it without a justification.
DiffScope to DiffOnly so variance on untouched files isn't a phantom delta (PR #973).CoberturaReport parser.| Risk | Likelihood | Mitigation |
|---|---|---|
| Phantom per-file deltas from run-to-run coverage variance on untouched files. | High | |
| Baseline lcov missing at the merge-base (cold start / expired artifact). | Medium | Recompute coverage at the merge-base in a worktree, rewriting absolute SF paths to the workspace prefix. |
| Cross-cutting: huge reports blow the parse budget β see the design. | ||
H2O is water; E = mc2. The candidate attribution algorithm is named after π¬ β no idea why.
This paragraph contains [fill in parse time once measured] and a #952.
This sentence carries an inline comment thread.
:::expand nested directly inside :::panel
parses fine in JFM but the Atlassian API will reject the
ADF. Invert the nesting (panel inside expand) or use
:::nested-expand inside table cells. omni-dev runs
the validator before pushing, so you'll see this locally β but
knowing the rule up-front saves a round-trip.
| Subcommand | What it does |
|---|---|
search | Run a JQL query, get hits as YAML. |
create | Create an issue from a JFM file with no key in frontmatter. |
transition | List or execute workflow transitions (e.g. In Progress β Done). |
comment | Add, edit, list comments on an issue. |
dev | Show linked PRs, branches, and repositories for an issue. |
sprint | List/create/update sprints, add issues to a sprint. |
link | Manage issue links (blocks, relates-to, duplicates, β¦). |
Confluence pages use the same JFM scheme as JIRA issues: YAML
frontmatter discriminated by type: confluence, plus a
markdown body that converts to ADF. The same set of unusual
ADF nodes is available, but Confluence pages tend to lean harder on
layout sections, media with captions,
inline annotations (Confluence's inline-comment
threads), and block/embed cards.
omni-dev atlassian confluence read 12345 > page.md # pull $EDITOR page.md # edit omni-dev atlassian confluence write page.md # push
page_id in the frontmatter does the routing. Omit it
and use create instead to create a new page:
omni-dev atlassian confluence create page.md
This page exercises multi-column layouts, captioned media, inline
media, decision lists, task lists, nested expanders, directive
tables with bordered cells and per-cell localIds,
inline-comment annotations, embed cards, block cards,
emoji, status pills, dates, mentions, indented blocks,
text-colour and background-colour marks, subscript/superscript,
and the adf-unsupported fenced block.
---
type: confluence
instance: https://myorg.atlassian.net
page_id: "98765432"
title: "Coverage Diff β Architecture Brief"
space_key: ENG
status: current
version: 14
parent_id: "12345"
---
# Coverage Diff β Architecture Brief
{align=center}
::card[https://github.com/rust-works/omni-dev/issues/949]
::embed[https://www.youtube.com/watch?v=dQw4w9WgXcQ]{width=480}
:::panel{type=info}
**Owner:** :mention[Alice Smith]{id=712020:abc accessLevel=CONTAINER} ·
**Reviewers:** :mention[Bob Lee]{id=712020:def}, :mention[Carla Ng]{id=712020:ghi} ·
**Last reviewed:** :date[2026-05-20]{timestamp=1779574400000}
:::
## Executive summary
{indent=1}
We propose a three-stage coverage-diff pipeline: parse →
diff → attribute. Stage 1 is shipped; stage 2 lands in
:status[Q2]{color=purple}; stage 3 begins :status[Q3]{color=neutral}.
[The patch-coverage figure is the load-bearing number.]{annotation-id=ann-budget annotation-type=inlineComment}
## Three-column layout: stages of the pipeline
::::layout
:::column{width=33.33}
### 1. Parse
:emoji{shortName=:inbox_tray: text="π₯"}
- lcov / llvm-cov JSON / Cobertura
- line hits per file
- format auto-detect (see #949)
:::
:::column{width=33.33}
### 2. Diff
:emoji{shortName=:twisted_rightwards_arrows: text="π"}
- `git diff` base → head
- added / changed lines only
- rename-aware path mapping
:::
:::column{width=33.34}
### 3. Attribute
:emoji{shortName=:bar_chart: text="π"}
- patch coverage %
- per-file project deltas
- indirect coverage flips
:::
::::
## Captioned diagram
![Pipeline overview](){type=file id=acab1234-5678-90ab-cdef-1122334455 collection=contentId-98765432 width=720 height=320}
:::caption{localId=cap-diagram-1}
Stage boundaries map 1:1 to the parse / diff / attribute modules. The
dashed arrows show the baseline path; solid arrows show the head path.
:::
The inline-media variant
:media-inline[]{type=file id=11112222-3333-4444-5555-666677778888 collection=contentId-98765432}
appears mid-sentence β handy for screenshots referenced inline.
## Decision log
:::decisions
- <> **Line coverage only; branch data ignored** (PR #952). Drives all
downstream decisions on this page.
- <> **DiffScope = DiffOnly by default.** Re-evaluate when cross-file
effects need surfacing past the magnitude gate.
- <> **No mutation testing in v1.** Deferred to PROJ-1500.
:::
## Open work
- [x] Land the lcov parser (#949).
- [x] Land the llvm-cov JSON parser (#952).
- [ ] Land the Cobertura parser (this page, #964).
- [ ] Wire the markdown renderer into the PR comment (#948).
- [ ] Make the coverage tests deterministic (#966).
## Two-column: configuration knobs vs. defaults
::::layout
:::column{width=50}
### Knobs
| Knob | Type |
| --- | --- |
| `base_ref` | String |
| `head_ref` | String |
| `diff_scope` | enum |
| `format` | enum |
| `gate_pct` | f64 |
:::
:::column{width=50}
### Defaults
| Knob | Default |
| --- | --- |
| `base_ref` | merge-base |
| `head_ref` | HEAD |
| `diff_scope` | `DiffOnly` |
| `format` | `markdown` |
| `gate_pct` | (none) |
:::
::::
## Runtime budget table (directive table β captures hard breaks, colspan, nested-expand)
::::table{layout=wide isNumberColumnEnabled=true}
:::tr
:::th
Stage
:::
:::th
Target p50
:::
:::th
Target p95
:::
:::th
Measured (this repo)
:::
:::
:::tr
:::td
Parse report (12k lines)
:::
:::td
< 50 ms
:::
:::td
< 120 ms
:::
:::td
:status[31 ms]{color=green}\
:status[98 ms]{color=green}
:::
:::
:::tr
:::td
Compute diff
:::
:::td
< 20 ms
:::
:::td
< 80 ms
:::
:::td
:status[12 ms]{color=green}\
:status[54 ms]{color=green}
:::
:::
:::tr
:::td{border-color=#cf222e border-size=2}
**Full PR comment** :emoji{shortName=:warning: text="β οΈ"}
:::
:::td
< 300 ms
:::
:::td
< 600 ms
:::
:::td
:status[412 ms]{color=red}\
:status[890 ms]{color=red}
:::
:::
:::tr
:::td{colspan=4}
:::nested-expand{title="Why is the full comment over budget?"}
The merge-base baseline recompute dominates when the artifact is
missing. Options:
1. Cache the baseline lcov as a CI artifact keyed on the merge-base.
2. Skip indirect-change detection when no baseline is available.
3. Run parse and diff concurrently.
Owner: :mention[Alice Smith]{id=712020:abc}. Due
:date[2026-06-01]{timestamp=1780617600000}.
:::
:::
:::
::::
## Expander with nested expander
:::expand{title="API surface (click to expand)" localId=exp-api}
The report trait is the only thing downstream consumers need to
import.
```rust
pub trait CoverageReport {
fn line_hits(
&self,
file: &Path,
) -> Option<&LineHits>;
fn files(&self) -> Box<dyn Iterator<Item = &Path> + '_>;
}
```
:::nested-expand{title="CoverageReport variants"}
- `Lcov` β parsed from `.lcov` / `.info` (`SF`/`DA` records).
- `LlvmCovJson` β llvm-cov `export` JSON.
- `Cobertura` β Cobertura XML (line coverage; branch data ignored).
:::
:::
## Highlights and inline marks
A :span[bright callout]{color=#cf222e bg=#fff8c5} marks the
load-bearing claim. We mix in :span[teal text]{color=#1a7f37} and
:span[a yellow highlight]{bg=#fff8c5} to signal severity.
Footnote-style :span[fn-1]{sup} sits superscript; chemical
H:span[2]{sub}O sits subscript. The
[entire sentence is annotated for inline comment thread]{annotation-id=ann-sentence-1 annotation-type=inlineComment}.
## Embedded BigQuery extension (unsupported — round-trips verbatim)
```adf-unsupported
{"type":"bodiedExtension","attrs":{"extensionKey":"bq-query","parameters":{"sql":"SELECT 1"}},"content":[{"type":"paragraph","content":[{"type":"text","text":"placeholder"}]}]}
```
## Manual hard breaks
Line one\
line two (same paragraph as line one β that's a hardBreak)\
line three.
---
© ENG space. Edits via `omni-dev atlassian confluence write page.md`.
#949 β Coverage-diff issue YouTube embed
We propose a three-stage coverage-diff pipeline: parse β diff β attribute. Stage 1 is shipped; stage 2 lands in Q2; stage 3 begins Q3.
The patch-coverage figure is the load-bearing number.
git diff base β head| # | Stage | Target p50 | Target p95 | Measured (this repo) |
|---|---|---|---|---|
| 1 | Parse report (12k lines) | < 50 ms | < 120 ms | 31 ms 98 ms |
| 2 | Compute diff | < 20 ms | < 80 ms | 12 ms 54 ms |
| 3 | Full PR comment β οΈ | < 300 ms | < 600 ms | 412 ms 890 ms |
A bright callout marks the load-bearing claim. We mix in teal text and a yellow highlight to signal severity.
Footnote-style fn-1 sits superscript; chemical H2O sits subscript.
validate_document against the assembled
ADF before sending. The most common rejection on Confluence pages
is :::expand nested directly inside a table cell β
use :::nested-expand. The schema source of truth is
src/atlassian/adf_schema/mod.rs, transcribed from the
upstream @atlaskit/adf-schema package per ADR-0023.
| Subcommand | What it does |
|---|---|
search | CQL search across spaces. |
children | List child pages of a page or top-level pages in a space. |
history | List version-history metadata for a page. |
compare | Structural diff between two versions of a page. |
download | Recursively download a page tree as .md files. |
label | Add/remove/list labels on a page. |
attachment | List/upload/delete attachments on a page. |
move | Reparent a page within the same space. |
omni-dev git branch create pr opens a pull request on the
current branch with an AI-generated title and body derived from the
commits and the diff against the base branch.
git checkout -b feature/coverage-diff # ... commits ... git push -u origin feature/coverage-diff
gh CLI.
omni-dev git branch create pr
| Flag | Effect |
|---|---|
--base <branch> | Override the auto-detected base branch. |
--draft | Create the PR as draft. |
--ai-backend claude-cli | Use a sandboxed nested claude -p session instead of the default HTTP backend. See ADR-0028. |
--claude-cli-max-budget-usd 0.50 | Cap nested-session spend per invocation. |
HEAD and the base branch (subject + body)..omni-dev/pr-template.md β used as a stylistic exemplar.origin/HEAD). Set --base when
opening PRs against a non-default branch (release branches,
long-running feature branches).
$ git log --oneline main..HEAD c5449c9 feat(coverage): land patch-coverage attribution 841187f feat(coverage): lcov + llvm-cov JSON parsers 728ce74 test(coverage): Cobertura fixture $ omni-dev git branch create pr --draft β analysing commits (3)β¦ β summarising diff (12 files, +984 β214)β¦ β calling claude (β $0.02)β¦ PROPOSED TITLE feat(coverage): diff/patch coverage analysis β parsers + attribution PROPOSED BODY (excerpt) ## Summary - Add CoverageReport trait with lcov and llvm-cov JSON parsers - Compute patch coverage and the uncovered-new-line list from the diff - Render the PR coverage comment via the markdown renderer ## Test plan - [ ] cargo test --all - [ ] cargo insta review - [ ] manual smoke against big_project.lcov Open the PR? [y/N] y β #890 opened: https://github.com/rust-works/omni-dev/pull/890
create pr runs. Worktree workflows in particular
often start with an empty branch β push at least one commit first
or PR creation will fail.
omni-dev git commit message twiddle rewrites recent
commit messages to comply with project guidelines while preserving
the commits themselves. It pairs with the
.omni-dev/commit-guidelines.md
rules in your repo.
omni-dev git commit message view HEAD~5..HEAD > commits.yaml
omni-dev git commit message twiddle --range HEAD~5..HEAD
You'll be asked to confirm before any git filter-branch-equivalent rewrite happens.
omni-dev git commit message check --range HEAD~5..HEAD
| Subcommand | Effect |
|---|---|
view | Analyse commits, emit a YAML report (with field-presence tracking). |
amend | Rewrite messages from a YAML configuration file (deterministic, no AI). |
twiddle | AI-driven rewrite. Reads guidelines and applies them. |
check | Validate against guidelines without modifying. Exit-codes are CI-friendly. |
staged | Generate a commit message from currently staged changes and commit. |
$ git log --oneline -5 c5449c9 wip 841187f more fixes 728ce74 fix tests 4d1290d coverage stuff 52ba037 initial
$ omni-dev git commit message twiddle --range HEAD~5..HEAD β reading .omni-dev/commit-guidelines.md β analysing 5 commitsβ¦ β calling claudeβ¦ PROPOSED REWRITES c5449c9 wip β feat(coverage): wire CoverageReport into the diff analyzer 841187f more fixes β fix(coverage): ignore branch records when computing line hits 728ce74 fix tests β test(coverage): unblock the markdown-renderer snapshot 4d1290d coverage stuff β feat(coverage): add patch-attribution skeleton + types 52ba037 initial β feat(coverage): add CoverageReport trait + lcov parser Apply rewrite? [y/N] y β rewrote 5 commits. Force-push with care: git push --force-with-lease origin issue-949-diff-patch-coverage-analysis
--force-with-lease).
Anyone with a local copy of the old branch must reset on top of the
rewritten history. Don't twiddle commits on shared long-running
branches (e.g. main, release branches).
.omni-dev/commit-guidelines.md are
included verbatim in the prompt. The AI is asked to rewrite
messages so they conform β conventional-commit prefix, scope,
imperative mood, body lines under N characters, etc. β whilst
preserving the original meaning.
Twiddle plus PR creation is the canonical pre-PR cleanup:
# clean up history, then open a PR with a fresh AI-generated body omni-dev git commit message twiddle --range HEAD~5..HEAD \ && git push --force-with-lease \ && omni-dev git branch create pr --draft
omni-dev browser bridge serve runs a long-lived local
process that lets you issue HTTP requests through an
authenticated browser tab. When you are poking at internal
services β Grafana/Loki, SSO-gated dashboards, admin panels β the
browser already holds sessions (SSO, OAuth, session cookies) that are
painful to replicate from a script. The bridge borrows that authority
without exfiltrating the cookies or tokens: the fetch()
runs in-page, so the credentials never leave the browser.
Two local planes joined by an id-keyed correlator: an HTTP
control plane you drive from the shell, and a WebSocket plane the browser
tab connects to.
operator CLI / authorized script browser tab (DevTools console)
β β
HTTP + bearer token + X-Omni-Bridge WebSocket + token subprotocol
β β
βΌ βΌ
βββββββββββββββββββββββββ omni-dev browser bridge βββββββββββββββββββββββββ
β HTTP control plane id-correlator WebSocket plane β
β 127.0.0.1:9998 βββΊ pending: id β oneshot βββΊ 127.0.0.1:9999 β
β (axum) βββ resolve on WS reply βββ (tungstenite) β
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
A request flows: control plane (authenticated) β assign
id + register a waiter β serialize a command frame β
WebSocket β browser fetch() β response frame β correlator
resolves the waiter by id β control plane returns the HTTP
response.
omni-dev browser bridge serve
https://grafana.internal/β¦). It connects over the WebSocket,
presenting the token via the subprotocol, and reconnects with backoff.
export OMNI_BRIDGE_TOKEN=<token printed by the bridge> omni-dev browser bridge request --url /loki/api/v1/labels
The bridge works only while the tab is open and the snippet is running.
request thin client
Reads the token (from OMNI_BRIDGE_TOKEN or
--token-file), adds the required headers, POSTs to a
running bridge, and prints the response envelope:
omni-dev browser bridge request --url /loki/api/v1/labels --method GET omni-dev browser bridge request --url /api/foo --method POST --body @payload.json omni-dev browser bridge request --url /api/foo --header "Accept: application/json" omni-dev browser bridge request --stream --url /events | your-consumer # SSE / chunked
Any control-plane path not under /__bridge/ is
forwarded verbatim to the browser, resolved against the page's origin.
Handy with curl β binary bodies are decoded automatically:
T="$OMNI_BRIDGE_TOKEN" curl -H "Authorization: Bearer $T" -H "X-Omni-Bridge: 1" \ http://localhost:9998/loki/api/v1/labels
Authorization: Bearer <token> and
X-Omni-Bridge: 1. The custom header forces a CORS preflight
that the server refuses to answer β which is exactly what blocks
simple-request CSRF from a random web page.
Paste the snippet into several authenticated tabs (e.g. a Grafana tab
and an admin tab) and each connects independently.
GET /__bridge/status lists them by connection id
and Origin; select one with --target (or the
X-Omni-Bridge-Target header):
omni-dev browser bridge request --target 2 --url /api/foo # by id omni-dev browser bridge request --target https://admin.internal --url /x # by origin
| Situation | Result |
|---|---|
| No tab connected | 503 |
| Exactly one tab, no target | Routes to it (v1 back-compat) |
| Several tabs, no target | 409 β specify a target (the error lists the tabs) |
| Target id / origin matches one tab | Routes to it |
| Target matches none | 404 |
| Target origin matches several tabs | 409 β target by connection id instead |
The trust boundary, stated once: a request is trusted only if it presents the session token AND is not a cross-origin browser request; everything else is denied. A localhost bind is necessary but not sufficient β it stops off-host access, not other local processes or the web pages you visit while the bridge runs.
ps).
The control plane requires Authorization: Bearer; the WebSocket
upgrade is rejected without the token in its subprotocol. An
unauthenticated peer can never connect or evict the browser connection.
localhost/127.0.0.1/[::1]
accepted (blocks DNS rebinding). Reject browser-originated
requests β any Origin header or
Sec-Fetch-Site: cross-site/same-site is denied.
Require X-Omni-Bridge: 1 on every request.
No CORS allow headers; OPTIONS is never answered.
serve --allow-origin <url>, or per-request with
request --allow-origin <url> (logged with a WARN, and
still bounded by the browser's own CORS).
The bridge issues the same same-origin requests the Grafana UI makes,
carrying the grafana_session cookie; CSRF passes because the
fetch() runs in-page. Pagination is client-side β walk the
window backward until a page comes back empty:
DS_UID=<loki-datasource-uid>
QUERY='{app="foo"}|="error"'
END=$(date +%s)000000000 # now, in nanoseconds
LIMIT=5000
while :; do
page=$(omni-dev browser bridge request \
--url "/api/datasources/proxy/uid/${DS_UID}/loki/api/v1/query_range?query=${QUERY}&end=${END}&limit=${LIMIT}&direction=backward")
# The upstream JSON is the envelope's `body` string; parse it once.
body=$(printf '%s' "$page" | jq -r '.body')
rows=$(printf '%s' "$body" | jq '[.data.result[].values[]] | length')
[ "$rows" -eq 0 ] && break
printf '%s\n' "$body" # collect / process this page
# Oldest timestamp on this page (ns) becomes the next `end`, minus 1ns.
oldest=$(printf '%s' "$body" | jq -r '[.data.result[].values[][0] | tonumber] | min')
END=$((oldest - 1))
done
Each page must still fit under --max-body-bytes. For genuinely
streaming endpoints (Grafana Live, SSE, chunked APIs), opt into
--stream instead of paginating.
Flag (on serve) | Default | Purpose |
|---|---|---|
--control-port <PORT> | 9998 | HTTP control-plane port. 0 binds a random free port. |
--ws-port <PORT> | 9999 | WebSocket-plane port. 0 binds a random free port. |
--allow-origin <URL> | β | Permit this exact cross-origin for the WS upgrade and outbound URLs. |
--request-timeout <SECS> | 30 | Per-request timeout before the control plane returns 504. |
--max-body-bytes <N> | 8388608 | Maximum browser response body size accepted. |
--max-concurrent <N> | 64 | Maximum concurrent in-flight requests. |
--token-file <PATH> | β | Read the token from a 0600 file instead of generating one. |
connect-src can block the WebSocket. CORS
constrains the in-page fetch(), not the bridge: same-origin
(relative URLs) is unaffected; cross-origin targets depend on their
Access-Control-Allow-Origin. The bridge works only while the
tab is open and the snippet is running.
omni-dev transcript youtube fetches captions and transcripts
from YouTube. The provider is the first positional argument, so the
namespace stays clean as more sources (Vimeo, podcast RSS, generic
VTT/SRT URLs) are added later. Three subcommands: info and
list-langs inspect a video; fetch renders a track.
omni-dev transcript youtube info jNQXAC9IVRw omni-dev transcript youtube list-langs jNQXAC9IVRw
Both default to a human table; pass
--output json for machine-readable output.
-o is given.
omni-dev transcript youtube fetch jNQXAC9IVRw --format srt -o me-at-the-zoo.srt
<url> accepts any of these β extra query params are ignored:
https://www.youtube.com/watch?v=jNQXAC9IVRw https://youtu.be/jNQXAC9IVRw https://www.youtube.com/shorts/<id> https://www.youtube.com/embed/<id> jNQXAC9IVRw # bare 11-character video ID
fetch flags| Flag | Default | Effect |
|---|---|---|
--lang <code> | en | Preferred language. Prefix fallback applies β en matches en-US. |
--format <fmt> | srt | One of srt, vtt, txt, json. |
--auto | off | Allow falling through to auto-generated (ASR) captions when no manual track matches. |
--translate <lang> | β | Synthesise a translated track in <lang> when no native track matches. |
-o, --output | stdout | Write the rendered transcript to a file. |
list-langs tags each track with a kind:
manual (human-authored), auto (ASR), or
translated. fetch only uses an auto
track when you pass --auto β otherwise it fails with
AutoCaptionsRequireOptIn rather than silently handing back a
lower-quality transcript.
Failures surface as named variants, so a script can branch on them:
| Variant | When |
|---|---|
InvalidLocator | URL did not parse, or bare ID failed validation. |
LanguageNotFound | No track matched --lang (manual or, with --auto, ASR). |
AutoCaptionsRequireOptIn | Only ASR matched, but --auto was not passed. |
PlayabilityRefused | Age-gated, region-locked, removed, or login-required. |
ParseError / Http | Response did not match the expected shape, or a non-2xx response. |
$ omni-dev transcript youtube info jNQXAC9IVRw Source: youtube ID: jNQXAC9IVRw Title: Me at the zoo Channel: jawed Duration: 00:19 Languages: - en [manual] English - en [auto] English (auto-generated) $ omni-dev transcript youtube fetch jNQXAC9IVRw --format txt All right, so here we are in front of the elephants. The cool thing about these guys is that they have really, really, really long trunks. And that's cool. And that's pretty much all there is to say.
src/transcript/ with no
clap dependency; the CLI is a thin bridge. Format converters
consume &[Cue] and never reach back into a source, so all
four output formats work for any future provider. See
docs/transcript.md
for the recipe to add one.
omni-dev ai claude history sync exports your Claude Code
conversation history to a target directory as one .jsonl
(and/or rendered .md) per chat. The corpus is a
behavioural transcript β prompts, responses, thinking,
tool calls, and tool-result metadata β sized for analyst use cases like
work-log generation and behavioural review.
*.txt sidecars, PDF rasters,
and auto-memory.
omni-dev ai claude history sync --target ~/claude-history \ --output-format jsonl,markdown
One folder per project (the encoded working-directory slug), one file per session UUID:
~/claude-history/
-Users-me-wrk-omni-dev/
0f1aβ¦2b.jsonl # append-only event log, byte-identical across runs
0f1aβ¦2b.md # rendered, human-readable transcript
-Users-me-tmp-spike/
a1b2β¦d4.jsonl
a1b2β¦d4.md
| Flag | Effect |
|---|---|
--target <PATH> | Target directory for the export (required). Created if absent. |
--source <PATH> | Override the source root. Defaults to ~/.claude/projects. |
--project <NAME> | Restrict to one project β the encoded slug (-Users-me-tmp) or a decoded cwd path. |
--since <DUR|DATE> | Only sessions at/after this point. Relative (30s, 2h, 7d, 4w) or RFC 3339. |
--output-format <LIST> | On-disk shapes: jsonl (default), markdown, or both comma-separated. |
--exclude-system | Hide system-side events from Markdown (JSONL is unaffected). |
--prune | Delete targets for sessions no longer in the source (scoped to managed extensions). |
--dry-run | Preview changes without touching the target. |
--format <text|yaml> | Shape of the run report printed to stdout. |
Each run reports created / updated /
skipped / pruned per file. A session is skipped
when its source mtime is unchanged, so re-running is cheap and safe to put
on a schedule:
$ omni-dev ai claude history sync --target ~/hist --output-format jsonl,markdown created jsonl -Users-me-omni-dev/0f1aβ¦2b -> ~/hist/β¦/0f1aβ¦2b.jsonl (48213 bytes) created markdown -Users-me-omni-dev/0f1aβ¦2b -> ~/hist/β¦/0f1aβ¦2b.md (21044 bytes) $ omni-dev ai claude history sync --target ~/hist --output-format jsonl,markdown skipped jsonl -Users-me-omni-dev/0f1aβ¦2b (unchanged) -> ~/hist/β¦/0f1aβ¦2b.jsonl skipped markdown -Users-me-omni-dev/0f1aβ¦2b (unchanged) -> ~/hist/β¦/0f1aβ¦2b.md
--prune is scoped, not a blunt delete<slug>/<uuid>.<ext> are
eligible for deletion, and <ext> is restricted to the
formats in --output-format β so artefacts the run was not asked
to manage are preserved regardless. Pair with --dry-run the
first time to preview what would go.
~/claude-history to a git
repo produces clean, meaningful diffs β a durable, greppable record of how
the work actually happened.