llm-party-cli 0.1.2 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -31304,9 +31304,9 @@ var require_main2 = __commonJS((exports) => {
31304
31304
 
31305
31305
  // src/index.ts
31306
31306
  var import_config = __toESM(require_config(), 1);
31307
- var import_react19 = __toESM(require_react(), 1);
31307
+ var import_react24 = __toESM(require_react(), 1);
31308
31308
  import { readFile as readFile5 } from "fs/promises";
31309
- import path10 from "path";
31309
+ import path11 from "path";
31310
31310
  import { fileURLToPath as fileURLToPath3 } from "url";
31311
31311
 
31312
31312
  // node_modules/@opentui/core/index-e89anq5x.js
@@ -73642,6 +73642,13 @@ function Eg({ prompt: Q2, options: X2 }) {
73642
73642
  return d7;
73643
73643
  }
73644
73644
 
73645
+ // src/adapters/base.ts
73646
+ function formatTranscript(messages) {
73647
+ return messages.map((m3) => `[${m3.from}]: ${m3.text}`).join(`
73648
+
73649
+ `);
73650
+ }
73651
+
73645
73652
  // src/adapters/claude-base.ts
73646
73653
  class ClaudeBaseAdapter {
73647
73654
  name;
@@ -73663,10 +73670,7 @@ class ClaudeBaseAdapter {
73663
73670
  return { ...process.env, ...config.env ?? {} };
73664
73671
  }
73665
73672
  async send(messages) {
73666
- const transcript = messages.map((m3) => `[${m3.from}]: ${m3.text}`).join(`
73667
-
73668
- `);
73669
- return await this.querySDK(transcript);
73673
+ return await this.querySDK(formatTranscript(messages));
73670
73674
  }
73671
73675
  async destroy() {
73672
73676
  return;
@@ -74166,10 +74170,7 @@ class CodexAdapter {
74166
74170
  if (!this.thread) {
74167
74171
  return "[Codex thread not initialized]";
74168
74172
  }
74169
- const transcript = messages.map((m3) => `[${m3.from}]: ${m3.text}`).join(`
74170
-
74171
- `);
74172
- const turn = await this.thread.run(transcript);
74173
+ const turn = await this.thread.run(formatTranscript(messages));
74173
74174
  if (turn.finalResponse && turn.finalResponse.length > 0) {
74174
74175
  return turn.finalResponse;
74175
74176
  }
@@ -75372,9 +75373,7 @@ class CopilotAdapter {
75372
75373
  if (!this.session) {
75373
75374
  return "[Copilot session not initialized]";
75374
75375
  }
75375
- const transcript = messages.map((m3) => `[${m3.from}]: ${m3.text}`).join(`
75376
-
75377
- `);
75376
+ const transcript = formatTranscript(messages);
75378
75377
  try {
75379
75378
  return await this.sendToSession(transcript);
75380
75379
  } catch (err) {
@@ -75490,7 +75489,59 @@ async function loadGlmAliasEnv() {
75490
75489
  import { readFile as readFile3, writeFile as writeFile3, access, mkdir as mkdir3 } from "fs/promises";
75491
75490
  import { homedir, userInfo } from "os";
75492
75491
  import path8 from "path";
75493
- var VALID_PROVIDERS = ["claude", "codex", "copilot", "glm"];
75492
+
75493
+ // src/config/defaults.ts
75494
+ var PROVIDERS = [
75495
+ {
75496
+ id: "claude",
75497
+ displayName: "Claude",
75498
+ description: "Anthropic Claude Agent SDK",
75499
+ unavailableHint: "claude CLI not found",
75500
+ defaultModel: "sonnet",
75501
+ defaultTag: "claude",
75502
+ detectCommand: "claude",
75503
+ detectType: "binary"
75504
+ },
75505
+ {
75506
+ id: "codex",
75507
+ displayName: "Codex",
75508
+ description: "OpenAI Codex SDK",
75509
+ unavailableHint: "codex CLI not found",
75510
+ defaultModel: "gpt-5.2",
75511
+ defaultTag: "codex",
75512
+ detectCommand: "codex",
75513
+ detectType: "binary"
75514
+ },
75515
+ {
75516
+ id: "copilot",
75517
+ displayName: "Copilot",
75518
+ description: "GitHub Copilot SDK",
75519
+ unavailableHint: "copilot CLI not found",
75520
+ defaultModel: "gpt-4.1",
75521
+ defaultTag: "copilot",
75522
+ detectCommand: "copilot",
75523
+ detectType: "binary"
75524
+ },
75525
+ {
75526
+ id: "glm",
75527
+ displayName: "GLM",
75528
+ description: "glm alias configured on Claude Code CLI",
75529
+ unavailableHint: "glm shell alias not configured",
75530
+ defaultModel: "glm-5",
75531
+ defaultTag: "glm",
75532
+ detectCommand: "glm",
75533
+ detectType: "alias",
75534
+ env: {
75535
+ ANTHROPIC_BASE_URL: "https://api.z.ai/api/anthropic",
75536
+ ANTHROPIC_DEFAULT_HAIKU_MODEL: "glm-4.5-air",
75537
+ ANTHROPIC_DEFAULT_SONNET_MODEL: "glm-4.5",
75538
+ ANTHROPIC_DEFAULT_OPUS_MODEL: "glm-5"
75539
+ }
75540
+ }
75541
+ ];
75542
+
75543
+ // src/config/loader.ts
75544
+ var VALID_PROVIDER_IDS = new Set(PROVIDERS.map((p2) => p2.id));
75494
75545
  var LLM_PARTY_HOME = path8.join(homedir(), ".llm-party");
75495
75546
  function validateConfig(data) {
75496
75547
  if (!data || typeof data !== "object") {
@@ -75520,8 +75571,8 @@ function validateConfig(data) {
75520
75571
  if (typeof agent.model !== "string" || agent.model.trim() === "") {
75521
75572
  throw new Error(`Agent '${agent.name}' must have a non-empty 'model' string`);
75522
75573
  }
75523
- if (!VALID_PROVIDERS.includes(agent.provider)) {
75524
- throw new Error(`Agent '${agent.name}' has invalid provider '${agent.provider}'. Valid: ${VALID_PROVIDERS.join(", ")}`);
75574
+ if (!VALID_PROVIDER_IDS.has(agent.provider)) {
75575
+ throw new Error(`Agent '${agent.name}' has invalid provider '${agent.provider}'. Valid: ${Array.from(VALID_PROVIDER_IDS).join(", ")}`);
75525
75576
  }
75526
75577
  if (agent.prompts !== undefined) {
75527
75578
  const isArray = Array.isArray(agent.prompts) && agent.prompts.every((p2) => typeof p2 === "string");
@@ -75562,24 +75613,22 @@ async function resolveArtifactsPrompt(appRoot) {
75562
75613
  const bundledArtifacts = path8.join(appRoot, "prompts", "artifacts.md");
75563
75614
  return await readFile3(bundledArtifacts, "utf8");
75564
75615
  }
75616
+ async function ensureFile(filePath, defaultContent) {
75617
+ try {
75618
+ await access(filePath);
75619
+ } catch {
75620
+ await writeFile3(filePath, defaultContent, "utf8");
75621
+ }
75622
+ }
75565
75623
  async function initProjectFolder(cwd) {
75566
75624
  const projectHome = path8.join(cwd, ".llm-party");
75567
75625
  const memoryDir = path8.join(projectHome, "memory");
75568
75626
  const skillsDir = path8.join(projectHome, "skills");
75569
75627
  await mkdir3(memoryDir, { recursive: true });
75570
75628
  await mkdir3(skillsDir, { recursive: true });
75571
- const tasksFile = path8.join(projectHome, "TASKS.md");
75572
- try {
75573
- await access(tasksFile);
75574
- } catch {
75575
- await writeFile3(tasksFile, `# Tasks
75576
- `, "utf8");
75577
- }
75578
- const projectMd = path8.join(memoryDir, "project.md");
75579
- try {
75580
- await access(projectMd);
75581
- } catch {
75582
- await writeFile3(projectMd, `# Project Memory
75629
+ await ensureFile(path8.join(projectHome, "TASKS.md"), `# Tasks
75630
+ `);
75631
+ await ensureFile(path8.join(memoryDir, "project.md"), `# Project Memory
75583
75632
 
75584
75633
  ## Current State
75585
75634
 
@@ -75591,41 +75640,26 @@ Next:
75591
75640
  ---
75592
75641
 
75593
75642
  ## Log
75594
- `, "utf8");
75595
- }
75596
- const decisionsMd = path8.join(memoryDir, "decisions.md");
75597
- try {
75598
- await access(decisionsMd);
75599
- } catch {
75600
- await writeFile3(decisionsMd, `# Decisions
75601
- `, "utf8");
75602
- }
75643
+ `);
75644
+ await ensureFile(path8.join(memoryDir, "decisions.md"), `# Decisions
75645
+ `);
75603
75646
  }
75604
75647
  async function initLlmPartyHome(appRoot) {
75605
75648
  await mkdir3(LLM_PARTY_HOME, { recursive: true });
75606
75649
  await mkdir3(path8.join(LLM_PARTY_HOME, "sessions"), { recursive: true });
75607
75650
  await mkdir3(path8.join(LLM_PARTY_HOME, "network"), { recursive: true });
75608
75651
  await mkdir3(path8.join(LLM_PARTY_HOME, "agents"), { recursive: true });
75609
- const projectsYml = path8.join(LLM_PARTY_HOME, "network", "projects.yml");
75610
- try {
75611
- await access(projectsYml);
75612
- } catch {
75613
- await writeFile3(projectsYml, `projects: []
75614
- `, "utf8");
75615
- }
75616
- const librariesYml = path8.join(LLM_PARTY_HOME, "network", "libraries.yml");
75617
- try {
75618
- await access(librariesYml);
75619
- } catch {
75620
- await writeFile3(librariesYml, `libraries: []
75621
- `, "utf8");
75622
- }
75623
- const globalConfig = path8.join(LLM_PARTY_HOME, "config.json");
75652
+ await ensureFile(path8.join(LLM_PARTY_HOME, "network", "projects.yml"), `projects: []
75653
+ `);
75654
+ await ensureFile(path8.join(LLM_PARTY_HOME, "network", "libraries.yml"), `libraries: []
75655
+ `);
75656
+ }
75657
+ async function configExists() {
75624
75658
  try {
75625
- await access(globalConfig);
75659
+ await access(path8.join(LLM_PARTY_HOME, "config.json"));
75660
+ return true;
75626
75661
  } catch {
75627
- const bundledConfig = await readFile3(path8.join(appRoot, "configs", "default.json"), "utf8");
75628
- await writeFile3(globalConfig, bundledConfig, "utf8");
75662
+ return false;
75629
75663
  }
75630
75664
  }
75631
75665
  async function loadConfig2(configPath) {
@@ -75826,8 +75860,8 @@ function createSessionId() {
75826
75860
  }
75827
75861
 
75828
75862
  // src/ui/App.tsx
75829
- var import_react17 = __toESM(require_react(), 1);
75830
- import { spawn as spawn4 } from "child_process";
75863
+ var import_react22 = __toESM(require_react(), 1);
75864
+ import { spawn as spawn5 } from "child_process";
75831
75865
 
75832
75866
  // src/ui/useOrchestrator.ts
75833
75867
  var import_react13 = __toESM(require_react(), 1);
@@ -75843,7 +75877,6 @@ function useOrchestrator(orchestrator, maxAutoHops) {
75843
75877
  const [stickyTarget, setStickyTarget] = import_react13.useState(undefined);
75844
75878
  const [dispatching, setDispatching] = import_react13.useState(false);
75845
75879
  const projectFolderReady = import_react13.useRef(false);
75846
- const knownChangedFiles = import_react13.useRef([]);
75847
75880
  const agentProviders = import_react13.useRef(new Map(orchestrator.listAgents().map((a2) => [a2.name.toUpperCase(), a2.provider])));
75848
75881
  const dispatch = import_react13.useCallback(async (line) => {
75849
75882
  if (!line.trim())
@@ -75872,7 +75905,7 @@ function useOrchestrator(orchestrator, maxAutoHops) {
75872
75905
  setMessages((prev) => [...prev, userDisplay]);
75873
75906
  setDispatching(true);
75874
75907
  try {
75875
- await dispatchWithHandoffs(orchestrator, targets, maxAutoHops, knownChangedFiles, agentProviders.current, setMessages, setAgentStates);
75908
+ await dispatchWithHandoffs(orchestrator, targets, maxAutoHops, agentProviders.current, setMessages, setAgentStates);
75876
75909
  } finally {
75877
75910
  setDispatching(false);
75878
75911
  }
@@ -75892,7 +75925,7 @@ function useOrchestrator(orchestrator, maxAutoHops) {
75892
75925
  }, []);
75893
75926
  return { messages, agentStates, stickyTarget, dispatching, dispatch, addSystemMessage, clearMessages };
75894
75927
  }
75895
- async function dispatchWithHandoffs(orchestrator, initialTargets, maxHops, knownChangedFilesRef, agentProviders, setMessages, setAgentStates) {
75928
+ async function dispatchWithHandoffs(orchestrator, initialTargets, maxHops, agentProviders, setMessages, setAgentStates) {
75896
75929
  let targets = initialTargets;
75897
75930
  let hops = 0;
75898
75931
  while (true) {
@@ -75931,21 +75964,6 @@ async function dispatchWithHandoffs(orchestrator, initialTargets, maxHops, known
75931
75964
  }
75932
75965
  return next;
75933
75966
  });
75934
- const latestChangedFiles = await getChangedFiles();
75935
- const newlyChanged = diffFiles(knownChangedFilesRef.current, latestChangedFiles);
75936
- if (newlyChanged.length > 0) {
75937
- const systemMsg = {
75938
- id: nextSystemId(),
75939
- from: "SYSTEM",
75940
- text: `Files modified:
75941
- ${newlyChanged.map((f) => ` ${f}`).join(`
75942
- `)}`,
75943
- createdAt: new Date().toISOString(),
75944
- type: "system"
75945
- };
75946
- setMessages((prev) => [...prev, systemMsg]);
75947
- }
75948
- knownChangedFilesRef.current = latestChangedFiles;
75949
75967
  const nextSelectors = extractNextSelectors(batch);
75950
75968
  if (nextSelectors.length === 0)
75951
75969
  return;
@@ -75996,10 +76014,6 @@ function getChangedFiles() {
75996
76014
  });
75997
76015
  });
75998
76016
  }
75999
- function diffFiles(before, after) {
76000
- const set = new Set(before);
76001
- return after.filter((f) => !set.has(f));
76002
- }
76003
76017
  function extractNextSelectors(messages) {
76004
76018
  const selectors = [];
76005
76019
  for (const msg of messages) {
@@ -76025,6 +76039,33 @@ function parseRouting(line) {
76025
76039
  return { mentions, raw: line };
76026
76040
  }
76027
76041
 
76042
+ // src/ui/theme.ts
76043
+ var COLORS = {
76044
+ primary: "#00BFFF",
76045
+ success: "#00FF88",
76046
+ human: "#00FF00",
76047
+ agent: "#FF00FF",
76048
+ error: "#FF4444",
76049
+ warning: "#FF8800",
76050
+ textPrimary: "#FFFFFF",
76051
+ textSecondary: "#AAAAAA",
76052
+ textMuted: "#888888",
76053
+ textDim: "#666666",
76054
+ textSubtle: "#555555",
76055
+ textFaint: "#444444",
76056
+ borderActive: "#00FF00",
76057
+ borderDim: "#555555",
76058
+ borderStrong: "#333333",
76059
+ bgPanel: "#0d0d1a",
76060
+ bgContent: "#111122",
76061
+ bgFocus: "#1a1a2e",
76062
+ bgActiveTab: "#1a2a1a",
76063
+ bgError: "#1a0000",
76064
+ bgSweepDim: "#1a1a1a",
76065
+ bgSweepLow: "#333333"
76066
+ };
76067
+ var PARTY_COLORS = [COLORS.agent, COLORS.success, COLORS.primary, "#FFE000"];
76068
+
76028
76069
  // node_modules/@opentui/react/jsx-dev-runtime.js
76029
76070
  var import_jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
76030
76071
 
@@ -76032,7 +76073,7 @@ var import_jsx_dev_runtime2 = __toESM(require_jsx_dev_runtime(), 1);
76032
76073
  function MessageBubble({ message, humanName }) {
76033
76074
  if (message.type === "system") {
76034
76075
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76035
- fg: "#666666",
76076
+ fg: COLORS.textDim,
76036
76077
  selectable: true,
76037
76078
  children: message.text
76038
76079
  }, undefined, false, undefined, this);
@@ -76042,7 +76083,7 @@ function MessageBubble({ message, humanName }) {
76042
76083
  selectable: true,
76043
76084
  children: [
76044
76085
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76045
- fg: "#00FF00",
76086
+ fg: COLORS.human,
76046
76087
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("strong", {
76047
76088
  children: [
76048
76089
  "[",
@@ -76064,7 +76105,7 @@ function MessageBubble({ message, humanName }) {
76064
76105
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76065
76106
  selectable: true,
76066
76107
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76067
- fg: "#FF00FF",
76108
+ fg: COLORS.agent,
76068
76109
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("strong", {
76069
76110
  children: [
76070
76111
  "[",
@@ -76084,7 +76125,11 @@ function MessageBubble({ message, humanName }) {
76084
76125
 
76085
76126
  // src/ui/StatusBar.tsx
76086
76127
  var import_react14 = __toESM(require_react(), 1);
76087
- var SPINNER = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
76128
+
76129
+ // src/ui/constants.ts
76130
+ var SPINNER_FRAMES = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
76131
+
76132
+ // src/ui/StatusBar.tsx
76088
76133
  var PULSE_COLORS = ["#005F87", "#0087AF", "#00AFD7", "#00D7FF", "#5FF", "#00D7FF", "#0087AF"];
76089
76134
  function useThinkingAnimation(active) {
76090
76135
  const [frame, setFrame] = import_react14.useState(0);
@@ -76094,13 +76139,13 @@ function useThinkingAnimation(active) {
76094
76139
  return;
76095
76140
  }
76096
76141
  const interval = setInterval(() => {
76097
- setFrame((f) => (f + 1) % (SPINNER.length * PULSE_COLORS.length));
76142
+ setFrame((f) => (f + 1) % (SPINNER_FRAMES.length * PULSE_COLORS.length));
76098
76143
  }, 80);
76099
76144
  return () => clearInterval(interval);
76100
76145
  }, [active]);
76101
76146
  return {
76102
- spinner: active ? SPINNER[frame % SPINNER.length] : "",
76103
- color: active ? PULSE_COLORS[frame % PULSE_COLORS.length] : "#888888"
76147
+ spinner: active ? SPINNER_FRAMES[frame % SPINNER_FRAMES.length] : "",
76148
+ color: active ? PULSE_COLORS[frame % PULSE_COLORS.length] : COLORS.textMuted
76104
76149
  };
76105
76150
  }
76106
76151
  function StatusBar({ agents, agentStates, sessionId, stickyTarget }) {
@@ -76118,15 +76163,15 @@ function StatusBar({ agents, agentStates, sessionId, stickyTarget }) {
76118
76163
  gap: 2,
76119
76164
  children: [
76120
76165
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76121
- fg: "#888888",
76166
+ fg: COLORS.textMuted,
76122
76167
  children: target
76123
76168
  }, undefined, false, undefined, this),
76124
76169
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76125
- fg: "#555555",
76170
+ fg: COLORS.textSubtle,
76126
76171
  children: "|"
76127
76172
  }, undefined, false, undefined, this),
76128
76173
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76129
- fg: "#666666",
76174
+ fg: COLORS.textDim,
76130
76175
  children: sessionId.slice(0, 20)
76131
76176
  }, undefined, false, undefined, this)
76132
76177
  ]
@@ -76147,7 +76192,7 @@ function AgentChip({ name, state }) {
76147
76192
  const { spinner, color } = useThinkingAnimation(state === "thinking");
76148
76193
  if (state === "error") {
76149
76194
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76150
- fg: "#FF4444",
76195
+ fg: COLORS.error,
76151
76196
  children: [
76152
76197
  name,
76153
76198
  " ERR"
@@ -76165,7 +76210,7 @@ function AgentChip({ name, state }) {
76165
76210
  }, undefined, true, undefined, this);
76166
76211
  }
76167
76212
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76168
- fg: "#888888",
76213
+ fg: COLORS.textMuted,
76169
76214
  children: name
76170
76215
  }, undefined, false, undefined, this);
76171
76216
  }
@@ -76186,7 +76231,7 @@ var FUNCTION_KEYS = new Set([
76186
76231
  "f11",
76187
76232
  "f12"
76188
76233
  ]);
76189
- function InputLine({ humanName, onSubmit, disabled }) {
76234
+ function InputLine({ humanName, onSubmit, disabled, disabledMessage }) {
76190
76235
  const valueRef = import_react15.useRef("");
76191
76236
  const cursorRef = import_react15.useRef(0);
76192
76237
  const [, forceRender] = import_react15.useState(0);
@@ -76337,10 +76382,11 @@ function InputLine({ humanName, onSubmit, disabled }) {
76337
76382
  });
76338
76383
  const value = valueRef.current;
76339
76384
  const cursor = cursorRef.current;
76340
- const borderColor = disabled ? "#555555" : "#00FF00";
76385
+ const borderColor = disabled ? COLORS.borderDim : COLORS.borderActive;
76341
76386
  const label = `${humanName} > `;
76342
76387
  const separator = "\u2500".repeat(Math.max(0, termWidth - 2));
76343
76388
  if (disabled) {
76389
+ const msg = disabledMessage !== undefined ? disabledMessage : "waiting for agents...";
76344
76390
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
76345
76391
  flexDirection: "column",
76346
76392
  paddingX: 1,
@@ -76351,13 +76397,16 @@ function InputLine({ humanName, onSubmit, disabled }) {
76351
76397
  fg: borderColor,
76352
76398
  children: separator
76353
76399
  }, undefined, false, undefined, this),
76354
- /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76355
- fg: "#666666",
76400
+ msg ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76401
+ fg: COLORS.textDim,
76356
76402
  children: [
76357
76403
  label,
76358
- "waiting for agents..."
76404
+ msg
76359
76405
  ]
76360
- }, undefined, true, undefined, this)
76406
+ }, undefined, true, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76407
+ fg: COLORS.textDim,
76408
+ children: label
76409
+ }, undefined, false, undefined, this)
76361
76410
  ]
76362
76411
  }, undefined, true, undefined, this);
76363
76412
  }
@@ -76375,18 +76424,18 @@ function InputLine({ humanName, onSubmit, disabled }) {
76375
76424
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76376
76425
  children: [
76377
76426
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76378
- fg: "#00FF00",
76427
+ fg: COLORS.human,
76379
76428
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("strong", {
76380
76429
  children: label
76381
76430
  }, undefined, false, undefined, this)
76382
76431
  }, undefined, false, undefined, this),
76383
76432
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76384
- bg: "#FFFFFF",
76433
+ bg: COLORS.textPrimary,
76385
76434
  fg: "#000000",
76386
76435
  children: " "
76387
76436
  }, undefined, false, undefined, this),
76388
76437
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76389
- fg: "#444444",
76438
+ fg: COLORS.textFaint,
76390
76439
  children: " Type a message or /command..."
76391
76440
  }, undefined, false, undefined, this)
76392
76441
  ]
@@ -76410,14 +76459,14 @@ function InputLine({ humanName, onSubmit, disabled }) {
76410
76459
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76411
76460
  children: [
76412
76461
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76413
- fg: "#00FF00",
76462
+ fg: COLORS.human,
76414
76463
  children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("strong", {
76415
76464
  children: label
76416
76465
  }, undefined, false, undefined, this)
76417
76466
  }, undefined, false, undefined, this),
76418
76467
  before,
76419
76468
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76420
- bg: "#FFFFFF",
76469
+ bg: COLORS.textPrimary,
76421
76470
  fg: "#000000",
76422
76471
  children: cursorChar
76423
76472
  }, undefined, false, undefined, this),
@@ -76428,9 +76477,1120 @@ function InputLine({ humanName, onSubmit, disabled }) {
76428
76477
  }, undefined, true, undefined, this);
76429
76478
  }
76430
76479
 
76480
+ // src/ui/ConfigWizard.tsx
76481
+ var import_react19 = __toESM(require_react(), 1);
76482
+ import { userInfo as userInfo3 } from "os";
76483
+
76484
+ // src/config/detector.ts
76485
+ import { spawn as spawn4 } from "child_process";
76486
+ var DETECT_TIMEOUT = 5000;
76487
+ function detectBinary(command) {
76488
+ return new Promise((resolve4) => {
76489
+ const timer = setTimeout(() => resolve4({ available: false }), DETECT_TIMEOUT);
76490
+ const proc = spawn4(command, ["--version"], {
76491
+ stdio: ["ignore", "pipe", "ignore"],
76492
+ shell: true,
76493
+ timeout: DETECT_TIMEOUT
76494
+ });
76495
+ let output = "";
76496
+ proc.stdout?.on("data", (chunk) => {
76497
+ output += chunk.toString();
76498
+ });
76499
+ proc.on("close", (code) => {
76500
+ clearTimeout(timer);
76501
+ resolve4({
76502
+ available: code === 0,
76503
+ version: code === 0 ? output.trim().split(`
76504
+ `)[0] : undefined
76505
+ });
76506
+ });
76507
+ proc.on("error", () => {
76508
+ clearTimeout(timer);
76509
+ resolve4({ available: false });
76510
+ });
76511
+ });
76512
+ }
76513
+ function detectAlias(command) {
76514
+ return new Promise((resolve4) => {
76515
+ const shell = process.env.SHELL || "/bin/bash";
76516
+ const timer = setTimeout(() => resolve4({ available: false }), DETECT_TIMEOUT);
76517
+ const proc = spawn4(shell, ["-ic", `type ${command}`], {
76518
+ stdio: ["ignore", "pipe", "ignore"],
76519
+ timeout: DETECT_TIMEOUT
76520
+ });
76521
+ proc.on("close", (code) => {
76522
+ clearTimeout(timer);
76523
+ resolve4({ available: code === 0 });
76524
+ });
76525
+ proc.on("error", () => {
76526
+ clearTimeout(timer);
76527
+ resolve4({ available: false });
76528
+ });
76529
+ });
76530
+ }
76531
+ async function detectProviders() {
76532
+ const results = await Promise.allSettled(PROVIDERS.map(async (provider) => {
76533
+ const result = provider.detectType === "alias" ? await detectAlias(provider.detectCommand) : await detectBinary(provider.detectCommand);
76534
+ return {
76535
+ id: provider.id,
76536
+ available: result.available,
76537
+ version: "version" in result ? result.version : undefined
76538
+ };
76539
+ }));
76540
+ return results.map((result, i2) => {
76541
+ if (result.status === "fulfilled")
76542
+ return result.value;
76543
+ return { id: PROVIDERS[i2].id, available: false };
76544
+ });
76545
+ }
76546
+
76547
+ // src/config/writer.ts
76548
+ import { writeFile as writeFile6, mkdir as mkdir6 } from "fs/promises";
76549
+ import { userInfo as userInfo2 } from "os";
76550
+ import path10 from "path";
76551
+ async function writeWizardConfig(selectedIds, overrides, existingConfig) {
76552
+ const overrideMap = new Map((overrides || []).map((o) => [o.id, o]));
76553
+ const existingByProvider = new Map;
76554
+ if (existingConfig?.agents) {
76555
+ for (const agent of existingConfig.agents) {
76556
+ existingByProvider.set(agent.provider, agent);
76557
+ }
76558
+ }
76559
+ const agents = selectedIds.map((id) => {
76560
+ const def = PROVIDERS.find((p2) => p2.id === id);
76561
+ if (!def)
76562
+ throw new Error(`Unknown provider: ${id}`);
76563
+ const override = overrideMap.get(id);
76564
+ const existing = existingByProvider.get(id);
76565
+ const agent = {
76566
+ name: override?.name || def.displayName,
76567
+ tag: override?.tag || def.defaultTag,
76568
+ provider: def.id,
76569
+ model: override?.model || def.defaultModel
76570
+ };
76571
+ if (existing?.env) {
76572
+ agent.env = { ...existing.env };
76573
+ } else if (def.env) {
76574
+ agent.env = { ...def.env };
76575
+ }
76576
+ if (existing?.prompts)
76577
+ agent.prompts = existing.prompts;
76578
+ if (existing?.executablePath)
76579
+ agent.executablePath = existing.executablePath;
76580
+ if (existing?.timeout)
76581
+ agent.timeout = existing.timeout;
76582
+ return agent;
76583
+ });
76584
+ const config = {
76585
+ humanName: existingConfig?.humanName || userInfo2().username || "USER",
76586
+ humanTag: existingConfig?.humanTag,
76587
+ maxAutoHops: existingConfig?.maxAutoHops ?? 15,
76588
+ timeout: existingConfig?.timeout,
76589
+ agents
76590
+ };
76591
+ if (!config.humanTag)
76592
+ delete config.humanTag;
76593
+ if (config.timeout === undefined)
76594
+ delete config.timeout;
76595
+ await mkdir6(LLM_PARTY_HOME, { recursive: true });
76596
+ const configPath = path10.join(LLM_PARTY_HOME, "config.json");
76597
+ await writeFile6(configPath, JSON.stringify(config, null, 2) + `
76598
+ `, "utf8");
76599
+ return configPath;
76600
+ }
76601
+
76602
+ // src/ui/MultiSelect.tsx
76603
+ var import_react17 = __toESM(require_react(), 1);
76604
+ function MultiSelect({ items, onConfirm, onCancel, initialSelected }) {
76605
+ const [focused, setFocused] = import_react17.useState(() => {
76606
+ return items.findIndex((item) => !item.disabled);
76607
+ });
76608
+ const [selected, setSelected] = import_react17.useState(() => {
76609
+ return new Set(initialSelected || []);
76610
+ });
76611
+ const [error, setError] = import_react17.useState("");
76612
+ const findNextEnabled = import_react17.useCallback((from, direction) => {
76613
+ let idx = from;
76614
+ for (let i2 = 0;i2 < items.length; i2++) {
76615
+ idx = (idx + direction + items.length) % items.length;
76616
+ if (!items[idx].disabled)
76617
+ return idx;
76618
+ }
76619
+ return from;
76620
+ }, [items]);
76621
+ useKeyboard((key) => {
76622
+ if (key.name === "up" || key.name === "k") {
76623
+ setFocused((f) => findNextEnabled(f, -1));
76624
+ setError("");
76625
+ return;
76626
+ }
76627
+ if (key.name === "down" || key.name === "j") {
76628
+ setFocused((f) => findNextEnabled(f, 1));
76629
+ setError("");
76630
+ return;
76631
+ }
76632
+ if (key.name === "space" || key.sequence === " ") {
76633
+ if (focused >= 0 && !items[focused].disabled) {
76634
+ setSelected((prev) => {
76635
+ const next = new Set(prev);
76636
+ if (next.has(focused)) {
76637
+ next.delete(focused);
76638
+ } else {
76639
+ next.add(focused);
76640
+ }
76641
+ return next;
76642
+ });
76643
+ setError("");
76644
+ }
76645
+ return;
76646
+ }
76647
+ if (key.name === "enter" || key.name === "return") {
76648
+ if (selected.size === 0) {
76649
+ setError("Select at least one agent");
76650
+ return;
76651
+ }
76652
+ onConfirm(Array.from(selected).sort((a2, b3) => a2 - b3));
76653
+ return;
76654
+ }
76655
+ if (key.name === "escape" && onCancel) {
76656
+ onCancel();
76657
+ return;
76658
+ }
76659
+ });
76660
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
76661
+ flexDirection: "column",
76662
+ children: [
76663
+ items.map((item, i2) => {
76664
+ const isSelected = selected.has(i2);
76665
+ const isFocused = i2 === focused;
76666
+ const isDisabled = !!item.disabled;
76667
+ const bullet = isSelected ? "\u25CF" : "\u25CB";
76668
+ const bulletColor = isDisabled ? COLORS.textFaint : isSelected ? COLORS.success : COLORS.textSecondary;
76669
+ const labelColor = isDisabled ? COLORS.textSubtle : COLORS.textPrimary;
76670
+ const descColor = isDisabled ? COLORS.textFaint : COLORS.textMuted;
76671
+ const bgColor = isFocused && !isDisabled ? COLORS.bgFocus : undefined;
76672
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76673
+ bg: bgColor,
76674
+ selectable: false,
76675
+ children: [
76676
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76677
+ fg: bulletColor,
76678
+ children: [
76679
+ " ",
76680
+ bullet,
76681
+ " "
76682
+ ]
76683
+ }, undefined, true, undefined, this),
76684
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76685
+ fg: labelColor,
76686
+ children: item.label
76687
+ }, undefined, false, undefined, this),
76688
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76689
+ fg: descColor,
76690
+ children: [
76691
+ " ",
76692
+ item.description
76693
+ ]
76694
+ }, undefined, true, undefined, this)
76695
+ ]
76696
+ }, i2, true, undefined, this);
76697
+ }),
76698
+ error && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76699
+ fg: COLORS.error,
76700
+ marginTop: 1,
76701
+ children: [
76702
+ " ",
76703
+ error
76704
+ ]
76705
+ }, undefined, true, undefined, this)
76706
+ ]
76707
+ }, undefined, true, undefined, this);
76708
+ }
76709
+
76710
+ // src/ui/ConfigWizard.tsx
76711
+ var TAG_PATTERN = /^[a-zA-Z0-9_-]+$/;
76712
+ function useSpinner() {
76713
+ const [frame, setFrame] = import_react19.useState(0);
76714
+ import_react19.useEffect(() => {
76715
+ const interval = setInterval(() => setFrame((f) => (f + 1) % SPINNER_FRAMES.length), 80);
76716
+ return () => clearInterval(interval);
76717
+ }, []);
76718
+ return SPINNER_FRAMES[frame];
76719
+ }
76720
+ var SWEEP_CHARS = ["\u2591", "\u2592", "\u2593", "\u2588", "\u2593", "\u2592", "\u2591"];
76721
+ var BAR_WIDTH = 6;
76722
+ function SweepBar({ title }) {
76723
+ const glow = SWEEP_CHARS.length;
76724
+ const totalWidth = BAR_WIDTH * 2 + title.length + 2;
76725
+ const [pos, setPos] = import_react19.useState(0);
76726
+ import_react19.useEffect(() => {
76727
+ const interval = setInterval(() => setPos((p2) => (p2 + 1) % (BAR_WIDTH + glow)), 50);
76728
+ return () => clearInterval(interval);
76729
+ }, []);
76730
+ function buildSide(reverse2) {
76731
+ const spans = [];
76732
+ for (let i2 = 0;i2 < BAR_WIDTH; i2++) {
76733
+ const idx = reverse2 ? BAR_WIDTH - 1 - i2 : i2;
76734
+ const dist = idx - pos;
76735
+ if (dist >= 0 && dist < glow) {
76736
+ const colorIdx = Math.floor((pos + idx) / 2) % PARTY_COLORS.length;
76737
+ const intensity = 1 - Math.abs(dist - 3) / 3;
76738
+ spans.push({
76739
+ char: SWEEP_CHARS[dist],
76740
+ color: intensity > 0.3 ? PARTY_COLORS[colorIdx] : COLORS.borderStrong
76741
+ });
76742
+ } else {
76743
+ spans.push({ char: "\u2591", color: COLORS.bgSweepDim });
76744
+ }
76745
+ }
76746
+ return spans;
76747
+ }
76748
+ const left = buildSide(false);
76749
+ const right = buildSide(true);
76750
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76751
+ children: [
76752
+ left.map((s2, i2) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76753
+ fg: s2.color,
76754
+ children: s2.char
76755
+ }, "l" + i2, false, undefined, this)),
76756
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76757
+ fg: COLORS.textPrimary,
76758
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("strong", {
76759
+ children: [
76760
+ " ",
76761
+ title,
76762
+ " "
76763
+ ]
76764
+ }, undefined, true, undefined, this)
76765
+ }, undefined, false, undefined, this),
76766
+ right.map((s2, i2) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
76767
+ fg: s2.color,
76768
+ children: s2.char
76769
+ }, "r" + i2, false, undefined, this))
76770
+ ]
76771
+ }, undefined, true, undefined, this);
76772
+ }
76773
+ function useDiscoColor() {
76774
+ const [idx, setIdx] = import_react19.useState(0);
76775
+ import_react19.useEffect(() => {
76776
+ const interval = setInterval(() => setIdx((i2) => (i2 + 1) % PARTY_COLORS.length), 800);
76777
+ return () => clearInterval(interval);
76778
+ }, []);
76779
+ return PARTY_COLORS[idx];
76780
+ }
76781
+ function ConfigWizard({ isFirstRun, onComplete, onCancel, existingConfig }) {
76782
+ const [step, setStep] = import_react19.useState("detect");
76783
+ const [detection, setDetection] = import_react19.useState([]);
76784
+ const [selectedIds, setSelectedIds] = import_react19.useState([]);
76785
+ const [, setAgentConfigs] = import_react19.useState([]);
76786
+ const [activeTab, setActiveTab] = import_react19.useState(0);
76787
+ const [focusedField, setFocusedField] = import_react19.useState(0);
76788
+ const [error, setError] = import_react19.useState("");
76789
+ const [, forceRender] = import_react19.useState(0);
76790
+ const inputRefs = import_react19.useRef([]);
76791
+ const humanRef = import_react19.useRef({
76792
+ name: existingConfig?.humanName || userInfo3().username || "USER",
76793
+ tag: existingConfig?.humanTag || toTag(existingConfig?.humanName || userInfo3().username || "USER")
76794
+ });
76795
+ const cursorRef = import_react19.useRef(0);
76796
+ const spinner = useSpinner();
76797
+ import_react19.useEffect(() => {
76798
+ detectProviders().then((results) => {
76799
+ setDetection(results);
76800
+ setStep("providers");
76801
+ });
76802
+ }, []);
76803
+ const existingByProvider = new Map((existingConfig?.agents || []).map((a2) => [a2.provider, a2]));
76804
+ const initialSelected = existingConfig ? PROVIDERS.map((p2, i2) => existingByProvider.has(p2.id) ? i2 : -1).filter((i2) => i2 >= 0) : undefined;
76805
+ const multiSelectItems = PROVIDERS.map((provider) => {
76806
+ const result = detection.find((d3) => d3.id === provider.id);
76807
+ const available = result?.available ?? false;
76808
+ return {
76809
+ label: provider.displayName,
76810
+ description: available ? provider.description : provider.unavailableHint,
76811
+ disabled: !available
76812
+ };
76813
+ });
76814
+ const handleProviderConfirm = import_react19.useCallback((selectedIndices) => {
76815
+ const ids = selectedIndices.map((i2) => PROVIDERS[i2].id);
76816
+ setSelectedIds(ids);
76817
+ const configs = ids.map((id) => {
76818
+ const def = PROVIDERS.find((p2) => p2.id === id);
76819
+ const existing = existingByProvider.get(id);
76820
+ return {
76821
+ id: def.id,
76822
+ name: existing?.name || def.displayName,
76823
+ tag: existing?.tag || def.defaultTag,
76824
+ model: existing?.model || def.defaultModel
76825
+ };
76826
+ });
76827
+ setAgentConfigs(configs);
76828
+ inputRefs.current = configs.map((c2) => ({ ...c2 }));
76829
+ setActiveTab(0);
76830
+ setFocusedField(0);
76831
+ cursorRef.current = configs[0]?.name.length || 0;
76832
+ setStep("configure");
76833
+ }, [existingByProvider]);
76834
+ const handleProviderCancel = import_react19.useCallback(() => {
76835
+ if (onCancel)
76836
+ onCancel();
76837
+ }, [onCancel]);
76838
+ const isYouTab = activeTab === 0;
76839
+ const agentTabIndex = activeTab - 1;
76840
+ const totalTabs = (inputRefs.current?.length || 0) + 1;
76841
+ const maxFieldIndex = isYouTab ? 1 : 2;
76842
+ const saveConfig = import_react19.useCallback(async () => {
76843
+ const configs = inputRefs.current;
76844
+ const human = humanRef.current;
76845
+ if (!human.name.trim()) {
76846
+ setError("Your name cannot be empty");
76847
+ return;
76848
+ }
76849
+ if (!human.tag.trim()) {
76850
+ setError("Your tag cannot be empty");
76851
+ return;
76852
+ }
76853
+ if (!TAG_PATTERN.test(human.tag.trim())) {
76854
+ setError("Your tag can only contain letters, numbers, hyphens, underscores");
76855
+ return;
76856
+ }
76857
+ for (const c2 of configs) {
76858
+ if (!c2.name.trim()) {
76859
+ setError(`Name cannot be empty for ${c2.id}`);
76860
+ return;
76861
+ }
76862
+ if (!c2.tag.trim()) {
76863
+ setError(`Tag cannot be empty for ${c2.id}`);
76864
+ return;
76865
+ }
76866
+ if (!TAG_PATTERN.test(c2.tag.trim())) {
76867
+ setError(`Tag for ${c2.name} can only contain letters, numbers, hyphens, underscores`);
76868
+ return;
76869
+ }
76870
+ if (!c2.model.trim()) {
76871
+ setError(`Model cannot be empty for ${c2.id}`);
76872
+ return;
76873
+ }
76874
+ }
76875
+ const names = new Set;
76876
+ for (const c2 of configs) {
76877
+ const lower = c2.name.trim().toLowerCase();
76878
+ if (names.has(lower)) {
76879
+ setError(`Duplicate agent name: ${c2.name}`);
76880
+ return;
76881
+ }
76882
+ names.add(lower);
76883
+ }
76884
+ const overrides = configs.map((c2) => ({
76885
+ id: c2.id,
76886
+ name: c2.name.trim(),
76887
+ tag: c2.tag.trim(),
76888
+ model: c2.model.trim()
76889
+ }));
76890
+ const mergedExisting = {
76891
+ ...existingConfig || {},
76892
+ humanName: human.name.trim(),
76893
+ humanTag: human.tag.trim(),
76894
+ agents: existingConfig?.agents || []
76895
+ };
76896
+ try {
76897
+ await writeWizardConfig(selectedIds, overrides, mergedExisting);
76898
+ setStep("done");
76899
+ } catch (err) {
76900
+ setError(`Failed to save: ${err.message}`);
76901
+ }
76902
+ }, [selectedIds, existingConfig]);
76903
+ useKeyboard((key) => {
76904
+ if (step !== "configure") {
76905
+ if (step === "done") {
76906
+ if (key.name === "enter" || key.name === "return" || key.name === "space") {
76907
+ onComplete();
76908
+ }
76909
+ return;
76910
+ }
76911
+ return;
76912
+ }
76913
+ const configs = inputRefs.current;
76914
+ const human = humanRef.current;
76915
+ let fieldValue;
76916
+ if (isYouTab) {
76917
+ fieldValue = focusedField === 0 ? human.name : human.tag;
76918
+ } else {
76919
+ const current = configs[agentTabIndex];
76920
+ if (!current)
76921
+ return;
76922
+ fieldValue = [current.name, current.tag, current.model][focusedField];
76923
+ }
76924
+ const cursor = cursorRef.current;
76925
+ const updateField = (value, newCursor) => {
76926
+ if (isYouTab) {
76927
+ if (focusedField === 0)
76928
+ human.name = value;
76929
+ else
76930
+ human.tag = value;
76931
+ } else {
76932
+ const current = configs[agentTabIndex];
76933
+ if (focusedField === 0)
76934
+ current.name = value;
76935
+ else if (focusedField === 1)
76936
+ current.tag = value;
76937
+ else
76938
+ current.model = value;
76939
+ }
76940
+ cursorRef.current = newCursor;
76941
+ setError("");
76942
+ forceRender((n2) => n2 + 1);
76943
+ };
76944
+ if (key.sequence === "[" || key.sequence === "]") {
76945
+ const dir = key.sequence === "[" ? -1 : 1;
76946
+ const next = (activeTab + dir + totalTabs) % totalTabs;
76947
+ setActiveTab(next);
76948
+ const newMax = next === 0 ? 1 : 2;
76949
+ const newField = Math.min(focusedField, newMax);
76950
+ setFocusedField(newField);
76951
+ let newVal;
76952
+ if (next === 0) {
76953
+ newVal = newField === 0 ? human.name : human.tag;
76954
+ } else {
76955
+ newVal = getFieldValue(configs[next - 1], newField);
76956
+ }
76957
+ cursorRef.current = newVal.length;
76958
+ forceRender((n2) => n2 + 1);
76959
+ return;
76960
+ }
76961
+ if (key.name === "tab") {
76962
+ const fieldCount = maxFieldIndex + 1;
76963
+ const nextField = key.shift ? (focusedField - 1 + fieldCount) % fieldCount : (focusedField + 1) % fieldCount;
76964
+ setFocusedField(nextField);
76965
+ let newVal;
76966
+ if (isYouTab) {
76967
+ newVal = nextField === 0 ? human.name : human.tag;
76968
+ } else {
76969
+ newVal = getFieldValue(configs[agentTabIndex], nextField);
76970
+ }
76971
+ cursorRef.current = newVal.length;
76972
+ forceRender((n2) => n2 + 1);
76973
+ return;
76974
+ }
76975
+ if (key.name === "enter" || key.name === "return") {
76976
+ saveConfig();
76977
+ return;
76978
+ }
76979
+ if (key.name === "escape") {
76980
+ setStep("providers");
76981
+ setFocusedField(0);
76982
+ setActiveTab(0);
76983
+ return;
76984
+ }
76985
+ if (key.name === "backspace") {
76986
+ if (cursor > 0) {
76987
+ updateField(fieldValue.slice(0, cursor - 1) + fieldValue.slice(cursor), cursor - 1);
76988
+ }
76989
+ return;
76990
+ }
76991
+ if (key.name === "delete") {
76992
+ if (cursor < fieldValue.length) {
76993
+ updateField(fieldValue.slice(0, cursor) + fieldValue.slice(cursor + 1), cursor);
76994
+ }
76995
+ return;
76996
+ }
76997
+ if (key.name === "left") {
76998
+ cursorRef.current = Math.max(0, cursor - 1);
76999
+ forceRender((n2) => n2 + 1);
77000
+ return;
77001
+ }
77002
+ if (key.name === "right") {
77003
+ cursorRef.current = Math.min(fieldValue.length, cursor + 1);
77004
+ forceRender((n2) => n2 + 1);
77005
+ return;
77006
+ }
77007
+ if (key.name === "home" || key.ctrl && key.name === "a") {
77008
+ cursorRef.current = 0;
77009
+ forceRender((n2) => n2 + 1);
77010
+ return;
77011
+ }
77012
+ if (key.name === "end" || key.ctrl && key.name === "e") {
77013
+ cursorRef.current = fieldValue.length;
77014
+ forceRender((n2) => n2 + 1);
77015
+ return;
77016
+ }
77017
+ if (key.ctrl && key.name === "u") {
77018
+ updateField("", 0);
77019
+ return;
77020
+ }
77021
+ if (key.ctrl || key.name === "up" || key.name === "down" || key.name === "pageup" || key.name === "pagedown") {
77022
+ return;
77023
+ }
77024
+ if (key.name === "space" || key.sequence === " ") {
77025
+ if (focusedField !== 1) {
77026
+ updateField(fieldValue.slice(0, cursor) + " " + fieldValue.slice(cursor), cursor + 1);
77027
+ }
77028
+ return;
77029
+ }
77030
+ const ch = key.sequence;
77031
+ if (ch && ch.length > 0 && !ch.startsWith("\x1B")) {
77032
+ if (ch === "'" || ch === '"' || ch === "`")
77033
+ return;
77034
+ if (focusedField === 1 && !TAG_PATTERN.test(ch))
77035
+ return;
77036
+ updateField(fieldValue.slice(0, cursor) + ch + fieldValue.slice(cursor), cursor + ch.length);
77037
+ }
77038
+ });
77039
+ const title = isFirstRun ? "Welcome to llm-party" : "Configure Agents";
77040
+ const subtitle = isFirstRun ? "Bring your models. We'll bring the party." : "Changes will take effect on next session";
77041
+ const subtitleColor = isFirstRun ? COLORS.textDim : COLORS.warning;
77042
+ function Subtitle() {
77043
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77044
+ alignSelf: "center",
77045
+ fg: subtitleColor,
77046
+ children: subtitle
77047
+ }, undefined, false, undefined, this);
77048
+ }
77049
+ const discoColor = useDiscoColor();
77050
+ if (step === "detect") {
77051
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77052
+ flexDirection: "column",
77053
+ width: "100%",
77054
+ height: "100%",
77055
+ justifyContent: "center",
77056
+ alignItems: "center",
77057
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77058
+ border: true,
77059
+ borderStyle: "double",
77060
+ borderColor: discoColor,
77061
+ paddingX: 4,
77062
+ paddingY: 1,
77063
+ backgroundColor: COLORS.bgPanel,
77064
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77065
+ flexDirection: "column",
77066
+ alignItems: "center",
77067
+ children: [
77068
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SweepBar, {
77069
+ title: "llm-party"
77070
+ }, undefined, false, undefined, this),
77071
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77072
+ fg: COLORS.success,
77073
+ marginTop: 1,
77074
+ children: [
77075
+ spinner,
77076
+ " Scanning for installed CLIs..."
77077
+ ]
77078
+ }, undefined, true, undefined, this)
77079
+ ]
77080
+ }, undefined, true, undefined, this)
77081
+ }, undefined, false, undefined, this)
77082
+ }, undefined, false, undefined, this);
77083
+ }
77084
+ if (step === "providers") {
77085
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77086
+ flexDirection: "column",
77087
+ width: "100%",
77088
+ height: "100%",
77089
+ justifyContent: "center",
77090
+ alignItems: "center",
77091
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77092
+ border: true,
77093
+ borderStyle: "double",
77094
+ borderColor: discoColor,
77095
+ paddingX: 3,
77096
+ paddingY: 1,
77097
+ backgroundColor: COLORS.bgPanel,
77098
+ minWidth: 50,
77099
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77100
+ flexDirection: "column",
77101
+ children: [
77102
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77103
+ alignSelf: "center",
77104
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SweepBar, {
77105
+ title
77106
+ }, undefined, false, undefined, this)
77107
+ }, undefined, false, undefined, this),
77108
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Subtitle, {}, undefined, false, undefined, this),
77109
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77110
+ alignSelf: "center",
77111
+ fg: COLORS.textSubtle,
77112
+ marginTop: 1,
77113
+ children: "\u2550".repeat(44)
77114
+ }, undefined, false, undefined, this),
77115
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77116
+ marginTop: 1,
77117
+ children: [
77118
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77119
+ fg: COLORS.textSecondary,
77120
+ children: "Select your agents "
77121
+ }, undefined, false, undefined, this),
77122
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77123
+ fg: COLORS.success,
77124
+ children: "Space"
77125
+ }, undefined, false, undefined, this),
77126
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77127
+ fg: COLORS.textFaint,
77128
+ children: " toggle "
77129
+ }, undefined, false, undefined, this),
77130
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77131
+ fg: COLORS.success,
77132
+ children: "Enter"
77133
+ }, undefined, false, undefined, this),
77134
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77135
+ fg: COLORS.textFaint,
77136
+ children: " confirm"
77137
+ }, undefined, false, undefined, this),
77138
+ !isFirstRun && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
77139
+ children: [
77140
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77141
+ fg: COLORS.textFaint,
77142
+ children: " "
77143
+ }, undefined, false, undefined, this),
77144
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77145
+ fg: COLORS.error,
77146
+ children: "Esc"
77147
+ }, undefined, false, undefined, this),
77148
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77149
+ fg: COLORS.textFaint,
77150
+ children: " cancel"
77151
+ }, undefined, false, undefined, this)
77152
+ ]
77153
+ }, undefined, true, undefined, this)
77154
+ ]
77155
+ }, undefined, true, undefined, this),
77156
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77157
+ marginTop: 1,
77158
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(MultiSelect, {
77159
+ items: multiSelectItems,
77160
+ onConfirm: handleProviderConfirm,
77161
+ onCancel: isFirstRun ? undefined : handleProviderCancel,
77162
+ initialSelected
77163
+ }, undefined, false, undefined, this)
77164
+ }, undefined, false, undefined, this)
77165
+ ]
77166
+ }, undefined, true, undefined, this)
77167
+ }, undefined, false, undefined, this)
77168
+ }, undefined, false, undefined, this);
77169
+ }
77170
+ if (step === "configure") {
77171
+ const configs = inputRefs.current;
77172
+ const human = humanRef.current;
77173
+ const tabLabels = ["You", ...selectedIds.map((id) => {
77174
+ const def = PROVIDERS.find((p2) => p2.id === id);
77175
+ return def?.displayName || id;
77176
+ })];
77177
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77178
+ flexDirection: "column",
77179
+ width: "100%",
77180
+ height: "100%",
77181
+ justifyContent: "center",
77182
+ alignItems: "center",
77183
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77184
+ border: true,
77185
+ borderStyle: "double",
77186
+ borderColor: discoColor,
77187
+ paddingX: 3,
77188
+ paddingY: 1,
77189
+ backgroundColor: COLORS.bgPanel,
77190
+ minWidth: 54,
77191
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77192
+ flexDirection: "column",
77193
+ children: [
77194
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77195
+ alignSelf: "center",
77196
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SweepBar, {
77197
+ title
77198
+ }, undefined, false, undefined, this)
77199
+ }, undefined, false, undefined, this),
77200
+ !isFirstRun && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(Subtitle, {}, undefined, false, undefined, this),
77201
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77202
+ flexDirection: "row",
77203
+ marginTop: 1,
77204
+ alignSelf: "center",
77205
+ children: tabLabels.map((label, i2) => {
77206
+ const isActive = i2 === activeTab;
77207
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77208
+ fg: isActive ? COLORS.success : COLORS.textSubtle,
77209
+ bg: isActive ? COLORS.bgActiveTab : undefined,
77210
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("strong", {
77211
+ children: [
77212
+ " ",
77213
+ label,
77214
+ " "
77215
+ ]
77216
+ }, undefined, true, undefined, this)
77217
+ }, label + i2, false, undefined, this);
77218
+ })
77219
+ }, undefined, false, undefined, this),
77220
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77221
+ alignSelf: "center",
77222
+ fg: COLORS.borderStrong,
77223
+ children: "\u2501".repeat(48)
77224
+ }, undefined, false, undefined, this),
77225
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77226
+ border: true,
77227
+ borderStyle: "rounded",
77228
+ borderColor: isYouTab ? COLORS.agent : COLORS.success,
77229
+ paddingX: 2,
77230
+ paddingY: 1,
77231
+ marginTop: 1,
77232
+ backgroundColor: COLORS.bgContent,
77233
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77234
+ flexDirection: "column",
77235
+ children: isYouTab ? /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
77236
+ children: [
77237
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77238
+ fg: COLORS.agent,
77239
+ marginBottom: 1,
77240
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("strong", {
77241
+ children: "Your Identity"
77242
+ }, undefined, false, undefined, this)
77243
+ }, undefined, false, undefined, this),
77244
+ renderField("Name", human.name, focusedField === 0),
77245
+ renderField("Tag ", human.tag, focusedField === 1)
77246
+ ]
77247
+ }, undefined, true, undefined, this) : /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(import_jsx_dev_runtime2.Fragment, {
77248
+ children: [
77249
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77250
+ fg: COLORS.success,
77251
+ marginBottom: 1,
77252
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("strong", {
77253
+ children: [
77254
+ tabLabels[activeTab],
77255
+ " Configuration"
77256
+ ]
77257
+ }, undefined, true, undefined, this)
77258
+ }, undefined, false, undefined, this),
77259
+ renderField("Name ", configs[agentTabIndex].name, focusedField === 0),
77260
+ renderField("Tag ", configs[agentTabIndex].tag, focusedField === 1),
77261
+ renderField("Model", configs[agentTabIndex].model, focusedField === 2)
77262
+ ]
77263
+ }, undefined, true, undefined, this)
77264
+ }, undefined, false, undefined, this)
77265
+ }, undefined, false, undefined, this),
77266
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77267
+ flexDirection: "row",
77268
+ marginTop: 1,
77269
+ justifyContent: "space-between",
77270
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77271
+ children: [
77272
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77273
+ fg: COLORS.textFaint,
77274
+ children: "\u25C2 "
77275
+ }, undefined, false, undefined, this),
77276
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77277
+ fg: COLORS.success,
77278
+ children: "["
77279
+ }, undefined, false, undefined, this),
77280
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77281
+ fg: COLORS.textFaint,
77282
+ children: " prev "
77283
+ }, undefined, false, undefined, this),
77284
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77285
+ fg: COLORS.success,
77286
+ children: "]"
77287
+ }, undefined, false, undefined, this),
77288
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77289
+ fg: COLORS.textFaint,
77290
+ children: " next "
77291
+ }, undefined, false, undefined, this),
77292
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77293
+ fg: COLORS.textFaint,
77294
+ children: "\u25B8 "
77295
+ }, undefined, false, undefined, this),
77296
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77297
+ fg: COLORS.success,
77298
+ children: "Tab"
77299
+ }, undefined, false, undefined, this),
77300
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77301
+ fg: COLORS.textFaint,
77302
+ children: " fields "
77303
+ }, undefined, false, undefined, this),
77304
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77305
+ fg: COLORS.success,
77306
+ children: "Enter"
77307
+ }, undefined, false, undefined, this),
77308
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77309
+ fg: COLORS.textFaint,
77310
+ children: " save & close"
77311
+ }, undefined, false, undefined, this),
77312
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77313
+ fg: COLORS.textFaint,
77314
+ children: " "
77315
+ }, undefined, false, undefined, this),
77316
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77317
+ fg: COLORS.warning,
77318
+ children: "Esc"
77319
+ }, undefined, false, undefined, this),
77320
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77321
+ fg: COLORS.textFaint,
77322
+ children: " back"
77323
+ }, undefined, false, undefined, this)
77324
+ ]
77325
+ }, undefined, true, undefined, this)
77326
+ }, undefined, false, undefined, this),
77327
+ error && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77328
+ border: true,
77329
+ borderStyle: "rounded",
77330
+ borderColor: COLORS.error,
77331
+ paddingX: 1,
77332
+ marginTop: 1,
77333
+ backgroundColor: COLORS.bgError,
77334
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77335
+ fg: COLORS.error,
77336
+ children: error
77337
+ }, undefined, false, undefined, this)
77338
+ }, undefined, false, undefined, this)
77339
+ ]
77340
+ }, undefined, true, undefined, this)
77341
+ }, undefined, false, undefined, this)
77342
+ }, undefined, false, undefined, this);
77343
+ }
77344
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77345
+ flexDirection: "column",
77346
+ width: "100%",
77347
+ height: "100%",
77348
+ justifyContent: "center",
77349
+ alignItems: "center",
77350
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77351
+ border: true,
77352
+ borderStyle: "double",
77353
+ borderColor: discoColor,
77354
+ paddingX: 4,
77355
+ paddingY: 2,
77356
+ backgroundColor: COLORS.bgPanel,
77357
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77358
+ flexDirection: "column",
77359
+ alignItems: "center",
77360
+ children: [
77361
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(SweepBar, {
77362
+ title: "Config Saved"
77363
+ }, undefined, false, undefined, this),
77364
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77365
+ fg: COLORS.textSubtle,
77366
+ marginTop: 1,
77367
+ children: "\u2500".repeat(36)
77368
+ }, undefined, false, undefined, this),
77369
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77370
+ fg: COLORS.textMuted,
77371
+ marginTop: 1,
77372
+ children: "Written to ~/.llm-party/config.json"
77373
+ }, undefined, false, undefined, this),
77374
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77375
+ fg: COLORS.textSubtle,
77376
+ marginTop: 1,
77377
+ children: "Edit this file anytime to add prompts,"
77378
+ }, undefined, false, undefined, this),
77379
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77380
+ fg: COLORS.textSubtle,
77381
+ children: "env vars, or tweak settings."
77382
+ }, undefined, false, undefined, this),
77383
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77384
+ fg: COLORS.textSubtle,
77385
+ marginTop: 1,
77386
+ children: "\u2500".repeat(36)
77387
+ }, undefined, false, undefined, this),
77388
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77389
+ fg: COLORS.primary,
77390
+ marginTop: 1,
77391
+ children: [
77392
+ "Press ",
77393
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77394
+ fg: COLORS.success,
77395
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("strong", {
77396
+ children: "Enter"
77397
+ }, undefined, false, undefined, this)
77398
+ }, undefined, false, undefined, this),
77399
+ " to continue"
77400
+ ]
77401
+ }, undefined, true, undefined, this)
77402
+ ]
77403
+ }, undefined, true, undefined, this)
77404
+ }, undefined, false, undefined, this)
77405
+ }, undefined, false, undefined, this);
77406
+ function renderField(label, value, isFocused) {
77407
+ const cursor = cursorRef.current;
77408
+ const labelColor = isFocused ? COLORS.primary : COLORS.textDim;
77409
+ const indicator = isFocused ? "\u25B8" : " ";
77410
+ const indicatorColor = COLORS.primary;
77411
+ const valueColor = isFocused ? COLORS.textPrimary : COLORS.textSecondary;
77412
+ if (!isFocused) {
77413
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77414
+ children: [
77415
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77416
+ fg: COLORS.borderStrong,
77417
+ children: [
77418
+ indicator,
77419
+ " "
77420
+ ]
77421
+ }, undefined, true, undefined, this),
77422
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77423
+ fg: labelColor,
77424
+ children: [
77425
+ label,
77426
+ ": "
77427
+ ]
77428
+ }, undefined, true, undefined, this),
77429
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77430
+ fg: valueColor,
77431
+ children: value
77432
+ }, undefined, false, undefined, this)
77433
+ ]
77434
+ }, undefined, true, undefined, this);
77435
+ }
77436
+ const before = value.slice(0, cursor);
77437
+ const cursorChar = cursor < value.length ? value[cursor] : " ";
77438
+ const after = cursor < value.length ? value.slice(cursor + 1) : "";
77439
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77440
+ children: [
77441
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77442
+ fg: indicatorColor,
77443
+ children: [
77444
+ indicator,
77445
+ " "
77446
+ ]
77447
+ }, undefined, true, undefined, this),
77448
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77449
+ fg: labelColor,
77450
+ children: [
77451
+ label,
77452
+ ": "
77453
+ ]
77454
+ }, undefined, true, undefined, this),
77455
+ before,
77456
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77457
+ bg: COLORS.textPrimary,
77458
+ fg: "#000000",
77459
+ children: cursorChar
77460
+ }, undefined, false, undefined, this),
77461
+ after
77462
+ ]
77463
+ }, undefined, true, undefined, this);
77464
+ }
77465
+ }
77466
+ function getFieldValue(config, field) {
77467
+ if (field === 0)
77468
+ return config.name;
77469
+ if (field === 1)
77470
+ return config.tag;
77471
+ return config.model;
77472
+ }
77473
+
77474
+ // src/ui/AgentsPanel.tsx
77475
+ function AgentsPanel({ agents, onClose, onConfig }) {
77476
+ useKeyboard((key) => {
77477
+ if (key.name === "escape") {
77478
+ onClose();
77479
+ return;
77480
+ }
77481
+ });
77482
+ const nameW = Math.max(4, ...agents.map((a2) => a2.name.length)) + 2;
77483
+ const tagW = Math.max(3, ...agents.map((a2) => a2.tag.length + 1)) + 2;
77484
+ const provW = Math.max(8, ...agents.map((a2) => a2.provider.length)) + 2;
77485
+ const modelW = Math.max(5, ...agents.map((a2) => a2.model.length)) + 2;
77486
+ const totalW = nameW + tagW + provW + modelW;
77487
+ const pad = (str, width) => str + " ".repeat(Math.max(0, width - str.length));
77488
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77489
+ position: "absolute",
77490
+ width: "100%",
77491
+ height: "100%",
77492
+ justifyContent: "center",
77493
+ alignItems: "center",
77494
+ zIndex: 10,
77495
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77496
+ border: true,
77497
+ borderStyle: "rounded",
77498
+ borderColor: COLORS.primary,
77499
+ paddingX: 3,
77500
+ paddingY: 1,
77501
+ backgroundColor: COLORS.bgPanel,
77502
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
77503
+ flexDirection: "column",
77504
+ children: [
77505
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77506
+ alignSelf: "center",
77507
+ fg: COLORS.primary,
77508
+ children: /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("strong", {
77509
+ children: "Active Agents"
77510
+ }, undefined, false, undefined, this)
77511
+ }, undefined, false, undefined, this),
77512
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77513
+ fg: COLORS.textSubtle,
77514
+ marginTop: 1,
77515
+ children: [
77516
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77517
+ fg: COLORS.textMuted,
77518
+ children: pad("Name", nameW)
77519
+ }, undefined, false, undefined, this),
77520
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77521
+ fg: COLORS.textMuted,
77522
+ children: pad("Tag", tagW)
77523
+ }, undefined, false, undefined, this),
77524
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77525
+ fg: COLORS.textMuted,
77526
+ children: pad("Provider", provW)
77527
+ }, undefined, false, undefined, this),
77528
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77529
+ fg: COLORS.textMuted,
77530
+ children: pad("Model", modelW)
77531
+ }, undefined, false, undefined, this)
77532
+ ]
77533
+ }, undefined, true, undefined, this),
77534
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77535
+ fg: COLORS.borderStrong,
77536
+ children: "\u2500".repeat(totalW)
77537
+ }, undefined, false, undefined, this),
77538
+ agents.map((a2) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77539
+ children: [
77540
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77541
+ fg: COLORS.textPrimary,
77542
+ children: pad(a2.name, nameW)
77543
+ }, undefined, false, undefined, this),
77544
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77545
+ fg: COLORS.success,
77546
+ children: pad("@" + a2.tag, tagW)
77547
+ }, undefined, false, undefined, this),
77548
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77549
+ fg: COLORS.textMuted,
77550
+ children: pad(a2.provider, provW)
77551
+ }, undefined, false, undefined, this),
77552
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77553
+ fg: COLORS.textDim,
77554
+ children: pad(a2.model, modelW)
77555
+ }, undefined, false, undefined, this)
77556
+ ]
77557
+ }, a2.name, true, undefined, this)),
77558
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77559
+ fg: COLORS.borderStrong,
77560
+ marginTop: 1,
77561
+ children: "\u2500".repeat(totalW)
77562
+ }, undefined, false, undefined, this),
77563
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
77564
+ marginTop: 1,
77565
+ alignSelf: "center",
77566
+ children: [
77567
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77568
+ fg: COLORS.error,
77569
+ children: "Esc"
77570
+ }, undefined, false, undefined, this),
77571
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77572
+ fg: COLORS.textFaint,
77573
+ children: " close "
77574
+ }, undefined, false, undefined, this),
77575
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77576
+ fg: COLORS.success,
77577
+ children: "/config"
77578
+ }, undefined, false, undefined, this),
77579
+ /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("span", {
77580
+ fg: COLORS.textFaint,
77581
+ children: " edit agents"
77582
+ }, undefined, false, undefined, this)
77583
+ ]
77584
+ }, undefined, true, undefined, this)
77585
+ ]
77586
+ }, undefined, true, undefined, this)
77587
+ }, undefined, false, undefined, this)
77588
+ }, undefined, false, undefined, this);
77589
+ }
77590
+
76431
77591
  // src/ui/App.tsx
