This walkthrough takes you from nothing to a rendered .mp4, timed to the measured beat
grid of your own audio, using the @kompo/kli CLI. It is written second-person to the
agent driving the CLI — a human is only needed for the one-time login. Prerequisites:
Bun, a kompo.ai account for the authenticated steps (accounts are
invitation-only — see service-overview), and optionally ffprobe
(from ffmpeg) if you want to verify the rendered output yourself. For the domain model
behind each step, see service-overview; for everything else, start
at knowledge.yaml.
Clone and install — the npm package is not published yet, so this is the only path today:
git clone https://github.com/StigLau/agent.kompo.se.git
cd agent.kompo.se && bun install
Check that the API and the KCP discovery chain are both reachable:
bun src/cli.ts health
health prints two summaries, reported separately: API availability, and the KCP
discovery chain (whether knowledge.yaml was reachable and how many units it declares).
Only an unreachable API is exit-nonzero — a degraded KCP summary means discovery is
incomplete, but the service may still be functional.
kli initbun src/cli.ts init
init fetches the tools manifest and the knowledge manifest, checks your auth status, and
writes an AGENTS.md context file into the current directory — environment, tools
operation count, knowledge unit count, and auth state. It fails closed: if any discovery
step fails (tools manifest, knowledge manifest, or a manifest that parses to 0 units), it
exits non-zero and writes no file. Pass --allow-partial to write the context file anyway
with a prominent warning banner. Pass --manifest <path-or-url> to point init at a local
knowledge.yaml or an alternate URL instead of the default published one.
Ordering nuance: the tools manifest is contractually a public bootstrap endpoint, so
init should work before you log in. If it fails with a 401 on the tools manifest, the
robust move is to authenticate first (step 3) and re-run init — treat auth-then-init as
the reliable order.
If you received an enrollment URL, open it in a browser first to create or sign in to the invited account. Do not put that URL in a project file or commit it.
Then start KLI’s PKCE login:
bun src/cli.ts auth/url
Open the printed URL in a browser and log in. The browser will land on a broken localhost
page after login — that is normal. Copy the full address-bar URL, including
?code=...&state=..., and paste it back:
bun src/cli.ts auth/complete "<full-callback-url>"
Tokens are stored in ~/.kompo/auth-<env>.json, mode 0600, and refresh automatically.
If the enrollment still needs accepting after PKCE login, inspect it first and then explicitly
confirm the claim:
bun src/cli.ts auth/claim-invitation "<enrollment-url>"
bun src/cli.ts auth/claim-invitation "<enrollment-url>" --confirm
Check bun src/cli.ts auth/status at any point to see identity, role(s), and token expiry.
Verify public and read-only authenticated access before uploading anything:
bun run verify:access -- --require-producer
Role matters for §6-7. Every authenticated account can upload and analyze audio (§4-5),
but composing a komposition (§6-7) additionally requires producer role. Role is set by
whoever invites you and isn’t self-service; auth/status (and the auth/complete output)
shows your role and warns if it’s below producer.
Common failures and fixes:
auth/url first.auth/url.auth/url.PKCE is the only supported auth flow — never attempt to pass credentials directly.
bun src/cli.ts upload-analyze <path-to-audio-file>
Supported formats: WAV, MP3, FLAC, AAC, OGG, M4A. Expected output shape:
# upload-analyze
- fileId: <id>
- analysisStatus (response): queued
- result: PASS
- Next: kli media-analysis/<fileId>
Capture the fileId — you need it to reference this audio in your komposition.
Analysis runs asynchronously. Poll the authenticated analysis endpoint until it completes:
GET /api/multimedia/<fileId>/analysis
Authorization: Bearer <token>
Accept: application/json
The response is flat (no wrapper object, no separate job/status field) and carries bpm,
confidence, method, beat1Ms, downbeats (the bar grid), beats, and analyzedAt.
Completion criterion: analyzedAt is set and bpm is finite.
Three things to do with the result before you compose anything:
downbeats is the product; the scalar bpm is a
label. Bar positions are read from the grid, never multiplied out of the BPM — real tracks
have intro offsets, which is exactly what beat1Ms is telling you.fileId this grid belongs to. It is valid for that exact audio file only —
not for a remaster, a different edit, or a re-upload.See media-analysis for the full field reference, and source-metadata-approach for the approach behind all three — including why naming a track’s segments in the user’s own words is part of this step.
Write a markdown komposition using the measured BPM from step 5 — never milliseconds.
Start from the user’s structure in bars — “intro 4 bars, verse 8, refrain 8, outro 4” — and convert those lengths to beat counts (in 4/4, bars × 4) as you author. Do not compute any millisecond or second position yourself; state beats and let the server resolve them. beats-and-bars is the full approach, and worth reading before your first komposition.
Minimal structure:
# My First Video
## Metadata
- BPM: <measured-bpm>
## Tracks
### Visuals
- [remotion:KompoTitle](source-generated) "Title card"
- Start: 0 beats
- End: 8 beats
- Props: {"title": "My First Video", "accentColor": "#6366f1"}
### Audio
- [<fileId>](source-audio) "My track"
- Start: 0 beats
- End: 32 beats
Three rules the platform enforces: use the measured BPM, never milliseconds; one global
BPM governs beat-to-time conversion across every track in the document (a single fixed
master tempo — a tempo ramp across the video is not supported); reference only
fileIds you have uploaded (or a source-generated Remotion visual where available). See
komposition-format for the full V1/V2 spec and
komposition-v3 for layered/z-ordered compositions.
bun src/cli.ts workstate/load-file <path-to-komposition>.kompo.md
Registers the komposition and opens it in your workstate — capture the komposition ID from the output. Then:
bun src/cli.ts workstate/render-qc
This submits the build job, polls until it reaches a terminal state (SUCCEEDED or
FAILED), resolves the production, and prints a stream URL. Expect 2-10 minutes depending
on render queue depth. If you need to check progress independently, kli job-status/<id>
polls live. Finished outputs are also listable with kli outputs.
Expected duration is total_beats × 60 / BPM — compute it, don’t hardcode it. If you have
ffprobe, check that the output has both a video and an audio stream at the expected
duration and resolution. scenarios/first-video/verify.ts in this repo is a ready-made
checker that does exactly this against a downloaded .mp4.
| Symptom | Likely cause | Action |
|---|---|---|
health reports the API unreachable |
Network issue or outage | Check connectivity, retry after a few minutes |
init or tools gets a 401 |
Tools manifest should be public; treat as a deployment issue | Authenticate first, then re-run (§2); if it persists, this is a server-side contract failure, not a client bug |
| Analysis never completes | Job still queued, or unsupported audio format | Keep polling for a few minutes; confirm the file is WAV/MP3/FLAC/AAC/OGG/M4A |
| Server rejects the komposition format | Missing section, wrong BPM field, or a fileId not yet in your library |
Re-check against komposition-format exactly — no missing sections, valid fileIds |
Build job reaches FAILED |
Invalid komposition, missing file reference, or a server-side render error | Inspect kli jobs/<id> for the error detail |
render-qc times out |
Render queue backed up, or job stuck | Check status manually with kli job-status/<id>; if still RUNNING, wait and retry |
workstate/load-file or the compose step returns 401 InsufficientPermissions |
Your account’s role is below producer (upload/analysis don’t check role, but composing does) | Run auth/status to confirm your role; ask whoever invited you to re-invite with producer role, or (if they’re an admin) have them grant it directly |
scenarios/first-video/ in this repository is this exact flow, captured as a deterministic
8-stage rails runner plus an LLM-facing playbook, with machine-checked success criteria
(duration, streams, resolution). Run it to see a known-good execution end to end.