Skip to content

Commit b06dfd6

Browse files
authored
feat: add browser support (#23)
## Summary - Browser build via tshy `esmDialects` with `"browser"` export condition — bundlers (Vite, webpack, esbuild, Next.js) auto-resolve to it with the same `"agentcrumbs"` import - Browser variants for context (sync stack), env (`configure()` API), trail (Web Crypto, no fs), and console sink (CSS-styled DevTools output) - `configure()` exported from both builds — primary config API in browsers, no-op in Node.js - New browser guide doc page, updated READMEs, quickstart, skills, env-var docs, sinks docs ## Test plan - [x] `pnpm build` produces `dist/browser/` alongside `dist/esm/` and `dist/commonjs/` - [x] All 53 existing Node.js tests pass - [x] No `node:` imports in browser library files - [x] Skills validate - [ ] Manual test: import in a Vite project, call `configure("*")`, verify crumbs in DevTools console 🤖 Generated with [Claude Code](https://claude.com/claude-code)
2 parents dff52e4 + cc8709c commit b06dfd6

File tree

21 files changed

+812
-18
lines changed

21 files changed

+812
-18
lines changed

.changeset/browser-support.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"agentcrumbs": minor
3+
---
4+
5+
Add browser support via tshy `esmDialects`. Bundlers that respect the `"browser"` export condition (Vite, webpack, esbuild, Next.js) automatically resolve to the browser build. Same `"agentcrumbs"` import path — no separate entry point. Adds `configure()` API for enabling tracing in the browser.

README.md

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ All methods are documented in detail at [agentcrumbs.dev/docs/api](https://agent
9090

9191
| Method | Purpose |
9292
| --- | --- |
93+
| `configure(config)` | Enable tracing in the browser (no-op in Node.js) |
9394
| `trail(namespace)` | Create a trail function for a namespace |
9495
| `crumb(msg, data?, options?)` | Drop a crumb with message and optional data |
9596
| `crumb.scope(name, fn)` | Wrap a function with entry/exit/error tracking |
@@ -104,9 +105,16 @@ All methods are documented in detail at [agentcrumbs.dev/docs/api](https://agent
104105

105106
Mark crumb lines with `// @crumbs` (single line) or `// #region @crumbs` / `// #endregion @crumbs` (block) so they can be stripped before merge. See the [markers docs](https://agentcrumbs.dev/docs/markers) for details and examples.
106107

107-
## Environment variable
108+
## Configuration
108109

109-
Everything is controlled by a single `AGENTCRUMBS` environment variable.
110+
In Node.js, everything is controlled by a single `AGENTCRUMBS` environment variable. In the browser, use `configure()`:
111+
112+
```typescript
113+
import { configure } from "agentcrumbs"; // @crumbs
114+
configure("*"); // @crumbs — enable all namespaces
115+
```
116+
117+
### Environment variable (Node.js)
110118

111119
| Value | Effect |
112120
| --- | --- |
@@ -173,9 +181,10 @@ curl -X POST http://localhost:8374/crumb \
173181

174182
## Runtime compatibility
175183

176-
Zero runtime dependencies. Node.js built-in modules only: `node:http`, `node:async_hooks`, `node:crypto`, `node:fs`, `node:util`.
184+
Zero runtime dependencies.
177185

178-
Verified compatible with **Node.js 18+** and **Bun**.
186+
- **Node.js 18+** and **Bun** — uses `node:async_hooks`, `node:crypto`, `node:fs`, `node:util`
187+
- **Browsers** — Vite, webpack, esbuild, Next.js auto-resolve to the browser build via the `"browser"` export condition. Same `"agentcrumbs"` import path. Use `configure()` instead of the env var to enable tracing. See the [browser guide](https://agentcrumbs.dev/docs/guides/browser).
179188

180189
## Docs
181190

docs/content/docs/api/trail.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ Returns a `TrailFn`, a callable function with additional methods:
4747
When a namespace is disabled, `trail()` returns a pre-built frozen noop function. There is no `if (enabled)` check on every call. The function itself IS the noop.
4848

4949
```typescript
50-
// When AGENTCRUMBS is unset:
50+
// When tracing is not enabled:
5151
const crumb = trail("my-service"); // returns frozen NOOP
5252
crumb("msg", { data }); // empty function, returns undefined
5353
crumb.scope("op", fn); // calls fn() directly

docs/content/docs/config/env-var.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: "Environment variable"
33
description: "Configure agentcrumbs with the AGENTCRUMBS env var"
44
---
55

6-
Everything is controlled by a single `AGENTCRUMBS` environment variable.
6+
In Node.js, everything is controlled by a single `AGENTCRUMBS` environment variable. In the browser, use [`configure()`](/guides/browser) instead.
77

88
## Shorthand values
99

docs/content/docs/config/sinks.mdx

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ title: "Sinks"
33
description: "Configure where crumbs are sent"
44
---
55

6-
By default, crumbs are sent via HTTP to the collector and also printed to stderr. You can add custom sinks or replace the defaults.
6+
By default, crumbs are sent via HTTP to the collector and also printed to the console. In Node.js, output goes to stderr with ANSI colors. In the browser, output goes to `console.debug()` with CSS styling. You can add custom sinks or replace the defaults.
77

88
## Custom sink
99

@@ -32,7 +32,10 @@ import { HttpSink } from "agentcrumbs";
3232

3333
### ConsoleSink
3434

35-
Pretty-printed stderr output. Added automatically when `AGENTCRUMBS` is set.
35+
Pretty-printed console output. Added automatically when tracing is enabled.
36+
37+
- **Node.js**: ANSI-colored output to stderr
38+
- **Browser**: CSS-styled output via `console.debug()`, with `console.groupCollapsed()` for scopes and interactive object rendering in DevTools
3639

3740
```typescript
3841
import { ConsoleSink } from "agentcrumbs";
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
---
2+
title: "Browser"
3+
description: "Use agentcrumbs in browser apps with the same import path"
4+
---
5+
6+
agentcrumbs works in the browser with the same `"agentcrumbs"` import. Bundlers that respect the `"browser"` export condition (Vite, webpack, esbuild, Next.js) automatically resolve to the browser build.
7+
8+
## Enable tracing
9+
10+
Browsers don't have environment variables. Use `configure()` instead:
11+
12+
```typescript
13+
import { configure, trail } from "agentcrumbs"; // @crumbs
14+
15+
configure("*"); // @crumbs — enable all namespaces
16+
17+
const crumb = trail("ui"); // @crumbs
18+
```
19+
20+
`configure()` accepts the same values as the `AGENTCRUMBS` env var:
21+
22+
```typescript
23+
// Enable all
24+
configure("*"); // @crumbs
25+
26+
// Namespace filter
27+
configure("ui-*,api-*"); // @crumbs
28+
29+
// Full config object
30+
configure({ ns: "ui-*", app: "my-app", format: "pretty" }); // @crumbs
31+
```
32+
33+
Call `configure()` before any `trail()` calls. A good place is your app's entry point.
34+
35+
### Declarative fallback
36+
37+
You can also set config on `globalThis` before importing agentcrumbs:
38+
39+
```html
40+
<script>
41+
window.__AGENTCRUMBS__ = "*";
42+
// or: window.__AGENTCRUMBS__ = { ns: "ui-*", app: "my-app" };
43+
// or set just the app name: window.__AGENTCRUMBS_APP__ = "my-app";
44+
</script>
45+
```
46+
47+
## App name
48+
49+
In the browser, the app name is resolved in this order:
50+
1. `app` field from `configure()` config
51+
2. `globalThis.__AGENTCRUMBS_APP__`
52+
3. Fallback: `"browser"`
53+
54+
## Console output
55+
56+
In the browser, crumbs are written to `console.debug()` with CSS styling:
57+
58+
- Namespace labels are color-coded
59+
- Scope enter/exit use `console.groupCollapsed()` / `console.groupEnd()` for collapsible nesting
60+
- Data objects are passed as additional arguments so DevTools renders them interactively
61+
62+
When `format: "json"` is set, crumbs are written as JSON strings via `console.debug()`.
63+
64+
## Collector support
65+
66+
The browser build includes the HTTP sink, so crumbs are sent to the collector just like in Node.js. Start `agentcrumbs collect` on your dev machine and crumbs from both your server and browser flow to the same place.
67+
68+
```bash
69+
# Terminal: Start collector
70+
agentcrumbs collect
71+
72+
# Browser crumbs + server crumbs appear together
73+
agentcrumbs tail --all-apps
74+
```
75+
76+
The browser defaults to `http://localhost:8374/crumb`. Make sure CORS allows it, or the HTTP sink silently fails (crumbs still appear in the DevTools console).
77+
78+
## Differences from Node.js
79+
80+
| | Node.js | Browser |
81+
|---|---------|---------|
82+
| **Config** | `AGENTCRUMBS` env var | `configure()` call |
83+
| **Console output** | ANSI-colored stderr | CSS-styled DevTools console |
84+
| **Async context** | `AsyncLocalStorage` | Sync stack (single-threaded) |
85+
| **Process ID** | `process.pid` | `0` |
86+
| **Session file** | Reads `/tmp/agentcrumbs.session` | Skipped |
87+
| **UUID** | `node:crypto` | Web Crypto API |
88+
| **App fallback** | Nearest `package.json` name | `"browser"` |
89+
90+
### Context isolation
91+
92+
The browser uses a sync stack instead of `AsyncLocalStorage`. This works for all linear async flows. However, concurrent branches in `Promise.all` won't isolate context from each other. This is acceptable for debugging — just be aware that nested scopes inside `Promise.all` may share context.
93+
94+
## configure() in Node.js
95+
96+
`configure()` is exported from both builds so your code compiles in both environments. In Node.js it's a no-op — use the `AGENTCRUMBS` env var instead.

docs/content/docs/guides/meta.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
22
"title": "Guides",
3-
"pages": ["node-modules", "multi-service", "pr-reviewers", "cross-language"]
3+
"pages": ["browser", "node-modules", "multi-service", "pr-reviewers", "cross-language"]
44
}

docs/content/docs/index.mdx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ Service C ──┘ (fire & forget) └── ~/.agentcrumbs/<app
3131
- **Strip before merge.** `agentcrumbs strip` removes all `// @crumbs` lines and `#region @crumbs` blocks. Clean diffs, no debug code on main.
3232
- **HTTP collector.** `agentcrumbs collect` receives crumbs from all services via fire-and-forget HTTP. Tail, query, and replay from the CLI.
3333
- **Works with any agent.** Claude Code, Cursor, Copilot, Aider, custom agents. If the agent can write code, it can write crumbs.
34+
- **Works in the browser.** Same import path. Bundlers auto-resolve to the browser build. Use `configure()` instead of the env var. See the [browser guide](/guides/browser).
3435
- **Ships with agent skills.** Built on [@tanstack/intent](https://tanstack.com/blog/from-docs-to-agents). Install the package, tell your agent to run `npx @tanstack/intent install`, and it learns how to use crumbs from the package itself. No stale training data.
3536

3637
## Install

docs/content/docs/quickstart.mdx

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,9 +98,25 @@ agentcrumbs strip
9898
agentcrumbs strip --check
9999
```
100100

101+
## Browser apps
102+
103+
agentcrumbs works in the browser with the same import. Use `configure()` instead of the env var:
104+
105+
```typescript
106+
import { configure, trail } from "agentcrumbs"; // @crumbs
107+
108+
configure("*"); // @crumbs
109+
const crumb = trail("ui"); // @crumbs
110+
111+
crumb("button clicked", { id: "submit" }); // @crumbs
112+
```
113+
114+
Bundlers that support the `"browser"` export condition (Vite, webpack, esbuild, Next.js) resolve to the browser build automatically. See the [browser guide](/guides/browser) for details.
115+
101116
## Next steps
102117

103118
- [Skills](/skills): how agents learn to use agentcrumbs from the package itself
104119
- [Workflow](/workflow): how crumbs fit into the branch lifecycle
105120
- [API reference](/api/trail): full API docs
106121
- [CLI reference](/cli/collect): collector, tail, query, strip
122+
- [Browser guide](/guides/browser): using agentcrumbs in browser apps

packages/agentcrumbs/.tshy/commonjs.json

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@
99
"exclude": [
1010
"../src/__tests__/**/*",
1111
"../src/**/*.mts",
12-
"../src/package.json"
12+
"../src/package.json",
13+
"../src/context-browser.mts",
14+
"../src/env-browser.mts",
15+
"../src/sinks/console-browser.mts",
16+
"../src/trail-browser.mts"
1317
],
1418
"compilerOptions": {
1519
"outDir": "../.tshy-build/commonjs"

0 commit comments

Comments
 (0)