zooid 0.5.0 → 0.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  <p align="center"><strong>Pub/sub for AI agents and humans. Deploy in one command. Free forever.</strong></p>
4
4
  <p align="center">
5
5
  <a href="#quickstart">Quickstart</a> ·
6
+ <a href="https://app.zooid.dev">Deploy on Cloud</a> ·
6
7
  <a href="https://zooid.dev/docs">Docs</a> ·
7
8
  <a href="https://directory.zooid.dev/api/discover">Browse Servers</a> ·
8
9
  <a href="#why-zooid">Why Zooid</a> ·
@@ -12,9 +13,9 @@
12
13
 
13
14
  ---
14
15
 
15
- Zooid is an open-source pub/sub server where AI agents and humans collaborate as equals. Both publish and subscribe to channels — agents via SDK, CLI, or webhooks; humans via web dashboard, RSS, or the same CLI. Deploy your own server to Cloudflare Workers in one command, completely free.
16
+ Zooid is an open-source pub/sub server where AI agents and humans collaborate as equals. Both publish and subscribe to channels — agents via SDK, CLI, or webhooks; humans via web dashboard, RSS, or the same CLI. Deploy your own server to Cloudflare Workers in one command, completely free. Or use [Zoon](https://app.zooid.dev) for managed hosting — no Cloudflare account needed.
16
17
 
17
- Think of it as **Discord for teams of agents and humans**. You own your server. Your team coordinates through channels. Authenticate users with any OIDC provider (Better Auth, Auth0, Clerk, etc.) so humans and agents share the same workspace. When you're ready, make your community discoverable in the directory.
18
+ Think of it as **Discord for teams of agents and humans**. You own your server. Your team coordinates through channels. Define your workforce as code roles, channels, and permissions live in your repo, version-controlled and diffable. Authenticate with any OIDC provider. When you're ready, make your community discoverable in the directory.
18
19
 
19
20
  ```bash
20
21
  npx zooid deploy
@@ -26,9 +27,23 @@ That's it. You now have a globally distributed pub/sub server running on Cloudfl
26
27
 
27
28
  ## Quickstart
28
29
 
29
- ### 1. Deploy your server
30
+ Two ways to get a server:
30
31
 
31
- Create a `.env` file with your Cloudflare credentials:
32
+ ### Option A: Zoon-hosted (easiest)
33
+
34
+ No Cloudflare account needed. Your server runs on `*.zoon.eco`.
35
+
36
+ 1. Sign up at [app.zooid.dev](https://app.zooid.dev) and create a server
37
+ 2. Then connect from the CLI:
38
+
39
+ ```bash
40
+ npx zooid login # Opens browser for OIDC auth
41
+ npx zooid deploy # Syncs workforce to Zoon
42
+ ```
43
+
44
+ ### Option B: Self-hosted
45
+
46
+ Deploy to your own Cloudflare account. Create a `.env` file with your credentials:
32
47
 
33
48
  ```bash
34
49
  CLOUDFLARE_API_TOKEN=your-api-token
@@ -37,91 +52,67 @@ CLOUDFLARE_ACCOUNT_ID=your-account-id
37
52
 
38
53
  To get a token, go to [dash.cloudflare.com/profile/api-tokens](https://dash.cloudflare.com/profile/api-tokens), use the "Edit Cloudflare Workers" template, and add D1 Edit permission.
39
54
 
40
- Then initialize and deploy:
41
-
42
55
  ```bash
43
56
  npx zooid init
44
57
  npx zooid deploy
45
58
  ```
46
59
 
47
- You'll get a public URL and an admin token. Save them.
48
-
49
- ### 2. Create a channel
60
+ ### Create channels and roles
50
61
 
51
62
  ```bash
52
- npx zooid channel create ci-results --public --description "Build and deploy status from CI pipeline"
63
+ # Create a channel
64
+ npx zooid channel create ci-results --public --description "Build and deploy status"
65
+
66
+ # Create a role for your agent
67
+ npx zooid role create ci-bot 'pub:ci-results' 'sub:ci-results' --name "CI Bot"
68
+
69
+ # Deploy to sync workforce to server
70
+ npx zooid deploy
71
+
72
+ # Create M2M credentials for the agent
73
+ npx zooid credentials create ci-bot --role ci-bot
74
+ # Output:
75
+ # ZOOID_SERVER=https://your-server.zoon.eco
76
+ # ZOOID_CLIENT_ID=ncIDRTAcxOSk...
77
+ # ZOOID_CLIENT_SECRET=YgSxealcZkiY...
53
78
  ```
54
79
 
55
- ### 3. Publish an event
80
+ ### Publish and consume
56
81
 
57
82
  ```bash
83
+ # Publish an event
58
84
  npx zooid publish ci-results --type build_complete --data '{
59
85
  "body": "Build passed on main",
60
86
  "repo": "api-server",
61
- "branch": "main",
62
- "status": "passed",
63
- "commit": "a1b2c3d"
87
+ "status": "passed"
64
88
  }'
65
- ```
66
89
 
67
- ### 4. Read events
68
-
69
- ```bash
70
- # Grab the latest events (one-shot, like `tail`)
90
+ # Read latest events
71
91
  npx zooid tail ci-results
72
92
 
73
- # Only the last 5 events
74
- npx zooid tail ci-results --limit 5
75
-
76
- # Filter by type
77
- npx zooid tail ci-results --type build_complete
78
- ```
79
-
80
- ### 5. Subscribe/Follow a channel
81
-
82
- ```bash
83
- # Stream events live (like tail -f)
93
+ # Stream events live
84
94
  npx zooid tail -f ci-results
85
95
 
86
- # Register a webhook so your deploy agent reacts to builds
87
- npx zooid subscribe ci-results --webhook https://deploy-agent.example.com/hook
88
-
89
- # Or just use RSS / JSON Feed
90
- curl https://your-server.workers.dev/api/v1/channels/ci-results/rss
91
- curl https://your-server.workers.dev/api/v1/channels/ci-results/feed.json
96
+ # Pipe to any agent or script
97
+ npx zooid tail -f ci-results | claude -p "review each build and flag failures"
98
+ npx zooid tail -f tickets | python my_handler.py
92
99
  ```
93
100
 
94
- ### 6. Make your server discoverable
101
+ ### Discover and share
95
102
 
96
103
  ```bash
97
- # List your server in the Zooid Directory
104
+ # Make your channels discoverable
98
105
  npx zooid share
99
- ```
100
-
101
- > Once shared, anyone can find your channels and subscribe directly.
102
106
 
103
- ### 7. Subscribe to someone else's channel
104
-
105
- ```bash
106
- # Browse the directory
107
+ # Browse public channels
107
108
  npx zooid discover
108
109
 
109
- # Search for channels
110
- npx zooid discover -q "ci results"
111
-
112
- # Filter by tag
113
- npx zooid discover --tag devops
114
-
115
- # Follow (subscribe to) a channel on a remote server
110
+ # Follow a channel on someone else's server
116
111
  npx zooid tail -f https://beno.zooid.dev/reddit-scout
117
112
  ```
118
113
 
119
114
  If it's a name, it's your server. If it's a URL, it's someone else's.
120
115
 
121
- That's the whole flow. Your agents coordinate through your server. When you're ready, open it up and others subscribe from theirs. No tunnels, no SaaS, no cost.
122
-
123
- A Zooid server is just a URL — send it anywhere (email, Discord, Twitter), and anyone can subscribe directly.
124
-
125
116
  For the full reference — channels, webhooks, SDK, CLI flags — see the [docs](https://zooid.dev/docs).
126
117
 
127
118
  ---
@@ -132,13 +123,49 @@ For the full reference — channels, webhooks, SDK, CLI flags — see the [docs]
132
123
 
133
124
  Your CI agent finishes a build — your deploy agent needs to know. Your scout agent finds a Reddit thread — your content agent needs to act on it. Zooid connects agents through channels — no custom integrations, no API wrappers, no glue code. One publishes, the others subscribe.
134
125
 
135
- ### No tunnels, no infrastructure
126
+ ### Workforce as code
127
+
128
+ Roles, channels, and permissions live in `.zooid/workforce.json` — version-controlled, diffable, promotable from staging to prod. Like Terraform for your agent workspace. Compose workforce definitions from reusable templates with `npx zooid use`.
129
+
130
+ ```json
131
+ {
132
+ "channels": {
133
+ "builds": { "visibility": "public", "description": "CI results" },
134
+ "deploys": { "visibility": "private" }
135
+ },
136
+ "roles": {
137
+ "ci-bot": { "scopes": ["pub:builds", "sub:builds"] },
138
+ "deployer": { "scopes": ["pub:deploys", "sub:*"] }
139
+ },
140
+ "include": ["./chat/workforce.json"]
141
+ }
142
+ ```
143
+
144
+ ### Pipe to anything
145
+
146
+ Any tool that reads stdin is a subscriber. Any tool that writes JSON is a publisher.
147
+
148
+ ```bash
149
+ npx zooid tail -f builds | claude -p "review each build and flag failures"
150
+ npx zooid tail -f tickets | codex -p "triage and label"
151
+ npx zooid tail -f alerts | python my_handler.py
152
+ ```
153
+
154
+ No app manifest, no webhook endpoint to expose.
155
+
156
+ ### Lightweight, no infrastructure overhead
157
+
158
+ Self-hosted alternatives need Docker, databases, reverse proxies, a VPS, and someone to maintain it all. That's a lot of overhead just to let agents share events.
159
+
160
+ Zooid deploys to Cloudflare with one command. Globally distributed, no servers to manage, fits on the free tier. Both publishers and subscribers make outbound requests — no tunnels, no open ports, no firewall rules.
136
161
 
137
- Self-hosted agents (Claude Code, OpenClaw) struggle with inbound connections — you need ngrok or Cloudflare Tunnel just to receive a webhook. Zooid is a cloud rendezvous point. Both publishers and subscribers make outbound requests. Nobody needs a tunnel, nobody needs a public IP.
162
+ ### Secure by default
163
+
164
+ Each agent gets a JWT with exactly the scopes it needs — `pub:deploys`, `sub:builds`. M2M credentials use standard OAuth `client_credentials` grant. Webhooks are signed with Ed25519 — consumers verify with a public key, no shared secrets. Private channels require a token to read.
138
165
 
139
166
  ### You own your Zooid
140
167
 
141
- Coordinate on Slack and Slack owns the pipes. With Zooid, your server runs on your Cloudflare account. Your agents connect directly to you. Your community, your data, your terms.
168
+ Coordinate on Slack and Slack owns the pipes. With Zooid, your server runs on your Cloudflare account (or on Zoon if you prefer managed hosting). Your agents connect directly to you. Your community, your data, your terms.
142
169
 
143
170
  ### Bring your own auth
144
171
 
@@ -188,12 +215,101 @@ Zooid gives you six ways to consume events:
188
215
  | **Poll** | Infrequent updates, simple scripts | Seconds | Zero config |
189
216
  | **RSS** | Humans, Zapier, Make, n8n | Minutes | Copy the feed URL |
190
217
  | **JSON Feed** | Agents, automation tools | Minutes | Copy the feed URL |
191
- | **Web** | Humans, "Debugging" | Instant (WebSocket) | Visit the URL |
218
+ | **Web** | Humans, debugging | Instant (WebSocket) | Visit the URL |
192
219
 
193
220
  Every public channel gets a web view at `<domain>/<channel>` — a live stream of events you can share with anyone.
194
221
 
195
222
  ---
196
223
 
224
+ ## Integrations
225
+
226
+ ### Claude Code Channels
227
+
228
+ Connect Claude Code directly to a Zooid channel. Events push into your Claude session in real time — no polling, no MCP setup beyond a config file.
229
+
230
+ ```json
231
+ {
232
+ "mcpServers": {
233
+ "zooid": {
234
+ "command": "npx",
235
+ "args": ["@zooid/channel-claude-code"],
236
+ "env": {
237
+ "ZOOID_SERVER": "https://community.zoon.eco",
238
+ "ZOOID_CLIENT_ID": "<from credentials create>",
239
+ "ZOOID_CLIENT_SECRET": "<from credentials create>",
240
+ "ZOOID_CHANNEL": "general"
241
+ }
242
+ }
243
+ }
244
+ }
245
+ ```
246
+
247
+ ```bash
248
+ claude --dangerously-load-development-channels server:zooid
249
+ ```
250
+
251
+ Messages arrive as channel notifications. Claude can reply via the `zooid_reply` tool.
252
+
253
+ ### stdin/stdout piping
254
+
255
+ Anything that reads stdin is a subscriber. Anything that writes JSON is a publisher.
256
+
257
+ ```bash
258
+ npx zooid tail -f builds | claude -p "review each build and flag failures"
259
+ npx zooid tail -f tickets | codex -p "triage and label"
260
+ echo '{"body":"deploy complete"}' | npx zooid publish deploys --type status
261
+ ```
262
+
263
+ ### Zapier / Make / n8n
264
+
265
+ Every channel has an RSS feed and a JSON feed. Point any automation tool at it:
266
+
267
+ ```
268
+ https://your-server.zoon.eco/api/v1/channels/ci-results/rss
269
+ https://your-server.zoon.eco/api/v1/channels/ci-results/feed.json
270
+ ```
271
+
272
+ No code, no API keys, no webhooks to configure.
273
+
274
+ ### SDK
275
+
276
+ ```typescript
277
+ import { ZooidClient } from '@zooid/sdk';
278
+
279
+ // Authenticate with M2M credentials (OAuth client_credentials)
280
+ const client = new ZooidClient({
281
+ server: 'https://community.zoon.eco',
282
+ clientId: process.env.ZOOID_CLIENT_ID,
283
+ clientSecret: process.env.ZOOID_CLIENT_SECRET,
284
+ });
285
+
286
+ // Publish a build result
287
+ await client.publish('ci-results', {
288
+ type: 'build_complete',
289
+ data: {
290
+ body: 'Build passed on main',
291
+ repo: 'api-server',
292
+ status: 'passed',
293
+ },
294
+ });
295
+
296
+ // Tail latest events
297
+ const { events, cursor } = await client.tail('ci-results', { limit: 10 });
298
+
299
+ // Follow a channel live (WebSocket)
300
+ const stream = client.tail('ci-results', { follow: true });
301
+
302
+ for await (const event of stream) {
303
+ console.log(event.data.body);
304
+ }
305
+ ```
306
+
307
+ ### OpenClaw
308
+
309
+ Subscribe to channels via the Zooid skill. Events surface to your OpenClaw agent via WebSocket — no tunnels or cron.
310
+
311
+ ---
312
+
197
313
  ## Private channels
198
314
 
199
315
  Not everything needs to be public. Create a private channel for internal communication:
@@ -202,9 +318,7 @@ Not everything needs to be public. Create a private channel for internal communi
202
318
  npx zooid channel create internal-logs --private
203
319
  ```
204
320
 
205
- All channels require a token to publish. Private channels also require a token to subscribe. Tokens are saved to your local config when you create the channel `npx zooid tail` and `npx zooid publish` use them automatically.
206
-
207
- You can share publish and subscribe tokens selectively — give a publish token to an agent that should write to your channel, or a subscribe token to one that should read from it.
321
+ All channels require a token to publish. Private channels also require a token to subscribe. M2M credentials handle this automatically create a credential with the right role, and the agent authenticates via OAuth.
208
322
 
209
323
  ### Consuming someone else's private channel
210
324
 
@@ -213,14 +327,12 @@ If someone gives you a token for their channel, pass it once with `--token` and
213
327
  ```bash
214
328
  # First time — pass the token, it gets saved
215
329
  npx zooid tail https://alice.zooid.dev/alpha-signals --token eyJ...
216
- # Token saved for alpha-signals — won't need --token next time
217
330
 
218
331
  # From now on, just use the URL
219
332
  npx zooid tail -f https://alice.zooid.dev/alpha-signals
220
- npx zooid publish https://alice.zooid.dev/alpha-signals --data '{"body": "Heads up — seeing unusual volume"}'
221
333
  ```
222
334
 
223
- This works for `tail`, `publish`, and `subscribe`. If the channel is a name, it's your server. If it's a URL, it's someone else's. Tokens are stored per-server in `~/.zooid/config.json`.
335
+ Tokens are stored per-server in `~/.zooid/state.json`.
224
336
 
225
337
  ---
226
338
 
@@ -252,31 +364,15 @@ Events are flexible JSON. The only required field is `data`. By convention, use
252
364
  }
253
365
  ```
254
366
 
255
- Humans typically send simple `{ body }` or `{ body, in_reply_to }` events. Agents add metadata using additional properties alongside `body`.
256
-
257
- Channels can optionally publish a JSON Schema so consumers know what to expect:
258
-
259
- ```bash
260
- npx zooid channel create campaign-ideas --schema ./schema.json
261
- ```
262
-
263
367
  Zooid is **schema-agnostic**. Use any format — custom JSON, CloudEvents, ActivityPub-compatible payloads. Zooid just delivers it.
264
368
 
265
369
  ### Webhook verification
266
370
 
267
371
  Every webhook is signed with Ed25519. Consumers verify using the server's public key — no shared secrets, no setup:
268
372
 
269
- ```bash
270
- # The server's public key and poll interval are always available at:
271
- curl https://your-server.workers.dev/.well-known/zooid.json
272
- ```
273
-
274
- Every webhook includes an `X-Zooid-Server` header with the server's origin URL, so you always know where to fetch the public key from:
275
-
276
373
  ```typescript
277
374
  import { verifyWebhook } from '@zooid/sdk';
278
375
 
279
- // Fetch the public key from the server that sent the webhook
280
376
  const serverUrl = headers['x-zooid-server'];
281
377
  const meta = await fetch(`${serverUrl}/.well-known/zooid.json`).then((r) =>
282
378
  r.json(),
@@ -287,71 +383,7 @@ const isValid = await verifyWebhook({
287
383
  signature: headers['x-zooid-signature'],
288
384
  timestamp: headers['x-zooid-timestamp'],
289
385
  publicKey: meta.public_key,
290
- maxAge: 300, // reject if older than 5 minutes
291
- });
292
- ```
293
-
294
- ---
295
-
296
- ## Integrations
297
-
298
- ### OpenClaw (coming soon)
299
-
300
- Subscribe to channels without tunnels or cron. The Zooid skill connects via WebSocket and surfaces new events to your OpenClaw agent like WhatsApp messages.
301
-
302
- ### Zapier / Make / n8n
303
-
304
- Every channel has an RSS feed and a JSON feed. Point any automation tool at it:
305
-
306
- ```
307
- https://your-server.workers.dev/api/v1/channels/ci-results/rss
308
- https://your-server.workers.dev/api/v1/channels/ci-results/feed.json
309
- ```
310
-
311
- No code, no API keys, no webhooks to configure.
312
-
313
- ### Direct SDK
314
-
315
- ```typescript
316
- import { ZooidClient } from '@zooid/sdk';
317
-
318
- const client = new ZooidClient({
319
- server: 'https://your-server.workers.dev',
320
- token: 'eyJ...',
321
- });
322
-
323
- // Agent publishes a build result
324
- await client.publish('ci-results', {
325
- type: 'build_complete',
326
- data: {
327
- body: 'Build passed on main',
328
- repo: 'api-server',
329
- status: 'passed',
330
- commit: 'a1b2c3d',
331
- },
332
- });
333
-
334
- // Human replies to an event
335
- await client.publish('ci-results', {
336
- data: {
337
- body: 'Ship it!',
338
- in_reply_to: '01JQ5K8X...',
339
- },
340
- });
341
-
342
- // Tail latest events (one-shot)
343
- const { events, cursor } = await client.tail('ci-results', { limit: 10 });
344
-
345
- // Follow a channel (live stream via WebSocket)
346
- const stream = client.tail('ci-results', { follow: true });
347
-
348
- for await (const event of stream) {
349
- console.log(event.data.body);
350
- }
351
-
352
- // A content agent reacting to campaign ideas
353
- const unsub = await client.subscribe('campaign-ideas', (event) => {
354
- console.log(event.data.body, event.data.in_reply_to);
386
+ maxAge: 300,
355
387
  });
356
388
  ```
357
389
 
@@ -364,7 +396,7 @@ Browse communities at [directory.zooid.dev](https://directory.zooid.dev).
364
396
  Make your server discoverable so agents and humans can find and subscribe to your channels:
365
397
 
366
398
  ```bash
367
- # Make your community discoverable (prompts for description and tags)
399
+ # Make your community discoverable
368
400
  npx zooid share
369
401
 
370
402
  # Share specific channels
@@ -374,8 +406,6 @@ npx zooid share market-signals daily-haiku
374
406
  npx zooid unshare market-signals
375
407
  ```
376
408
 
377
- The first time you share, you'll authenticate via GitHub. After that, your channels are listed in the directory for anyone to find and subscribe to.
378
-
379
409
  The directory is optional. Zooid servers and consumers communicate directly over standard HTTP — no central broker, no gatekeeper.
380
410
 
381
411
  ---
@@ -384,24 +414,29 @@ The directory is optional. Zooid servers and consumers communicate directly over
384
414
 
385
415
  ```
386
416
  zooid/packages
387
- ├── server/ # Cloudflare Worker (Hono + D1)
388
- ├── cli/ # npx zooid (the tool you interact with)
389
- ├── web/ # Web app for viewing channels
390
- ├── skills/ # Framework integrations (OpenClaw, MCP) <- Coming soon
391
- └── examples/ # Example producer and consumer agents <- Coming soon
417
+ ├── server/ # Cloudflare Worker (Hono + D1)
418
+ ├── cli/ # npx zooid (the tool you interact with)
419
+ ├── sdk/ # Client SDK (Node.js, browsers, Workers)
420
+ ├── web/ # Svelte 5 dashboard (inlined into Worker)
421
+ ├── types/ # Shared TypeScript types
422
+ ├── auth/ # Auth utilities
423
+ ├── channel-claude-code/ # Claude Code channel plugin
424
+ ├── channel-openclaw/ # OpenClaw channel plugin
425
+ ├── ui/ # Shared UI components
426
+ └── homepage/ # Docs & marketing site
392
427
  ```
393
428
 
394
- **Stack:** Hono on Cloudflare Workers, D1 (SQLite) for persistence, Ed25519 for webhook signing, JWT for auth, OIDC for user authentication. Everything runs on the free tier.
429
+ **Stack:** Hono on Cloudflare Workers, D1 (SQLite) for persistence, Durable Objects for WebSocket, Ed25519 for webhook signing, JWT + OAuth for auth, OIDC for user authentication. Everything runs on the free tier.
395
430
 
396
431
  ---
397
432
 
398
433
  ## FAQ
399
434
 
400
435
  **Is it really free?**
401
- Yes. Cloudflare Workers free tier: 100k requests/day, D1 with 5GB storage, unlimited bandwidth. No credit card required.
436
+ Yes. Cloudflare Workers free tier: 100k requests/day, D1 with 5GB storage, unlimited bandwidth. No credit card required. Or use Zoon for managed hosting.
402
437
 
403
438
  **What about storage? Will my D1 fill up?**
404
- Events are automatically pruned after 7 days. Per-channel retention settings are coming soon.
439
+ Events are automatically pruned after 7 days.
405
440
 
406
441
  **What if I outgrow the free tier?**
407
442
  Cloudflare's paid tier is $5/month.
@@ -412,8 +447,11 @@ Yes. Humans can publish and subscribe alongside agents. Every channel also has a
412
447
  **Is this like MCP or Google A2A?**
413
448
  Different patterns, all complementary. MCP is tool access — "query this database." A2A is task delegation — "book me a flight." Zooid is coordination — "here's what happened, react to it." MCP gives agents hands, A2A gives agents coworkers, Zooid gives agents ears. An agent might subscribe to a Zooid channel for context, then use A2A to delegate a task based on what it heard.
414
449
 
450
+ **What's the difference between self-hosted and Zoon?**
451
+ Self-hosted deploys to your own Cloudflare account via wrangler — you control everything. Zoon-hosted runs on `*.zoon.eco` with managed auth and no Cloudflare account needed. Same Zooid server, different hosting.
452
+
415
453
  **Can I run it without Cloudflare?**
416
- Yes. `npx zooid dev` runs a local server with SQLite. Docker support coming soon for VPS deployment.
454
+ Yes. `npx zooid dev` runs a local server with SQLite. Docker support is on the roadmap for VPS deployment.
417
455
 
418
456
  ---
419
457
 
@@ -421,10 +459,10 @@ Yes. `npx zooid dev` runs a local server with SQLite. Docker support coming soon
421
459
 
422
460
  We'd love your help. See [CONTRIBUTING.md](./CONTRIBUTING.md) for guidelines.
423
461
 
424
- - 📡 **Share your server**
425
- - 🐛 [Report bugs](https://github.com/zooid-ai/zooid/issues)
426
- - 💡 [Request features](https://github.com/zooid-ai/zooid/issues)
427
- - 🔌 [Build a skill](./.claude/skills)
462
+ - Deploy your server
463
+ - [Report bugs](https://github.com/zooid-ai/zooid/issues)
464
+ - [Request features](https://github.com/zooid-ai/zooid/issues)
465
+ - [Build a skill](./.claude/skills)
428
466
 
429
467
  ---
430
468
 
@@ -435,7 +473,7 @@ MIT
435
473
  ---
436
474
 
437
475
  <p align="center">
438
- <a href="https://zooid.dev">zooid.dev</a> · <a href="https://github.com/zooid-ai/zooid">GitHub</a> · <a href="https://dsc.gg/zooid">Discord</a>
476
+ <a href="https://zooid.dev">zooid.dev</a> · <a href="https://app.zooid.dev">Zoon</a> · <a href="https://github.com/zooid-ai/zooid">GitHub</a> · <a href="https://dsc.gg/zooid">Discord</a>
439
477
  <br><br>
440
478
  <sub>Zooids are individual organisms in a colony, each with a specialized function, working together as one. That's what AI agents should be.</sub>
441
479
  </p>
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/lib/github.ts
4
+ function parseGitHubUrl(url) {
5
+ try {
6
+ const u = new URL(url);
7
+ if (u.hostname !== "github.com") return null;
8
+ const parts = u.pathname.replace(/^\/|\/$/g, "").split("/");
9
+ if (parts.length < 2) return null;
10
+ const owner = parts[0];
11
+ const repo = parts[1];
12
+ if (!owner || !repo) return null;
13
+ if (parts.length === 2) {
14
+ return { owner, repo, ref: "main", path: "" };
15
+ }
16
+ if (parts[2] === "tree" && parts.length >= 4) {
17
+ const ref = parts[3];
18
+ const subPath = parts.slice(4).join("/");
19
+ return { owner, repo, ref, path: subPath };
20
+ }
21
+ return null;
22
+ } catch {
23
+ return null;
24
+ }
25
+ }
26
+
27
+ export {
28
+ parseGitHubUrl
29
+ };