76432
77592
  function copyToClipboard(text) {
76433
- const proc = spawn4("pbcopy", [], { stdio: ["pipe", "ignore", "ignore"] });
77593
+ const proc = spawn5("pbcopy", [], { stdio: ["pipe", "ignore", "ignore"] });
76434
77594
  proc.stdin?.write(text);
76435
77595
  proc.stdin?.end();
76436
77596
  }
@@ -76445,23 +77605,31 @@ function copySelection(renderer) {
76445
77605
  renderer.clearSelection?.();
76446
77606
  return true;
76447
77607
  }
76448
- function App({ orchestrator, maxAutoHops, renderer }) {
77608
+ function App({ orchestrator, maxAutoHops, renderer, config }) {
76449
77609
  const { messages, agentStates, stickyTarget, dispatching, dispatch, addSystemMessage, clearMessages } = useOrchestrator(orchestrator, maxAutoHops);
76450
77610
  const humanName = orchestrator.getHumanName();
76451
77611
  const agents = orchestrator.listAgents();
76452
- const scrollRef = import_react17.useRef(null);
76453
- import_react17.useEffect(() => {
77612
+ const scrollRef = import_react22.useRef(null);
77613
+ const [screen, setScreen] = import_react22.useState("chat");
77614
+ const [showAgents, setShowAgents] = import_react22.useState(false);
77615
+ import_react22.useEffect(() => {
76454
77616
  const sb = scrollRef.current;
76455
77617
  if (sb && !sb.isDestroyed) {
76456
77618
  sb.scrollTo(sb.scrollHeight);
76457
77619
  }
76458
77620
  }, [messages]);
76459
- const gracefulExit = import_react17.useCallback(() => {
77621
+ const gracefulExit = import_react22.useCallback(() => {
76460
77622
  renderer.destroy();
76461
77623
  const adapters = orchestrator.getAdapters();
76462
77624
  Promise.allSettled(adapters.map((a2) => a2.destroy()));
76463
77625
  }, [orchestrator, renderer]);
76464
77626
  useKeyboard((key) => {
77627
+ if (key.ctrl && key.name === "p") {
77628
+ setShowAgents((v2) => !v2);
77629
+ return;
77630
+ }
77631
+ if (showAgents)
77632
+ return;
76465
77633
  if (key.ctrl && key.name === "c") {
76466
77634
  if (!copySelection(renderer)) {
76467
77635
  gracefulExit();
@@ -76485,14 +77653,13 @@ function App({ orchestrator, maxAutoHops, renderer }) {
76485
77653
  return;
76486
77654
  }
76487
77655
  });
76488
- const handleSubmit = import_react17.useCallback(async (line) => {
77656
+ const handleSubmit = import_react22.useCallback(async (line) => {
76489
77657
  if (line === "/exit") {
76490
77658
  gracefulExit();
76491
77659
  return;
76492
77660
  }
76493
77661
  if (line === "/agents") {
76494
- addSystemMessage(orchestrator.listAgents().map((a2) => `${a2.name} tag=@${a2.tag} provider=${a2.provider} model=${a2.model}`).join(`
76495
- `));
77662
+ setShowAgents(true);
76496
77663
  return;
76497
77664
  }
76498
77665
  if (line === "/session") {
@@ -76514,18 +77681,12 @@ Transcript: ${orchestrator.getTranscriptPath()}`);
76514
77681
  clearMessages();
76515
77682
  return;
76516
77683
  }
77684
+ if (line === "/config") {
77685
+ setScreen("config");
77686
+ return;
77687
+ }
76517
77688
  if (line === "/changes") {
76518
- const { execFile: execFile2 } = await import("child_process");
76519
- const files = await new Promise((resolve4) => {
76520
- execFile2("git", ["status", "--porcelain"], { cwd: process.cwd() }, (err, stdout) => {
76521
- if (err) {
76522
- resolve4([]);
76523
- return;
76524
- }
76525
- resolve4(stdout.split(`
76526
- `).filter((l) => l.length >= 4).map((l) => l.slice(3)));
76527
- });
76528
- });
77689
+ const files = await getChangedFiles();
76529
77690
  addSystemMessage(files.length > 0 ? `Modified files:
76530
77691
  ${files.map((f) => ` ${f}`).join(`
76531
77692
  `)}` : "No modified files.");
@@ -76549,6 +77710,17 @@ ${lines.join(`
76549
77710
  await dispatch(line);
76550
77711
  }, [orchestrator, dispatch, addSystemMessage, clearMessages, gracefulExit]);
76551
77712
  const tagsLine = agents.map((a2) => `@${a2.tag}`).join(", ");
77713
+ if (screen === "config") {
77714
+ return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(ConfigWizard, {
77715
+ isFirstRun: false,
77716
+ existingConfig: config,
77717
+ onComplete: () => {
77718
+ addSystemMessage("Config saved. Restart llm-party to apply changes.");
77719
+ setScreen("chat");
77720
+ },
77721
+ onCancel: () => setScreen("chat")
77722
+ }, undefined, false, undefined, this);
77723
+ }
76552
77724
  return /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("box", {
76553
77725
  flexDirection: "column",
76554
77726
  width: "100%",
@@ -76564,12 +77736,12 @@ ${lines.join(`
76564
77736
  flexShrink: 1,
76565
77737
  children: [
76566
77738
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV("text", {
76567
- fg: "#00BFFF",
77739
+ fg: COLORS.primary,
76568
77740
  selectable: true,
76569
77741
  children: [
76570
77742
  "llm-party | ",
76571
77743
  tagsLine,
76572
- " | /agents /session /save /changes /clear /exit"
77744
+ " | /agents /config /session /save /changes /clear /exit"
76573
77745
  ]
76574
77746
  }, undefined, true, undefined, this),
76575
77747
  messages.map((msg) => /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(MessageBubble, {
@@ -76587,7 +77759,16 @@ ${lines.join(`
76587
77759
  /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(InputLine, {
76588
77760
  humanName,
76589
77761
  onSubmit: handleSubmit,
76590
- disabled: dispatching
77762
+ disabled: dispatching || showAgents,
77763
+ disabledMessage: showAgents ? "" : undefined
77764
+ }, undefined, false, undefined, this),
77765
+ showAgents && /* @__PURE__ */ import_jsx_dev_runtime2.jsxDEV(AgentsPanel, {
77766
+ agents,
77767
+ onClose: () => setShowAgents(false),
77768
+ onConfig: () => {
77769
+ setShowAgents(false);
77770
+ setScreen("config");
77771
+ }
76591
77772
  }, undefined, false, undefined, this)
76592
77773
  ]
76593
77774
  }, undefined, true, undefined, this);
@@ -76595,8 +77776,30 @@ ${lines.join(`
76595
77776
 
76596
77777
  // src/index.ts
76597
77778
  async function main2() {
76598
- const appRoot = path10.resolve(path10.dirname(fileURLToPath3(import.meta.url)), "..");
77779
+ const appRoot = path11.resolve(path11.dirname(fileURLToPath3(import.meta.url)), "..");
76599
77780
  await initLlmPartyHome(appRoot);
77781
+ const renderer = await createCliRenderer({
77782
+ exitOnCtrlC: false,
77783
+ useMouse: true,
77784
+ useKittyKeyboard: {}
77785
+ });
77786
+ process.on("SIGINT", () => {
77787
+ renderer.destroy();
77788
+ });
77789
+ const root = createRoot(renderer);
77790
+ const hasConfig = await configExists();
77791
+ if (!hasConfig) {
77792
+ root.render(import_react24.default.createElement(ConfigWizard, {
77793
+ isFirstRun: true,
77794
+ onComplete: async () => {
77795
+ await bootApp(appRoot, renderer, root);
77796
+ }
77797
+ }));
77798
+ } else {
77799
+ await bootApp(appRoot, renderer, root);
77800
+ }
77801
+ }
77802
+ async function bootApp(appRoot, renderer, root) {
76600
77803
  const configPath = await resolveConfigPath(appRoot);
76601
77804
  const config = await loadConfig2(configPath);
76602
77805
  const humanName = config.humanName?.trim() || "USER";
@@ -76610,7 +77813,7 @@ async function main2() {
76610
77813
 
76611
77814
  ` + artifactsPrompt;
76612
77815
  const resolveFromAppRoot = (value) => {
76613
- return path10.isAbsolute(value) ? value : path10.resolve(appRoot, value);
77816
+ return path11.isAbsolute(value) ? value : path11.resolve(appRoot, value);
76614
77817
  };
76615
77818
  const adapters = await Promise.all(config.agents.map(async (agent, _index, allAgents) => {
76616
77819
  const promptParts = [mergedBase];
@@ -76654,18 +77857,7 @@ async function main2() {
76654
77857
  const defaultTimeout = typeof config.timeout === "number" && config.timeout > 0 ? config.timeout * 1000 : 600000;
76655
77858
  const agentTimeouts = Object.fromEntries(config.agents.filter((agent) => typeof agent.timeout === "number" && agent.timeout > 0).map((agent) => [agent.name, agent.timeout * 1000]));
76656
77859
  const orchestrator = new Orchestrator(adapters, humanName, Object.fromEntries(config.agents.map((agent) => [agent.name, agent.tag?.trim() || toTag(agent.name)])), humanTag, defaultTimeout, agentTimeouts);
76657
- const renderer = await createCliRenderer({
76658
- exitOnCtrlC: false,
76659
- useMouse: true,
76660
- useKittyKeyboard: {},
76661
- onDestroy: () => {
76662
- Promise.allSettled(adapters.map((a2) => a2.destroy()));
76663
- }
76664
- });
76665
- process.on("SIGINT", () => {
76666
- renderer.destroy();
76667
- });
76668
- createRoot(renderer).render(import_react19.default.createElement(App, { orchestrator, maxAutoHops, renderer }));
77860
+ root.render(import_react24.default.createElement(App, { orchestrator, maxAutoHops, renderer, config }));
76669
77861
  }
76670
77862
  function resolveMaxAutoHops(value) {
76671
77863
  if (value === "unlimited") {