Repository navigation
Releases: dedomorozoff/dmcode
Release list
v0.2.4
v0.2.4 is a provider release. The agent gets better instruments — it can fetch a
page, edit several files in one call, move and delete — and the provider you end
up on is no longer chosen by accident: the free ones come first, in the list and
in the automatic search, and a test fails the build if that stops being true.
?? Read this before trusting a provider
Pollinations moved, and the two hosts are not the same. The anonymous
text.pollinations.aihost is still free and still serves tool calls; the newer
gen.pollinations.aihost answers401 A valid API key is requiredthe moment
a request carriestools, and meters usage in pollen. Both are offered, on
purpose, and they are separate rows in/setupfor that reason.Nothing has been driven by a person for hours on any platform. The startup
path changed the most in this release and is covered by tests that drive the
wizard end to end, but a fresh machine going through setup by hand has not been
watched.
Install
# macOS / Linux / BSD
curl -fsSL https://raw.githubusercontent.com/dedomorozoff/dmcode/main/install.sh | bash# Windows (PowerShell)
irm https://raw.githubusercontent.com/dedomorozoff/dmcode/main/install.ps1 | iexOr from a checkout:
make install # -> $(go env GOBIN), or GOPATH/binOr with Go 1.26+:
go install github.com/dedomorozoff/dmcode@latestWhat's new since v0.2.3
ctrl+z takes the files back too
A rewind used to cut the conversation and leave the disk exactly as the turn had
left it. It now undoes the writes as well. Every tool write passes through one
journal: two writes to one file undo to where the first one found it, and a file
that was written, moved and then edited lands exactly where the turn began. An
mv or rm issued through run_command is recorded as not restored and says
so on the rewind line, because no content was ever captured for it — better a
line that admits it than a file restored to a guess.
The agent can fetch a page
fetch_url reads a URL and hands the page over as text. web_search was always
there for finding something; this is for reading what it found.
One call edits several files, and it can move and delete
move_file and delete_file exist because the only route before them was
mv/rm through the shell, which could report that something changed but could
not be inverted by the rewind. edit_file takes several edits in one call, so a
refactor across four files does not cost four round trips.
/setup is ordered free-first, and a test holds it there
This is the release's one rule: every option whose whole cost is zero is listed
above every option that can bill you. TestNoFreeOptionIsRankedBelowAPaidOne
fails the build if that ever stops being true, and it reads a model's own id for
cost as well as the Free flag — otherwise OpenRouter and Kilo, whose catalogue
is mixed but whose default model is :free, would be ranked as paid and a free
choice would sit below the line. Credits count as billable, because a credit that
runs out stops being free.
Pollinations, twice
The anonymous host answers with tools and no key; the newer gen.pollinations.ai
host has the large catalogue and needs a key (POLLINATIONS_API_KEY, signed up at
enter.pollinations.ai/keys) before it will
carry a single tool call. Most of its catalogue runs on the free Quest Pollen tier
with no card attached, so it meters rather than bills. The newer host is kept out
of the automatic search on purpose: its /models is open, so probing it with no
key would pass startup and then fail every real turn with a 401 that points at
the provider rather than at the missing credential.
Choosing a provider now starts
The startup wizard collected the whole configuration and then threw it away — the
provider was rebuilt from the environment, and the environment cannot carry two
things the wizard had just been told. A keyless option writes no
OPENAI_API_KEY, on purpose, so the key read back empty and every free option in
the list was answered with no provider configured. DMCODE_MODEL is kept out
of .env too, so the chosen model came back as a qwen default and was sent to
whichever host you had picked. The wizard now hands back the provider it
described. Picking a .gguf also starts llama-server instead of reporting that
nothing is configured.
A dead proxy says so
The one failure that makes every endpoint look dead at once is a proxy that no
longer answers, and nothing said so: the search found nothing, the wizard listed
seventeen options, and all seventeen failed identically for a reason that is one
line in your own settings file. dmcode now names the proxy before the wizard,
because "no free provider is configured" is a claim about providers that a dead
proxy makes false.
A proxy you typed is checked before it is remembered
/proxy <addr> used to save the address and apply it at once, which is how a
proxy that stopped running two weeks later ended up failing every turn with a
complaint about the provider. It is now verified first, and an address that does
not answer is put back to whatever was working before and named on screen. The
same goes for a failed request: instead of Go's own text — which mentions a proxy
once, in the middle of a wall of syscall detail — it says the proxy is not
answering and that the provider may be perfectly fine.
Tool output is prose again
A tool call in the transcript read as grep({"limit":20,"path":"","pattern":"..."})
and its answer as {"matches":"","next_offset":0,"offset":0,"returned":0,"total":0, "truncated":false} — the envelope, and none of the information. Both are now
written for a person: the line says what the call was about, and the result says
what came back. grep "pattern" in path, no matches, 3 of 17 — more from offset 3, wrote 148213 bytes, moved a.go → b.go, failed for a command that
did. A run_command verdict is pulled out of its output and given a line of its
own, because a bracketed marker twenty lines under build output is not a report.
Settings moved out of .env
Everything dmcode saves now lives in ~/.dmcode/settings.json. A .env in the
working directory is still read at start-up, so an older setup keeps working —
but nothing writes it any more.
/models scrolls
A window that grows used to show nothing at all.
Known limitations
Unchanged from v0.2.3 unless noted: sending a picture to a model is still
unverified end to end — attaching and previewing are confirmed on Windows against
a live session, but no picture has been through a real vision endpoint. The
editor's keys and the terminal panel are exercised by tests, not by a person over
a long session. A rewind does not rewrite ~/.dmcode/history.jsonl; cancellation
reaches the context but not the process; the workspace boundary is lexical, so a
symlink can escape it. See AGENTS.md for the full list and for what each entry
is worth.
Full Changelog: v0.2.3...v0.2.4
v0.2.3
dmCode v0.2.3
dmcode is a workspace now. The chat is still the agent you know; the same
window also holds a real code editor — project tree, git, a terminal, splits,
bookmarks and LSP — and switches between them with one key.
Install
# macOS / Linux / BSD
curl -fsSL https://raw.githubusercontent.com/dedomorozoff/dmcode/main/install.sh | bash# Windows (PowerShell)
irm https://raw.githubusercontent.com/dedomorozoff/dmcode/main/install.ps1 | iexOr from a checkout:
make install # -> $(go env GOBIN), or GOPATH/binOr with Go 1.26+:
go install github.com/dedomorozoff/dmcode@latestWhat's new since v0.2.2
The editor draws pictures instead of their bytes
Open a PNG and the editor used to show you the file's bytes as prose: a column
of ÿØ boxes, one per pixel row, that scrolled like source and — this is the
part that mattered — saved back over the picture. ctrl+s on a .png wrote
whatever was in the buffer into the file.
A picture is now a tab of its own kind. It is drawn where the lines would be,
at the pane's width, out of the same half-block renderer the chat's preview
uses — one picture, one drawing routine, rather than two that differ by which
half of dmcode opened it. There is no gutter: line numbers beside an image
describe a document that does not exist. The caption on the last row carries the
name, the dimensions, the format and — for an animation — which frame you are
looking at, because a GIF with no frame counter reads as a flicker.
A picture tab is read-only, and that is enforced rather than promised. Typing,
enter, backspace, paste and ctrl+s do not reach the buffer: the key
handler for a picture drops them before the editing switch every text tab falls
into, and a paste — which arrives as one message carrying the whole text, and
would walk straight past a check written for key presses — is refused where it
lands. The keys that are about the workspace rather than the file keep working:
switch tabs with alt+←/alt+→, scroll a tall picture with the wheel or
pgup/pgdn.
Animated GIFs play
A GIF is not a still with a .gif extension — the motion is the content, and a
preview of frame zero is a preview of a video in the same sense. Every frame is
decoded and composited at open, then advanced on a timer.
Compositing is the part that is easy to leave out and obvious once you know: a
GIF frame is the rectangle that changed, drawn over the frame before it, and
the disposal method says whether the canvas is put back afterwards. Rendering
the frames as decoded shows an empty cell for every partial frame, which in a
real animation is most of them — so each frame is drawn onto the canvas the last
one left behind, with DisposalBackground and DisposalPrevious honoured.
The timer is one tea.Tick per frame, each scheduling the next, rather than a
goroutine with a clock of its own. That is what makes the animation stop by
itself: a closed tab, a switch to another file, or the chat screen covering the
editor all mean there is nothing to advance, and the chain ends with no cancel
path to get wrong. A flag keeps one animation from becoming two chains — every
message asks for a frame, and without it the picture would run at twice the
speed its own delays declare. A frame delay the file asks for is honoured, with
a floor and a ceiling: a GIF may declare zero, which is a strobe rather than a
picture, and a declared ten seconds is a picture that looks frozen.
Frames are capped at 240, and a file over 64 MB opens as text with the reason
on the status line — a worse view of a picture, and a far better outcome than
pulling an arbitrarily large file into memory.
A file named .png that is not one opens as text, and says why
The extension is the cheap first question and the bytes are the real one, so a
.png that does not decode is not a tab that refuses to open: it is the text it
actually is, with one line in the status line naming the reason. The same is true
of the other direction — a picture overwritten with text reloads as text rather
than going on showing the frame of a file that no longer exists.
WebP is in the list of extensions worth trying and has no decoder in this build,
exactly as in the chat: you get a named refusal rather than a filename being
treated as prose.
The status bar stops describing a document that is not there
Ln 1, Col 1 under a rendered image is a cursor that is nowhere, and LF UTF-8
is a question nobody asks about a PNG. A picture tab reports its dimensions and
format instead, in the single pane and in each pane of a split alike.
Everything else from v0.2.2 stands
The rest of what shipped in v0.2.2 is unchanged and still current. In brief: one
file arriving under two names and the code comparing them — which made the
workspace refuse its own files, made git operations fail on files that were
plainly in the tree, made the editor open a file twice, and made saving not
reload the tab, all fixed through internal/pathnorm; a closed terminal that
left its shell holding the project directory; a white picture that drew as a
field of dots because Go fuses multiply-add on arm64; Cline as a first-class
provider in /setup; and /setup no longer outgrows a 22-row terminal.
One file has more than one name
CI grew macOS and Windows runners in that release, and on the first run eight
tests failed. All eight were the same thing: the same file arrived under two
names, and the code compared the names. On a developer's machine those names
are the same string — the temp directory, the checkout and the home directory
are all spelled the one way the process already spells them — so nothing here
was ever visible locally. Change where either side comes from, and it appears:
t.TempDir()says/var/folders/...,os.Getwd()says
/private/var/folders/....- A Windows runner's
%TEMP%isC:\Users\RUNNER~1, and the repository it
walks up into isC:\Users\runneradmin. - A symlinked checkout is not the directory it points at.
What that cost, before this release:
- The workspace refused its own files.
write_fileinto a path inside your
project could be turned away as outside the working directory, naming a
directory the file was in. On macOS and on a Windows runner, this was
constant rather than occasional. - Git operations failed on files that were plainly in the tree. Staging,
unstaging, blame and the gutter's HEAD content all computed
/private/var/.../a.goagainst a root of/var/..., got../../.., and go-git
refused it:invalid path "../../..": cannot use "..". The error arrives in
plumbing vocabulary, several layers from the spelling that caused it. It now
names both directories, in dmcode's words. - The editor opened the same file twice. A path typed by hand, a restored
session and a row clicked in the finder all spell it differently, so
ctrl+oon a file already open gave you a second tab, and every "is this
already open" question downstream was wrong along with it. - Saving a file did not reload its tab. The watcher reports the name the
operating system gave it; the tab holds the name the editor was opened with.
internal/pathnorm is the answer in two functions — Canonical puts a path in
the spelling this machine agrees on (links followed, 8.3 short name and letter
case on Windows, missing tail appended for a file that does not exist yet), and
Same is the comparison. The workspace boundary, the git layer, and the
editor's tab list and file watcher all ask it now, and the boundary still refuses
a symlink that leaves the tree — the retry is a fix for the spelling, not a way
out of the check.
Closing a terminal now ends the shell
ClosePseudoConsole signals the console and returns; it does not terminate the
attached process. Closing the process handle straight afterwards only drops our
reference to something still running. The shell lives on, a shell holds its
working directory open, and a directory with a live handle in it cannot be
deleted — so quitting the editor could leave a cmd.exe behind holding your
project directory. The child is now terminated by pid and waited for, with a
bounded wait: a shell that somehow refuses to die must not hang the UI on
shutdown.
A white picture stopped drawing as a field of dots
The ASCII preview picked its cell with three float multiplies, and Go fuses
multiply-add on arm64 — one rounding where an x86 build does three. For pure
white the fused sum lands a hair under 255, int() truncates rather than
rounds, and the brightest pixel in the picture drew as the second-darkest glyph
on a ramp that ends in space. An all-white image rendered as the emptiest
possible picture on a Mac; a pale photograph came out one step too dark. It is
integer arithmetic now, and the ramp length is folded into the numerator rather
than dividing twice.
One more provider, and /setup fits a small terminal
- Cline is a first-class option in
/setup. It is a gateway rather than a
client-gated service:api.cline.botserves plain OpenAI/chat/completions
with tool calling, and wants an ordinary key fromapp.cline.bot. Nothing
about it is faked, which is why it sits next to the others. The default model
isanthropic/claude-sonnet-4-6, because that is what Cline's docs name for
coding. /setupno longer outgrows the screen. The panel's height budget counted
its header as three rows when the header is one row that wraps to two at 72
columns, so on a 22-row terminal the bottom border fell off. The header is
measured now rather than counted — which fixes the palette and the model
picker too, since all three go through the same helper. Sixteen ...
v0.2.2
dmCode v0.2.2
A patch release, and most of it is one bug wearing four hats.
CI grew macOS and Windows runners since v0.2.1, and the first run on macOS failed
eight tests. All eight were the same thing: the same file arrived under two names
and the code compared the names. On a developer's machine those names are one
string - the temp directory, the checkout and the home directory are all spelled
the way the process already spells them - so nothing here was ever visible
locally.
What it cost:
- The workspace could refuse its own files: write_file into a path inside your
project turned away as outside the working directory, naming the directory the
file was in. On macOS and on a Windows runner this was constant. - Git operations failed on files plainly in the tree. Staging, unstaging, blame
and head content computed /private/var/... against a root of /var/..., got
../../.., and go-git refused it as invalid path "../../..": cannot use "..". - The editor opened the same file in a second tab, and a save did not reload its
tab, because the watcher reports the name the OS gave it and the tab holds the
name the editor was opened with.
internal/pathnorm fixes it in two functions and the boundary still refuses a
symlink that leaves the tree - the retry is a fix for the spelling, not a way
out of the check.
Also in this release:
- Closing a terminal now terminates the shell instead of only signalling the
pseudo console, which was leaving a cmd.exe holding your project directory. - A white picture stopped drawing as a field of dots: Go fuses multiply-add on
arm64, the luminance landed a hair under 255, and truncation picked the cell
short of the ramp's end. Integer arithmetic now. - Cline joins the providers as a real gateway, and /setup fits a 22-row terminal
because the panel's header is measured rather than counted. - OpenCode Zen's default model is big-pickle.
- Every list marks the row under the cursor with the same glyph.
- Both installers verify the release's SHA-256 sums before installing. That is
integrity against a corrupt download, not a signature; releases are still
unsigned. - CI runs the suite on macOS and Windows as well as Linux, under -race where a C
toolchain is available, and golangci-lint gates the push.
Still unverified, unchanged from v0.2.1: nothing here has been driven by a
person for hours, and no picture has been through a real vision endpoint. See
RELEASE_NOTES.md.
Full changelog: v0.2.1...v0.2.2
Full notes: https://github.com/dedomorozoff/blob/v0.2.2/RELEASE_NOTES.md
v0.2.1
dmCode v0.2.1
dmcode is a workspace now. The chat is still the agent you know; the same
window also holds a real code editor — project tree, git, a terminal, splits,
bookmarks and LSP — and switches between them with one key.
⚠️ Two things to read before trusting themThe editor's keys and the terminal panel have been exercised, but not
driven for hours by a person. The frames, the mouse arithmetic and the
chat-mode composition are covered by tests that assert the row counts and
the hit-testing; what is not proven is how the editor feels over a long
session, or how the panels behave on platforms other than the one this was
built on.Sending a picture to a model is still unverified. Attaching and
previewing are confirmed working on Windows against a live session; no
picture has been through a real vision endpoint, so whether theimage_url
data URL dmcode puts on the wire is one a live endpoint accepts is untested.
Please report what breaks.
Install
# macOS / Linux / BSD
curl -fsSL https://raw.githubusercontent.com/dedomorozoff/dmcode/main/install.sh | bash# Windows (PowerShell)
irm https://raw.githubusercontent.com/dedomorozoff/dmcode/main/install.ps1 | iexOr from a checkout:
make install # -> $(go env GOBIN), or GOPATH/binOr with Go 1.26+:
go install github.com/dedomorozoff/dmcode@latestWhat's new since v0.2.0
Three fixes from the workspace's first days, no new surface:
ctrl+ein the editor now returns to the chat — the key that opened the
editor toggles both ways.ctrl+qstill works,F1keeps the help panel,
and the help table and the status hints name the new binding.- The project tree no longer opens itself when the editor is entered. It
answersctrl+b,F9or its own status-bar icon, and otherwise stays
where the user last left it. - Status-icon tooltips no longer freeze over the transcript. Chat mode keeps
tracking the icon strip on mouse motion, so a callout left over from the
editor mode clears on the first move instead of sitting on the screen with
nothing to remove it. ctrl+qquits dmcode from the chat — the key the sidebar has advertised
all along actually works now. Inside the editor it still means "back to
the chat"; from the chat the key ends the program, the wayctrl+cdid
on an empty prompt.- A multi-cursor crash reported from the field is fixed: a line join moved
the lines under the cursors below it without moving the cursors, and the
next backspace sliced past the end of an empty line, taking the whole
session down. Joins now run strictly bottom-up regardless of cursor
order — which also un-skips the higher of two joins — and shift every
cursor at or below the joined line; a stale cursor is clamped instead of
panicking. - Every
edit_fileandwrite_filenow draws what it changed in the
transcript: a header naming the file, real line numbers from the old and
the new text in a gutter, and the code highlighted with the editor's
syntax theme over a dim green or red wash for the added and removed rows. - A
write_filecut off by the model's output limit no longer loses the
turn when the model had narrated first: the re-ask goes ahead, preceded
by a blank line, with a concrete recipe for writing the file in chunks.
When the turn really cannot be saved, the error says what to ask for
next. - The editor's status notes (config reloaded, terminal exited and friends)
no longer surface on the chat screen; the open git panel keeps its
context line. /newand/sessionsnow close the workspace's clean tabs, so the
nextctrl+estarts from a bare editor rather than the previous
conversation's files. A tab with unsaved typing survives the reset.- A fresh program start opens a bare editor too: the workspace no longer
restores the tabs the last run saved. The standalone editor keeps its
session restore.
v0.2.0
dmCode v0.2.0 — the chat and an editor, in one window
ctrl+e opens the editor, ctrl+q puts it back. One window instead of two: the transcript fills the main area when the editor is closed, and the panels work in both modes.
What came across is the editor body — project tree, git panel with inline diffs and blame, a real PTY, splits, bookmarks, LSP. Its AI panel, ghost text and debug panel are gone along with the agent that had them: dmcode has its own.
The status bar
Four clickable icons: editor, tree, git, terminal. The editor one is first, because it changes the whole screen rather than opening a panel, so that is where it belongs. It is also the only icon whose state is a mode rather than a panel's.
In chat mode the bar carries the icons and the git branch, and nothing else. Ln, Col, the encoding, the language tag and the F1 hint are gone: they described a file nobody can see on that screen, and they took the space next to the only part of that row a user can act on. In the editor mode all of it comes back — a file's own state belongs to the screen the file is open on.
Also in this release
- Pictures. Drag a screenshot into the prompt, paste one from the clipboard (
ctrl+v, Windows), or attach one with/image. The preview is drawn in colour before you send it. Reduction to 1568px happens before the drawing, so what goes on the wire is exactly what is on screen. - Rewind and sessions.
ctrl+zbrings the last prompt back and takes the answer with it;/sessionsswitches between conversations. - Yolo mode —
shift+tab: the agent stops stopping to ask. - Mouse selection, copied on release, with
/mouseto turn it off. - Two tools that did nothing.
ask_userandtodo_writecompiled, passed their tests and never ran: the argument schema is generated from the Go type, so a nested object meant every shape a model sent was refused. Both are flat now, every field described.
Fixes
- The chat frame was one row short with the terminal docked, so the status bar sat a character above the bottom edge.
- The tests wrote to your clipboard: every copy and cut test left its own text there. The clipboard is now swapped for the duration of a test, and a test fails if anything writes to it directly again.
- The
F1help had rotted — a duplicated row, a pointer to a key that does not exist, andF12,Ctrl+SpaceandCtrl+/missing. A test now holds it against the catalog.
What is not verified
Stated plainly: the editor's keys and panels are covered by tests on frames and mouse handling but have not been driven for hours by a person, and a picture still has not been through a real vision endpoint.
Install
curl -fsSL https://raw.githubusercontent.com/dedomorozoff/dmcode/main/install.sh | bash
irm https://raw.githubusercontent.com/dedomorozoff/dmcode/main/install.ps1 | iex
Full notes: RELEASE_NOTES.md
v0.1.7
What's Changed
- Three tools that did nothing, a yolo mode, and selection that copies by @dedomorozoff in #3
Full Changelog: v0.1.6...v0.1.7
v0.1.6
Release v0.1.6: an agent that reaches the web
The headline: dmcode is no longer confined to the workspace for its answers. The agent can search the web and can call
external MCP servers, and the frame learned to show what a turn is doing without stealing a row from the interface.
web_search
Deliberately plain: DuckDuckGo's HTML endpoint, no key, no JavaScript. Titles, URLs and snippets come back parsed and
capped; an empty page is reported as such instead of passing silence off as an answer. The tool is read-only, so plan
mode and sub-agents get it too.
MCP support
Reads the common mcpServers format — the user's ~/.dmcode/mcp.json and the workspace .mcp.json, a stdio command
or a streamable-HTTP url — and connects lazily through the ADK toolsets: a server is started only when a turn needs
its tools and reconnects on its own when it dies between turns. One eager listing puts the tool names in the sidebar and tells the user which server did not come up.
The frame
• The progress note moved out of the status bar into the transcript itself, right under the last message, where the work it describes is happening.
• The header yields its rows to the transcript while the sidebar shows the same facts, and now carries the brand — the
block art and a right-aligned build version.
• The sidebar got two columns wider, its tool list became a wrapped comma list instead of a row per name, and a terminal too short for everything drops the hotkeys first and keeps the plan.
v0.1.5
v0.1.4
dmCode v0.1.4
The interface gets out of the way, there are more providers that are genuinely
free, and dmcode works behind a proxy.
Install
# macOS / Linux / BSD
curl -fsSL https://raw.githubusercontent.com/dedomorozoff/dmcode/main/install.sh | bash# Windows (PowerShell)
irm https://raw.githubusercontent.com/dedomorozoff/dmcode/main/install.ps1 | iexOr from a checkout:
make install # -> $(go env GOBIN), or GOPATH/binOr with Go 1.26+:
go install github.com/dedomorozoff/dmcode@latestWhat's new since v0.1.3
Four more free providers
The setup wizard now offers Cerebras, NVIDIA NIM, SambaNova and
Hugging Face. All four issue a working OpenAI-compatible key on signup with no
card attached, and each was probed live before being listed — /v1/models answers,
so a key from the signup page is all it takes to reach the agent. NVIDIA NIM alone
serves eighty-odd models, including GLM, Kimi and Nemotron.
GitHub Models and Mistral were already picked up from a key in the
environment but were missing from the wizard, so they are offered there too.
The preset table and the wizard are two hand-maintained copies of the same
endpoints, free to drift with nothing to show it: a user with NVIDIA_API_KEY
exported would run on one model, and the same user picking NVIDIA in /setup on
another. The preset list is now a named function and a test compares it against the
wizard by URL, model and wire.
The status bar says what matters
It carried the model name, a run of hotkeys, the mode and the state. The model was
the third place on screen showing it — the header and the sidebar already have it —
and the hotkey list was a fixed string competing for one row, so on a narrow
terminal it was the part that dropped off, leaving ctrl+p │ ctrl+y │ esc half
printed. Neither earns its place.
The bar is now the mode badge leading, then the state, then whatever status fits.
The mode leads because it decides whether the agent is allowed to touch your files,
so it is the one thing that must never be hunted for.
tab means one thing
It completed a suggestion inside a / command and switched mode everywhere else —
the same key doing two unrelated things depending on what happened to be in the
input. The command list is now picked with the arrows, which leaves tab meaning
one thing everywhere: the mode.
The command list is a dialog
Typing / opens a bordered dialog just above the input, in the same chrome as
ctrl+p. ↑ ↓ move through it, enter runs the highlighted command, esc
closes it and keeps what you typed.
It is drawn over the chat panel rather than taking rows from it. As rows of the
frame the list shrank the transcript every time a / was typed and jumped back when
it closed, and on a short terminal it ran into the bottom of the screen with the
input box still to fit. The frame is now identical with the dialog open and closed.
Only the chat panel's columns are rewritten, so the sidebar — model, folder, tools —
stays readable while you pick.
The sidebar reports what changed
The SESSION block now carries a file count and a signed line count:
SESSION
sess-1448381972…
turns: 4
tools: 11
files: 3
+128 -34
The numbers come from the tools, not the interface: every write passes through one
place, and it is the only place that knows a file's before and after. The tally is
diffed against the content a file had when the session first touched it, so an agent
that rewrites one line three times has changed one line — the sidebar reports the
end state, not the agent's steps. The block stays hidden until something has
changed, and /new clears it.
Behind a proxy
HTTP_PROXY, HTTPS_PROXY and NO_PROXY are honoured, so a proxy already
configured for the rest of the system works with no setup. /proxy shows the
setting, changes it, and says whether the provider answers through it:
/proxy show what is in effect
/proxy 127.0.0.1:3128 set one (a bare host:port is fine)
/proxy off clear it
/proxy no localhost,127.0.0.1 bypass list, without touching the proxy
The change applies to the running session and is written to .env, so it survives
a restart. Two details worth knowing:
- Go's own
http.ProxyFromEnvironmentreads the environment once, on the first
request, and caches it — a proxy set from inside the program would appear to do
nothing until a restart. dmcode consults the proxy per request instead, which is
what makes/proxytake effect immediately. /proxy offremoves the variables from.envrather than just ceasing to
write them. A proxy left in the file would be read back on the next launch and
silently come back.
localhost and the loopback addresses are never proxied: a proxy that cannot reach
the machine's own services is common enough that following it would break local
providers that otherwise work.
Assets
Binaries for linux, darwin, freebsd, openbsd and netbsd on amd64
and arm64, plus windows-amd64 as both a raw .exe and a zip. Packages:
.deb, .rpm, Arch .pkg.tar.zst, and a termux tarball.
Known limitations
- No cancellation yet:
Escinterrupts a turn, but a running shell command is
not cancellable. - Switching models starts a new session, so conversation history is dropped.
- The transcript is re-rendered on every streamed token, so a long session can
lag. The fix is an incremental per-line cache. - The workspace boundary is a lexical check. A symlink inside the directory can
still reach outside it, becausewrite_filemust be allowed to create files
that do not exist yet and cannot be resolved first. - The change tally counts what passed through the tools. A file changed by
run_commandis not counted, and neither is a change made outside the session.
v0.1.3
What's Changed
- Release v0.1.3: markdown replies, mouse wheel, plan mode by @dedomorozoff in #1
New Contributors
- @dedomorozoff made their first contribution in #1
Full Changelog: v0.1.2...v0.1.3