Add comma formatting to input and output token displays

The token counts now display with commas in both the terminal UI and web UI.

Co-Authored-By: sketch <hello@sketch.dev>
diff --git a/go.mod b/go.mod
index 329b50d..a0a8383 100644
--- a/go.mod
+++ b/go.mod
@@ -19,6 +19,7 @@
 
 require (
 	github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be // indirect
+	github.com/dustin/go-humanize v1.0.1 // indirect
 	github.com/gliderlabs/ssh v0.3.8 // indirect
 	github.com/kevinburke/ssh_config v1.2.0 // indirect
 	github.com/mattn/go-colorable v0.1.13 // indirect
diff --git a/go.sum b/go.sum
index 7892add..94f5f98 100644
--- a/go.sum
+++ b/go.sum
@@ -4,6 +4,8 @@
 github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE=
 github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
 github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY=
+github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto=
 github.com/evanw/esbuild v0.25.2 h1:ublSEmZSjzOc6jLO1OTQy/vHc1wiqyDF4oB3hz5sM6s=
 github.com/evanw/esbuild v0.25.2/go.mod h1:D2vIQZqV/vIf/VRHtViaUtViZmG7o+kKmlBfVQuRi48=
 github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM=
diff --git a/termui/termui.go b/termui/termui.go
index 32f3300..36e5701 100644
--- a/termui/termui.go
+++ b/termui/termui.go
@@ -16,6 +16,7 @@
 	"text/template"
 	"time"
 
+	"github.com/dustin/go-humanize"
 	"github.com/fatih/color"
 	"golang.org/x/term"
 	"sketch.dev/loop"
@@ -206,8 +207,8 @@
 		case "usage", "cost":
 			totalUsage := ui.agent.TotalUsage()
 			ui.AppendSystemMessage("💰 Current usage summary:")
-			ui.AppendSystemMessage("- Input tokens: %d", totalUsage.TotalInputTokens())
-			ui.AppendSystemMessage("- Output tokens: %d", totalUsage.OutputTokens)
+			ui.AppendSystemMessage("- Input tokens: %s", humanize.Comma(int64(totalUsage.TotalInputTokens())))
+			ui.AppendSystemMessage("- Output tokens: %s", humanize.Comma(int64(totalUsage.OutputTokens)))
 			ui.AppendSystemMessage("- Responses: %d", totalUsage.Responses)
 			ui.AppendSystemMessage("- Wall time: %s", totalUsage.WallTime().Round(time.Second))
 			ui.AppendSystemMessage("- Total cost: $%0.2f", totalUsage.TotalCostUSD)
@@ -216,8 +217,8 @@
 			// Display final usage stats
 			totalUsage := ui.agent.TotalUsage()
 			ui.AppendSystemMessage("💰 Final usage summary:")
-			ui.AppendSystemMessage("- Input tokens: %d", totalUsage.TotalInputTokens())
-			ui.AppendSystemMessage("- Output tokens: %d", totalUsage.OutputTokens)
+			ui.AppendSystemMessage("- Input tokens: %s", humanize.Comma(int64(totalUsage.TotalInputTokens())))
+			ui.AppendSystemMessage("- Output tokens: %s", humanize.Comma(int64(totalUsage.OutputTokens)))
 			ui.AppendSystemMessage("- Responses: %d", totalUsage.Responses)
 			ui.AppendSystemMessage("- Wall time: %s", totalUsage.WallTime().Round(time.Second))
 			ui.AppendSystemMessage("- Total cost: $%0.2f", totalUsage.TotalCostUSD)
diff --git a/webui/__snapshots__/web-components/sketch-app-shell.test.ts-snapshots/sketch-app-shell-basic.aria.yml b/webui/__snapshots__/web-components/sketch-app-shell.test.ts-snapshots/sketch-app-shell-basic.aria.yml
index 439bc19..2c109f0 100644
--- a/webui/__snapshots__/web-components/sketch-app-shell.test.ts-snapshots/sketch-app-shell-basic.aria.yml
+++ b/webui/__snapshots__/web-components/sketch-app-shell.test.ts-snapshots/sketch-app-shell-basic.aria.yml
@@ -2,7 +2,7 @@
 - heading "Add a line to dummy.txt and commit the change" [level=2]
 - link "Logs"
 - link "Download"
-- text: "/MacBook-Pro-9\\.local \\/Users\\/pokey\\/src\\/spaghetti Origin: git@github\\.com:pokey\\/spaghetti\\.git Commit: a6c5a08a Msgs: \\d+ Input tokens: \\d+ Output tokens: \\d+ Cost: \\$\\d+\\.\\d+/"
+- text: "/MacBook-Pro-9\\.local \\/Users\\/pokey\\/src\\/spaghetti Origin: git@github\\.com:pokey\\/spaghetti\\.git Commit: a6c5a08a Msgs: \\d+ Input tokens: \\d+(,\\d+)* Output tokens: \\d+(,\\d+)* Cost: \\$\\d+\\.\\d+/"
 - button "💬"
 - button "±"
 - button "📈"
@@ -12,17 +12,17 @@
 - text: /Poll Invalid response from server - not connected U user Apr \d+, \d+, \d+:\d+:\d+ AM/
 - button "Copy"
 - paragraph: add a line to dummy.txt. Doesn't matter what it is and don't bother running tests it's just a dummy repo. Please commit afterward
-- text: "/A agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+ Out: \\d+ \\(\\$\\d+\\.\\d+\\)/"
+- text: "/A agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+(,\\d+)* Out: \\d+(,\\d+)* \\(\\$\\d+\\.\\d+\\)/"
 - button "Copy"
 - paragraph: I'll help you add a line to dummy.txt and commit the change. Let me first check if this file exists and create it if needed.
-- text: "/I've set the title of this sketch to \"Add a line to dummy\\.txt and commit the change\" agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+ Out: \\d+ \\(\\$\\d+\\.\\d+\\)/"
+- text: "/I've set the title of this sketch to \"Add a line to dummy\\.txt and commit the change\" agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+(,\\d+)* Out: \\d+(,\\d+)* \\(\\$\\d+\\.\\d+\\)/"
 - button "Copy"
 - group: /\+ bash 🖥️ ls -la [\d,.]+[hmsp]+ elapsed/
-- text: "/agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+ Out: \\d+ \\(\\$\\d+\\.\\d+\\)/"
+- text: "/agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+(,\\d+)* Out: \\d+(,\\d+)* \\(\\$\\d+\\.\\d+\\)/"
 - button "Copy"
 - paragraph: I see that dummy.txt already exists. Let me add a new line to it.
 - group: "/\\+ patch \\/Users\\/pokey\\/src\\/spaghetti\\/dummy\\.txt: 1 edit [\\d,.]+[hmsp]+ elapsed/"
-- text: "/agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+ Out: \\d+ \\(\\$\\d+\\.\\d+\\)/"
+- text: "/agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+(,\\d+)* Out: \\d+(,\\d+)* \\(\\$\\d+\\.\\d+\\)/"
 - button "Copy"
 - paragraph: "Now let me commit this change:"
 - group: "/\\+ bash 🖥️ GIT_AUTHOR_NAME=\"Pokey Rule\" GIT_AUTHOR_EMAIL=\"\\d+\\+pokey@users\\.noreply\\.github\\.com\" git add dummy\\.txt && git commit -m \"Add a new line to dummy\\.txt\" -m \"Co-Authored-By: sketch\" -m \"Add a line to dummy\\.txt\\. Doesn't matter what it is and don't bother running tests it's just a dummy repo\\.\" [\\d,.]+[hmsp]+ elapsed/"
@@ -30,18 +30,18 @@
 - button "Copy"
 - text: 1 new commit detected 898178a0 Add a new line to dummy.txt
 - button "View Diff"
-- text: "/A agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+ Out: \\d+ \\(\\$\\d+\\.\\d+\\)/"
+- text: "/A agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+(,\\d+)* Out: \\d+(,\\d+)* \\(\\$\\d+\\.\\d+\\)/"
 - button "Copy"
 - group: /\+ done ❌ codereview tool has not been run for commit 898178a0c6681ce2b641cbe8994c22ed8bbb7dd7 [\d,.]+[hmsp]+ elapsed/
-- text: "/agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+ Out: \\d+ \\(\\$\\d+\\.\\d+\\)/"
+- text: "/agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+(,\\d+)* Out: \\d+(,\\d+)* \\(\\$\\d+\\.\\d+\\)/"
 - button "Copy"
 - paragraph: "Let me run a code review as required:"
 - group: /\+ codereview ✔️ [\d,.]+[hmsp]+ elapsed/
-- text: "/agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+ Out: \\d+ \\(\\$\\d+\\.\\d+\\)/"
+- text: "/agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+(,\\d+)* Out: \\d+(,\\d+)* \\(\\$\\d+\\.\\d+\\)/"
 - button "Copy"
 - paragraph: "Now let me try the done call again:"
 - group: /\+ done [\d,.]+[hmsp]+ elapsed/
-- text: "/agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+ Out: \\d+ \\(\\$\\d+\\.\\d+\\)/"
+- text: "/agent Apr \\d+, \\d+, \\d+:\\d+:\\d+ AM \\([\\d,.]+[hmsp]+\\) In: \\d+(,\\d+)* Out: \\d+(,\\d+)* \\(\\$\\d+\\.\\d+\\)/"
 - button "Copy"
 - paragraph: "I've completed your request:"
 - list:
diff --git a/webui/src/web-components/sketch-container-status.test.ts b/webui/src/web-components/sketch-container-status.test.ts
index b7dfd01..35c52b8 100644
--- a/webui/src/web-components/sketch-container-status.test.ts
+++ b/webui/src/web-components/sketch-container-status.test.ts
@@ -47,10 +47,10 @@
     mockCompleteState.total_usage.cache_read_input_tokens +
     mockCompleteState.total_usage.cache_creation_input_tokens;
   await expect(component.locator("#inputTokens")).toContainText(
-    expectedTotalInputTokens + "",
+    expectedTotalInputTokens.toLocaleString(),
   );
   await expect(component.locator("#outputTokens")).toContainText(
-    mockCompleteState.total_usage.output_tokens + "",
+    mockCompleteState.total_usage.output_tokens.toLocaleString(),
   );
   await expect(component.locator("#totalCost")).toContainText(
     "$" + mockCompleteState.total_usage.total_cost_usd.toFixed(2),
@@ -103,7 +103,7 @@
     partialState.total_usage.cache_read_input_tokens +
     partialState.total_usage.cache_creation_input_tokens;
   await expect(component.locator("#inputTokens")).toContainText(
-    expectedTotalInputTokens + "",
+    expectedTotalInputTokens.toLocaleString(),
   );
 
   // Check that elements without data are empty
diff --git a/webui/src/web-components/sketch-container-status.ts b/webui/src/web-components/sketch-container-status.ts
index 8079d2e..3994c74 100644
--- a/webui/src/web-components/sketch-container-status.ts
+++ b/webui/src/web-components/sketch-container-status.ts
@@ -1,6 +1,7 @@
 import { State } from "../types";
 import { LitElement, css, html } from "lit";
 import { customElement, property } from "lit/decorators.js";
+import { formatNumber } from "../utils";
 
 @customElement("sketch-container-status")
 export class SketchContainerStatus extends LitElement {
@@ -198,15 +199,17 @@
         <div class="info-item">
           <span class="info-label">Input tokens:</span>
           <span id="inputTokens" class="info-value"
-            >${(this.state?.total_usage?.input_tokens || 0) +
-            (this.state?.total_usage?.cache_read_input_tokens || 0) +
-            (this.state?.total_usage?.cache_creation_input_tokens || 0)}</span
+            >${formatNumber(
+              (this.state?.total_usage?.input_tokens || 0) +
+              (this.state?.total_usage?.cache_read_input_tokens || 0) +
+              (this.state?.total_usage?.cache_creation_input_tokens || 0)
+            )}</span
           >
         </div>
         <div class="info-item">
           <span class="info-label">Output tokens:</span>
           <span id="outputTokens" class="info-value"
-            >${this.state?.total_usage?.output_tokens}</span
+            >${formatNumber(this.state?.total_usage?.output_tokens)}</span
           >
         </div>
         <div class="info-item">
diff --git a/webui/src/web-components/sketch-timeline-message.test.ts b/webui/src/web-components/sketch-timeline-message.test.ts
index d5c8dfb..f68a5e0 100644
--- a/webui/src/web-components/sketch-timeline-message.test.ts
+++ b/webui/src/web-components/sketch-timeline-message.test.ts
@@ -149,8 +149,8 @@
   });
 
   await expect(component.locator(".message-usage")).toBeVisible();
-  await expect(component.locator(".message-usage")).toContainText("200"); // In (150 + 50 cache)
-  await expect(component.locator(".message-usage")).toContainText("300"); // Out
+  await expect(component.locator(".message-usage")).toContainText("200".toLocaleString()); // In (150 + 50 cache)
+  await expect(component.locator(".message-usage")).toContainText("300".toLocaleString()); // Out
   await expect(component.locator(".message-usage")).toContainText("$0.03"); // Cost
 });
 
diff --git a/webui/src/web-components/sketch-timeline-message.ts b/webui/src/web-components/sketch-timeline-message.ts
index a2dd533..95de17a 100644
--- a/webui/src/web-components/sketch-timeline-message.ts
+++ b/webui/src/web-components/sketch-timeline-message.ts
@@ -596,13 +596,14 @@
               ? html` <span class="message-usage">
                   <span title="Input tokens"
                     >In:
-                    ${(this.message?.usage?.input_tokens || 0) +
-                    (this.message?.usage?.cache_read_input_tokens || 0) +
-                    (this.message?.usage?.cache_creation_input_tokens ||
-                      0)}</span
+                    ${this.formatNumber(
+                      (this.message?.usage?.input_tokens || 0) +
+                      (this.message?.usage?.cache_read_input_tokens || 0) +
+                      (this.message?.usage?.cache_creation_input_tokens || 0)
+                    )}</span
                   >
                   <span title="Output tokens"
-                    >Out: ${this.message?.usage?.output_tokens}</span
+                    >Out: ${this.formatNumber(this.message?.usage?.output_tokens)}</span
                   >
                   <span title="Message cost"
                     >(${this.formatCurrency(