vibegroup 0.1.0 → 0.1.1

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.
@@ -0,0 +1,18 @@
1
+ {
2
+ "name": "vibegroup",
3
+ "owner": {
4
+ "name": "Terry Cruz Melo",
5
+ "email": "terry@cruz.pe"
6
+ },
7
+ "description": "vibegroup: let your Claude Code agents talk to each other across repos, machines, and networks.",
8
+ "plugins": [
9
+ {
10
+ "name": "vibegroup",
11
+ "source": "./plugin",
12
+ "description": "Ask peer Claude Code agents what they're working on, over an end-to-end-encrypted relay. Answers come from their live session via Claude Code Channels.",
13
+ "homepage": "https://vibegroup.sh",
14
+ "license": "MIT",
15
+ "keywords": ["claude-code", "channel", "mcp", "agents", "collaboration", "relay"]
16
+ }
17
+ ]
18
+ }
package/dist/cli.js CHANGED
@@ -81,6 +81,12 @@ function mergeManagedSettings(existing) {
81
81
  next.allowedChannelPlugins = has ? current : [...current, VIBEGROUP_ENTRY];
82
82
  return next;
83
83
  }
84
+ function isAllowlisted(existing) {
85
+ if (!existing || existing.channelsEnabled !== true)
86
+ return false;
87
+ const current = Array.isArray(existing.allowedChannelPlugins) ? existing.allowedChannelPlugins : [];
88
+ return current.some((e) => e?.marketplace === "vibegroup" && e?.plugin === "vibegroup");
89
+ }
84
90
  var VIBEGROUP_ENTRY;
