<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>coding.sh blog</title>
  <id>https://coding.sh/blog</id>
  <link href="https://coding.sh/blog"/>
  <link rel="self" href="https://coding.sh/blog.xml"/>
  <updated>2026-11-08T00:00:00Z</updated>
  <author><name>coding.sh</name></author>
  <entry>
    <title>Coding agents run in your shell</title>
    <id>https://coding.sh/blog/agents-run-in-your-shell</id>
    <link href="https://coding.sh/blog/agents-run-in-your-shell"/>
    <published>2026-11-08T00:00:00Z</published>
    <updated>2026-11-08T00:00:00Z</updated>
    <summary>Claude Code, Codex and Cursor do their real work as shell commands. coding.sh captures them at the shell, through a hook under 50 ms.</summary>
    <content type="html">&lt;p&gt;The &lt;code&gt;.sh&lt;/code&gt; in coding.sh is not a pun. A coding agent does its real work in a shell. It reads files, runs tests, greps the tree, stages a commit, starts a server — all of it as shell commands the model decides to run. The model's prose is a plan; the shell is where the plan becomes an action that can pass, fail, or change your repo. That is the layer worth watching, and it is the layer coding.sh records.&lt;/p&gt;&lt;h2 id="where-the-work-and-the-risk-are"&gt;Where the work and the risk are&lt;/h2&gt;&lt;p&gt;Open any agent session and the shape is the same. A few prompts, a lot of tool calls, and most of the tool calls are shell commands. &lt;code&gt;npm test&lt;/code&gt;. &lt;code&gt;git diff&lt;/code&gt;. &lt;code&gt;rg TODO&lt;/code&gt;. A one-liner that edits a file in place. The interesting moments are all here: the command that failed and got retried without a change, the test that went green only after a test file was edited, the &lt;code&gt;rm&lt;/code&gt; that was wider than it looked, the key that ended up on a command line.&lt;/p&gt;&lt;p&gt;coding.sh reads each shell command the agent ran, pairs it with its result, and keeps the diff of what changed. From that it can tell you plain things that are tedious to reconstruct by hand: which commands failed, which ones changed files without a test running afterward, and whether a passing test run was earned or edited into passing.&lt;/p&gt;&lt;h2 id="a-hook-not-an-integration"&gt;A hook, not an integration&lt;/h2&gt;&lt;p&gt;coding.sh captures through a hook — a small process the agent's own harness runs after each step. The rules it follows are shell-citizen rules. It exits 0 so it can never break the agent. It prints nothing unless the protocol needs a reply. It does no network and no git work. It writes the raw payload to a spool file and returns, well under 50 ms, so the agent never waits on it.&lt;/p&gt;&lt;p&gt;That is the whole install for Claude Code, Codex, Gemini CLI and the rest: a hook line in the agent's config, added by &lt;code&gt;coding.sh init&lt;/code&gt;. No SDK, no wrapper around your commands, no proxy in the default path. The agent keeps running exactly as it did.&lt;/p&gt;&lt;h2 id="it-is-a-cli-so-it-lives-where-you-do"&gt;It is a CLI, so it lives where you do&lt;/h2&gt;&lt;p&gt;The tool is a command. You read sessions, usage and diffs in your terminal, next to the agent that produced them. &lt;code&gt;coding.sh sessions&lt;/code&gt;, &lt;code&gt;coding.sh usage --by model&lt;/code&gt;, &lt;code&gt;coding.sh show &amp;lt;id&amp;gt;&lt;/code&gt;. The viewer is the same binary: &lt;code&gt;coding.sh ui&lt;/code&gt; serves a local page on 127.0.0.1 when you want to click through a replay, and closes when you are done.&lt;/p&gt;&lt;p&gt;Underneath, coding.sh has zero runtime dependencies. Everything is Node's own built-ins — the SQLite index through &lt;code&gt;node:sqlite&lt;/code&gt;, compression through &lt;code&gt;node:zlib&lt;/code&gt;. Install is one line:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;curl -fsSL https://coding.sh/install.sh | sh&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A tool for agents that work in the shell should be a shell tool. That is the one coding.sh tries to be.&lt;/p&gt;</content>
  </entry>
  <entry>
    <title>Why coding.sh is local-first</title>
    <id>https://coding.sh/blog/why-local-first</id>
    <link href="https://coding.sh/blog/why-local-first"/>
    <published>2026-11-07T00:00:00Z</published>
    <updated>2026-11-07T00:00:00Z</updated>
    <summary>Agent sessions hold your prompts, your code and sometimes your keys, so coding.sh keeps them on your machine unless you choose to send them.</summary>
    <content type="html">&lt;p&gt;An agent session is a record of your work. It holds your prompts, the model's replies, every tool call with its input and output, and the files that changed. Sometimes it also holds a key that ended up in a shell command. coding.sh records all of that, so where the record lives matters.&lt;/p&gt;&lt;h2 id="one-folder-on-your-machine"&gt;One folder on your machine&lt;/h2&gt;&lt;p&gt;Everything coding.sh captures goes into &lt;code&gt;~/.coding.sh&lt;/code&gt;. Set &lt;code&gt;CODING_SH_HOME&lt;/code&gt; to put it somewhere else. Sessions and events go into a SQLite index, &lt;code&gt;index.db&lt;/code&gt;, through Node's built-in &lt;code&gt;node:sqlite&lt;/code&gt;. Large prompts and tool output go into &lt;code&gt;blobs/&lt;/code&gt;, compressed with zstd.&lt;/p&gt;&lt;p&gt;The viewer runs from the same CLI. It binds to 127.0.0.1 and asks for the token in the URL it prints.&lt;/p&gt;&lt;h2 id="nothing-is-sent-unless-you-ask"&gt;Nothing is sent unless you ask&lt;/h2&gt;&lt;p&gt;With no sinks configured, no session data leaves your machine. The one default network request is a daily update check: a GET to &lt;code&gt;api.github.com&lt;/code&gt;, falling back to &lt;code&gt;registry.npmjs.org&lt;/code&gt;. Set &lt;code&gt;CODING_SH_NO_UPDATE_CHECK=1&lt;/code&gt; or &lt;code&gt;[update] check = false&lt;/code&gt; to turn it off.&lt;/p&gt;&lt;p&gt;The hook that agents run writes the raw payload to &lt;code&gt;~/.coding.sh/spool&lt;/code&gt; with mode 600 and exits. It does no network work.&lt;/p&gt;&lt;p&gt;To send events somewhere else, add a sink. A sink sends metadata unless you set &lt;code&gt;mode = &amp;quot;full&amp;quot;&lt;/code&gt;: ids, timestamps, kinds, durations, token counts and short labels. Prompts, code and tool output stay on your machine.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;# ~/.coding.sh/config.toml
