@meet-ai/cli 0.0.35 → 0.0.36

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.
Files changed (3) hide show
  1. package/dist/index.js +1155 -276
  2. package/package.json +2 -1
  3. package/README.md +0 -114
package/dist/index.js CHANGED
@@ -14390,6 +14390,100 @@ var init_zod = __esm(() => {
14390
14390
  init_external();
14391
14391
  });
14392
14392
 
14393
+ // src/lib/codex.ts
14394
+ import { existsSync, mkdirSync, readFileSync, readdirSync, statSync, writeFileSync } from "node:fs";
14395
+ import { homedir } from "node:os";
14396
+ import { dirname, join, resolve } from "node:path";
14397
+ function getCodexHome(options) {
14398
+ if (options?.codexHome)
14399
+ return options.codexHome;
14400
+ if (options?.env?.CODEX_HOME)
14401
+ return options.env.CODEX_HOME;
14402
+ if (process.env.CODEX_HOME)
14403
+ return process.env.CODEX_HOME;
14404
+ if (process.env.MEET_AI_CODEX_STATE_DIR)
14405
+ return process.env.MEET_AI_CODEX_STATE_DIR;
14406
+ return join(homedir(), ".codex");
14407
+ }
14408
+ function getCodexConfigPaths(options) {
14409
+ const home = getCodexHome(options);
14410
+ return [
14411
+ resolve(".codex/config.json"),
14412
+ resolve(".codex/config.toml"),
14413
+ join(home, "config.json"),
14414
+ join(home, "config.toml")
14415
+ ];
14416
+ }
14417
+ function parseCodexTomlEnv(path) {
14418
+ if (!existsSync(path))
14419
+ return null;
14420
+ const env = {};
14421
+ let inEnvSection = false;
14422
+ for (const line of readFileSync(path, "utf-8").split(/\r?\n/)) {
14423
+ const trimmed = line.trim();
14424
+ if (!trimmed || trimmed.startsWith("#"))
14425
+ continue;
14426
+ if (trimmed.startsWith("[") && trimmed.endsWith("]")) {
14427
+ inEnvSection = trimmed === "[env]";
14428
+ continue;
14429
+ }
14430
+ if (!inEnvSection)
14431
+ continue;
14432
+ const match = trimmed.match(/^([A-Z0-9_]+)\s*=\s*"([^"]*)"$/);
14433
+ if (!match)
14434
+ continue;
14435
+ env[match[1]] = match[2];
14436
+ }
14437
+ return Object.keys(env).length > 0 ? env : null;
14438
+ }
14439
+ function readCodexConfigEnv(options) {
14440
+ for (const path of getCodexConfigPaths(options)) {
14441
+ if (!existsSync(path))
14442
+ continue;
14443
+ if (path.endsWith(".json")) {
14444
+ try {
14445
+ const parsed = JSON.parse(readFileSync(path, "utf-8"));
14446
+ if (parsed.env && Object.keys(parsed.env).length > 0) {
14447
+ return parsed.env;
14448
+ }
14449
+ } catch {
14450
+ continue;
14451
+ }
14452
+ continue;
14453
+ }
14454
+ const env = parseCodexTomlEnv(path);
14455
+ if (env)
14456
+ return env;
14457
+ }
14458
+ return null;
14459
+ }
14460
+ function getInboxPath(sessionId, options) {
14461
+ return join(getCodexHome(options), "meet-ai", "inbox", `${sessionId}.json`);
14462
+ }
14463
+ function appendCodexInboxEntry(sessionId, entry, options) {
14464
+ const path = getInboxPath(sessionId, options);
14465
+ mkdirSync(dirname(path), { recursive: true });
14466
+ let entries = [];
14467
+ try {
14468
+ entries = JSON.parse(readFileSync(path, "utf-8"));
14469
+ } catch {
14470
+ entries = [];
14471
+ }
14472
+ entries.push(entry);
14473
+ writeFileSync(path, `${JSON.stringify(entries, null, 2)}
14474
+ `);
14475
+ return path;
14476
+ }
14477
+ var init_codex = () => {};
14478
+
14479
+ // src/runtime.ts
14480
+ function getMeetAiRuntime(env = process.env) {
14481
+ const raw = env.MEET_AI_RUNTIME?.trim().toLowerCase();
14482
+ if (raw === "codex")
14483
+ return "codex";
14484
+ return "claude";
14485
+ }
14486
+
14393
14487
  // src/config.ts
14394
14488
  var exports_config = {};
