@lightupai/polaris 0.0.32 → 0.0.33

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
@@ -119,6 +119,7 @@ tests/ Test suite (bun test)
119
119
  - [ ] Reconciliation and recovery — `polaris recover` command that diffs the daemon JSONL log against the DB, backfills missing events, and posts an abridged recovery summary to Slack as a thread reply at the correct timeline position
120
120
  - [ ] CD pipeline for Hetzner — auto-deploy to production on merge to master (SSH + docker compose up), similar to the npm publish job
121
121
  - [ ] Auto-update local skill/hooks — locally installed skill and hook files go stale when the repo changes. `polaris install` fixes it but there's no staleness detection or auto-update mechanism
122
+ - [ ] Update available indicator — daemon periodically checks npm for newer version, caches the result. Status line shows "update available" when stale. `polaris update` command installs the latest version and rewrites skill/hooks.
122
123
  - [ ] Slack channel name collision — if a channel name was previously deleted, Slack reserves it. Bridge should handle `name_taken` by trying a prefix/suffix (e.g., `p-project-name`)
123
124
 
124
125
  ## Development
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lightupai/polaris",
3
- "version": "0.0.32",
3
+ "version": "0.0.33",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "polaris": "bin/polaris",
@@ -53,12 +53,12 @@ mcp.setRequestHandler(ListToolsRequestSchema, async () => ({
53
53
  inputSchema: {
54
54
  type: "object" as const,
55
55
  properties: {
56
- channel: { type: "string", description: "Channel name (e.g., #polaris-dev or polaris-dev)" },
56
+ channel: { type: "string", description: "Channel name (e.g., #polaris-dev). Omit to list available channels." },
57
57
  user: { type: "string", description: "Your participant ID (e.g., user:manu)" },
58
58
  session: { type: "string", description: "Session name (optional — auto-generated if omitted)" },
59
59
  agent: { type: "string", description: "Agent identity (optional — defaults to agent:claude)" },
60
60
  },
61
- required: ["channel", "user"],
61
+ required: ["user"],
62
62
  },
63
63
  },
64
64
  {
@@ -128,7 +128,23 @@ mcp.setRequestHandler(CallToolRequestSchema, async (req) => {
128
128
  const { name, arguments: args } = req.params;
129
129
 
130
130
  if (name === "polaris_connect") {
131
- const { channel, user, session, agent } = args as { channel: string; user: string; session?: string; agent?: string };
131
+ const { channel, user, session, agent } = args as { channel?: string; user: string; session?: string; agent?: string };
132
+
133
+ // If no channel specified, list available channels
134
+ if (!channel) {
135
+ try {
136
+ const res = await daemonGet("/channels");
137
+ if (res.ok) {
138
+ const body = await res.json() as { channels: string[] };
139
+ if (body.channels.length === 0) {
140
+ return { content: [{ type: "text", text: "No channels found. Create one by joining: `/polaris join #channel-name`" }] };
141
+ }
142
+ return { content: [{ type: "text", text: `Available channels:\n${body.channels.map(c => ` ${c}`).join("\n")}\n\nJoin one with: /polaris join #channel-name` }] };
143
+ }
144
+ } catch { /* fall through */ }
145
+ return { content: [{ type: "text", text: "Specify a channel: `/polaris join #channel-name`" }] };
146
+ }
147
+
132
148
  const project = channel.replace(/^#/, ""); // strip leading # if present
133
149
  try {
134
150
  const res = await daemonPost("/connect", {
@@ -651,6 +651,24 @@ export function startDaemon(port = Number(process.env.POLARIS_DAEMON_PORT ?? 432
651
651
  return json(data);
652
652
  }
653
653
 
654
+ // GET /channels — list available channels/projects
655
+ if (method === "GET" && pathname === "/channels") {
656
+ try {
657
+ const serviceUrl = getServiceUrl();
658
+ const res = await fetch(`${serviceUrl}/projects`, {
659
+ headers: await authHeaders(),
660
+ });
661
+ if (!res.ok) return error("Failed to fetch channels", res.status);
662
+ const projects = (await res.json()) as Array<{ name: string; slack_channel_name?: string }>;
663
+ const channels = projects
664
+ .filter((p) => p.name !== "_system")
665
+ .map((p) => `#${p.slack_channel_name || p.name}`);
666
+ return json({ channels });
667
+ } catch {
668
+ return error("API unreachable", 503);
669
+ }
670
+ }
671
+
654
672
  // POST /backfill — recover lost events from daemon log
655
673
  if (method === "POST" && pathname === "/backfill") {
656
674
  try {
package/src/web/pages.ts CHANGED
@@ -6,51 +6,108 @@ import type { Org } from "../service/db";
6
6
  export function renderLandingPage(): string {
7
7
  return `
8
8
  ${nav()}
9
- <div class="max-w-5xl mx-auto px-6">
10
- <div class="pt-24 pb-16 text-center">
11
- <h1 class="text-5xl font-bold tracking-tight text-gray-900 sm:text-6xl">
12
- Multiplayer AI collaboration
9
+ <div class="max-w-3xl mx-auto px-6">
10
+
11
+ <!-- Hero -->
12
+ <div class="pt-24 pb-16">
13
+ <h1 class="text-4xl font-bold tracking-tight text-gray-900 sm:text-5xl">
14
+ Multiplayer collaboration<br>for Claude Code
13
15
  </h1>
14
- <p class="mt-6 text-lg leading-8 text-gray-600 max-w-2xl mx-auto">
15
- Polaris connects your AI agent sessions to your team. Capture every interaction, pool context across workstreams, and let anyone contribute — all in real time.
16
+ <p class="mt-4 text-lg text-gray-500 max-w-xl">
17
+ Your teammates see what your agent is doing. They can jump in from Slack and steer it in real time.
16
18
  </p>
17
- <div class="mt-10 flex flex-col items-center gap-4">
18
- <a href="/signup" class="inline-flex items-center gap-3 px-6 py-3 bg-white border border-gray-300 rounded-lg shadow-sm hover:bg-gray-50 transition text-sm font-semibold text-gray-700">
19
- <svg width="18" height="18" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844a4.14 4.14 0 01-1.796 2.716v2.259h2.908c1.702-1.567 2.684-3.875 2.684-6.615z" fill="#4285F4"/><path d="M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 009 18z" fill="#34A853"/><path d="M3.964 10.71A5.41 5.41 0 013.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.996 8.996 0 000 9c0 1.452.348 2.827.957 4.042l3.007-2.332z" fill="#FBBC05"/><path d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 00.957 4.958L3.964 6.29C4.672 4.163 6.656 2.58 9 3.58z" fill="#EA4335"/></svg>
20
- Sign up with Google
21
- </a>
22
- <a href="/login" class="px-6 py-3 text-sm font-semibold text-gray-500 hover:text-gray-700 transition">Already have an account? Sign in</a>
19
+ <div class="mt-8 flex items-center gap-4">
20
+ <a href="#get-started" class="px-5 py-2.5 bg-gray-900 text-white text-sm font-medium rounded-lg hover:bg-gray-800 transition">Get started</a>
21
+ <a href="https://github.com/anthropics/polaris" class="text-sm font-medium text-gray-500 hover:text-gray-700 transition">GitHub</a>
23
22
  </div>
24
- </div>
25
23
 
26
- <div class="grid grid-cols-1 md:grid-cols-3 gap-8 py-16 border-t border-gray-200">
27
- <div>
28
- <div class="w-10 h-10 rounded-lg bg-polaris-100 flex items-center justify-center mb-4">
29
- <svg class="w-5 h-5 text-polaris-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z"/><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M2.458 12C3.732 7.943 6.523 5 10 5c3.477 0 6.268 2.943 7.542 7-.274.985-.633 1.928-1.065 2.813M15 12a3 3 0 11-6 0 3 3 0 016 0z"/></svg>
24
+ <!-- Terminal demo -->
25
+ <div class="mt-12 bg-gray-900 rounded-xl overflow-hidden shadow-2xl">
26
+ <div class="flex items-center gap-1.5 px-4 py-3 bg-gray-800">
27
+ <div class="w-3 h-3 rounded-full bg-red-500/80"></div>
28
+ <div class="w-3 h-3 rounded-full bg-yellow-500/80"></div>
29
+ <div class="w-3 h-3 rounded-full bg-green-500/80"></div>
30
+ <span class="ml-3 text-xs text-gray-500 font-mono">claude code</span>
30
31
  </div>
31
- <h3 class="text-sm font-semibold text-gray-900">Session capture</h3>
32
- <p class="mt-2 text-sm text-gray-600">Every prompt, response, and tool call is captured and broadcast to your team's floor.</p>
33
- </div>
34
- <div>
35
- <div class="w-10 h-10 rounded-lg bg-polaris-100 flex items-center justify-center mb-4">
36
- <svg class="w-5 h-5 text-polaris-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M17 8h2a2 2 0 012 2v6a2 2 0 01-2 2h-2v4l-4-4H9a1.994 1.994 0 01-1.414-.586m0 0L11 14h4a2 2 0 002-2V6a2 2 0 00-2-2H5a2 2 0 00-2 2v6a2 2 0 002 2h2v4l.586-.586z"/></svg>
32
+ <div class="px-5 py-4 font-mono text-sm leading-relaxed space-y-3">
33
+ <div>
34
+ <span class="text-green-400">$</span> <span class="text-gray-300">npm install -g @lightupai/polaris</span>
35
+ </div>
36
+ <div>
37
+ <span class="text-green-400">$</span> <span class="text-gray-300">polaris</span>
38
+ </div>
39
+ <div class="text-gray-500"> Hooks installed. MCP server registered. Logged in as manu@acme.dev</div>
40
+ <div class="border-t border-gray-700 pt-3">
41
+ <span class="text-polaris-400">&gt;</span> <span class="text-gray-300">/polaris join #webapp</span>
42
+ </div>
43
+ <div class="text-gray-500"> Connected to webapp/s-4f2a as user:manu</div>
44
+ <div class="border-t border-gray-700 pt-3">
45
+ <span class="text-polaris-400">&gt;</span> <span class="text-gray-300">implement the auth middleware using RS256</span>
46
+ </div>
47
+ <div class="text-gray-400"> I'll create src/middleware/auth.ts with RS256 JWT verification...</div>
48
+ <div class="mt-1 pl-4 border-l-2 border-yellow-500/50">
49
+ <span class="text-yellow-400 text-xs">priya via slack</span>
50
+ <span class="text-gray-400"> &mdash; make sure to add rate limiting on that endpoint</span>
51
+ </div>
52
+ <div class="text-gray-400"> Good call. Adding rate limiting middleware before deploying...</div>
37
53
  </div>
38
- <h3 class="text-sm font-semibold text-gray-900">Context injection</h3>
39
- <p class="mt-2 text-sm text-gray-600">Teammates inject expertise directly into your agent session from Slack, WhatsApp, or any floor.</p>
40
54
  </div>
41
- <div>
42
- <div class="w-10 h-10 rounded-lg bg-polaris-100 flex items-center justify-center mb-4">
43
- <svg class="w-5 h-5 text-polaris-700" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197m3 5.197V21"/></svg>
55
+ </div>
56
+
57
+ <!-- How it works -->
58
+ <div class="py-16 border-t border-gray-200">
59
+ <h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">How it works</h2>
60
+ <div class="mt-8 space-y-8">
61
+ <div class="flex gap-4">
62
+ <div class="w-8 h-8 rounded-lg bg-gray-900 flex items-center justify-center text-white text-sm font-bold shrink-0">1</div>
63
+ <div>
64
+ <h3 class="font-semibold text-gray-900">Install</h3>
65
+ <p class="mt-1 text-sm text-gray-500"><code class="bg-gray-100 px-1.5 py-0.5 rounded text-gray-700 text-xs">npm install -g @lightupai/polaris && polaris</code> sets up hooks, MCP server, and authenticates your team.</p>
66
+ </div>
67
+ </div>
68
+ <div class="flex gap-4">
69
+ <div class="w-8 h-8 rounded-lg bg-gray-900 flex items-center justify-center text-white text-sm font-bold shrink-0">2</div>
70
+ <div>
71
+ <h3 class="font-semibold text-gray-900">Connect</h3>
72
+ <p class="mt-1 text-sm text-gray-500"><code class="bg-gray-100 px-1.5 py-0.5 rounded text-gray-700 text-xs">/polaris join #your-channel</code> links your Claude Code session to your team's Slack channel.</p>
73
+ </div>
74
+ </div>
75
+ <div class="flex gap-4">
76
+ <div class="w-8 h-8 rounded-lg bg-gray-900 flex items-center justify-center text-white text-sm font-bold shrink-0">3</div>
77
+ <div>
78
+ <h3 class="font-semibold text-gray-900">Collaborate</h3>
79
+ <p class="mt-1 text-sm text-gray-500">Every prompt and response streams to Slack. Teammates reply there and their messages appear inline in your agent session.</p>
80
+ </div>
44
81
  </div>
45
- <h3 class="text-sm font-semibold text-gray-900">Multiplayer</h3>
46
- <p class="mt-2 text-sm text-gray-600">Multiple drivers, concurrent sessions, seamless handoffs. Humans and AI agents as first-class participants.</p>
47
82
  </div>
48
83
  </div>
49
84
 
85
+ <!-- Why Polaris -->
50
86
  <div class="py-16 border-t border-gray-200">
51
- <h2 class="text-2xl font-bold text-gray-900 text-center">How it works</h2>
52
- <p class="mt-2 text-center text-sm text-gray-500">Everything streams to the floor — your team's Slack channel.</p>
87
+ <h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">Why Polaris</h2>
88
+ <div class="mt-8 grid grid-cols-1 md:grid-cols-2 gap-6">
89
+ <div class="border border-gray-200 rounded-lg p-5">
90
+ <h3 class="font-semibold text-gray-900">No context switching</h3>
91
+ <p class="mt-2 text-sm text-gray-500">Teammate messages arrive directly in your coding session. No tab switching, no copy-pasting, no "hey can you check Slack."</p>
92
+ </div>
93
+ <div class="border border-gray-200 rounded-lg p-5">
94
+ <h3 class="font-semibold text-gray-900">Full session visibility</h3>
95
+ <p class="mt-2 text-sm text-gray-500">Every prompt, tool call, and response is captured and streamed to your team. Anyone can see what's happening and jump in.</p>
96
+ </div>
97
+ <div class="border border-gray-200 rounded-lg p-5">
98
+ <h3 class="font-semibold text-gray-900">Human + AI multiplayer</h3>
99
+ <p class="mt-2 text-sm text-gray-500">Multiple developers, multiple agents, concurrent sessions. Humans and AI are first-class participants on the same floor.</p>
100
+ </div>
101
+ <div class="border border-gray-200 rounded-lg p-5">
102
+ <h3 class="font-semibold text-gray-900">Two-minute setup</h3>
103
+ <p class="mt-2 text-sm text-gray-500">One npm install, one command. No config files, no Docker, no infrastructure. Works with your existing Slack workspace.</p>
104
+ </div>
105
+ </div>
106
+ </div>
53
107
 
108
+ <!-- Slack demo -->
109
+ <div class="py-16 border-t border-gray-200">
110
+ <h2 class="text-sm font-semibold text-gray-400 uppercase tracking-wider">What your team sees in Slack</h2>
54
111
  <div class="mt-8 bg-white border border-gray-200 rounded-xl shadow-lg overflow-hidden">
55
112
  <div class="flex">
56
113
  <div class="w-14 bg-[#4A154B] shrink-0 flex flex-col items-center py-3 gap-3">
@@ -58,9 +115,6 @@ export function renderLandingPage(): string {
58
115
  <div class="w-8 h-8 rounded-lg bg-white/10 flex items-center justify-center">
59
116
  <svg class="w-4 h-4 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 10h.01M12 10h.01M16 10h.01M21 12c0 4.418-4.03 8-9 8a9.863 9.863 0 01-4.255-.949L3 20l1.395-3.72C3.512 15.042 3 13.574 3 12c0-4.418 4.03-8 9-8s9 3.582 9 8z"/></svg>
60
117
  </div>
61
- <div class="w-8 h-8 rounded-lg bg-white/10 flex items-center justify-center">
62
- <svg class="w-4 h-4 text-white/60" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9"/></svg>
63
- </div>
64
118
  </div>
65
119
  <div class="flex-1">
66
120
  <div class="border-b border-gray-200 px-4 py-2.5 flex items-center justify-between">
@@ -107,33 +161,46 @@ export function renderLandingPage(): string {
107
161
  <p class="text-gray-700">Good point from Priya. Switching to RS256 and updating the key config...</p>
108
162
  </div>
109
163
  </div>
110
- <div class="flex gap-3">
111
- <div class="w-8 h-8 rounded-md bg-purple-600 flex items-center justify-center text-white text-xs font-bold shrink-0 mt-0.5">
112
- <svg class="w-4 h-4 text-white" fill="currentColor" viewBox="0 0 24 24"><path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-2 15l-5-5 1.41-1.41L10 14.17l7.59-7.59L19 8l-9 9z"/></svg>
113
- </div>
114
- <div>
115
- <div class="flex items-baseline gap-2"><span class="text-gray-900 font-bold text-sm">security-bot</span><span class="bg-gray-100 text-gray-600 text-xs px-1.5 py-0.5 rounded">agent</span><span class="text-gray-400 text-xs">&rarr; auth</span><span class="text-gray-400 text-xs">10:34 AM</span></div>
116
- <p class="text-gray-700">This auth endpoint needs rate limiting before going to production</p>
117
- </div>
118
- </div>
119
- <div class="flex gap-3">
120
- <div class="w-8 h-8 rounded-md bg-green-600 flex items-center justify-center text-white text-xs font-bold shrink-0 mt-0.5">AI</div>
121
- <div>
122
- <div class="flex items-baseline gap-2"><span class="text-gray-900 font-bold text-sm">Agent</span><span class="text-gray-400 text-xs">&rarr; manu/auth</span><span class="text-gray-400 text-xs">10:34 AM</span></div>
123
- <p class="text-gray-700">Adding rate limiting middleware to the auth endpoints...</p>
124
- </div>
125
- </div>
126
164
  </div>
127
165
  </div>
128
166
  </div>
129
167
  </div>
130
- <p class="mt-4 text-center text-sm text-gray-500">The floor — a continuous, attributed log of how your work gets built.</p>
168
+ </div>
169
+
170
+ <!-- Get Started -->
171
+ <div id="get-started" class="py-16 border-t border-gray-200">
172
+ <h2 class="text-2xl font-bold text-gray-900">Get started</h2>
173
+ <div class="mt-6 bg-gray-900 rounded-xl overflow-hidden">
174
+ <div class="flex items-center justify-between px-4 py-2.5 bg-gray-800">
175
+ <span class="text-xs text-gray-500 font-mono">terminal</span>
176
+ <button class="polaris-copy text-gray-500 hover:text-gray-300 transition" data-copy="install-cmd">
177
+ <svg class="w-3.5 h-3.5" fill="none" stroke="currentColor" viewBox="0 0 24 24"><path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M8 16H6a2 2 0 01-2-2V6a2 2 0 012-2h8a2 2 0 012 2v2m-6 12h8a2 2 0 002-2v-8a2 2 0 00-2-2h-8a2 2 0 00-2 2v8a2 2 0 002 2z"/></svg>
178
+ </button>
179
+ </div>
180
+ <pre class="px-5 py-4 font-mono text-sm text-gray-300 leading-relaxed" id="install-cmd"><span class="text-green-400">$</span> npm install -g @lightupai/polaris
181
+ <span class="text-green-400">$</span> polaris</pre>
182
+ </div>
183
+ <p class="mt-4 text-sm text-gray-500">Then in Claude Code:</p>
184
+ <div class="mt-2 bg-gray-900 rounded-xl overflow-hidden">
185
+ <pre class="px-5 py-4 font-mono text-sm text-gray-300 leading-relaxed"><span class="text-polaris-400">&gt;</span> /polaris join #your-channel</pre>
186
+ </div>
187
+ <p class="mt-6 text-sm text-gray-500">That's it. Your session is now live to your team.</p>
188
+ <div class="mt-8">
189
+ <a href="/signup" class="inline-flex items-center gap-3 px-5 py-2.5 bg-white border border-gray-300 rounded-lg shadow-sm hover:bg-gray-50 transition text-sm font-medium text-gray-700">
190
+ <svg width="16" height="16" viewBox="0 0 18 18" xmlns="http://www.w3.org/2000/svg"><path d="M17.64 9.2c0-.637-.057-1.251-.164-1.84H9v3.481h4.844a4.14 4.14 0 01-1.796 2.716v2.259h2.908c1.702-1.567 2.684-3.875 2.684-6.615z" fill="#4285F4"/><path d="M9 18c2.43 0 4.467-.806 5.956-2.18l-2.908-2.259c-.806.54-1.837.86-3.048.86-2.344 0-4.328-1.584-5.036-3.711H.957v2.332A8.997 8.997 0 009 18z" fill="#34A853"/><path d="M3.964 10.71A5.41 5.41 0 013.682 9c0-.593.102-1.17.282-1.71V4.958H.957A8.996 8.996 0 000 9c0 1.452.348 2.827.957 4.042l3.007-2.332z" fill="#FBBC05"/><path d="M9 3.58c1.321 0 2.508.454 3.44 1.345l2.582-2.58C13.463.891 11.426 0 9 0A8.997 8.997 0 00.957 4.958L3.964 6.29C4.672 4.163 6.656 2.58 9 3.58z" fill="#EA4335"/></svg>
191
+ Sign up with Google
192
+ </a>
193
+ </div>
131
194
  </div>
132
195
  </div>
133
196
 
134
- <footer class="border-t border-gray-200 mt-16">
135
- <div class="max-w-5xl mx-auto px-6 py-8 text-center text-sm text-gray-500">
136
- Polaris by Lightup
197
+ <footer class="border-t border-gray-200 mt-8">
198
+ <div class="max-w-3xl mx-auto px-6 py-8 flex items-center justify-between text-sm text-gray-400">
199
+ <span>Polaris</span>
200
+ <div class="flex items-center gap-6">
201
+ <a href="https://github.com/anthropics/polaris" class="hover:text-gray-600 transition">GitHub</a>
202
+ <a href="/login" class="hover:text-gray-600 transition">Sign in</a>
203
+ </div>
137
204
  </div>
138
205
  </footer>`;
139
206
  }
package/tests/web.test.ts CHANGED
@@ -275,7 +275,7 @@ describe("routes", () => {
275
275
  const res = await app.request("/");
276
276
  expect(res.status).toBe(200);
277
277
  const body = await res.text();
278
- expect(body).toContain("Multiplayer AI collaboration");
278
+ expect(body).toContain("Multiplayer collaboration");
279
279
  });
280
280
 
281
281
  test("GET /preview returns 200", async () => {