[[sinks]]
kind = "otlp"                      # or "http"
endpoint = "http://localhost:4318"
mode = "metadata"                  # default; "full" sends content&lt;/code&gt;&lt;/pre&gt;&lt;h2 id="secrets-are-redacted-before-they-are-written"&gt;Secrets are redacted before they are written&lt;/h2&gt;&lt;p&gt;Redaction runs at ingest, before anything reaches the index, the blobs or a sink. The built-in rules catch private keys, vendor API keys and tokens, JWTs, and bearer and basic auth.&lt;/p&gt;&lt;p&gt;Redaction keeps a secret out of the store, but the agent and its model provider still saw it. So a session that held a vendor token gets the &lt;code&gt;secret:exposed&lt;/code&gt; tag, and this lists the sessions whose credentials to rotate:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;coding.sh sessions --flag secret:exposed&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;If a value got past redaction, &lt;code&gt;coding.sh purge --literal leaked.txt&lt;/code&gt; replaces every stored copy with &lt;code&gt;[redacted:purged]&lt;/code&gt;.&lt;/p&gt;&lt;h2 id="a-cloned-repo-cannot-widen-it"&gt;A cloned repo cannot widen it&lt;/h2&gt;&lt;p&gt;A &lt;code&gt;.coding.sh/config.toml&lt;/code&gt; inside a repository can add redaction rules or switch capture to metadata mode. It cannot add sinks or move the store. Opening a repository you cloned does not send your sessions anywhere.&lt;/p&gt;&lt;h2 id="read-more"&gt;Read more&lt;/h2&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="https://github.com/jackulau/coding.sh/blob/main/docs/guides/privacy.md"&gt;Storage, sinks and privacy&lt;/a&gt; lists everything that is captured and every setting that changes it.&lt;/li&gt;&lt;li&gt;&lt;a href="https://coding.sh/docs/concepts"&gt;Concepts&lt;/a&gt; defines the words coding.sh uses: sessions, events, flags and the rest.&lt;/li&gt;&lt;/ul&gt;</content>
  </entry>
</feed>
