Repository navigation
v0.3.0
dawg projects are now plain TypeScript files that you, an agent or another window can edit, with sampler tracks, local media tools, a computer-keyboard play mode and menus for every edit by hand.
Play mode (computer keyboard)
/playor Ctrl-P turns the computer keyboard into a MIDI keyboard for the focused track:A S D F G H J K L ; 'are white keys from C,W E T Y U O Pthe black keys,Z/Xmove an octave,C/Vchange velocity, Shift sustains and Tab latches sustain. Esc leaves.- Bass tracks start an octave lower and leads an octave higher; the header shows the range, velocity, record state, click and grid.
- Recording.
Rarms overdub andShift-Rreplaces the bar; notes are quantized to/gridand land as ordinary score edits, so other windows, undo andtrack.tsall see them. One undo step per recorded bar. - Click track.
Mor/click on|off|<volume>toggles a tempo-synced metronome that never reaches renders or exports;/count-in 0|1|2sets the count-in before recording. - Sampler tracks play their voices from the keyboard: oneshot voices from MIDI 36, keyed samplers repitched from their root.
- Terminals send no key-up, so held notes last one grid step and extend while the key auto-repeats.
Menus
/menu [section]or Ctrl-K opens Track, Parameters, Effects, Automation, Mix and Transport. Arrows orj kmove, Enter opens or toggles,← →or+ -nudge, digits type a value,/filters,xdeletes an automation point, Esc steps back.- Each row shows its current value and the command it runs; every change is one receipt and one undo step.
- New prompt commands behind the menu:
automate <lane> points <b:v>...,automate <lane> remove <beat>,track name <text>andmeter <n>.
Performance
- Session records store reverse deltas instead of whole compositions, so a session reaches the 2000-event cap instead of failing around edit 70 (or on the first edit of a 16-bar loop).
- Audio renders run off the main thread with a per-track stem cache: a one-note edit re-renders in about 44 ms instead of blocking for 165–190 ms.
- AI Gateway and OpenRouter requests retry and time out when no response arrives; transport keys no longer wait on the daemon.
Sample playback
- Sampler tracks play.
sampler({...})voices now render in playback,dawg renderand exports, with Strudel's semantics:begin/endwindows,speed(negative reverses),loopfor the note's length,gain,chokegroups (Strudel'scut), keyed repitching fromroot, and oneshot voices on pitch slots from 36. Starts, stops and cuts fade over a few milliseconds. Volume, pan, automation, filter, delay and reverb apply as on any track, and sampler tracks are cached stems keyed by the files' sha256. - Decoding. WAV (PCM 16/24/32-bit and float32) and AIFF decode natively and resample to the engine rate; MP3, FLAC, Ogg and M4A decode through
ffmpegwhen it is onPATH, otherwise the voice is skipped with a diagnostic. Decoded audio is cached at.dawg/assets/<sha256>.pcm(512 MiB LRU). Files over 50 MiB or 10 minutes and paths that escape the project, symlinks included, are rejected; a stalesha256warns and still plays. /sample <path> [as <voice>]adds a voice to the focused track (copying the file intotracks/<slug>/samples/and reprintingtrack.ts);/samplelists voices. Oneshot samplers get one highway lane per voice,/tracksshows sample counts and missing files, and load problems are receipts.dawg renderin a project with no--sessionrenders the project files (song.ts).
Sign-in, model picker and spend
- One sign-in picker.
dawg login(and the firstdawgwith no provider) finds what is already set up, in parallel within 4 s:AI_GATEWAY_API_KEY,OPENROUTER_API_KEY, stored keys, a logged-in Vercel CLI,VERCEL_OIDC_TOKEN, and xcb Codex and Claude accounts. It then shows one Codex-style picker (arrows, numbers, Enter), with the first detected option as the default. When one option is ready, it asksUse <it>? [Y/n]. Non-interactive runs pick the best detected option or exit with a hint. - OpenRouter is a fourth provider. Sign-in is OpenRouter's OAuth PKCE flow (browser plus a
127.0.0.1callback with a state check, URL printed as a fallback, 5 min timeout), or a pasted key with hidden input. Turns stream with tool calls through the same agent tools as the gateway. - Subscriptions (
dawg login codex,dawg login claude) go through xcb 0.20+: pick the account and model, and dawg runsxcb setup <family>when none is ready. An account is usable iff xcb reportsavailable. Pending admission works, with a longer first call (the child timeout istimeoutMs + 75 s) andbusyretried with backoff. Accounts reportingmodels_unavailableare refreshed once, anddawg auth statusprints the xcb version with an upgrade hint below 0.20.0. - The choice sticks. The provider, model and account are saved in
~/.config/dawg/config.json(0600, atomic, no keys) and reused silently.dawg logout [provider]//logout,dawg login <provider>and/modelchange it. A saved provider that stops working is reported once and reopens the picker, never swapped. /loginin the TUI suspends the screen, runs the same flow (browser andvercel loginincluded) and redraws, replacing "run dawg login in a shell".- Model picker.
/modelordawg modellists frontier (Opus 5.5, Fable 5.1, GPT-6.1 Sol, Gemini 3.1 Pro), fast (Sonnet 5.5, Haiku 4.5, GPT-5.4 mini, Gemini 3.8 Flash) and open-weight models (DeepSeek V4 Pro, Kimi K3, Qwen3.8 27B, GLM-5.3, Llama 4 Maverick). Only tool-calling models the provider serves are listed. Each row shows an estimated~$0.004/promptfrom models.dev pricing (cached 24 h; OpenRouter's own prices on OpenRouter), and subscriptions list xcb's models asincluded. Type to filter; the current model is marked.DAWG_MODEL=<unknown>is now an error listing the choices. - Spend under the prompt.
$0.12 session · $0.48 today · opus-5.5 · gateway, from the usage each response reports (include_usage; the provider's cost when given). Today's total is shared across windows through~/.config/dawg/usage.json. Billed web searches count too. Subscriptions showsubscription; with no provider it readsno model · dawg login, the placeholder teaches direct commands and the STEER pill hides. The model shows once, in the header. dawg auth statuslists all four options with detected, active and validated state.
npm
@hraness/dawg is on npm: npm i -g @hraness/dawg or bun add -g @hraness/dawg, alongside the install script and the GitHub Release tarball.
UX review fixes
/track <name>(and baretrack <name>) focuses the track in this window, creating it when new; a track another window has open answers<name> is open in another window. The quickstarttrack drums→pattern kick …now works.- Receipts carry their outcome structurally, so
main is not a drum track,no kick hits,score is fulland friends render as errors instead of green checks. - An unknown
/wordis rejected locally and a known verb with bad arguments (pan 3,volume 2,add H4 at 0,/exportwith no file) gets usage; neither reaches the model. /help(or?) opens a grouped, scrollable overlay listing every command once;/sessionsand/tracksopen the same overlay and leave one summary card.dawg --version; unknown subcommands and options are rejected before.dawg/is created; the launch that creates.dawg/sayscreated .dawg/ · add it to .gitignore;dawg --session <typo>is an error instead of a silent new session;render --helpandsessions --help./resume <n>accepts any list index as well as a name or id prefix.- Errors share one shape,
<what> · <why> · <next step>(no such file · nope.json). - The
^z undohint rides only on receipts that changed the score, and only the first three in a session. - An empty track shows
main · empty · add C4 at 0 to startinstead of stray lane labels. /statussaysshared via dawgdorsaved locally · no daemon; auto-names announce as<name> (auto-named) · rename with /rename <name>.- Docs: header order frozen in a test and corrected, render is stereo, the
metaframe is listed, one environment table.
Agent workspace and web tools
The agent can now work with the project directory and the web. list_files and read_file cover the whole project except .dawg/; write_file and edit_file are limited to song.ts and the focused track's tracks/<slug>/ directory, write atomically and cap sizes. web_search answers through the AI Gateway's server-side search tools when a gateway key is configured (DAWG_WEB_SEARCH picks exa, perplexity, parallel or browserbase), through OpenRouter's web plugin when an OpenRouter key exists, and otherwise through DuckDuckGo; BRAVE_SEARCH_API_KEY overrides the chain. fetch_url reads one public page with private-address blocking and bounded output. The composition brief includes a bounded project tree and the head of the focused track's notes.md. New trackSlug() in core/slug.ts and an optional onWorkspaceWrite host hook.
Project files
dawg init [dir]creates a project:dawg.json,tsconfig.json,song.ts,tracks/, a vendored typed SDK in.dawg/sdk/v1.tsand.gitignorelines. It is idempotent and refreshes the SDK only for a newer 1.x.- Every window keeps
song.tsandtracks/<slug>/track.tsin two-way sync with the session: file edits apply as onefiles.applyrevision (applied from files · …, orfiles rejected · <file:line:col …>), and score edits reprint only the files that changed. dawg checktypechecks (native TypeScript 7, incremental) and evaluates the project; the header showstypes ✓ortypes ✗ N. Agent writes to project sources report the apply outcome and type errors in the tool result.- Score: optional
sampleron tracks (validated, rendered silent for now) andremoveTrack,moveTrack,setKey,setMeteroperations. The format staystrack.loop/v1. typescriptis now a runtime dependency; the package shipscore/sdk/**.
Local media tools
Six agent tools and dawg media <verb> turn reference audio into track material under tracks/<slug>/downloads/: download_audio (YouTube via yt-dlp or StemDeck, with a sidecar and reuse), split_stems (six stems via StemDeck or demucs), analyze_audio (tempo, key, beat grid, waveform), transcribe_notes (drums via a vendored classifier, pitched stems via basic-pitch, quantized to note()/hit() snippets), import_sample (48 kHz stereo samples/<name>.wav and a sampler() snippet) and transcribe_lyrics (whisper-cli). StemDeck at DAWG_STEMDECK_URL is preferred when it answers; dawg never installs a binary and dawg media doctor names the install commands. Helpers report progress on the activity card, are bounded in time and output, are stopped with SIGTERM then SIGKILL on Esc, and pause the turn deadline while they run.