14395
14489
  __export(exports_config, {
@@ -14397,15 +14491,15 @@ __export(exports_config, {
14397
14491
  getMeetAiConfig: () => getMeetAiConfig,
14398
14492
  configSchema: () => configSchema
14399
14493
  });
14400
- import { readFileSync, existsSync } from "node:fs";
14401
- import { join, resolve } from "node:path";
14402
- import { homedir } from "node:os";
14494
+ import { readFileSync as readFileSync2, existsSync as existsSync2 } from "node:fs";
14495
+ import { join as join2, resolve as resolve2 } from "node:path";
14496
+ import { homedir as homedir2 } from "node:os";
14403
14497
  function loadSettingsFromPath(path) {
14404
14498
  try {
14405
- if (!existsSync(path)) {
14499
+ if (!existsSync2(path)) {
14406
14500
  return null;
14407
14501
  }
14408
- const content = readFileSync(path, "utf-8");
14502
+ const content = readFileSync2(path, "utf-8");
14409
14503
  return JSON.parse(content);
14410
14504
  } catch {
14411
14505
  return null;
@@ -14418,7 +14512,16 @@ function resolveRawConfig(opts) {
14418
14512
  key: process.env.MEET_AI_KEY
14419
14513
  };
14420
14514
  }
14421
- const projectSettingsPath = opts?.projectSettingsPath ?? resolve("./.claude/settings.json");
14515
+ if (getMeetAiRuntime() === "codex") {
14516
+ const codexEnv = readCodexConfigEnv({ codexHome: opts?.codexHome });
14517
+ if (codexEnv?.MEET_AI_URL) {
14518
+ return {
14519
+ url: codexEnv.MEET_AI_URL,
14520
+ key: codexEnv.MEET_AI_KEY
14521
+ };
14522
+ }
14523
+ }
14524
+ const projectSettingsPath = opts?.projectSettingsPath ?? resolve2("./.claude/settings.json");
14422
14525
  const projectSettings = loadSettingsFromPath(projectSettingsPath);
14423
14526
  if (projectSettings?.env?.MEET_AI_URL) {
14424
14527
  return {
@@ -14426,7 +14529,7 @@ function resolveRawConfig(opts) {
14426
14529
  key: projectSettings.env.MEET_AI_KEY
14427
14530
  };
14428
14531
  }
14429
- const userSettingsPath = join(opts?.homeDir ?? homedir(), ".claude/settings.json");
14532
+ const userSettingsPath = join2(opts?.homeDir ?? homedir2(), ".claude/settings.json");
14430
14533
  const userSettings = loadSettingsFromPath(userSettingsPath);
14431
14534
  if (userSettings?.env?.MEET_AI_URL) {
14432
14535
  return {
@@ -14450,6 +14553,7 @@ function getMeetAiConfig(opts) {
14450
14553
  var configSchema;
14451
14554
  var init_config = __esm(() => {
14452
14555
  init_zod();
14556
+ init_codex();
14453
14557
  configSchema = exports_external.object({
14454
14558
  url: exports_external.string().url("MEET_AI_URL must be a valid URL"),
14455
14559
  key: exports_external.string().optional()
@@ -14483,7 +14587,7 @@ async function withRetry(fn, options) {
14483
14587
  delay_ms: delay,
14484
14588
  error: lastError.message
14485
14589
  }));
14486
- await new Promise((resolve2) => setTimeout(resolve2, delay));
14590
+ await new Promise((resolve3) => setTimeout(resolve3, delay));
14487
14591
  }
14488
14592
  }
14489
14593
  throw lastError;
@@ -14583,6 +14687,18 @@ class HttpTransport {
14583
14687
  }
14584
14688
  }
14585
14689
 
14690
+ // src/coding-agents.ts
14691
+ function isCodingAgentId(value) {
14692
+ return CODING_AGENT_DEFINITIONS.some((agent) => agent.id === value);
14693
+ }
14694
+ var CODING_AGENT_DEFINITIONS;
14695
+ var init_coding_agents = __esm(() => {
14696
+ CODING_AGENT_DEFINITIONS = [
14697
+ { id: "claude", label: "Claude Code" },
14698
+ { id: "codex", label: "Codex" }
14699
+ ];
14700
+ });
14701
+
14586
14702
  // src/domain/adapters/ConnectionAdapter.ts
14587
14703
  function wsLog(data) {
14588
14704
  const json2 = JSON.stringify({ ...data, ts: new Date().toISOString() });
@@ -14754,7 +14870,8 @@ class ConnectionAdapter {
14754
14870
  options?.onRoomCreated?.(data.id, data.name);
14755
14871
  }
14756
14872
  if (data.type === "spawn_request" && data.room_name) {
14757
- options?.onSpawnRequest?.(data.room_name);
14873
+ const codingAgent = typeof data.coding_agent === "string" && isCodingAgentId(data.coding_agent) ? data.coding_agent : "claude";
14874
+ options?.onSpawnRequest?.({ roomName: data.room_name, codingAgent });
14758
14875
  }
14759
14876
  } catch {}
14760
14877
  };
@@ -14775,31 +14892,34 @@ class ConnectionAdapter {
14775
14892
  return this.transport.postJson("/api/keys");
14776
14893
  }
14777
14894
  }
14895
+ var init_ConnectionAdapter = __esm(() => {
14896
+ init_coding_agents();
14897
+ });
14778
14898
 
14779
14899
  // src/domain/adapters/FileSystemAdapter.ts
14780
14900
  import {
14781
- readFileSync as readFileSync2,
14782
- writeFileSync,
14783
- mkdirSync,
14784
- existsSync as existsSync2,
14785
- statSync
14901
+ readFileSync as readFileSync3,
14902
+ writeFileSync as writeFileSync2,
14903
+ mkdirSync as mkdirSync2,
14904
+ existsSync as existsSync3,
14905
+ statSync as statSync2
14786
14906
  } from "node:fs";
14787
14907
 
14788
14908
  class FileSystemAdapter {
14789
14909
  readFileSync(path, encoding) {
14790
- return readFileSync2(path, encoding);
14910
+ return readFileSync3(path, encoding);
14791
14911
  }
14792
14912
  writeFileSync(path, data, encoding) {
14793
- writeFileSync(path, data, encoding);
14913
+ writeFileSync2(path, data, encoding);
14794
14914
  }
14795
14915
  mkdirSync(path, opts) {
14796
- mkdirSync(path, opts);
14916
+ mkdirSync2(path, opts);
14797
14917
  }
14798
14918
  existsSync(path) {
14799
- return existsSync2(path);
14919
+ return existsSync3(path);
14800
14920
  }
14801
14921
  statSync(path) {
14802
- const stat = statSync(path);
14922
+ const stat = statSync2(path);
14803
14923
  return { mtimeMs: stat.mtimeMs, size: stat.size };
14804
14924
  }
14805
14925
  }
@@ -14979,7 +15099,7 @@ class SendTerminalData {
14979
15099
  }
14980
15100
 
14981
15101
  // src/domain/services/InboxRouter.ts
14982
- import { dirname } from "node:path";
15102
+ import { dirname as dirname2 } from "node:path";
14983
15103
 
14984
15104
  class InboxRouter {
14985
15105
  fs;
@@ -15002,7 +15122,7 @@ class InboxRouter {
15002
15122
  for (const target of targets) {
15003
15123
  this.appendToInbox(`${opts.inboxDir}/${target}.json`, entry);
15004
15124
  }
15005
- } else if (opts.stdinPane) {} else if (opts.defaultInboxPath) {
15125
+ } else if (opts.defaultInboxPath) {
15006
15126
  this.appendToInbox(opts.defaultInboxPath, entry);
15007
15127
  }
15008
15128
  }
@@ -15022,7 +15142,7 @@ class InboxRouter {
15022
15142
  }
15023
15143
  }
15024
15144
  appendToInbox(path, entry) {
15025
- this.fs.mkdirSync(dirname(path), { recursive: true });
15145
+ this.fs.mkdirSync(dirname2(path), { recursive: true });
15026
15146
  let messages = [];
15027
15147
  try {
15028
15148
  messages = JSON.parse(this.fs.readFileSync(path, "utf-8"));
@@ -15150,12 +15270,12 @@ __export(exports_bootstrap, {
15150
15270
  });
15151
15271
  function cleanupOldAttachments() {
15152
15272
  try {
15153
- const { readdirSync, statSync: statSync2, unlinkSync } = __require("node:fs");
15273
+ const { readdirSync: readdirSync2, statSync: statSync3, unlinkSync } = __require("node:fs");
15154
15274
  const now = Date.now();
15155
- for (const entry of readdirSync(ATTACHMENTS_DIR)) {
15275
+ for (const entry of readdirSync2(ATTACHMENTS_DIR)) {
15156
15276
  try {
15157
15277
  const filePath = `${ATTACHMENTS_DIR}/${entry}`;
15158
- const mtime = statSync2(filePath).mtimeMs;
15278
+ const mtime = statSync3(filePath).mtimeMs;
15159
15279
  if (now - mtime > MAX_AGE_MS) {
15160
15280
  unlinkSync(filePath);
15161
15281
  }
@@ -15211,12 +15331,12 @@ function getClient() {
15211
15331
  downloadAttachment: async (attachmentId) => {
15212
15332
  cleanupOldAttachments();
15213
15333
  const response = await c.downloadAttachment.execute(attachmentId);
15214
- const { mkdirSync: mkdirSync2, writeFileSync: writeFileSync2 } = await import("node:fs");
15215
- mkdirSync2(ATTACHMENTS_DIR, { recursive: true });
15334
+ const { mkdirSync: mkdirSync3, writeFileSync: writeFileSync3 } = await import("node:fs");
15335
+ mkdirSync3(ATTACHMENTS_DIR, { recursive: true });
15216
15336
  const safeId = attachmentId.replace(/[^a-zA-Z0-9_-]/g, "") || "unknown";
15217
15337
  const localPath = `${ATTACHMENTS_DIR}/${safeId}.bin`;
15218
15338
  const buffer = Buffer.from(await response.arrayBuffer());
15219
- writeFileSync2(localPath, buffer);
15339
+ writeFileSync3(localPath, buffer);
15220
15340
  return localPath;
15221
15341
  },
15222
15342
  listenLobby: (options) => c.listenLobby.execute(options),
@@ -15228,6 +15348,7 @@ function getClient() {
15228
15348
  var ATTACHMENTS_DIR = "/tmp/meet-ai-attachments", MAX_AGE_MS, container = null;
15229
15349
  var init_bootstrap = __esm(() => {
15230
15350
  init_config();
15351
+ init_ConnectionAdapter();
15231
15352
  init_FileSystemAdapter();
15232
15353
  init_MessageRepository();
15233
15354
  init_RoomRepository();
@@ -15643,29 +15764,28 @@ var init_command5 = __esm(() => {
15643
15764
  });
15644
15765
  });
15645
15766
 
15767
+ // src/inbox-router.ts
15768
+ var IDLE_CHECK_INTERVAL_MS = 60000, IDLE_THRESHOLD_MS2;
15769
+ var init_inbox_router = __esm(() => {
15770
+ IDLE_THRESHOLD_MS2 = 5 * 60 * 1000;
15771
+ });
15772
+
15646
15773
  // src/commands/listen/schema.ts
15647
15774
  var ListenInput;
15648
15775
  var init_schema6 = __esm(() => {
15649
15776
  init_zod();
15650
15777
  ListenInput = exports_external.object({
15651
- roomId: exports_external.string().min(1, "Room ID is required").regex(/^[a-zA-Z0-9_-]+$/, "Room ID must contain only alphanumeric characters, hyphens, and underscores"),
15778
+ roomId: exports_external.uuid({ error: "Room ID must be a valid uuid" }),
15652
15779
  exclude: exports_external.string().optional(),
15653
15780
  senderType: exports_external.string().optional(),
15654
15781
  team: exports_external.string().optional(),
15655
- inbox: exports_external.string().optional(),
15656
- stdinPane: exports_external.string().optional()
15782
+ inbox: exports_external.string().optional()
15657
15783
  }).refine((data) => !(data.inbox && !data.team), {
15658
15784
  message: "--inbox requires --team",
15659
15785
  path: ["inbox"]
15660
15786
  });
15661
15787
  });
15662
15788
 
15663
- // src/inbox-router.ts
15664
- var IDLE_CHECK_INTERVAL_MS = 60000, IDLE_THRESHOLD_MS2;
15665
- var init_inbox_router = __esm(() => {
15666
- IDLE_THRESHOLD_MS2 = 5 * 60 * 1000;
15667
- });
15668
-
15669
15789
  // src/lib/tmux-client.ts
15670
15790
  import { execFileSync, execFile as execFileCb } from "node:child_process";
15671
15791
  function validateSessionName(name) {
@@ -15696,6 +15816,9 @@ class TmuxClient {
15696
15816
  timeout: 5000,
15697
15817
  stdio: ["pipe", "pipe", "pipe"]
15698
15818
  }).trim();
15819
+ if (!result) {
15820
+ return { available: false, version: null, error: "tmux returned an empty version string" };
15821
+ }
15699
15822
  return { available: true, version: result };
15700
15823
  } catch (error48) {
15701
15824
  return {
@@ -15707,11 +15830,7 @@ class TmuxClient {
15707
15830
  }
15708
15831
  newSession(name, commandArgs, env) {
15709
15832
  validateSessionName(name);
15710
- if (env) {
15711
- for (const [key, value] of Object.entries(env)) {
15712
- this.exec(["-L", this.server, "set-environment", "-g", key, value]);
15713
- }
15714
- }
15833
+ const envArgs = env ? ["env", ...Object.entries(env).map(([key, value]) => `${key}=${value}`)] : [];
15715
15834
  const args = [
15716
15835
  "-L",
15717
15836
  this.server,
@@ -15724,14 +15843,10 @@ class TmuxClient {
15724
15843
  "-y",
15725
15844
  "40",
15726
15845
  "--",
15846
+ ...envArgs,
15727
15847
  ...commandArgs
15728
15848
  ];
15729
15849
  const result = this.exec(args);
15730
- if (env) {
15731
- for (const key of Object.keys(env)) {
15732
- this.exec(["-L", this.server, "set-environment", "-g", "-u", key]);
15733
- }
15734
- }
15735
15850
  if (!result.ok)
15736
15851
  return result;
15737
15852
  this.exec(["-L", this.server, "set-option", "-t", name, "remain-on-exit", "on"]);
@@ -15771,13 +15886,13 @@ class TmuxClient {
15771
15886
  "-F",
15772
15887
  "#{pane_id}\t#{session_name}\t#{pane_title}\t#{pane_current_command}"
15773
15888
  ];
15774
- return new Promise((resolve2) => {
15889
+ return new Promise((resolve3) => {
15775
15890
  execFileCb("tmux", args, { encoding: "utf8", timeout: 5000 }, (error48, stdout) => {
15776
15891
  if (error48) {
15777
- resolve2([]);
15892
+ resolve3([]);
15778
15893
  return;
15779
15894
  }
15780
- resolve2(stdout.trim().split(`
15895
+ resolve3(stdout.trim().split(`
15781
15896
  `).filter((line) => line.trim()).map((line) => {
15782
15897
  const [paneId, sessionName, title, command] = line.split("\t");
15783
15898
  return { paneId: paneId ?? "", sessionName: sessionName ?? "", title: title ?? "", command: command ?? "" };
@@ -15830,10 +15945,10 @@ class TmuxClient {
15830
15945
  if (lines > 0) {
15831
15946
  args.push("-S", `-${lines}`);
15832
15947
  }
15833
- return new Promise((resolve2) => {
15948
+ return new Promise((resolve3) => {
15834
15949
  execFileCb("tmux", args, { encoding: "utf8", timeout: 5000 }, (error48, stdout) => {
15835
15950
  if (error48) {
15836
- resolve2([]);
15951
+ resolve3([]);
15837
15952
  return;
15838
15953
  }
15839
15954
  const result = stdout.split(`
@@ -15841,7 +15956,7 @@ class TmuxClient {
15841
15956
  while (result.length > 0 && result[result.length - 1] === "") {
15842
15957
  result.pop();
15843
15958
  }
15844
- resolve2(result);
15959
+ resolve3(result);
15845
15960
  });
15846
15961
  });
15847
15962
  }
@@ -15885,16 +16000,12 @@ var init_tmux_client = __esm(() => {
15885
16000
  SESSION_NAME_RE = /^[a-zA-Z0-9_-]+$/;
15886
16001
  });
15887
16002
 
15888
- // src/commands/listen/usecase.ts
15889
- function listen(client, input, inboxRouter) {
15890
- const parsed = ListenInput.parse(input);
15891
- const { roomId, exclude, senderType, team, inbox, stdinPane } = parsed;
15892
- const inboxDir = team ? `${process.env.HOME}/.claude/teams/${team}/inboxes` : null;
15893
- const defaultInboxPath = inboxDir && inbox ? `${inboxDir}/${inbox}.json` : null;
15894
- const teamDir = team ? `${process.env.HOME}/.claude/teams/${team}` : null;
16003
+ // src/commands/listen/shared.ts
16004
+ function createTerminalControlHandler(input) {
16005
+ const { client, roomId, teamDir } = input;
15895
16006
  const tmuxClient = new TmuxClient({ server: "meet-ai", scrollback: 50000 });
15896
16007
  let terminalInterval = null;
15897
- const onMessage = (msg) => {
16008
+ function handle(msg) {
15898
16009
  if (msg.type === "terminal_resize") {
15899
16010
  const cols = msg.cols;
15900
16011
  if (typeof cols === "number" && cols > 0) {
@@ -15906,7 +16017,7 @@ function listen(client, input, inboxRouter) {
15906
16017
  }
15907
16018
  });
15908
16019
  }
15909
- return;
16020
+ return true;
15910
16021
  }
15911
16022
  if (msg.type === "terminal_subscribe") {
15912
16023
  const roomPrefix = roomId.slice(0, 8);
@@ -15959,29 +16070,61 @@ function listen(client, input, inboxRouter) {
15959
16070
  await client.sendTerminalData(roomId, payload);
15960
16071
  } catch {}
15961
16072
  }, TERMINAL_POLL_MS);
15962
- return;
16073
+ return true;
15963
16074
  }
15964
16075
  if (msg.type === "terminal_unsubscribe") {
15965
16076
  if (terminalInterval) {
15966
16077
  clearInterval(terminalInterval);
15967
16078
  terminalInterval = null;
15968
16079
  }
15969
- return;
16080
+ return true;
15970
16081
  }
15971
16082
  if (msg.type === "terminal_data") {
15972
- return;
16083
+ return true;
16084
+ }
16085
+ return false;
16086
+ }
16087
+ function shutdown() {
16088
+ if (terminalInterval) {
16089
+ clearInterval(terminalInterval);
16090
+ terminalInterval = null;
15973
16091
  }
15974
- if (msg.id && msg.room_id && msg.attachment_count > 0) {
16092
+ }
16093
+ return { handle, shutdown };
16094
+ }
16095
+ var init_shared = __esm(() => {
16096
+ init_tmux_client();
16097
+ });
16098
+
16099
+ // src/commands/listen/listen-claude.ts
16100
+ function listenClaude(client, input, inboxRouter) {
16101
+ const parsed = ListenInput.parse(input);
16102
+ const { roomId, exclude, senderType, team, inbox } = parsed;
16103
+ const inboxDir = team ? `${process.env.HOME}/.claude/teams/${team}/inboxes` : null;
16104
+ const defaultInboxPath = inboxDir && inbox ? `${inboxDir}/${inbox}.json` : null;
16105
+ const teamDir = team ? `${process.env.HOME}/.claude/teams/${team}` : undefined;
16106
+ const terminal = createTerminalControlHandler({ client, roomId, teamDir, inboxRouter });
16107
+ const onMessage = (msg) => {
16108
+ if (terminal.handle(msg))
16109
+ return;
16110
+ if (msg.id && msg.room_id && (msg.attachment_count ?? 0) > 0) {
15975
16111
  downloadMessageAttachments(client, msg.room_id, msg.id).then((paths) => {
15976
16112
  const output = paths.length ? { ...msg, attachments: paths } : msg;
15977
16113
  console.log(JSON.stringify(output));
15978
- if (inboxDir && teamDir && inboxRouter)
15979
- inboxRouter.route(msg, { inboxDir, defaultInboxPath, teamDir, stdinPane, attachmentPaths: paths });
16114
+ if (inboxDir && teamDir && inboxRouter) {
16115
+ inboxRouter.route(msg, {
16116
+ inboxDir,
16117
+ defaultInboxPath,
16118
+ teamDir,
16119
+ attachmentPaths: paths
16120
+ });
16121
+ }
15980
16122
  });
15981
- } else {
15982
- console.log(JSON.stringify(msg));
15983
- if (inboxDir && teamDir && inboxRouter)
15984
- inboxRouter.route(msg, { inboxDir, defaultInboxPath, teamDir, stdinPane });
16123
+ return;
16124
+ }
16125
+ console.log(JSON.stringify(msg));
16126
+ if (inboxDir && teamDir && inboxRouter) {
16127
+ inboxRouter.route(msg, { inboxDir, defaultInboxPath, teamDir });
15985
16128
  }
15986
16129
  };
15987
16130
  const ws = client.listen(roomId, { exclude, senderType, onMessage });
@@ -15990,7 +16133,13 @@ function listen(client, input, inboxRouter) {
15990
16133
  if (inboxDir && inbox && teamDir && inboxRouter) {
15991
16134
  let scheduleIdleCheck = function() {
15992
16135
  idleCheckTimeout = setTimeout(() => {
15993
- inboxRouter.checkIdle({ inboxDir, teamDir, inbox, defaultInboxPath, notified: idleNotified });
16136
+ inboxRouter.checkIdle({
16137
+ inboxDir,
16138
+ teamDir,
16139
+ inbox,
16140
+ defaultInboxPath: defaultInboxPath ?? null,
16141
+ notified: idleNotified
16142
+ });
15994
16143
  scheduleIdleCheck();
15995
16144
  }, IDLE_CHECK_INTERVAL_MS);
15996
16145
  };
@@ -15999,10 +16148,621 @@ function listen(client, input, inboxRouter) {
15999
16148
  function shutdown() {
16000
16149
  if (idleCheckTimeout)
16001
16150
  clearTimeout(idleCheckTimeout);
16002
- if (terminalInterval) {
16003
- clearInterval(terminalInterval);
16004
- terminalInterval = null;
16151
+ terminal.shutdown();
16152
+ if (ws.readyState === WebSocket.OPEN) {
16153
+ ws.close(1000, "client shutdown");
16154
+ }
16155
+ process.exit(0);
16156
+ }
16157
+ process.on("SIGINT", shutdown);
16158
+ process.on("SIGTERM", shutdown);
16159
+ process.on("SIGHUP", shutdown);
16160
+ return ws;
16161
+ }
16162
+ var init_listen_claude = __esm(() => {
16163
+ init_inbox_router();
16164
+ init_schema6();
16165
+ init_shared();
16166
+ });
16167
+
16168
+ // src/lib/codex-app-server.ts
16169
+ import {
16170
+ spawn
16171
+ } from "node:child_process";
16172
+ import { stderr } from "node:process";
16173
+ import { createInterface } from "node:readline";
16174
+ function isObject2(value) {
16175
+ return typeof value === "object" && value !== null;
16176
+ }
16177
+ function isJsonRpcResponse(value) {
16178
+ return isObject2(value) && "id" in value && (("result" in value) || ("error" in value));
16179
+ }
16180
+ function isJsonRpcServerRequest(value) {
16181
+ return isObject2(value) && typeof value.method === "string" && "id" in value && !("result" in value) && !("error" in value);
16182
+ }
16183
+ function isJsonRpcNotification(value) {
16184
+ return isObject2(value) && typeof value.method === "string" && !("id" in value);
16185
+ }
16186
+ function toErrorMessage(error48) {
16187
+ if (error48 instanceof Error)
16188
+ return error48.message;
16189
+ return String(error48);
16190
+ }
16191
+ function formatRoomMessageForCodex(input) {
16192
+ const lines = [
16193
+ `New message from meet-ai sender "${input.sender}" at ${input.timestamp ?? new Date().toISOString()}:`,
16194
+ "",
16195
+ input.content
16196
+ ];
16197
+ if (input.attachments?.length) {
16198
+ lines.push("", `Attachments: ${input.attachments.join(", ")}`);
16199
+ }
16200
+ return lines.join(`
16201
+ `);
16202
+ }
16203
+ function maybeActiveTurnId(thread) {
16204
+ if (!thread || !Array.isArray(thread.turns))
16205
+ return null;
16206
+ for (let index = thread.turns.length - 1;index >= 0; index -= 1) {
16207
+ const turn = thread.turns[index];
16208
+ if (turn?.status === "inProgress" && typeof turn.id === "string") {
16209
+ return turn.id;
16210
+ }
16211
+ }
16212
+ return null;
16213
+ }
16214
+ function isSteerPreconditionError(error48) {
16215
+ if (!isObject2(error48))
16216
+ return false;
16217
+ const message = typeof error48.message === "string" ? error48.message.toLowerCase() : "";
16218
+ return message.includes("expected") || message.includes("active turn") || message.includes("precondition");
16219
+ }
16220
+ function isAgentMessageDeltaNotification(params) {
16221
+ return isObject2(params) && typeof params.threadId === "string" && typeof params.turnId === "string" && typeof params.itemId === "string" && typeof params.delta === "string";
16222
+ }
16223
+ function extractAgentMessageDelta(params) {
16224
+ if (!isAgentMessageDeltaNotification(params)) {
16225
+ return { itemId: null, turnId: null, text: null };
16226
+ }
16227
+ return {
16228
+ itemId: params.itemId,
16229
+ turnId: params.turnId,
16230
+ text: params.delta
16231
+ };
16232
+ }
16233
+ function isItemCompletedNotification(params) {
16234
+ return isObject2(params) && typeof params.threadId === "string" && typeof params.turnId === "string" && isObject2(params.item) && typeof params.item.type === "string" && typeof params.item.id === "string";
16235
+ }
16236
+ function extractCompletedAgentMessage(params) {
16237
+ if (!isItemCompletedNotification(params))
16238
+ return null;
16239
+ if (params.item.type !== "agentMessage")
16240
+ return null;
16241
+ return {
16242
+ itemId: params.item.id,
16243
+ turnId: params.turnId,
16244
+ text: params.item.text
16245
+ };
16246
+ }
16247
+ function isThreadStartedNotification(params) {
16248
+ return isObject2(params) && isObject2(params.thread) && typeof params.thread.id === "string";
16249
+ }
16250
+ function isTurnStartedNotification(params) {
16251
+ return isObject2(params) && typeof params.threadId === "string" && isObject2(params.turn) && typeof params.turn.id === "string";
16252
+ }
16253
+ function isTurnCompletedNotification(params) {
16254
+ return isObject2(params) && typeof params.threadId === "string" && isObject2(params.turn) && typeof params.turn.id === "string";
16255
+ }
16256
+
16257
+ class CodexAppServerBridge {
16258
+ threadId;
16259
+ cwd;
16260
+ codexBin;
16261
+ clientName;
16262
+ clientTitle;
16263
+ clientVersion;
16264
+ experimentalApi;
16265
+ env;
16266
+ spawnFn;
16267
+ stderrStream;
16268
+ child = null;
16269
+ stdoutReader = null;
16270
+ readyPromise = null;
16271
+ pendingRequests = new Map;
16272
+ nextRequestId = 1;
16273
+ activeTurnId = null;
16274
+ injectionQueue = Promise.resolve();
16275
+ eventHandler = null;
16276
+ constructor(options) {
16277
+ this.threadId = options.threadId ?? null;
16278
+ this.cwd = options.cwd;
16279
+ this.codexBin = options.codexBin ?? options.env?.MEET_AI_CODEX_PATH ?? process.env.MEET_AI_CODEX_PATH ?? "codex";
16280
+ this.clientName = options.clientName ?? "meet_ai";
16281
+ this.clientTitle = options.clientTitle ?? "meet-ai CLI";
16282
+ this.clientVersion = options.clientVersion ?? "0.0.0";
16283
+ this.experimentalApi = options.experimentalApi ?? false;
16284
+ this.env = options.env ?? process.env;
16285
+ this.spawnFn = options.spawnFn ?? spawn;
16286
+ this.stderrStream = options.stderr ?? stderr;
16287
+ }
16288
+ async start() {
16289
+ if (!this.readyPromise) {
16290
+ this.readyPromise = this.startInternal().catch((error48) => {
16291
+ this.readyPromise = null;
16292
+ throw error48;
16293
+ });
16294
+ }
16295
+ return this.readyPromise;
16296
+ }
16297
+ async injectText(input) {
16298
+ const text = formatRoomMessageForCodex(input);
16299
+ return this.injectPrompt(text);
16300
+ }
16301
+ async injectPrompt(text) {
16302
+ return this.enqueue(async () => {
16303
+ await this.start();
16304
+ const payload = [{ type: "text", text, text_elements: [] }];
16305
+ const threadId = this.threadId;
16306
+ if (!threadId) {
16307
+ throw new Error("Codex app-server bridge does not have an active thread");
16308
+ }
16309
+ if (this.activeTurnId) {
16310
+ try {
16311
+ const result2 = await this.request("turn/steer", {
16312
+ threadId,
16313
+ input: payload,
16314
+ expectedTurnId: this.activeTurnId
16315
+ });
16316
+ this.activeTurnId = result2.turnId;
16317
+ return { mode: "steer", threadId, turnId: result2.turnId };
16318
+ } catch (error48) {
16319
+ if (!isSteerPreconditionError(error48))
16320
+ throw error48;
16321
+ this.activeTurnId = null;
16322
+ }
16323
+ }
16324
+ const result = await this.request("turn/start", {
16325
+ threadId,
16326
+ input: payload
16327
+ });
16328
+ this.activeTurnId = result.turn.id;
16329
+ return { mode: "start", threadId, turnId: result.turn.id };
16330
+ });
16331
+ }
16332
+ getThreadId() {
16333
+ return this.threadId;
16334
+ }
16335
+ async close() {
16336
+ this.readyPromise = null;
16337
+ this.activeTurnId = null;
16338
+ if (this.stdoutReader) {
16339
+ this.stdoutReader.close();
16340
+ this.stdoutReader = null;
16341
+ }
16342
+ const child = this.child;
16343
+ this.child = null;
16344
+ if (!child)
16345
+ return;
16346
+ for (const pending of this.pendingRequests.values()) {
16347
+ pending.reject(new Error("Codex app-server bridge closed"));
16348
+ }
16349
+ this.pendingRequests.clear();
16350
+ child.kill();
16351
+ }
16352
+ setEventHandler(handler) {
16353
+ this.eventHandler = handler;
16354
+ }
16355
+ enqueue(fn) {
16356
+ const next = this.injectionQueue.then(fn, fn);
16357
+ this.injectionQueue = next.catch(() => {
16358
+ return;
16359
+ });
16360
+ return next;
16361
+ }
16362
+ async startInternal() {
16363
+ const child = this.spawnFn(this.codexBin, [
16364
+ "app-server",
16365
+ "--enable",
16366
+ "multi_agent",
16367
+ "--enable",
16368
+ "memories",
16369
+ "--enable",
16370
+ "realtime_conversation",
16371
+ "-c",
16372
+ 'sandbox_mode="workspace-write"',
16373
+ "-c",
16374
+ "sandbox_workspace_write.network_access=true",
16375
+ "-c",
16376
+ 'web_search="live"',
16377
+ "--listen",
16378
+ "stdio://"
16379
+ ], {
16380
+ cwd: this.cwd,
16381
+ env: this.env,
16382
+ stdio: ["pipe", "pipe", "pipe"]
16383
+ });
16384
+ this.child = child;
16385
+ this.stdoutReader = createInterface({ input: child.stdout });
16386
+ this.stdoutReader.on("line", (line) => {
16387
+ this.handleLine(line);
16388
+ });
16389
+ child.stderr.on("data", (chunk) => {
16390
+ this.stderrStream.write(chunk);
16391
+ });
16392
+ child.on("exit", (_code, signal) => {
16393
+ const reason = new Error(`codex app-server exited${signal ? ` with signal ${signal}` : ""}`);
16394
+ for (const pending of this.pendingRequests.values()) {
16395
+ pending.reject(reason);
16396
+ }
16397
+ this.pendingRequests.clear();
16398
+ this.child = null;
16399
+ this.stdoutReader = null;
16400
+ this.readyPromise = null;
16401
+ });
16402
+ await this.request("initialize", {
16403
+ clientInfo: {
16404
+ name: this.clientName,
16405
+ title: this.clientTitle,
16406
+ version: this.clientVersion
16407
+ },
16408
+ capabilities: {
16409
+ experimentalApi: this.experimentalApi
16410
+ }
16411
+ });
16412
+ this.notify("initialized");
16413
+ if (this.threadId) {
16414
+ const resumeParams = {
16415
+ threadId: this.threadId,
16416
+ persistExtendedHistory: this.experimentalApi
16417
+ };
16418
+ try {
16419
+ const resumeResult = await this.request("thread/resume", resumeParams);
16420
+ this.threadId = typeof resumeResult.thread?.id === "string" ? resumeResult.thread.id : this.threadId;
16421
+ this.activeTurnId = maybeActiveTurnId(resumeResult.thread);
16422
+ return;
16423
+ } catch {
16424
+ this.stderrStream.write(`meet-ai: codex app-server could not resume thread ${this.threadId}, starting fresh
16425
+ `);
16426
+ this.threadId = null;
16427
+ this.activeTurnId = null;
16428
+ }
16429
+ }
16430
+ const startResult = await this.request("thread/start", {
16431
+ cwd: this.cwd,
16432
+ experimentalRawEvents: false,
16433
+ persistExtendedHistory: this.experimentalApi
16434
+ });
16435
+ this.threadId = typeof startResult.thread?.id === "string" ? startResult.thread.id : null;
16436
+ this.activeTurnId = maybeActiveTurnId(startResult.thread);
16437
+ }
16438
+ async handleLine(line) {
16439
+ const trimmed = line.trim();
16440
+ if (!trimmed)
16441
+ return;
16442
+ let message;
16443
+ try {
16444
+ message = JSON.parse(trimmed);
16445
+ } catch {
16446
+ this.stderrStream.write(`meet-ai: failed to parse codex app-server line: ${trimmed}
16447
+ `);
16448
+ return;
16449
+ }
16450
+ if (isJsonRpcResponse(message)) {
16451
+ const pending = this.pendingRequests.get(message.id);
16452
+ if (!pending)
16453
+ return;
16454
+ this.pendingRequests.delete(message.id);
16455
+ if (message.error) {
16456
+ pending.reject(new Error(message.error.message ?? "Unknown app-server error"));
16457
+ return;
16458
+ }
16459
+ pending.resolve(message.result);
16460
+ return;
16461
+ }
16462
+ if (isJsonRpcServerRequest(message)) {
16463
+ this.respondToServerRequest(message);
16464
+ return;
16465
+ }
16466
+ if (isJsonRpcNotification(message)) {
16467
+ this.handleNotification(message);
16468
+ }
16469
+ }
16470
+ handleNotification(message) {
16471
+ if (message.method === "thread/started") {
16472
+ if (isThreadStartedNotification(message.params))
16473
+ this.threadId = message.params.thread.id;
16474
+ return;
16475
+ }
16476
+ if (message.method === "turn/started") {
16477
+ if (isTurnStartedNotification(message.params) && message.params.threadId === this.threadId) {
16478
+ this.activeTurnId = message.params.turn.id;
16479
+ }
16480
+ return;
16481
+ }
16482
+ if (message.method === "turn/completed") {
16483
+ if (isTurnCompletedNotification(message.params) && message.params.threadId === this.threadId && message.params.turn.id === this.activeTurnId) {
16484
+ this.activeTurnId = null;
16485
+ }
16486
+ this.emitEvent({
16487
+ type: "turn_completed",
16488
+ turnId: isTurnCompletedNotification(message.params) ? message.params.turn.id : null
16489
+ });
16490
+ return;
16491
+ }
16492
+ if (message.method === "item/agentMessage/delta") {
16493
+ const event = extractAgentMessageDelta(message.params);
16494
+ if (event.text) {
16495
+ this.emitEvent({
16496
+ type: "agent_message_delta",
16497
+ itemId: event.itemId,
16498
+ turnId: event.turnId,
16499
+ text: event.text
16500
+ });
16501
+ }
16502
+ return;
16503
+ }
16504
+ if (message.method === "item/completed") {
16505
+ const event = extractCompletedAgentMessage(message.params);
16506
+ if (event?.text) {
16507
+ this.emitEvent({
16508
+ type: "agent_message_completed",
16509
+ itemId: event.itemId,
16510
+ turnId: event.turnId,
16511
+ text: event.text
16512
+ });
16513
+ }
16514
+ }
16515
+ }
16516
+ emitEvent(event) {
16517
+ if (!this.eventHandler)
16518
+ return;
16519
+ try {
16520
+ this.eventHandler(event);
16521
+ } catch (error48) {
16522
+ this.stderrStream.write(`meet-ai: codex app-server event handler failed: ${toErrorMessage(error48)}
16523
+ `);
16524
+ }
16525
+ }
16526
+ respondToServerRequest(message) {
16527
+ switch (message.method) {
16528
+ case "item/commandExecution/requestApproval": {
16529
+ this.stderrStream.write(`meet-ai: auto-resolving unsupported codex app-server request ${message.method}
16530
+ `);
16531
+ this.writeMessage({ id: message.id, result: { decision: "decline" } });
16532
+ return;
16533
+ }
16534
+ case "item/fileChange/requestApproval": {
16535
+ this.stderrStream.write(`meet-ai: auto-resolving unsupported codex app-server request ${message.method}
16536
+ `);
16537
+ this.writeMessage({ id: message.id, result: { decision: "decline" } });
16538
+ return;
16539
+ }
16540
+ case "item/tool/requestUserInput": {
16541
+ this.stderrStream.write(`meet-ai: auto-resolving unsupported codex app-server request ${message.method}
16542
+ `);
16543
+ this.writeMessage({ id: message.id, result: { answers: {} } });
16544
+ return;
16545
+ }
16546
+ case "mcpServer/elicitation/request": {
16547
+ this.stderrStream.write(`meet-ai: auto-resolving unsupported codex app-server request ${message.method}
16548
+ `);
16549
+ this.writeMessage({ id: message.id, result: { action: "cancel", content: null } });
16550
+ return;
16551
+ }
16552
+ case "applyPatchApproval": {
16553
+ this.stderrStream.write(`meet-ai: auto-resolving unsupported codex app-server request ${message.method}
16554
+ `);
16555
+ this.writeMessage({ id: message.id, result: { decision: "denied" } });
16556
+ return;
16557
+ }
16558
+ case "execCommandApproval": {
16559
+ this.stderrStream.write(`meet-ai: auto-resolving unsupported codex app-server request ${message.method}
16560
+ `);
16561
+ this.writeMessage({ id: message.id, result: { decision: "denied" } });
16562
+ return;
16563
+ }
16564
+ default: {
16565
+ this.stderrStream.write(`meet-ai: rejecting unsupported codex app-server request ${message.method}
16566
+ `);
16567
+ this.writeMessage({
16568
+ id: message.id,
16569
+ error: {
16570
+ message: `meet-ai app-server bridge does not support ${message.method}`
16571
+ }
16572
+ });
16573
+ }
16574
+ }
16575
+ }
16576
+ notify(method, params) {
16577
+ this.writeMessage(params === undefined ? { method } : { method, params });
16578
+ }
16579
+ request(method, params) {
16580
+ const id = this.nextRequestId++;
16581
+ return new Promise((resolve3, reject) => {
16582
+ this.pendingRequests.set(id, { resolve: resolve3, reject });
16583
+ try {
16584
+ this.writeMessage(params === undefined ? { method, id } : { method, id, params });
16585
+ } catch (error48) {
16586
+ this.pendingRequests.delete(id);
16587
+ reject(error48);
16588
+ }
16589
+ });
16590
+ }
16591
+ writeMessage(message) {
16592
+ const child = this.child;
16593
+ if (!child) {
16594
+ throw new Error("Codex app-server bridge is not connected");
16595
+ }
16596
+ child.stdin.write(`${JSON.stringify(message)}
16597
+ `);
16598
+ }
16599
+ }
16600
+ function createCodexAppServerBridge(options) {
16601
+ return new CodexAppServerBridge(options);
16602
+ }
16603
+ function describeCodexAppServerError(error48) {
16604
+ return `meet-ai: failed to inject message into Codex via app-server: ${toErrorMessage(error48)}`;
16605
+ }
16606
+ var init_codex_app_server = () => {};
16607
+
16608
+ // src/commands/listen/listen-codex.ts
16609
+ function formatCodexListenOutput(msg) {
16610
+ const lines = [
16611
+ `[meet-ai] ${msg.sender} ${msg.created_at ?? new Date().toISOString()}`,
16612
+ msg.content
16613
+ ];
16614
+ if (msg.attachments?.length) {
16615
+ lines.push(`attachments: ${msg.attachments.join(", ")}`);
16616
+ }
16617
+ return `${lines.join(`
16618
+ `)}
16619
+ `;
16620
+ }
16621
+ function formatCodexInjectionOutput(result) {
16622
+ return `[meet-ai->codex] ${result.mode} ${result.turnId}
16623
+ `;
16624
+ }
16625
+ function isTruthyEnv(value) {
16626
+ if (!value)
16627
+ return false;
16628
+ const normalized = value.trim().toLowerCase();
16629
+ return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
16630
+ }
16631
+ function normalizeFinalText(value) {
16632
+ return value.replace(/\r\n/g, `
16633
+ `).trim();
16634
+ }
16635
+ function makeMessageKey(event) {
16636
+ return event.itemId ?? event.turnId ?? "unknown";
16637
+ }
16638
+ function listenCodex(client, input, codexBridgeOverride) {
16639
+ const parsed = ListenInput.parse(input);
16640
+ const { roomId, exclude, senderType, team, inbox } = parsed;
16641
+ let shutdownStarted = false;
16642
+ if (team || inbox) {
16643
+ const flags = [team ? "--team" : null, inbox ? "--inbox" : null].filter(Boolean).join(", ");
16644
+ throw new Error(`Codex listen does not support Claude inbox routing flags (${flags}). Run meet-ai listen without Claude-specific routing options.`);
16645
+ }
16646
+ const terminal = createTerminalControlHandler({ client, roomId });
16647
+ const codexBridge = codexBridgeOverride ?? createCodexAppServerBridge({
16648
+ threadId: null,
16649
+ cwd: process.cwd(),
16650
+ experimentalApi: isTruthyEnv(process.env.MEET_AI_CODEX_APP_SERVER_EXPERIMENTAL)
16651
+ });
16652
+ const bootstrapPrompt = process.env.MEET_AI_CODEX_BOOTSTRAP_PROMPT?.trim();
16653
+ const codexSender = process.env.MEET_AI_AGENT_NAME?.trim() || "codex";
16654
+ const messageState = new Map;
16655
+ let publishQueue = Promise.resolve();
16656
+ const enqueuePublish = (task) => {
16657
+ publishQueue = publishQueue.then(task, task).catch((error48) => {
16658
+ console.error(`meet-ai: failed to publish Codex output to room: ${error48 instanceof Error ? error48.message : String(error48)}`);
16659
+ });
16660
+ };
16661
+ const publishBufferedMessage = (key) => {
16662
+ const state = messageState.get(key);
16663
+ const text = normalizeFinalText(state?.text ?? "");
16664
+ if (!state || state.sent || state.sending || !text)
16665
+ return;
16666
+ state.sending = true;
16667
+ enqueuePublish(async () => {
16668
+ try {
16669
+ await client.sendMessage(roomId, codexSender, text);
16670
+ state.sent = true;
16671
+ } finally {
16672
+ state.sending = false;
16673
+ }
16674
+ });
16675
+ };
16676
+ const mergeEventText = (event) => {
16677
+ const key = makeMessageKey(event);
16678
+ const nextText = event.text.replace(/\r\n/g, `
16679
+ `);
16680
+ if (!nextText)
16681
+ return;
16682
+ const existing = messageState.get(key);
16683
+ if (!existing) {
16684
+ messageState.set(key, { turnId: event.turnId, text: nextText, sent: false, sending: false });
16685
+ return;
16686
+ }
16687
+ existing.turnId = event.turnId ?? existing.turnId;
16688
+ if (event.type === "agent_message_completed") {
16689
+ existing.text = nextText;
16690
+ return;
16691
+ }
16692
+ existing.text += nextText;
16693
+ };
16694
+ codexBridge.setEventHandler((event) => {
16695
+ if (event.type === "agent_message_delta" || event.type === "agent_message_completed") {
16696
+ mergeEventText(event);
16697
+ if (event.type === "agent_message_completed") {
16698
+ publishBufferedMessage(makeMessageKey(event));
16699
+ }
16700
+ return;
16701
+ }
16702
+ if (event.type === "turn_completed") {
16703
+ for (const [key, state] of messageState.entries()) {
16704
+ if (state.turnId === event.turnId || !event.turnId && !state.sent) {
16705
+ publishBufferedMessage(key);
16706
+ }
16707
+ }
16708
+ }
16709
+ });
16710
+ const injectMessage = (message) => {
16711
+ codexBridge.injectText({
16712
+ sender: message.sender,
16713
+ content: message.content,
16714
+ timestamp: new Date().toISOString(),
16715
+ attachments: message.attachments
16716
+ }).then((result) => {
16717
+ appendCodexInboxEntry(result.threadId, {
16718
+ from: `meet-ai:${message.sender}`,
16719
+ text: message.content,
16720
+ timestamp: new Date().toISOString(),
16721
+ read: false,
16722
+ ...message.attachments?.length ? { attachments: message.attachments } : {}
16723
+ });
16724
+ console.log(formatCodexInjectionOutput(result));
16725
+ }).catch((error48) => {
16726
+ console.error(describeCodexAppServerError(error48));
16727
+ });
16728
+ };
16729
+ const onMessage = (msg) => {
16730
+ if (terminal.handle(msg))
16731
+ return;
16732
+ if (msg.id && msg.room_id && (msg.attachment_count ?? 0) > 0) {
16733
+ downloadMessageAttachments(client, msg.room_id, msg.id).then((paths) => {
16734
+ const output = paths.length ? { ...msg, attachments: paths } : msg;
16735
+ console.log(formatCodexListenOutput(output));
16736
+ injectMessage({
16737
+ sender: msg.sender,
16738
+ content: msg.content,
16739
+ attachments: paths
16740
+ });
16741
+ });
16742
+ return;
16005
16743
  }
16744
+ console.log(formatCodexListenOutput(msg));
16745
+ injectMessage({
16746
+ sender: msg.sender,
16747
+ content: msg.content
16748
+ });
16749
+ };
16750
+ const ws = client.listen(roomId, { exclude, senderType, onMessage });
16751
+ if (bootstrapPrompt) {
16752
+ const bootstrapRequest = codexBridge.injectPrompt(bootstrapPrompt);
16753
+ bootstrapRequest.then((result) => {
16754
+ console.log(formatCodexInjectionOutput(result));
16755
+ }).catch((error48) => {
16756
+ console.error(describeCodexAppServerError(error48));
16757
+ });
16758
+ }
16759
+ function shutdown() {
16760
+ if (shutdownStarted)
16761
+ return;
16762
+ shutdownStarted = true;
16763
+ terminal.shutdown();
16764
+ codexBridge.setEventHandler(null);
16765
+ codexBridge.close();
16006
16766
  if (ws.readyState === WebSocket.OPEN) {
16007
16767
  ws.close(1000, "client shutdown");
16008
16768
  }
@@ -16013,10 +16773,11 @@ function listen(client, input, inboxRouter) {
16013
16773
  process.on("SIGHUP", shutdown);
16014
16774
  return ws;
16015
16775
  }
16016
- var init_usecase6 = __esm(() => {
16776
+ var init_listen_codex = __esm(() => {
16777
+ init_codex();
16778
+ init_codex_app_server();
16017
16779
  init_schema6();
16018
- init_inbox_router();
16019
- init_tmux_client();
16780
+ init_shared();
16020
16781
  });
16021
16782
 
16022
16783
  // src/commands/listen/command.ts
@@ -16026,10 +16787,11 @@ __export(exports_command6, {
16026
16787
  });
16027
16788
  var command_default6;
16028
16789
  var init_command6 = __esm(() => {
16029
- init_dist();
16030
16790
  init_bootstrap();
16031
- init_usecase6();
16032
16791
  init_output();
16792
+ init_dist();
16793
+ init_listen_claude();
16794
+ init_listen_codex();
16033
16795
  command_default6 = defineCommand({
16034
16796
  meta: {
16035
16797
  name: "listen",
@@ -16060,25 +16822,24 @@ var init_command6 = __esm(() => {
16060
16822
  type: "string",
16061
16823
  alias: "i",
16062
16824
  description: "Inbox name for routing (requires --team)"
16063
- },
16064
- "stdin-pane": {
16065
- type: "string",
16066
- alias: "s",
16067
- description: "tmux pane ID to inject non-@mention messages into via send-keys"
16068
16825
  }
16069
16826
  },
16070
16827
  run({ args }) {
16071
16828
  try {
16072
16829
  const client = getClient();
16073
16830
  const container2 = getContainer();
16074
- listen(client, {
16831
+ const input = {
16075
16832
  roomId: args.roomId,
16076
16833
  exclude: args.exclude,
16077
16834
  senderType: args["sender-type"],
16078
16835
  team: args.team,
16079
- inbox: args.inbox,
16080
- stdinPane: args["stdin-pane"]
16081
- }, container2.inboxRouter);
16836
+ inbox: args.inbox
16837
+ };
16838
+ if (getMeetAiRuntime() === "codex") {
16839
+ listenCodex(client, input);
16840
+ return;
16841
+ }
16842
+ listenClaude(client, input, container2.inboxRouter);
16082
16843
  } catch (error48) {
16083
16844
  err(error48 instanceof Error ? error48.message : String(error48));
16084
16845
  process.exit(1);
@@ -16110,7 +16871,7 @@ async function sendTeamInfo(client, input) {
16110
16871
  await client.sendTeamInfo(parsed.roomId, parsed.payload);
16111
16872
  ok("Team info sent");
16112
16873
  }
16113
- var init_usecase7 = __esm(() => {
16874
+ var init_usecase6 = __esm(() => {
16114
16875
  init_schema7();
16115
16876
  init_output();
16116
16877
  });
@@ -16124,7 +16885,7 @@ var command_default7;
16124
16885
  var init_command7 = __esm(() => {
16125
16886
  init_dist();
16126
16887
  init_bootstrap();
16127
- init_usecase7();
16888
+ init_usecase6();
16128
16889
  init_output();
16129
16890
  command_default7 = defineCommand({
16130
16891
  meta: {
@@ -16178,7 +16939,7 @@ async function sendTasks(client, input) {
16178
16939
  await client.sendTasks(parsed.roomId, parsed.payload);
16179
16940
  ok("Tasks info sent");
16180
16941
  }
16181
- var init_usecase8 = __esm(() => {
16942
+ var init_usecase7 = __esm(() => {
16182
16943
  init_schema8();
16183
16944
  init_output();
16184
16945
  });
@@ -16192,7 +16953,7 @@ var command_default8;
16192
16953
  var init_command8 = __esm(() => {
16193
16954
  init_dist();
16194
16955
  init_bootstrap();
16195
- init_usecase8();
16956
+ init_usecase7();
16196
16957
  init_output();
16197
16958
  command_default8 = defineCommand({
16198
16959
  meta: {
@@ -16239,7 +17000,7 @@ async function downloadAttachment(client, input) {
16239
17000
  ok(localPath);
16240
17001
  return localPath;
16241
17002
  }
16242
- var init_usecase9 = __esm(() => {
17003
+ var init_usecase8 = __esm(() => {
16243
17004
  init_schema9();
16244
17005
  init_output();
16245
17006
  });
@@ -16253,7 +17014,7 @@ var command_default9;
16253
17014
  var init_command9 = __esm(() => {
16254
17015
  init_dist();
16255
17016
  init_bootstrap();
16256
- init_usecase9();
17017
+ init_usecase8();
16257
17018
  init_output();
16258
17019
  command_default9 = defineCommand({
16259
17020
  meta: {
@@ -16316,12 +17077,12 @@ var init_command10 = __esm(() => {
16316
17077
  });
16317
17078
 
16318
17079
  // src/lib/hooks/find-room.ts
16319
- import { readdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2, createReadStream } from "node:fs";
16320
- import { join as join2 } from "node:path";
16321
- import { createInterface } from "node:readline";
17080
+ import { readdirSync as readdirSync2, readFileSync as readFileSync4, writeFileSync as writeFileSync3, createReadStream } from "node:fs";
17081
+ import { join as join3 } from "node:path";
17082
+ import { createInterface as createInterface2 } from "node:readline";
16322
17083
  async function extractTeamName(transcriptPath) {
16323
17084
  try {
16324
- const rl = createInterface({
17085
+ const rl = createInterface2({
16325
17086
  input: createReadStream(transcriptPath, "utf-8"),
16326
17087
  crlfDelay: Infinity
16327
17088
  });
@@ -16349,7 +17110,7 @@ function registerSession(filePath, data, sessionId) {
16349
17110
  }
16350
17111
  data.session_ids = ids;
16351
17112
  try {
16352
- writeFileSync2(filePath, JSON.stringify(data));
17113
+ writeFileSync3(filePath, JSON.stringify(data));
16353
17114
  } catch {}
16354
17115
  }
16355
17116
  async function findRoom(sessionId, teamsDir, transcriptPath) {
@@ -16357,9 +17118,9 @@ async function findRoom(sessionId, teamsDir, transcriptPath) {
16357
17118
  if (transcriptPath) {
16358
17119
  const teamName = await extractTeamName(transcriptPath);
16359
17120
  if (teamName) {
16360
- const filePath = join2(dir, teamName, "meet-ai.json");
17121
+ const filePath = join3(dir, teamName, "meet-ai.json");
16361
17122
  try {
16362
- const raw = readFileSync3(filePath, "utf-8");
17123
+ const raw = readFileSync4(filePath, "utf-8");
16363
17124
  const data = JSON.parse(raw);
16364
17125
  registerSession(filePath, data, sessionId);
16365
17126
  if (data.room_id)
@@ -16369,14 +17130,14 @@ async function findRoom(sessionId, teamsDir, transcriptPath) {
16369
17130
  }
16370
17131
  let entries;
16371
17132
  try {
16372
- entries = readdirSync(dir);
17133
+ entries = readdirSync2(dir);
16373
17134
  } catch {
16374
17135
  return null;
16375
17136
  }
16376
17137
  for (const entry of entries) {
16377
- const filePath = join2(dir, entry, "meet-ai.json");
17138
+ const filePath = join3(dir, entry, "meet-ai.json");
16378
17139
  try {
16379
- const raw = readFileSync3(filePath, "utf-8");
17140
+ const raw = readFileSync4(filePath, "utf-8");
16380
17141
  const data = JSON.parse(raw);
16381
17142
  const knownIds = data.session_ids ?? [data.session_id];
16382
17143
  if (knownIds.includes(sessionId) || data.session_id === sessionId) {
@@ -16539,17 +17300,17 @@ var init_cookie = __esm(() => {
16539
17300
  var init_fetch_result_please = () => {};
16540
17301
 
16541
17302
  // ../../node_modules/.bun/hono@4.11.8/node_modules/hono/dist/client/utils.js
16542
- function isObject2(item) {
17303
+ function isObject3(item) {
16543
17304
  return typeof item === "object" && item !== null && !Array.isArray(item);
16544
17305
  }
16545
17306
  function deepMerge(target, source) {
16546
- if (!isObject2(target) && !isObject2(source)) {
17307
+ if (!isObject3(target) && !isObject3(source)) {
16547
17308
  return source;
16548
17309
  }
16549
17310
  const merged = { ...target };
16550
17311
  for (const key in source) {
16551
17312
  const value = source[key];
16552
- if (isObject2(merged[key]) && isObject2(value)) {
17313
+ if (isObject3(merged[key]) && isObject3(value)) {
16553
17314
  merged[key] = deepMerge(merged[key], value);
16554
17315
  } else {
16555
17316
  merged[key] = value;
@@ -16829,23 +17590,23 @@ var init_hooks = __esm(() => {
16829
17590
  });
16830
17591
 
16831
17592
  // src/commands/hook/log-tool-use/usecase.ts
16832
- import { readFileSync as readFileSync4, writeFileSync as writeFileSync3, statSync as statSync2, rmSync } from "node:fs";
17593
+ import { readFileSync as readFileSync5, writeFileSync as writeFileSync4, statSync as statSync3, rmSync } from "node:fs";
16833
17594
  function getOrCreateParentId(sessionId) {
16834
17595
  const path = `/tmp/meet-ai-hook-${sessionId}.msgid`;
16835
17596
  try {
16836
- const mtime = statSync2(path).mtimeMs;
17597
+ const mtime = statSync3(path).mtimeMs;
16837
17598
  if (Date.now() - mtime > PARENT_MSG_TTL_SEC * 1000) {
16838
17599
  rmSync(path, { force: true });
16839
17600
  return null;
16840
17601
  }
16841
- return readFileSync4(path, "utf-8").trim() || null;
17602
+ return readFileSync5(path, "utf-8").trim() || null;
16842
17603
  } catch {
16843
17604
  return null;
16844
17605
  }
16845
17606
  }
16846
17607
  function saveParentId(sessionId, msgId) {
16847
17608
  try {
16848
- writeFileSync3(`/tmp/meet-ai-hook-${sessionId}.msgid`, msgId);
17609
+ writeFileSync4(`/tmp/meet-ai-hook-${sessionId}.msgid`, msgId);
16849
17610
  } catch {}
16850
17611
  }
16851
17612
  async function processHookInput(rawInput, teamsDir) {
@@ -16949,7 +17710,7 @@ async function processHookInput(rawInput, teamsDir) {
16949
17710
  return "sent";
16950
17711
  }
16951
17712
  var PARENT_MSG_TTL_SEC = 120;
16952
- var init_usecase10 = __esm(() => {
17713
+ var init_usecase9 = __esm(() => {
16953
17714
  init_hooks();
16954
17715
  });
16955
17716
 
@@ -16961,7 +17722,7 @@ __export(exports_command11, {
16961
17722
  var command_default11;
16962
17723
  var init_command11 = __esm(() => {
16963
17724
  init_dist();
16964
- init_usecase10();
17725
+ init_usecase9();
16965
17726
  command_default11 = defineCommand({
16966
17727
  meta: {
16967
17728
  name: "log-tool-use",
@@ -17126,7 +17887,7 @@ async function processPlanReview(rawInput, teamsDir) {
17126
17887
  }
17127
17888
  }
17128
17889
  var POLL_INTERVAL_MS = 2000, POLL_TIMEOUT_MS = 2592000000;
17129
- var init_usecase11 = __esm(() => {
17890
+ var init_usecase10 = __esm(() => {
17130
17891
  init_client3();
17131
17892
  init_find_room();
17132
17893
  });
@@ -17150,7 +17911,7 @@ var init_command12 = __esm(() => {
17150
17911
  for await (const chunk of process.stdin) {
17151
17912
  input += chunk;
17152
17913
  }
17153
- const { processPlanReview: processPlanReview2 } = await Promise.resolve().then(() => (init_usecase11(), exports_usecase));
17914
+ const { processPlanReview: processPlanReview2 } = await Promise.resolve().then(() => (init_usecase10(), exports_usecase));
17154
17915
  await processPlanReview2(input);
17155
17916
  } catch (error48) {
17156
17917
  process.stderr.write(`[plan-review] fatal: ${error48}
@@ -17222,7 +17983,7 @@ async function pollForAnswer(client, roomId, reviewId, pollInterval = POLL_INTER
17222
17983
  process.stderr.write(`[question-review] poll error: ${error48}
17223
17984
  `);
17224
17985
  }
17225
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
17986
+ await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
17226
17987
  }
17227
17988
  return null;
17228
17989
  }
@@ -17322,7 +18083,7 @@ async function processQuestionReview(rawInput, teamsDir, opts) {
17322
18083
  }
17323
18084
  }
17324
18085
  var POLL_INTERVAL_MS2 = 2000, POLL_TIMEOUT_MS2 = 1800000;
17325
- var init_usecase12 = __esm(() => {
18086
+ var init_usecase11 = __esm(() => {
17326
18087
  init_client3();
17327
18088
  init_find_room();
17328
18089
  });
@@ -17335,7 +18096,7 @@ __export(exports_command13, {
17335
18096
  var command_default13;
17336
18097
  var init_command13 = __esm(() => {
17337
18098
  init_dist();
17338
- init_usecase12();
18099
+ init_usecase11();
17339
18100
  command_default13 = defineCommand({
17340
18101
  meta: {
17341
18102
  name: "question-review",
@@ -17414,7 +18175,7 @@ async function pollForDecision2(client, roomId, reviewId, pollInterval = POLL_IN
17414
18175
  process.stderr.write(`[permission-review] poll error: ${error48}
17415
18176
  `);
17416
18177
  }
17417
- await new Promise((resolve2) => setTimeout(resolve2, pollInterval));
18178
+ await new Promise((resolve3) => setTimeout(resolve3, pollInterval));
17418
18179
  }
17419
18180
  return null;
17420
18181
  }
@@ -17520,7 +18281,7 @@ async function processPermissionReview(rawInput, teamsDir, opts) {
17520
18281
  }
17521
18282
  }
17522
18283
  var POLL_INTERVAL_MS3 = 2000, POLL_TIMEOUT_MS3 = 1800000;
17523
- var init_usecase13 = __esm(() => {
18284
+ var init_usecase12 = __esm(() => {
17524
18285
  init_client3();
17525
18286
  init_find_room();
17526
18287
  });
@@ -17533,7 +18294,7 @@ __export(exports_command14, {
17533
18294
  var command_default14;
17534
18295
  var init_command14 = __esm(() => {
17535
18296
  init_dist();
17536
- init_usecase13();
18297
+ init_usecase12();
17537
18298
  command_default14 = defineCommand({
17538
18299
  meta: {
17539
18300
  name: "permission-review",
@@ -17578,21 +18339,21 @@ var init_command15 = __esm(() => {
17578
18339
  });
17579
18340
 
17580
18341
  // src/commands/setup-hooks/usecase.ts
17581
- import { existsSync as existsSync3 } from "node:fs";
18342
+ import { existsSync as existsSync4 } from "node:fs";
17582
18343
  import { readFile, writeFile, mkdir } from "node:fs/promises";
17583
- import { homedir as homedir2 } from "node:os";
17584
- import { resolve as resolve2, dirname as dirname2 } from "node:path";
18344
+ import { homedir as homedir3 } from "node:os";
18345
+ import { resolve as resolve3, dirname as dirname3 } from "node:path";
17585
18346
  function isMeetAiHook(entry) {
17586
18347
  return entry.hooks?.some((h) => typeof h.command === "string" && h.command.startsWith("meet-ai hook ")) ?? false;
17587
18348
  }
17588
18349
  function getSettingsPath(project) {
17589
18350
  if (project) {
17590
- return resolve2(process.cwd(), ".claude", "settings.json");
18351
+ return resolve3(process.cwd(), ".claude", "settings.json");
17591
18352
  }
17592
- return resolve2(homedir2(), ".claude", "settings.json");
18353
+ return resolve3(homedir3(), ".claude", "settings.json");
17593
18354
  }
17594
18355
  async function readSettings(path) {
17595
- if (!existsSync3(path)) {
18356
+ if (!existsSync4(path)) {
17596
18357
  return {};
17597
18358
  }
17598
18359
  const raw = await readFile(path, "utf-8");
@@ -17674,7 +18435,7 @@ async function setupHooks(options) {
17674
18435
  if (Object.keys(cleaned).length === 0) {
17675
18436
  delete updated.hooks;
17676
18437
  }
17677
- await mkdir(dirname2(settingsPath), { recursive: true });
18438
+ await mkdir(dirname3(settingsPath), { recursive: true });
17678
18439
  await writeFile(settingsPath, `${JSON.stringify(updated, null, 2)}
17679
18440
  `);
17680
18441
  for (const r of removed) {
@@ -17691,7 +18452,7 @@ async function setupHooks(options) {
17691
18452
  return;
17692
18453
  }
17693
18454
  const updated = { ...settings, hooks: merged };
17694
- await mkdir(dirname2(settingsPath), { recursive: true });
18455
+ await mkdir(dirname3(settingsPath), { recursive: true });
17695
18456
  await writeFile(settingsPath, `${JSON.stringify(updated, null, 2)}
17696
18457
  `);
17697
18458
  for (const a of added) {
@@ -17701,7 +18462,7 @@ async function setupHooks(options) {
17701
18462
  }
17702
18463
  }
17703
18464
  var import_picocolors2, MEET_AI_HOOKS;
17704
- var init_usecase14 = __esm(() => {
18465
+ var init_usecase13 = __esm(() => {
17705
18466
  init_output();
17706
18467
  import_picocolors2 = __toESM(require_picocolors(), 1);
17707
18468
  MEET_AI_HOOKS = {
@@ -17760,7 +18521,7 @@ __export(exports_command16, {
17760
18521
  var command_default16;
17761
18522
  var init_command16 = __esm(() => {
17762
18523
  init_dist();
17763
- init_usecase14();
18524
+ init_usecase13();
17764
18525
  init_output();
17765
18526
  command_default16 = defineCommand({
17766
18527
  meta: {
@@ -17800,10 +18561,10 @@ var init_command16 = __esm(() => {
17800
18561
  });
17801
18562
 
17802
18563
  // src/commands/list-commands/usecase.ts
17803
- import { existsSync as existsSync4 } from "node:fs";
18564
+ import { existsSync as existsSync5 } from "node:fs";
17804
18565
  import { readFile as readFile2, readdir } from "node:fs/promises";
17805
- import { homedir as homedir3 } from "node:os";
17806
- import { join as join3, resolve as resolve3 } from "node:path";
18566
+ import { homedir as homedir4 } from "node:os";
18567
+ import { join as join4, resolve as resolve4 } from "node:path";
17807
18568
  function parseYamlFrontmatter(content) {
17808
18569
  const match = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
17809
18570
  if (!match)
@@ -17819,8 +18580,8 @@ function parseYamlFrontmatter(content) {
17819
18580
  };
17820
18581
  }
17821
18582
  async function readSkillsFromDir(baseDir, source, scope) {
17822
- const skillsDir = join3(baseDir, "skills");
17823
- if (!existsSync4(skillsDir))
18583
+ const skillsDir = join4(baseDir, "skills");
18584
+ if (!existsSync5(skillsDir))
17824
18585
  return [];
17825
18586
  let entries;
17826
18587
  try {
@@ -17830,8 +18591,8 @@ async function readSkillsFromDir(baseDir, source, scope) {
17830
18591
  }
17831
18592
  const results = [];
17832
18593
  for (const entry of entries) {
17833
- const skillFile = join3(skillsDir, entry, "SKILL.md");
17834
- if (!existsSync4(skillFile))
18594
+ const skillFile = join4(skillsDir, entry, "SKILL.md");
18595
+ if (!existsSync5(skillFile))
17835
18596
  continue;
17836
18597
  try {
17837
18598
  const content = await readFile2(skillFile, "utf-8");
@@ -17854,7 +18615,7 @@ async function readSkillsFromDir(baseDir, source, scope) {
17854
18615
  return results;
17855
18616
  }
17856
18617
  async function readCommandsFromDir(commandsDir, source, scope) {
17857
- if (!existsSync4(commandsDir))
18618
+ if (!existsSync5(commandsDir))
17858
18619
  return [];
17859
18620
  const results = [];
17860
18621
  async function scanDir(dir) {
@@ -17865,7 +18626,7 @@ async function readCommandsFromDir(commandsDir, source, scope) {
17865
18626
  return;
17866
18627
  }
17867
18628
  for (const entry of entries) {
17868
- const fullPath = join3(dir, entry.name);
18629
+ const fullPath = join4(dir, entry.name);
17869
18630
  if (entry.isDirectory()) {
17870
18631
  await scanDir(fullPath);
17871
18632
  } else if (entry.isFile() && entry.name.endsWith(".md")) {
@@ -17892,7 +18653,7 @@ async function readCommandsFromDir(commandsDir, source, scope) {
17892
18653
  return results;
17893
18654
  }
17894
18655
  async function readSettings2(settingsPath) {
17895
- if (!existsSync4(settingsPath))
18656
+ if (!existsSync5(settingsPath))
17896
18657
  return {};
17897
18658
  try {
17898
18659
  const raw = await readFile2(settingsPath, "utf-8");
@@ -17902,7 +18663,7 @@ async function readSettings2(settingsPath) {
17902
18663
  }
17903
18664
  }
17904
18665
  async function readInstalledPlugins(pluginsFile) {
17905
- if (!existsSync4(pluginsFile))
18666
+ if (!existsSync5(pluginsFile))
17906
18667
  return {};
17907
18668
  try {
17908
18669
  const raw = await readFile2(pluginsFile, "utf-8");
@@ -17913,17 +18674,17 @@ async function readInstalledPlugins(pluginsFile) {
17913
18674
  }
17914
18675
  }
17915
18676
  async function listCommands(options) {
17916
- const projectPath = resolve3(options.projectPath ?? process.cwd());
17917
- const userClaudeDir = options._userClaudeDir ?? join3(homedir3(), ".claude");
17918
- const pluginsFile = options._pluginsFile ?? join3(homedir3(), ".claude", "plugins", "installed_plugins.json");
18677
+ const projectPath = resolve4(options.projectPath ?? process.cwd());
18678
+ const userClaudeDir = options._userClaudeDir ?? join4(homedir4(), ".claude");
18679
+ const pluginsFile = options._pluginsFile ?? join4(homedir4(), ".claude", "plugins", "installed_plugins.json");
17919
18680
  const results = [];
17920
18681
  const userSkills = await readSkillsFromDir(userClaudeDir, "standalone", "user");
17921
18682
  results.push(...userSkills);
17922
- const projectClaudeDir = join3(projectPath, ".claude");
18683
+ const projectClaudeDir = join4(projectPath, ".claude");
17923
18684
  const projectSkills = await readSkillsFromDir(projectClaudeDir, "standalone", "project");
17924
18685
  results.push(...projectSkills);
17925
- const userSettings = await readSettings2(join3(userClaudeDir, "settings.json"));
17926
- const projectSettings = await readSettings2(join3(projectClaudeDir, "settings.json"));
18686
+ const userSettings = await readSettings2(join4(userClaudeDir, "settings.json"));
18687
+ const projectSettings = await readSettings2(join4(projectClaudeDir, "settings.json"));
17927
18688
  const userEnabled = userSettings.enabledPlugins ?? {};
17928
18689
  const projectEnabled = projectSettings.enabledPlugins ?? {};
17929
18690
  const enabledPlugins = new Map;
@@ -17941,7 +18702,7 @@ async function listCommands(options) {
17941
18702
  continue;
17942
18703
  const { installPath } = installations[0];
17943
18704
  const source = `plugin:${scopeName}`;
17944
- const pluginCommands = await readCommandsFromDir(join3(installPath, "commands"), source, pluginScope);
18705
+ const pluginCommands = await readCommandsFromDir(join4(installPath, "commands"), source, pluginScope);
17945
18706
  results.push(...pluginCommands);
17946
18707
  const pluginSkills = await readSkillsFromDir(installPath, source, pluginScope);
17947
18708
  results.push(...pluginSkills);
@@ -17949,7 +18710,7 @@ async function listCommands(options) {
17949
18710
  }
17950
18711
  return results;
17951
18712
  }
17952
- var init_usecase15 = () => {};
18713
+ var init_usecase14 = () => {};
17953
18714
 
17954
18715
  // src/commands/list-commands/command.ts
17955
18716
  var exports_command17 = {};
@@ -17959,7 +18720,7 @@ __export(exports_command17, {
17959
18720
  var command_default17;
17960
18721
  var init_command17 = __esm(() => {
17961
18722
  init_dist();
17962
- init_usecase15();
18723
+ init_usecase14();
17963
18724
  init_output();
17964
18725
  command_default17 = defineCommand({
17965
18726
  meta: {
@@ -17993,8 +18754,8 @@ async function sendCommands(client, input) {
17993
18754
  await client.sendCommands(input.roomId, payload);
17994
18755
  ok("Commands sent");
17995
18756
  }
17996
- var init_usecase16 = __esm(() => {
17997
- init_usecase15();
18757
+ var init_usecase15 = __esm(() => {
18758
+ init_usecase14();
17998
18759
  init_output();
17999
18760
  });
18000
18761
 
@@ -18007,7 +18768,7 @@ var command_default18;
18007
18768
  var init_command18 = __esm(() => {
18008
18769
  init_dist();
18009
18770
  init_bootstrap();
18010
- init_usecase16();
18771
+ init_usecase15();
18011
18772
  init_output();
18012
18773
  command_default18 = defineCommand({
18013
18774
  meta: {
@@ -18398,14 +19159,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
18398
19159
  prevActScopeDepth !== actScopeDepth - 1 && console.error("You seem to have overlapping act() calls, this is not supported. Be sure to await previous act() calls before making a new one. ");
18399
19160
  actScopeDepth = prevActScopeDepth;
18400
19161
  }
18401
- function recursivelyFlushAsyncActWork(returnValue, resolve4, reject) {
19162
+ function recursivelyFlushAsyncActWork(returnValue, resolve5, reject) {
18402
19163
  var queue = ReactSharedInternals.actQueue;
18403
19164
  if (queue !== null)
18404
19165
  if (queue.length !== 0)
18405
19166
  try {
18406
19167
  flushActQueue(queue);
18407
19168
  enqueueTask(function() {
18408
- return recursivelyFlushAsyncActWork(returnValue, resolve4, reject);
19169
+ return recursivelyFlushAsyncActWork(returnValue, resolve5, reject);
18409
19170
  });
18410
19171
  return;
18411
19172
  } catch (error48) {
@@ -18413,7 +19174,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
18413
19174
  }
18414
19175
  else
18415
19176
  ReactSharedInternals.actQueue = null;
18416
- 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve4(returnValue);
19177
+ 0 < ReactSharedInternals.thrownErrors.length ? (queue = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(queue)) : resolve5(returnValue);
18417
19178
  }
18418
19179
  function flushActQueue(queue) {
18419
19180
  if (!isFlushing) {
@@ -18589,14 +19350,14 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
18589
19350
  didAwaitActCall || didWarnNoAwaitAct || (didWarnNoAwaitAct = true, console.error("You called act(async () => ...) without await. This could lead to unexpected testing behaviour, interleaving multiple act calls and mixing their scopes. You should - await act(async () => ...);"));
18590
19351
  });
18591
19352
  return {
18592
- then: function(resolve4, reject) {
19353
+ then: function(resolve5, reject) {
18593
19354
  didAwaitActCall = true;
18594
19355
  thenable.then(function(returnValue) {
18595
19356
  popActScope(prevActQueue, prevActScopeDepth);
18596
19357
  if (prevActScopeDepth === 0) {
18597
19358
  try {
18598
19359
  flushActQueue(queue), enqueueTask(function() {
18599
- return recursivelyFlushAsyncActWork(returnValue, resolve4, reject);
19360
+ return recursivelyFlushAsyncActWork(returnValue, resolve5, reject);
18600
19361
  });
18601
19362
  } catch (error$0) {
18602
19363
  ReactSharedInternals.thrownErrors.push(error$0);
@@ -18607,7 +19368,7 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
18607
19368
  reject(_thrownError);
18608
19369
  }
18609
19370
  } else
18610
- resolve4(returnValue);
19371
+ resolve5(returnValue);
18611
19372
  }, function(error48) {
18612
19373
  popActScope(prevActQueue, prevActScopeDepth);
18613
19374
  0 < ReactSharedInternals.thrownErrors.length ? (error48 = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, reject(error48)) : reject(error48);
@@ -18623,11 +19384,11 @@ See https://react.dev/link/invalid-hook-call for tips about how to debug and fix
18623
19384
  if (0 < ReactSharedInternals.thrownErrors.length)
18624
19385
  throw callback = aggregateErrors(ReactSharedInternals.thrownErrors), ReactSharedInternals.thrownErrors.length = 0, callback;
18625
19386
  return {
18626
- then: function(resolve4, reject) {
19387
+ then: function(resolve5, reject) {
18627
19388
  didAwaitActCall = true;
18628
19389
  prevActScopeDepth === 0 ? (ReactSharedInternals.actQueue = queue, enqueueTask(function() {
18629
- return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve4, reject);
18630
- })) : resolve4(returnValue$jscomp$0);
19390
+ return recursivelyFlushAsyncActWork(returnValue$jscomp$0, resolve5, reject);
19391
+ })) : resolve5(returnValue$jscomp$0);
18631
19392
  }
18632
19393
  };
18633
19394
  };
@@ -19410,14 +20171,14 @@ var require_signal_exit = __commonJS((exports, module) => {
19410
20171
  import { PassThrough } from "node:stream";
19411
20172
  var consoleMethods, originalMethods, patchConsole = (callback) => {
19412
20173
  const stdout = new PassThrough;
19413
- const stderr = new PassThrough;
20174
+ const stderr2 = new PassThrough;
19414
20175
  stdout.write = (data) => {
19415
20176
  callback("stdout", data);
19416
20177
  };
19417
- stderr.write = (data) => {
20178
+ stderr2.write = (data) => {
19418
20179
  callback("stderr", data);
19419
20180
  };
19420
- const internalConsole = new console.Console(stdout, stderr);
20181
+ const internalConsole = new console.Console(stdout, stderr2);
19421
20182
  for (const method of consoleMethods) {
19422
20183
  originalMethods[method] = console[method];
19423
20184
  console[method] = internalConsole[method];
@@ -21614,12 +22375,12 @@ import { execFileSync as execFileSync2 } from "node:child_process";
21614
22375
  import fs from "node:fs";
21615
22376
  import tty from "node:tty";
21616
22377
  function terminalSize() {
21617
- const { env: env2, stdout, stderr } = process4;
22378
+ const { env: env2, stdout, stderr: stderr2 } = process4;
21618
22379
  if (stdout?.columns && stdout?.rows) {
21619
22380
  return create(stdout.columns, stdout.rows);
21620
22381
  }
21621
- if (stderr?.columns && stderr?.rows) {
21622
- return create(stderr.columns, stderr.rows);
22382
+ if (stderr2?.columns && stderr2?.rows) {
22383
+ return create(stderr2.columns, stderr2.rows);
21623
22384
  }
21624
22385
  if (env2.COLUMNS && env2.LINES) {
21625
22386
  return create(env2.COLUMNS, env2.LINES);
@@ -24124,8 +24885,8 @@ It can also happen if the client has a browser extension installed which messes
24124
24885
  currentEntangledActionThenable = {
24125
24886
  status: "pending",
24126
24887
  value: undefined,
24127
- then: function(resolve4) {
24128
- entangledListeners.push(resolve4);
24888
+ then: function(resolve5) {
24889
+ entangledListeners.push(resolve5);
24129
24890
  }
24130
24891
  };
24131
24892
  }
@@ -24149,8 +24910,8 @@ It can also happen if the client has a browser extension installed which messes
24149
24910
  status: "pending",
24150
24911
  value: null,
24151
24912
  reason: null,
24152
- then: function(resolve4) {
24153
- listeners.push(resolve4);
24913
+ then: function(resolve5) {
24914
+ listeners.push(resolve5);
24154
24915
  }
24155
24916
  };
24156
24917
  thenable.then(function() {
@@ -50940,7 +51701,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
50940
51701
  ws.onmessage = handleMessage;
50941
51702
  ws.onopen = function() {
50942
51703
  bridge = new src_bridge({
50943
- listen: function listen2(fn) {
51704
+ listen: function listen(fn) {
50944
51705
  messageListeners.push(fn);
50945
51706
  return function() {
50946
51707
  var index = messageListeners.indexOf(fn);
@@ -51073,7 +51834,7 @@ In order to be iterable, non-array objects must have a [Symbol.iterator]() metho
51073
51834
  return;
51074
51835
  }
51075
51836
  var wall = {
51076
- listen: function listen2(fn) {
51837
+ listen: function listen(fn) {
51077
51838
  onSubscribe(fn);
51078
51839
  return function() {
51079
51840
  onUnsubscribe(fn);
@@ -53829,7 +54590,7 @@ var init_ErrorBoundary = __esm(() => {
53829
54590
  // ../../node_modules/.bun/ink@6.8.0+b7580ee3b5d80903/node_modules/ink/build/components/App.js
53830
54591
  import { EventEmitter as EventEmitter2 } from "node:events";
53831
54592
  import process12 from "node:process";
53832
- function App({ children, stdin, stdout, stderr, writeToStdout, writeToStderr, exitOnCtrlC, onExit, setCursorPosition }) {
54593
+ function App({ children, stdin, stdout, stderr: stderr2, writeToStdout, writeToStderr, exitOnCtrlC, onExit, setCursorPosition }) {
53833
54594
  const [isFocusEnabled, setIsFocusEnabled] = import_react14.useState(true);
53834
54595
  const [activeFocusId, setActiveFocusId] = import_react14.useState(undefined);
53835
54596
  const [, setFocusables] = import_react14.useState([]);
@@ -54101,9 +54862,9 @@ Read about how to prevent this error on https://github.com/vadimdemedes/ink/#isr
54101
54862
  write: writeToStdout
54102
54863
  }), [stdout, writeToStdout]);
54103
54864
  const stderrContextValue = import_react14.useMemo(() => ({
54104
- stderr,
54865
+ stderr: stderr2,
54105
54866
  write: writeToStderr
54106
- }), [stderr, writeToStderr]);
54867
+ }), [stderr2, writeToStderr]);
54107
54868
  const cursorContextValue = import_react14.useMemo(() => ({
54108
54869
  setCursorPosition
54109
54870
  }), [setCursorPosition]);
@@ -54557,8 +55318,8 @@ class Ink {
54557
55318
  }
54558
55319
  }
54559
55320
  async waitUntilExit() {
54560
- this.exitPromise ||= new Promise((resolve4, reject) => {
54561
- this.resolveExitPromise = resolve4;
55321
+ this.exitPromise ||= new Promise((resolve5, reject) => {
55322
+ this.resolveExitPromise = resolve5;
54562
55323
  this.rejectExitPromise = reject;
54563
55324
  });
54564
55325
  if (!this.beforeExitHandler) {
@@ -55394,13 +56155,88 @@ var init_build2 = __esm(async () => {
55394
56155
  ]);
55395
56156
  });
55396
56157
 
56158
+ // src/spawner.ts
56159
+ import { execSync } from "node:child_process";
56160
+ import { existsSync as existsSync7 } from "node:fs";
56161
+ import { homedir as homedir5, platform as platform2 } from "node:os";
56162
+ import { join as join5 } from "node:path";
56163
+ function findBinary(binaryName, envVarName, commonPaths) {
56164
+ try {
56165
+ const command = platform2() === "win32" ? `where ${binaryName}` : `which ${binaryName}`;
56166
+ const result = execSync(command, { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }).trim();
56167
+ const binaryPath = result.split(`
56168
+ `)[0].trim();
56169
+ if (binaryPath && existsSync7(binaryPath)) {
56170
+ return binaryPath;
56171
+ }
56172
+ } catch {}
56173
+ const envPath = process.env[envVarName];
56174
+ if (envPath && existsSync7(envPath)) {
56175
+ return envPath;
56176
+ }
56177
+ for (const path of commonPaths) {
56178
+ if (existsSync7(path)) {
56179
+ return path;
56180
+ }
56181
+ }
56182
+ throw new Error(`${binaryName} is not installed`);
56183
+ }
56184
+ function findClaudeCli() {
56185
+ const home = homedir5();
56186
+ try {
56187
+ return findBinary("claude", "MEET_AI_CLAUDE_PATH", [
56188
+ join5(home, ".bun", "bin", "claude"),
56189
+ "/opt/homebrew/bin/claude",
56190
+ "/usr/local/bin/claude",
56191
+ join5(home, ".local", "bin", "claude")
56192
+ ]);
56193
+ } catch {
56194
+ throw new Error(`
56195
+ Claude Code is not installed
56196
+
56197
+ Please install Claude Code:
56198
+ bun add -g @anthropic-ai/claude-code
56199
+
56200
+ Or set MEET_AI_CLAUDE_PATH to the Claude Code CLI path.
56201
+ `.trim());
56202
+ }
56203
+ }
56204
+ function findCodexCli() {
56205
+ const home = homedir5();
56206
+ try {
56207
+ return findBinary("codex", "MEET_AI_CODEX_PATH", [
56208
+ join5(home, ".bun", "bin", "codex"),
56209
+ "/opt/homebrew/bin/codex",
56210
+ "/usr/local/bin/codex",
56211
+ join5(home, ".local", "bin", "codex")
56212
+ ]);
56213
+ } catch {
56214
+ throw new Error(`
56215
+ Codex CLI is not installed
56216
+
56217
+ Please install Codex:
56218
+ npm install -g @openai/codex
56219
+
56220
+ Or set MEET_AI_CODEX_PATH to the Codex CLI path.
56221
+ `.trim());
56222
+ }
56223
+ }
56224
+ var init_spawner = () => {};
56225
+
55397
56226
  // src/lib/process-manager.ts
55398
- import { mkdirSync as mkdirSync2, readFileSync as readFileSync6, writeFileSync as writeFileSync4, renameSync, lstatSync } from "node:fs";
55399
- import { homedir as homedir4 } from "node:os";
55400
- import { join as join4 } from "node:path";
56227
+ import { existsSync as existsSync8, lstatSync, mkdirSync as mkdirSync3, readFileSync as readFileSync7, renameSync, writeFileSync as writeFileSync5 } from "node:fs";
56228
+ import { homedir as homedir6 } from "node:os";
56229
+ import { fileURLToPath } from "node:url";
56230
+ import { join as join6 } from "node:path";
56231
+ function getRegistryDir() {
56232
+ return join6(process.env.HOME ?? homedir6(), ".meet-ai");
56233
+ }
56234
+ function getRegistryPath() {
56235
+ return join6(getRegistryDir(), "sessions.json");
56236
+ }
55401
56237
  function readRegistry() {
55402
56238
  try {
55403
- const data = readFileSync6(REGISTRY_PATH, "utf8");
56239
+ const data = readFileSync7(getRegistryPath(), "utf8");
55404
56240
  const parsed = exports_external.array(SessionEntrySchema).safeParse(JSON.parse(data));
55405
56241
  return parsed.success ? parsed.data : [];
55406
56242
  } catch {
@@ -55408,17 +56244,18 @@ function readRegistry() {
55408
56244
  }
55409
56245
  }
55410
56246
  function writeRegistry(entries) {
55411
- mkdirSync2(REGISTRY_DIR, { recursive: true, mode: 448 });
56247
+ const registryDir = getRegistryDir();
56248
+ mkdirSync3(registryDir, { recursive: true, mode: 448 });
55412
56249
  try {
55413
- const stat = lstatSync(REGISTRY_DIR);
56250
+ const stat = lstatSync(registryDir);
55414
56251
  if (!stat.isDirectory())
55415
56252
  return;
55416
56253
  } catch {
55417
56254
  return;
55418
56255
  }
55419
- const tmpPath = join4(REGISTRY_DIR, `sessions.${process.pid}.${Date.now()}.tmp`);
55420
- writeFileSync4(tmpPath, JSON.stringify(entries, null, 2), { mode: 384 });
55421
- renameSync(tmpPath, REGISTRY_PATH);
56256
+ const tmpPath = join6(registryDir, `sessions.${process.pid}.${Date.now()}.tmp`);
56257
+ writeFileSync5(tmpPath, JSON.stringify(entries, null, 2), { mode: 384 });
56258
+ renameSync(tmpPath, getRegistryPath());
55422
56259
  }
55423
56260
  function addToRegistry(entry) {
55424
56261
  const entries = readRegistry();
@@ -55433,6 +56270,17 @@ function removeFromRegistry(sessionName) {
55433
56270
  const entries = readRegistry().filter((e) => e.sessionName !== sessionName);
55434
56271
  writeRegistry(entries);
55435
56272
  }
56273
+ function resolveSelfCliCommand() {
56274
+ const sourceEntry = fileURLToPath(new URL("../index.ts", import.meta.url));
56275
+ if (existsSync8(sourceEntry) && typeof Bun !== "undefined") {
56276
+ return [process.execPath, "run", sourceEntry];
56277
+ }
56278
+ const builtEntry = fileURLToPath(new URL("../index.js", import.meta.url));
56279
+ if (existsSync8(builtEntry)) {
56280
+ return [process.execPath, builtEntry];
56281
+ }
56282
+ return ["meet-ai"];
56283
+ }
55436
56284
 
55437
56285
  class ProcessManager {
55438
56286
  teams = new Map;
@@ -55446,11 +56294,12 @@ class ProcessManager {
55446
56294
  sessionName(roomId) {
55447
56295
  return `mai-${roomId}`;
55448
56296
  }
55449
- spawn(roomId, roomName) {
56297
+ spawn(roomId, roomName, codingAgent = "claude") {
55450
56298
  const sessionName = this.sessionName(roomId);
55451
56299
  const team = {
55452
56300
  roomId,
55453
56301
  roomName,
56302
+ codingAgent,
55454
56303
  sessionName,
55455
56304
  status: "starting",
55456
56305
  exitCode: null,
@@ -55461,22 +56310,24 @@ class ProcessManager {
55461
56310
  this.spawned++;
55462
56311
  if (this.opts.dryRun)
55463
56312
  return team;
55464
- const fullPrompt = [
55465
- `ROOM_ID: ${roomId}`,
55466
- "",
55467
- "You are a team lead. IMMEDIATELY:",
55468
- "1. Start agent-team to start accepting commands from Meet AI",
55469
- "2. Connect to the meet-ai room using the /meet-ai skill",
55470
- "3. Just send a welcome message to the room, do not perform any work yet."
55471
- ].join(`
56313
+ const fullPrompt = [`ROOM_ID: ${roomId}`, "", ...this.buildPromptLines(codingAgent)].join(`
55472
56314
  `);
55473
- const claudeArgs = [
55474
- "--dangerously-skip-permissions",
55475
- "--model",
55476
- this.opts.model ?? "opus",
55477
- fullPrompt
55478
- ];
55479
- const sessionEnv = { DISABLE_AUTOUPDATER: "1" };
56315
+ const agentBinary = this.opts.agentBinaries[codingAgent];
56316
+ if (!agentBinary) {
56317
+ team.status = "error";
56318
+ team.lines.push(`[error] No CLI binary configured for coding agent: ${codingAgent}`);
56319
+ this.opts.onStatusChange?.(roomId, "error");
56320
+ return team;
56321
+ }
56322
+ const commandArgs = codingAgent === "codex" ? this.buildCodexListenCommandArgs(roomId) : [agentBinary, ...this.buildClaudeCommandArgs(fullPrompt)];
56323
+ const sessionEnv = {
56324
+ DISABLE_AUTOUPDATER: "1",
56325
+ MEET_AI_RUNTIME: codingAgent
56326
+ };
56327
+ if (codingAgent === "codex") {
56328
+ sessionEnv.MEET_AI_CODEX_PATH = agentBinary;
56329
+ sessionEnv.MEET_AI_CODEX_BOOTSTRAP_PROMPT = fullPrompt;
56330
+ }
55480
56331
  for (const key of ENV_ALLOWLIST) {
55481
56332
  const value = process.env[key];
55482
56333
  if (value)
@@ -55485,15 +56336,21 @@ class ProcessManager {
55485
56336
  if (this.opts.env)
55486
56337
  Object.assign(sessionEnv, this.opts.env);
55487
56338
  if (this.opts.debug) {
55488
- team.lines.push(`[debug] CMD: ${this.opts.claudePath} ${claudeArgs.join(" ").slice(0, 200)}`);
56339
+ team.lines.push(`[debug] AGENT: ${codingAgent}`);
56340
+ team.lines.push(`[debug] CMD: ${commandArgs.join(" ").slice(0, 200)}`);
55489
56341
  team.lines.push(`[debug] ENV: ${Object.keys(sessionEnv).join(", ")}`);
55490
56342
  }
55491
- const commandArgs = [this.opts.claudePath, ...claudeArgs];
55492
56343
  const result = this.tmux.newSession(sessionName, commandArgs, sessionEnv);
55493
56344
  if (result.ok) {
55494
56345
  team.status = "running";
55495
56346
  this.opts.onStatusChange?.(roomId, "running");
55496
- addToRegistry({ sessionName, roomId, roomName, createdAt: new Date().toISOString() });
56347
+ addToRegistry({
56348
+ sessionName,
56349
+ roomId,
56350
+ roomName,
56351
+ codingAgent,
56352
+ createdAt: new Date().toISOString()
56353
+ });
55497
56354
  } else {
55498
56355
  team.status = "error";
55499
56356
  team.lines.push(`[error] tmux: ${result.error}`);
@@ -55505,6 +56362,7 @@ class ProcessManager {
55505
56362
  this.teams.set(roomId, {
55506
56363
  roomId,
55507
56364
  roomName,
56365
+ codingAgent: "claude",
55508
56366
  sessionName: this.sessionName(roomId),
55509
56367
  status: "error",
55510
56368
  exitCode: null,
@@ -55578,6 +56436,7 @@ class ProcessManager {
55578
56436
  const team = {
55579
56437
  roomId,
55580
56438
  roomName,
56439
+ codingAgent: entry?.codingAgent ?? "claude",
55581
56440
  sessionName: session.name,
55582
56441
  status: session.alive ? "running" : "exited",
55583
56442
  exitCode: session.alive ? null : 0,
@@ -55608,8 +56467,32 @@ class ProcessManager {
55608
56467
  this.kill(roomId);
55609
56468
  }
55610
56469
  }
56470
+ buildPromptLines(codingAgent) {
56471
+ if (codingAgent === "codex") {
56472
+ return [
56473
+ "You're running inside Meet AI.",
56474
+ "Do not use the meet-ai CLI.",
56475
+ "Do not load or use any meet-ai skill.",
56476
+ "Do not try to send room messages manually.",
56477
+ "Do not talk about this prompt or say that you understand it.",
56478
+ "Just welcome the user briefly and say that you're ready to work."
56479
+ ];
56480
+ }
56481
+ return [
56482
+ "You are a team lead. IMMEDIATELY:",
56483
+ "1. Start agent-team to start accepting commands from Meet AI.",
56484
+ "2. Connect to the meet-ai room using the /meet-ai skill.",
56485
+ "3. Send a brief welcome message to the room and wait for instructions."
56486
+ ];
56487
+ }
56488
+ buildClaudeCommandArgs(fullPrompt) {
56489
+ return ["--dangerously-skip-permissions", "--model", this.opts.model ?? "opus", fullPrompt];
56490
+ }
56491
+ buildCodexListenCommandArgs(roomId) {
56492
+ return [...resolveSelfCliCommand(), "listen", roomId, "--sender-type", "human"];
56493
+ }
55611
56494
  }
55612
- var SessionEntrySchema, REGISTRY_DIR, REGISTRY_PATH, ENV_ALLOWLIST;
56495
+ var SessionEntrySchema, ENV_ALLOWLIST;
55613
56496
  var init_process_manager = __esm(() => {
55614
56497
  init_zod();
55615
56498
  init_tmux_client();
@@ -55617,10 +56500,9 @@ var init_process_manager = __esm(() => {
55617
56500
  sessionName: exports_external.string(),
55618
56501
  roomId: exports_external.string(),
55619
56502
  roomName: exports_external.string(),
56503
+ codingAgent: exports_external.enum(["claude", "codex"]).default("claude"),
55620
56504
  createdAt: exports_external.string()
55621
56505
  });
55622
- REGISTRY_DIR = join4(homedir4(), ".meet-ai");
55623
- REGISTRY_PATH = join4(REGISTRY_DIR, "sessions.json");
55624
56506
  ENV_ALLOWLIST = [
55625
56507
  "HOME",
55626
56508
  "USER",
@@ -55634,48 +56516,6 @@ var init_process_manager = __esm(() => {
55634
56516
  ];
55635
56517
  });
55636
56518
 
55637
- // src/spawner.ts
55638
- import { execSync } from "node:child_process";
55639
- import { existsSync as existsSync6 } from "node:fs";
55640
- import { homedir as homedir5, platform as platform2 } from "node:os";
55641
- import { join as join5 } from "node:path";
55642
- function findClaudeCli() {
55643
- try {
55644
- const command = platform2() === "win32" ? "where claude" : "which claude";
55645
- const result = execSync(command, { encoding: "utf8", stdio: ["pipe", "pipe", "ignore"] }).trim();
55646
- const claudePath = result.split(`
55647
- `)[0].trim();
55648
- if (claudePath && existsSync6(claudePath)) {
55649
- return claudePath;
55650
- }
55651
- } catch {}
55652
- const envPath = process.env.MEET_AI_CLAUDE_PATH;
55653
- if (envPath && existsSync6(envPath)) {
55654
- return envPath;
55655
- }
55656
- const home = homedir5();
55657
- const commonPaths = [
55658
- join5(home, ".bun", "bin", "claude"),
55659
- "/opt/homebrew/bin/claude",
55660
- "/usr/local/bin/claude",
55661
- join5(home, ".local", "bin", "claude")
55662
- ];
55663
- for (const path of commonPaths) {
55664
- if (existsSync6(path)) {
55665
- return path;
55666
- }
55667
- }
55668
- throw new Error(`
55669
- Claude Code is not installed
55670
-
55671
- Please install Claude Code:
55672
- bun add -g @anthropic-ai/claude-code
55673
-
55674
- Or set MEET_AI_CLAUDE_PATH to the Claude Code CLI path.
55675
- `.trim());
55676
- }
55677
- var init_spawner = () => {};
55678
-
55679
56519
  // ../../node_modules/.bun/react@19.2.4/node_modules/react/cjs/react-jsx-dev-runtime.development.js
55680
56520
  var require_react_jsx_dev_runtime_development = __commonJS((exports) => {
55681
56521
  var React11 = __toESM(require_react());
@@ -56367,16 +57207,25 @@ var init_dashboard = __esm(async () => {
56367
57207
  });
56368
57208
 
56369
57209
  // src/tui/spawn-dialog.tsx
56370
- function SpawnDialog({ onSubmit, onCancel }) {
57210
+ function SpawnDialog({ codingAgents, onSubmit, onCancel }) {
56371
57211
  const [roomName, setRoomName] = import_react29.useState("");
56372
57212
  const [cursor, setCursor] = import_react29.useState(0);
57213
+ const [selectedAgentIndex, setSelectedAgentIndex] = import_react29.useState(0);
56373
57214
  use_input_default((input, key) => {
56374
57215
  if (key.escape) {
56375
57216
  onCancel();
56376
57217
  return;
56377
57218
  }
56378
57219
  if (key.return && roomName.trim()) {
56379
- onSubmit(roomName.trim());
57220
+ onSubmit(roomName.trim(), codingAgents[selectedAgentIndex]?.id ?? "claude");
57221
+ return;
57222
+ }
57223
+ if (key.upArrow) {
57224
+ setSelectedAgentIndex((current) => Math.max(0, current - 1));
57225
+ return;
57226
+ }
57227
+ if (key.downArrow) {
57228
+ setSelectedAgentIndex((current) => Math.min(codingAgents.length - 1, current + 1));
56380
57229
  return;
56381
57230
  }
56382
57231
  if (key.leftArrow) {
@@ -56394,7 +57243,7 @@ function SpawnDialog({ onSubmit, onCancel }) {
56394
57243
  }
56395
57244
  return;
56396
57245
  }
56397
- if (input && !key.ctrl && !key.meta && !key.upArrow && !key.downArrow) {
57246
+ if (input && !key.ctrl && !key.meta) {
56398
57247
  setRoomName((v) => v.slice(0, cursor) + input + v.slice(cursor));
56399
57248
  setCursor((c) => c + input.length);
56400
57249
  }
@@ -56432,7 +57281,21 @@ function SpawnDialog({ onSubmit, onCancel }) {
56432
57281
  children: after
56433
57282
  }, undefined, false, undefined, this)
56434
57283
  ]
56435
- }, undefined, true, undefined, this)
57284
+ }, undefined, true, undefined, this),
57285
+ /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
57286
+ children: [
57287
+ "Agent:",
57288
+ " ",
57289
+ /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
57290
+ color: "yellow",
57291
+ children: codingAgents[selectedAgentIndex]?.label ?? "Claude Code"
57292
+ }, undefined, false, undefined, this)
57293
+ ]
57294
+ }, undefined, true, undefined, this),
57295
+ /* @__PURE__ */ jsx_dev_runtime4.jsxDEV(Text, {
57296
+ dimColor: true,
57297
+ children: "Use ←/→ to edit the room name, ↑/↓ to choose the coding agent, Enter to spawn."
57298
+ }, undefined, false, undefined, this)
56436
57299
  ]
56437
57300
  }, undefined, true, undefined, this);
56438
57301
  }
@@ -56580,7 +57443,7 @@ var init_status_bar = __esm(async () => {
56580
57443
  });
56581
57444
 
56582
57445
  // src/tui/app.tsx
56583
- function AppInner({ processManager, client, onAttach, onDetach }) {
57446
+ function AppInner({ processManager, client, codingAgents, onAttach, onDetach }) {
56584
57447
  const { exit } = use_app_default();
56585
57448
  const { stdout } = use_stdout_default();
56586
57449
  const { setRawMode } = use_stdin_default();
@@ -56590,7 +57453,7 @@ function AppInner({ processManager, client, onAttach, onDetach }) {
56590
57453
  const [killTargetId, setKillTargetId] = import_react30.useState(null);
56591
57454
  const [, setRenderTick] = import_react30.useState(0);
56592
57455
  const terminalHeight = stdout?.rows ?? 24;
56593
- const bottomHeight = showSpawn ? 4 : killTargetId ? 1 : 1;
57456
+ const bottomHeight = showSpawn ? 6 : killTargetId ? 1 : 1;
56594
57457
  const dashboardHeight = terminalHeight - bottomHeight;
56595
57458
  const focusedRoomRef = import_react30.useRef(null);
56596
57459
  const focusedTeam = teams[focusedIndex];
@@ -56605,10 +57468,10 @@ function AppInner({ processManager, client, onAttach, onDetach }) {
56605
57468
  return [...current];
56606
57469
  });
56607
57470
  }, [processManager]);
56608
- const handleSpawn = import_react30.useCallback(async (roomName) => {
57471
+ const handleSpawn = import_react30.useCallback(async (roomName, codingAgent) => {
56609
57472
  try {
56610
57473
  const room = await client.createRoom(roomName);
56611
- processManager.spawn(room.id, roomName);
57474
+ processManager.spawn(room.id, roomName, codingAgent);
56612
57475
  refreshTeams();
56613
57476
  } catch (error48) {
56614
57477
  const msg = error48 instanceof Error ? error48.message : String(error48);
@@ -56726,9 +57589,10 @@ function AppInner({ processManager, client, onAttach, onDetach }) {
56726
57589
  }, undefined, false, undefined, this)
56727
57590
  ]
56728
57591
  }, undefined, true, undefined, this) : showSpawn ? /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(SpawnDialog, {
56729
- onSubmit: (name) => {
57592
+ codingAgents: [...codingAgents],
57593
+ onSubmit: (roomName, codingAgent) => {
56730
57594
  setShowSpawn(false);
56731
- handleSpawn(name);
57595
+ handleSpawn(roomName, codingAgent);
56732
57596
  },
56733
57597
  onCancel: () => setShowSpawn(false)
56734
57598
  }, undefined, false, undefined, this) : /* @__PURE__ */ jsx_dev_runtime6.jsxDEV(StatusBar, {
@@ -56804,9 +57668,21 @@ async function startDashboard(client, config2, options) {
56804
57668
  console.error(`tmux >= 3.2 required, found ${check3.version}`);
56805
57669
  process.exit(1);
56806
57670
  }
56807
- const claudePath = findClaudeCli();
57671
+ const agentBinaries = {};
57672
+ try {
57673
+ agentBinaries.claude = findClaudeCli();
57674
+ } catch {}
57675
+ try {
57676
+ agentBinaries.codex = findCodexCli();
57677
+ } catch {}
57678
+ const availableCodingAgents = CODING_AGENT_DEFINITIONS.filter((agent) => Boolean(agentBinaries[agent.id]));
57679
+ if (availableCodingAgents.length === 0) {
57680
+ console.error("No supported coding agent CLI was found.");
57681
+ console.error("Install Claude Code or Codex, or set MEET_AI_CLAUDE_PATH / MEET_AI_CODEX_PATH.");
57682
+ process.exit(1);
57683
+ }
56808
57684
  const processManager = new ProcessManager({
56809
- claudePath,
57685
+ agentBinaries,
56810
57686
  debug: options?.debug,
56811
57687
  tmux,
56812
57688
  env: {
@@ -56830,15 +57706,16 @@ async function startDashboard(client, config2, options) {
56830
57706
  try {
56831
57707
  lobbyWs = client.listenLobby({
56832
57708
  silent: true,
56833
- onSpawnRequest: async (roomName) => {
56834
- if (pendingSpawns.has(roomName))
57709
+ onSpawnRequest: async ({ roomName, codingAgent }) => {
57710
+ const key = `${roomName}:${codingAgent}`;
57711
+ if (pendingSpawns.has(key))
56835
57712
  return;
56836
- pendingSpawns.add(roomName);
57713
+ pendingSpawns.add(key);
56837
57714
  try {
56838
57715
  const room = await client.createRoom(roomName);
56839
- processManager.spawn(room.id, roomName);
57716
+ processManager.spawn(room.id, roomName, codingAgent);
56840
57717
  } finally {
56841
- pendingSpawns.delete(roomName);
57718
+ pendingSpawns.delete(key);
56842
57719
  }
56843
57720
  }
56844
57721
  });
@@ -56854,6 +57731,7 @@ async function startDashboard(client, config2, options) {
56854
57731
  const element = import_react31.default.createElement(App2, {
56855
57732
  processManager,
56856
57733
  client,
57734
+ codingAgents: availableCodingAgents,
56857
57735
  onAttach: () => {
56858
57736
  lobbyWs?.close();
56859
57737
  lobbyWs = null;
@@ -56867,10 +57745,11 @@ async function startDashboard(client, config2, options) {
56867
57745
  cleanup();
56868
57746
  }
56869
57747
  var import_react31;
56870
- var init_usecase17 = __esm(async () => {
57748
+ var init_usecase16 = __esm(async () => {
57749
+ init_coding_agents();
57750
+ init_spawner();
56871
57751
  init_process_manager();
56872
57752
  init_tmux_client();
56873
- init_spawner();
56874
57753
  await __promiseAll([
56875
57754
  init_build2(),
56876
57755
  init_app()
@@ -56881,7 +57760,7 @@ var init_usecase17 = __esm(async () => {
56881
57760
  // src/index.ts
56882
57761
  init_dist();
56883
57762
  // package.json
56884
- var version = "0.0.35";
57763
+ var version = "0.0.36";
56885
57764
 
56886
57765
  // src/index.ts
56887
57766
  init_output();
@@ -56921,7 +57800,7 @@ var main = defineCommand({
56921
57800
  try {
56922
57801
  const { getClient: getClient2 } = await Promise.resolve().then(() => (init_bootstrap(), exports_bootstrap));
56923
57802
  const { getMeetAiConfig: getMeetAiConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
56924
- const { startDashboard: startDashboard2 } = await init_usecase17().then(() => exports_usecase2);
57803
+ const { startDashboard: startDashboard2 } = await init_usecase16().then(() => exports_usecase2);
56925
57804
  const client = getClient2();
56926
57805
  const config2 = getMeetAiConfig2();
56927
57806
  await startDashboard2(client, config2, { debug: args.debug });