85
91
  var init_allowlist = __esm(() => {
86
92
  VIBEGROUP_ENTRY = { marketplace: "vibegroup", plugin: "vibegroup" };
@@ -111,6 +117,25 @@ var init_allowlist2 = __esm(() => {
111
117
  init_allowlist();
112
118
  });
113
119
 
120
+ // src/ui/runner.ts
121
+ import { render } from "ink";
122
+ function runInk(make) {
123
+ return new Promise((resolve) => {
124
+ let done = false;
125
+ let instance;
126
+ const finish = (code) => {
127
+ if (done)
128
+ return;
129
+ done = true;
130
+ instance?.unmount();
131
+ resolve(code);
132
+ };
133
+ instance = render(make(finish));
134
+ instance.waitUntilExit().then(() => finish(130));
135
+ });
136
+ }
137
+ var init_runner = () => {};
138
+
114
139
  // src/lib/workosAuth.ts
115
140
  async function json(res) {
116
141
  const text = await res.text();
@@ -191,18 +216,6 @@ async function register(ep, body, f) {
191
216
  claim: parseCeremony(data.claim)
192
217
  };
193
218
  }
194
- async function startClaim(ep, claimToken, email, f) {
195
- const res = await f(ep.claimEndpoint, {
196
- method: "POST",
197
- headers: { "content-type": "application/json" },
198
- body: JSON.stringify({ claim_token: claimToken, email })
199
- });
200
- const data = await json(res);
201
- const ceremony = parseCeremony(data.claim_attempt) ?? parseCeremony(data.claim);
202
- if (!ceremony)
203
- throw new Error(`claim start failed: ${data.error ?? `HTTP ${res.status}`}`);
204
- return ceremony;
205
- }
206
219
  async function pollClaim(tokenEndpoint, claimToken, f, nowMs = Date.now()) {
207
220
  const res = await f(tokenEndpoint, {
208
221
  method: "POST",
@@ -223,24 +236,7 @@ async function pollClaim(tokenEndpoint, claimToken, f, nowMs = Date.now()) {
223
236
  throw new Error(`claim poll failed: ${data.error ?? `HTTP ${res.status}`}`);
224
237
  }
225
238
  }
226
- async function exchangeAssertion(tokenEndpoint, assertion, resource, f, nowMs = Date.now()) {
227
- const params = new URLSearchParams({ grant_type: JWT_BEARER_GRANT, assertion });
228
- if (resource)
229
- params.set("resource", resource);
230
- const res = await f(tokenEndpoint, {
231
- method: "POST",
232
- headers: { "content-type": "application/x-www-form-urlencoded" },
233
- body: params.toString()
234
- });
235
- const data = await json(res);
236
- if (!res.ok || !data.access_token)
237
- throw new Error(`assertion exchange failed: ${data.error ?? `HTTP ${res.status}`}`);
238
- const tokens = tokensFromResponse(data, nowMs);
239
- if (!tokens.identityAssertion)
240
- tokens.identityAssertion = assertion;
241
- return tokens;
242
- }
243
- var CLAIM_GRANT = "urn:workos:agent-auth:grant-type:claim", JWT_BEARER_GRANT = "urn:ietf:params:oauth:grant-type:jwt-bearer";
239
+ var CLAIM_GRANT = "urn:workos:agent-auth:grant-type:claim";
244
240
 
245
241
  // src/lib/loginFlow.ts
246
242
  async function login(apiBase, f, hooks) {
@@ -250,116 +246,332 @@ async function login(apiBase, f, hooks) {
250
246
  emit({ type: "discovering", apiBase });
251
247
  const ep = await discover(apiBase, f);
252
248
  emit({ type: "discovered", endpoints: ep });
253
- if (!await hooks.consent({ resource: ep.resource, scopes: ep.scopesSupported }))
249
+ const email = (await hooks.email())?.trim();
250
+ if (!email)
254
251
  return null;
255
- emit({ type: "registering" });
256
- const reg = await register(ep, { type: "anonymous" }, f);
257
- if (!reg.identityAssertion)
258
- throw new Error("register did not return a pre-claim assertion");
259
- let tokens = await exchangeAssertion(ep.tokenEndpoint, reg.identityAssertion, ep.resource, f, now());
260
- emit({ type: "registered", scopes: reg.preClaimScopes });
261
- const email = hooks.emailFor ? await hooks.emailFor() : null;
262
- if (email && reg.claimToken) {
263
- const ceremony = await startClaim(ep, reg.claimToken, email, f);
264
- emit({ type: "claim", ceremony });
265
- if (hooks.waitForCode)
266
- await hooks.waitForCode();
267
- emit({ type: "polling" });
268
- let interval = Math.max(1, ceremony.interval);
269
- for (;; ) {
270
- const r = await pollClaim(ep.tokenEndpoint, reg.claimToken, f, now());
271
- if (r.status === "done") {
272
- tokens = r.tokens;
273
- emit({ type: "unlocked", scopes: reg.postClaimScopes });
274
- return { tokens, claimed: true };
275
- }
276
- if (r.status === "expired")
277
- break;
278
- if (r.status === "slow_down")
279
- interval += 5;
280
- await sleep(interval * 1000);
252
+ emit({ type: "registering", email });
253
+ const reg = await register(ep, { type: "service_auth", login_hint: email }, f);
254
+ if (!reg.claim || !reg.claimToken)
255
+ throw new Error("service_auth did not return a claim ceremony");
256
+ emit({ type: "claim", ceremony: reg.claim });
257
+ emit({ type: "polling" });
258
+ let interval = Math.max(1, reg.claim.interval);
259
+ const deadline = now() + (hooks.timeoutMs ?? 10 * 60 * 1000);
260
+ for (;; ) {
261
+ const r = await pollClaim(ep.tokenEndpoint, reg.claimToken, f, now());
262
+ if (r.status === "done") {
263
+ emit({ type: "done" });
264
+ return { tokens: r.tokens, email };
281
265
  }
266
+ if (r.status === "expired")
267
+ throw new Error("the code expired — run `vibegroup login` again");
268
+ if (r.status === "slow_down")
269
+ interval += 5;
270
+ if (now() > deadline)
271
+ throw new Error("login timed out");
272
+ await sleep(interval * 1000);
282
273
  }
283
- return { tokens, claimed: false };
284
274
  }
285
275
  var defaultSleep = (ms) => new Promise((r) => setTimeout(r, ms));
286
276
  var init_loginFlow = () => {};
287
277
 
278
+ // src/ui/session.ts
279
+ function jwtClaim(jwt, key) {
280
+ try {
281
+ return JSON.parse(Buffer.from(jwt.split(".")[1], "base64url").toString("utf8"))[key];
282
+ } catch {
283
+ return;
284
+ }
285
+ }
286
+ function sessionFromTokens(tokens, email) {
287
+ const sub = jwtClaim(tokens.accessToken, "sub");
288
+ const exp = jwtClaim(tokens.accessToken, "exp");
289
+ return {
290
+ ...tokens,
291
+ user: { id: typeof sub === "string" ? sub : email, email },
292
+ accessTokenExpiresAt: typeof exp === "number" ? new Date(exp * 1000).toISOString() : tokens.accessTokenExpiresAt
293
+ };
294
+ }
295
+
296
+ // src/lib/openBrowser.ts
297
+ import { spawn } from "node:child_process";
298
+ function openBrowser(url) {
299
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "cmd" : "xdg-open";
300
+ const args = process.platform === "win32" ? ["/c", "start", "", url] : [url];
301
+ try {
302
+ spawn(cmd, args, { stdio: "ignore", detached: true }).unref();
303
+ } catch {}
304
+ }
305
+ var init_openBrowser = () => {};
306
+
307
+ // src/ui/theme.ts
308
+ var color;
309
+ var init_theme = __esm(() => {
310
+ color = {
311
+ brand: "magenta",
312
+ accent: "cyan",
313
+ ok: "green",
314
+ warn: "yellow",
315
+ err: "red",
316
+ dim: "gray"
317
+ };
318
+ });
319
+
320
+ // src/ui/Login.tsx
321
+ import { useEffect, useRef, useState } from "react";
322
+ import { Box, Text } from "ink";
323
+ import { Spinner, StatusMessage, TextInput, Alert, Badge } from "@inkjs/ui";
324
+ import { jsx, jsxs } from "react/jsx-runtime";
325
+ function Login({
326
+ apiBase,
327
+ initialEmail,
328
+ onExit
329
+ }) {
330
+ const [phase, setPhase] = useState(initialEmail ? { t: "connecting" } : { t: "email" });
331
+ const opened = useRef(false);
332
+ const started = useRef(false);
333
+ const start = (email) => {
334
+ if (started.current)
335
+ return;
336
+ started.current = true;
337
+ setPhase({ t: "connecting" });
338
+ login(apiBase, fetch, {
339
+ email: async () => email,
340
+ onEvent: (e) => {
341
+ if (e.type === "registering")
342
+ setPhase({ t: "registering", email: e.email });
343
+ else if (e.type === "claim")
344
+ setPhase({ t: "waiting", code: e.ceremony.userCode, url: e.ceremony.verificationUri });
345
+ }
346
+ }).then((res) => {
347
+ if (!res) {
348
+ setPhase({ t: "error", message: "Login cancelled." });
349
+ setTimeout(() => onExit(1), 50);
350
+ return;
351
+ }
352
+ writeAuth(sessionFromTokens(res.tokens, res.email));
353
+ setPhase({ t: "done", email: res.email });
354
+ setTimeout(() => onExit(0), 500);
355
+ }).catch((err) => {
356
+ setPhase({ t: "error", message: err?.message ?? String(err) });
357
+ setTimeout(() => onExit(1), 800);
358
+ });
359
+ };
360
+ useEffect(() => {
361
+ if (initialEmail)
362
+ start(initialEmail);
363
+ }, []);
364
+ useEffect(() => {
365
+ if (phase.t === "waiting" && !opened.current) {
366
+ opened.current = true;
367
+ openBrowser(phase.url);
368
+ }
369
+ }, [phase]);
370
+ return /* @__PURE__ */ jsxs(Box, {
371
+ flexDirection: "column",
372
+ gap: 1,
373
+ paddingY: 1,
374
+ children: [
375
+ /* @__PURE__ */ jsxs(Text, {
376
+ children: [
377
+ /* @__PURE__ */ jsx(Text, {
378
+ color: color.brand,
379
+ bold: true,
380
+ children: "vibegroup"
381
+ }),
382
+ /* @__PURE__ */ jsx(Text, {
383
+ dimColor: true,
384
+ children: " · sign in"
385
+ })
386
+ ]
387
+ }),
388
+ phase.t === "email" && /* @__PURE__ */ jsxs(Box, {
389
+ flexDirection: "column",
390
+ children: [
391
+ /* @__PURE__ */ jsx(Text, {
392
+ children: "Email to sign in with:"
393
+ }),
394
+ /* @__PURE__ */ jsx(Box, {
395
+ marginTop: 1,
396
+ children: /* @__PURE__ */ jsx(TextInput, {
397
+ placeholder: "you@company.com",
398
+ onSubmit: (value) => {
399
+ const email = value.trim();
400
+ if (email)
401
+ start(email);
402
+ else
403
+ onExit(1);
404
+ }
405
+ })
406
+ })
407
+ ]
408
+ }),
409
+ phase.t === "connecting" && /* @__PURE__ */ jsx(Spinner, {
410
+ label: "Connecting to vibegroup…"
411
+ }),
412
+ phase.t === "registering" && /* @__PURE__ */ jsx(Spinner, {
413
+ label: `Registering ${phase.email}…`
414
+ }),
415
+ phase.t === "waiting" && /* @__PURE__ */ jsxs(Box, {
416
+ flexDirection: "column",
417
+ gap: 1,
418
+ children: [
419
+ /* @__PURE__ */ jsxs(Box, {
420
+ borderStyle: "round",
421
+ borderColor: color.accent,
422
+ paddingX: 1,
423
+ flexDirection: "column",
424
+ children: [
425
+ /* @__PURE__ */ jsx(Text, {
426
+ children: "Open this link, sign in, and enter the code:"
427
+ }),
428
+ /* @__PURE__ */ jsx(Box, {
429
+ marginY: 1,
430
+ children: /* @__PURE__ */ jsx(Badge, {
431
+ color: "cyan",
432
+ children: phase.code
433
+ })
434
+ }),
435
+ /* @__PURE__ */ jsx(Text, {
436
+ color: color.accent,
437
+ children: phase.url
438
+ }),
439
+ /* @__PURE__ */ jsx(Text, {
440
+ dimColor: true,
441
+ children: "(the code goes into that page — not back here)"
442
+ })
443
+ ]
444
+ }),
445
+ /* @__PURE__ */ jsx(Spinner, {
446
+ label: "Waiting for you to confirm in the browser…"
447
+ })
448
+ ]
449
+ }),
450
+ phase.t === "done" && /* @__PURE__ */ jsxs(StatusMessage, {
451
+ variant: "success",
452
+ children: [
453
+ "Signed in as ",
454
+ phase.email,
455
+ ". You're good to go!"
456
+ ]
457
+ }),
458
+ phase.t === "error" && /* @__PURE__ */ jsx(Alert, {
459
+ variant: "error",
460
+ children: phase.message
461
+ })
462
+ ]
463
+ });
464
+ }
465
+ var init_Login = __esm(() => {
466
+ init_loginFlow();
467
+ init_auth();
468
+ init_openBrowser();
469
+ init_theme();
470
+ });
471
+
288
472
  // src/commands/login.ts
289
473
  var exports_login = {};
290
474
  __export(exports_login, {
291
475
  loginCommand: () => loginCommand
292
476
  });
293
- import { createInterface } from "node:readline/promises";
294
- async function ask(question) {
295
- const rl = createInterface({ input: process.stdin, output: process.stdout });
296
- try {
297
- return (await rl.question(question)).trim();
298
- } finally {
299
- rl.close();
300
- }
477
+ import { createElement } from "react";
478
+ async function loginCommand(rest, env = process.env) {
479
+ const base = apiBase(env);
480
+ return runInk((onExit) => createElement(Login, { apiBase: base, initialEmail: rest[0], onExit }));
301
481
  }
302
- async function confirm(question) {
303
- return /^y(es)?$/i.test(await ask(`${question} [y/N] `));
304
- }
305
- function render(e) {
306
- switch (e.type) {
307
- case "discovering":
308
- console.log(`
309
- Checking ${e.apiBase}/auth.md for instructions`);
310
- break;
311
- case "discovered":
312
- console.log(" └ Found agent_auth · anonymous registration supported · claim to unlock full access");
313
- break;
314
- case "registering":
315
- console.log("• Registering with vibegroup");
316
- break;
317
- case "registered":
318
- console.log(" 200 OK · registered, acting with limited access");
319
- break;
320
- case "claim":
321
- console.log(`
322
- To unlock your account, sign in at vibegroup and enter this code: ${e.ceremony.userCode}`);
323
- console.log(`${e.ceremony.verificationUri} — the code goes in there, not back to me.`);
324
- break;
325
- case "polling":
326
- console.log(`
327
- Waiting for you to confirm…`);
328
- break;
329
- case "unlocked":
330
- console.log(" └ Unlocked · full access");
331
- break;
332
- }
482
+ var init_login = __esm(() => {
483
+ init_runner();
484
+ init_Login();
485
+ init_cli();
486
+ });
487
+
488
+ // src/commands/init.ts
489
+ var exports_init = {};
490
+ __export(exports_init, {
491
+ initCommand: () => initCommand
492
+ });
493
+ import { spawnSync } from "node:child_process";
494
+ import { existsSync as existsSync3, readFileSync as readFileSync3 } from "node:fs";
495
+ import { fileURLToPath } from "node:url";
496
+ import { dirname as dirname2, join as join2 } from "node:path";
497
+ function packageRoot() {
498
+ return join2(dirname2(fileURLToPath(import.meta.url)), "..");
333
499
  }
334
- async function loginCommand(env = process.env) {
335
- const base = apiBase(env);
500
+ function commandExists(cmd) {
501
+ return spawnSync(cmd, ["--version"], { stdio: "ignore" }).status === 0;
502
+ }
503
+ function pluginInstalled() {
504
+ const r = spawnSync("claude", ["plugin", "list"], { encoding: "utf8" });
505
+ return r.status === 0 && /vibegroup@vibegroup/.test(r.stdout ?? "");
506
+ }
507
+ function readManagedSettings() {
336
508
  try {
337
- const result = await login(base, fetch, {
338
- consent: ({ resource }) => confirm(`Allow agent to register for vibegroup (${resource}) on your behalf?`),
339
- emailFor: async () => {
340
- const email = await ask("Email to link your account (leave blank to stay anonymous): ");
341
- return email || null;
342
- },
343
- onEvent: render
344
- });
345
- if (!result) {
346
- console.log("Cancelled nothing was registered.");
509
+ const p = managedSettingsPath();
510
+ if (existsSync3(p))
511
+ return JSON.parse(readFileSync3(p, "utf8"));
512
+ } catch {}
513
+ return null;
514
+ }
515
+ function writeAllowlistWithSudo() {
516
+ const path = managedSettingsPath();
517
+ const json2 = JSON.stringify(mergeManagedSettings(readManagedSettings()), null, 2);
518
+ const script = `mkdir -p "${dirname2(path)}" && cat > "${path}"`;
519
+ const r = spawnSync("sudo", ["sh", "-c", script], { input: json2, stdio: ["pipe", "inherit", "inherit"] });
520
+ return r.status === 0;
521
+ }
522
+ async function initCommand(rest, flags = {}, env = process.env) {
523
+ const dev = flags.dev === true;
524
+ console.log(`vibegroup setup
525
+ `);
526
+ if (!commandExists("claude")) {
527
+ console.error("Claude Code (`claude`) is not on your PATH. Install it first: https://docs.claude.com/claude-code");
528
+ return 1;
529
+ }
530
+ if (pluginInstalled()) {
531
+ console.log("✓ Plugin already installed.");
532
+ } else {
533
+ console.log("• Installing the vibegroup plugin into Claude Code…");
534
+ spawnSync("claude", ["plugin", "marketplace", "add", packageRoot()], { stdio: "inherit" });
535
+ spawnSync("claude", ["plugin", "install", "vibegroup@vibegroup"], { stdio: "inherit" });
536
+ if (!pluginInstalled()) {
537
+ console.error("Plugin install did not complete. Try manually: claude plugin install vibegroup@vibegroup");
347
538
  return 1;
348
539
  }
349
- writeAuth(result.tokens, env);
350
- console.log(result.claimed ? `
351
- Logged in. You're good to go!` : "\nRegistered with limited access. Run `vibegroup login` again anytime to link your account and unlock full access.");
352
- return 0;
353
- } catch (err) {
354
- console.error(`
355
- login failed: ${err.message}`);
356
- return 1;
540
+ console.log("✓ Plugin installed.");
357
541
  }
542
+ if (dev) {
543
+ console.log("• Dev mode: skipping the channel allowlist (launch with `vibegroup claude --dev`).");
544
+ } else if (isAllowlisted(readManagedSettings())) {
545
+ console.log("✓ Channel already enabled.");
546
+ } else {
547
+ console.log(`
548
+ • Enabling the vibegroup channel needs admin once (Claude Code gates channel plugins).`);
549
+ console.log(` Writing ${managedSettingsPath()} — enter your password if prompted.`);
550
+ if (!writeAllowlistWithSudo()) {
551
+ console.error(" Could not write managed settings. Retry, or run `vibegroup init --dev` to skip it and use `vibegroup claude --dev`.");
552
+ return 1;
553
+ }
554
+ console.log("✓ Channel enabled.");
555
+ }
556
+ if (isLoggedIn(readAuth(env))) {
557
+ console.log("✓ Already signed in.");
558
+ } else {
559
+ console.log(`
560
+ • Sign in to vibegroup:`);
561
+ const code = await loginCommand(rest, env);
562
+ if (code !== 0)
563
+ return code;
564
+ }
565
+ console.log(`
566
+ ✓ Setup complete. Next:`);
567
+ console.log(' vibegroup team create <slug> --name "<name>" # if you need a team');
568
+ console.log(" vibegroup claude --team <slug> # launch a channel session");
569
+ return 0;
358
570
  }
359
- var init_login = __esm(() => {
360
- init_loginFlow();
571
+ var init_init = __esm(() => {
361
572
  init_auth();
362
- init_cli();
573
+ init_allowlist();
574
+ init_login();
363
575
  });
364
576
 
365
577
  // src/lib/apiClient.ts
@@ -480,7 +692,7 @@ var init_team = __esm(() => {
480
692
  });
481
693
 
482
694
  // src/lib/claudeLaunch.ts
483
- import { spawnSync } from "node:child_process";
695
+ import { spawnSync as spawnSync2 } from "node:child_process";
484
696
  function buildClaudeArgs(opts = {}) {
485
697
  const extra = opts.extraArgs ?? [];
486
698
  return opts.dangerously ? ["--dangerously-load-development-channels", CHANNEL_SPEC, ...extra] : ["--channels", CHANNEL_SPEC, ...extra];
@@ -489,7 +701,7 @@ function launchClaude(opts = {}, launcher = defaultLauncher) {
489
701
  return launcher("claude", buildClaudeArgs(opts), opts.env);
490
702
  }
491
703
  var CHANNEL_SPEC = "plugin:vibegroup@vibegroup", defaultLauncher = (cmd, args, env) => {
492
- const r = spawnSync(cmd, args, { stdio: "inherit", env: env ? { ...process.env, ...env } : process.env });
704
+ const r = spawnSync2(cmd, args, { stdio: "inherit", env: env ? { ...process.env, ...env } : process.env });
493
705
  if (r.error)
494
706
  throw r.error;
495
707
  return r.status ?? 0;
@@ -620,9 +832,13 @@ async function run(argv, env = process.env) {
620
832
  const { allowlistPathCommand: allowlistPathCommand2 } = await Promise.resolve().then(() => (init_allowlist2(), exports_allowlist));
621
833
  return allowlistPathCommand2();
622
834
  }
835
+ case "init": {
836
+ const { initCommand: initCommand2 } = await Promise.resolve().then(() => (init_init(), exports_init));
837
+ return initCommand2(rest, flags, env);
838
+ }
623
839
  case "login": {
624
840
  const { loginCommand: loginCommand2 } = await Promise.resolve().then(() => (init_login(), exports_login));
625
- return loginCommand2(env);
841
+ return loginCommand2(rest, env);
626
842
  }
627
843
  case "team": {
628
844
  const { teamCommand: teamCommand2 } = await Promise.resolve().then(() => (init_team(), exports_team));
@@ -652,7 +868,8 @@ var VERSION = "0.1.0", DEFAULT_API_BASE2 = "https://api.vibegroup.sh", HELP = `v
652
868
  Usage: vibegroup <command> [options]
653
869
 
654
870
  Commands:
655
- login Sign in / sign up (opens your browser)
871
+ init [email] One-time setup: install the plugin, enable the channel, sign in
872
+ login [email] Sign in / sign up (opens your browser, verifies email)
656
873
  logout Clear the cached session
657
874
  status Show your auth + connection status
658
875
  team create <slug> [--name] Create a team (a WorkOS org + a general room)
package/package.json CHANGED
@@ -1,29 +1,54 @@
1
1
  {
2
2
  "name": "vibegroup",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Talk to your teammates' Claude Code agents — agent-to-agent collaboration for Claude Code over a shared channel.",
5
5
  "type": "module",
6
- "bin": { "vibegroup": "dist/cli.js" },
7
- "files": ["dist/cli.js", "README.md"],
8
- "engines": { "node": ">=18" },
6
+ "bin": {
7
+ "vibegroup": "dist/cli.js"
8
+ },
9
+ "files": [
10
+ "dist/cli.js",
11
+ "README.md",
12
+ ".claude-plugin/marketplace.json",
13
+ "plugin/.claude-plugin",
14
+ "plugin/.mcp.json",
15
+ "plugin/dist",
16
+ "plugin/commands",
17
+ "plugin/hooks"
18
+ ],
19
+ "engines": {
20
+ "node": ">=18"
21
+ },
9
22
  "license": "UNLICENSED",
10
23
  "homepage": "https://vibegroup.sh",
11
- "bugs": { "url": "https://vibegroup.sh" },
24
+ "bugs": {
25
+ "url": "https://vibegroup.sh"
26
+ },
12
27
  "keywords": ["claude", "claude-code", "agents", "collaboration", "cli", "vibegroup"],
13
- "publishConfig": { "access": "public" },
28
+ "publishConfig": {
29
+ "access": "public"
30
+ },
14
31
  "workspaces": ["packages/*", "plugin"],
15
32
  "scripts": {
16
33
  "build:relay": "cd packages/relay && bun run build",
17
34
  "build:plugin": "cd plugin && bun run build",
18
- "build:cli": "bun build src/bin.ts --target node --outfile dist/cli.js --banner \"#!/usr/bin/env node\"",
35
+ "build:cli": "bun run scripts/build-cli.ts",
19
36
  "build:server": "cd packages/server && bun run build",
20
37
  "build": "bun run build:relay && bun run build:server && bun run build:plugin && bun run build:cli",
21
- "prepublishOnly": "bun run build:cli",
38
+ "prepublishOnly": "bun run build:plugin && bun run build:cli",
22
39
  "test:cli": "bun test ./test/",
23
40
  "test:protocol": "cd packages/protocol && bun test",
24
41
  "test:relay": "cd packages/relay && bun test",
25
42
  "test:server": "cd packages/server && bun test",
26
43
  "test:plugin": "cd plugin && bun test",
27
44
  "test": "bun run test:cli && bun run test:protocol && bun run test:relay && bun run test:server && bun run test:plugin"
45
+ },
46
+ "dependencies": {
47
+ "@inkjs/ui": "^2.0.0",
48
+ "ink": "^7.1.0",
49
+ "react": "^19.2.7"
50
+ },
51
+ "devDependencies": {
52
+ "@types/react": "^19.2.17"
28
53
  }
29
54
  }
@@ -0,0 +1,13 @@
1
+ {
2
+ "name": "vibegroup",
3
+ "version": "0.0.1",
4
+ "description": "Ask peer Claude Code agents in other repos and machines what they're working on, over an end-to-end-encrypted relay. Answers come from their live session via Claude Code Channels.",
5
+ "author": {
6
+ "name": "Terry Cruz Melo",
7
+ "email": "terry@cruz.pe"
8
+ },
9
+ "homepage": "https://vibegroup.sh",
10
+ "repository": "https://github.com/TerryCM/vibegroup",
11
+ "license": "MIT",
12
+ "keywords": ["claude-code", "channel", "mcp", "agents", "collaboration", "relay"]
13
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "mcpServers": {
3
+ "vibegroup": {
4
+ "command": "bun",
5
+ "args": ["${CLAUDE_PLUGIN_ROOT}/dist/channel.js"]
6
+ }
7
+ }
8
+ }