Skip to content

v0.3.0

Choose a tag to compare

@github-actions github-actions released this 07 Oct 05:06
· 34 commits to main since this release
Immutable release. Only release title and notes can be modified.
f6b167c

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)

  • /play or 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 P the black keys, Z/X move an octave, C/V change 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. R arms overdub and Shift-R replaces the bar; notes are quantized to /grid and land as ordinary score edits, so other windows, undo and track.ts all see them. One undo step per recorded bar.
  • Click track. M or /click on|off|<volume> toggles a tempo-synced metronome that never reaches renders or exports; /count-in 0|1|2 sets 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 or j k move, Enter opens or toggles, ← → or + - nudge, digits type a value, / filters, x deletes 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> and meter <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 render and exports, with Strudel's semantics: begin/end windows, speed (negative reverses), loop for the note's length, gain, choke groups (Strudel's cut), keyed repitching from root, 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 ffmpeg when it is on PATH, 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 stale sha256 warns and still plays.
  • /sample <path> [as <voice>] adds a voice to the focused track (copying the file into tracks/<slug>/samples/ and reprinting track.ts); /sample lists voices. Oneshot samplers get one highway lane per voice, /tracks shows sample counts and missing files, and load problems are receipts.
  • dawg render in a project with no --session renders the project files (song.ts).

Sign-in, model picker and spend

  • One sign-in picker. dawg login (and the first dawg with 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 asks Use <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.1 callback 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 runs xcb setup <family> when none is ready. An account is usable iff xcb reports available. Pending admission works, with a longer first call (the child timeout is timeoutMs + 75 s) and busy retried with backoff. Accounts reporting models_unavailable are refreshed once, and dawg auth status prints 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 /model change it. A saved provider that stops working is reported once and reopens the picker, never swapped.
  • /login in the TUI suspends the screen, runs the same flow (browser and vercel login included) and redraws, replacing "run dawg login in a shell".
  • Model picker. /model or dawg model lists 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/prompt from models.dev pricing (cached 24 h; OpenRouter's own prices on OpenRouter), and subscriptions list xcb's models as included. 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 show subscription; with no provider it reads no model · dawg login, the placeholder teaches direct commands and the STEER pill hides. The model shows once, in the header.
  • dawg auth status lists 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 bare track <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 quickstart track drums → pattern kick … now works.
  • Receipts carry their outcome structurally, so main is not a drum track, no kick hits, score is full and friends render as errors instead of green checks.
  • An unknown /word is rejected locally and a known verb with bad arguments (pan 3, volume 2, add H4 at 0, /export with no file) gets usage; neither reaches the model.
  • /help (or ?) opens a grouped, scrollable overlay listing every command once; /sessions and /tracks open 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/ says created .dawg/ · add it to .gitignore; dawg --session <typo> is an error instead of a silent new session; render --help and sessions --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 undo hint 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 start instead of stray lane labels.
  • /status says shared via dawgd or saved 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 meta frame 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.ts and .gitignore lines. It is idempotent and refreshes the SDK only for a newer 1.x.
  • Every window keeps song.ts and tracks/<slug>/track.ts in two-way sync with the session: file edits apply as one files.apply revision (applied from files · …, or files rejected · <file:line:col …>), and score edits reprint only the files that changed.
  • dawg check typechecks (native TypeScript 7, incremental) and evaluates the project; the header shows types ✓ or types ✗ N. Agent writes to project sources report the apply outcome and type errors in the tool result.
  • Score: optional sampler on tracks (validated, rendered silent for now) and removeTrack, moveTrack, setKey, setMeter operations. The format stays track.loop/v1.
  • typescript is now a runtime dependency; the package ships core/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.