kimiflare 0.81.0 → 0.82.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
@@ -7198,6 +7198,41 @@ function isNewer(local, remote) {
7198
7198
  }
7199
7199
  return false;
7200
7200
  }
7201
+ async function readOptionalDepVersion(name) {
7202
+ try {
7203
+ const here = dirname6(fileURLToPath2(import.meta.url));
7204
+ const candidate = join12(here, "..", "..", "node_modules", name, "package.json");
7205
+ const raw = await readFile7(candidate, "utf8");
7206
+ const parsed = JSON.parse(raw);
7207
+ return parsed.version ?? null;
7208
+ } catch {
7209
+ return null;
7210
+ }
7211
+ }
7212
+ async function fetchDistTagVersion(name, tag2) {
7213
+ try {
7214
+ const res = await fetch(`https://registry.npmjs.org/${name}/${tag2}`, {
7215
+ headers: { "User-Agent": getUserAgent(), Accept: "application/json" }
7216
+ });
7217
+ if (!res.ok) return null;
7218
+ const data = await res.json();
7219
+ return data.version ?? null;
7220
+ } catch {
7221
+ return null;
7222
+ }
7223
+ }
7224
+ async function checkOptionalDependency(name, tag2) {
7225
+ const localVersion = await readOptionalDepVersion(name);
7226
+ if (!localVersion) {
7227
+ return { name, hasUpdate: false, localVersion: null, latestVersion: null };
7228
+ }
7229
+ const latestVersion = await fetchDistTagVersion(name, tag2);
7230
+ if (!latestVersion) {
7231
+ return { name, hasUpdate: false, localVersion, latestVersion: null };
7232
+ }
7233
+ const hasUpdate = isNewer(localVersion, latestVersion);
7234
+ return { name, hasUpdate, localVersion, latestVersion };
7235
+ }
7201
7236
  async function checkForUpdate(force = false) {
7202
7237
  const localVersion = await readLocalVersion();
7203
7238
  if (!localVersion) return { hasUpdate: false, localVersion: null, latestVersion: null };
@@ -13997,6 +14032,30 @@ Do not include speculation. Do not include chat-style pleasantries. Use short bu
13997
14032
  }
13998
14033
  });
13999
14034
 
14035
+ // src/agent/distill.ts
14036
+ function distillSessionPlan(messages) {
14037
+ for (let i = messages.length - 1; i >= 0; i--) {
14038
+ const m = messages[i];
14039
+ if (m?.role !== "assistant") continue;
14040
+ let text = "";
14041
+ if (typeof m.content === "string") {
14042
+ text = m.content;
14043
+ } else if (Array.isArray(m.content)) {
14044
+ text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
14045
+ }
14046
+ text = text.trim();
14047
+ if (text.length > 20) {
14048
+ return text;
14049
+ }
14050
+ }
14051
+ return null;
14052
+ }
14053
+ var init_distill = __esm({
14054
+ "src/agent/distill.ts"() {
14055
+ "use strict";
14056
+ }
14057
+ });
14058
+
14000
14059
  // src/ui/greetings.ts
14001
14060
  function pick(arr) {
14002
14061
  if (arr.length === 0) throw new Error("pick() called with empty array");
@@ -14861,6 +14920,40 @@ var init_theme = __esm({
14861
14920
  }
14862
14921
  });
14863
14922
 
14923
+ // src/util/clipboard.ts
14924
+ import { execSync as execSync3 } from "child_process";
14925
+ import { platform as platform3 } from "os";
14926
+ function writeToClipboard(text) {
14927
+ const os2 = platform3();
14928
+ try {
14929
+ if (os2 === "darwin") {
14930
+ execSync3("pbcopy", { input: text, timeout: 5e3 });
14931
+ return { success: true, message: "Copied to clipboard" };
14932
+ }
14933
+ if (os2 === "win32") {
14934
+ execSync3("clip", { input: text, timeout: 5e3 });
14935
+ return { success: true, message: "Copied to clipboard" };
14936
+ }
14937
+ try {
14938
+ execSync3("xclip -selection clipboard", { input: text, timeout: 5e3 });
14939
+ return { success: true, message: "Copied to clipboard" };
14940
+ } catch {
14941
+ execSync3("xsel --clipboard --input", { input: text, timeout: 5e3 });
14942
+ return { success: true, message: "Copied to clipboard" };
14943
+ }
14944
+ } catch {
14945
+ return {
14946
+ success: false,
14947
+ message: "Clipboard not available \u2014 plan will be shown below"
14948
+ };
14949
+ }
14950
+ }
14951
+ var init_clipboard = __esm({
14952
+ "src/util/clipboard.ts"() {
14953
+ "use strict";
14954
+ }
14955
+ });
14956
+
14864
14957
  // src/cloud/ai-gateway-api.ts
14865
14958
  function baseUrl(accountId) {
14866
14959
  return `https://api.cloudflare.com/client/v4/accounts/${encodeURIComponent(accountId)}/ai-gateway/gateways`;
@@ -16088,11 +16181,11 @@ __export(ui_mode_exports, {
16088
16181
  runCamouflageOnboarding: () => runCamouflageOnboarding,
16089
16182
  runUiMode: () => runUiMode
16090
16183
  });
16091
- import { execSync as execSync3, spawn as spawn5 } from "child_process";
16184
+ import { execSync as execSync4, spawn as spawn5 } from "child_process";
16092
16185
  import { appendFileSync, openSync } from "fs";
16093
- import { readdir as readdir7 } from "fs/promises";
16186
+ import { readdir as readdir7, unlink as unlink4 } from "fs/promises";
16094
16187
  import { join as join28, relative as relative6 } from "path";
16095
- import { platform as platform3 } from "os";
16188
+ import { platform as platform4 } from "os";
16096
16189
  function kimiLog(payload) {
16097
16190
  if (!KIMI_LOG_PATH) return;
16098
16191
  try {
@@ -16209,6 +16302,17 @@ ${err instanceof Error ? err.message : err}`);
16209
16302
  }
16210
16303
  } catch {
16211
16304
  }
16305
+ try {
16306
+ const dep = await checkOptionalDependency("camouflage-tui", "beta");
16307
+ if (dep.hasUpdate && dep.latestVersion) {
16308
+ cam.send("ShowToast", {
16309
+ text: `camouflage-tui update available: ${dep.localVersion} \u2192 ${dep.latestVersion} \xB7 run npm update camouflage-tui`,
16310
+ kind: "info",
16311
+ ttl_ms: 6e3
16312
+ });
16313
+ }
16314
+ } catch {
16315
+ }
16212
16316
  })();
16213
16317
  cam.send("ShowToast", {
16214
16318
  text: "EXPERIMENTAL \u2014 switch back any time with `kimiflare --ui ink`",
@@ -17625,20 +17729,41 @@ Executor opened PR: ${prUrl}` : plan });
17625
17729
  return true;
17626
17730
  }
17627
17731
  case "update": {
17628
- cam.send("ShowToast", { text: "checking for updates\u2026", kind: "info", ttl_ms: 1500 });
17629
- try {
17630
- const r = await checkForUpdate(true);
17631
- if (r.hasUpdate && r.latestVersion) {
17632
- cam.send("ShowToast", {
17633
- text: `update available: ${r.localVersion} \u2192 ${r.latestVersion}. Run: npm i -g kimiflare@latest`,
17634
- kind: "success",
17635
- ttl_ms: 5e3
17636
- });
17637
- } else {
17638
- cam.send("ShowToast", { text: `up to date (${r.localVersion ?? "unknown"})`, kind: "info", ttl_ms: 2500 });
17732
+ const updateArg = (rest[0] ?? "").toLowerCase();
17733
+ if (updateArg === "camouflage") {
17734
+ cam.send("ShowToast", { text: "checking camouflage-tui for updates\u2026", kind: "info", ttl_ms: 1500 });
17735
+ try {
17736
+ const dep = await checkOptionalDependency("camouflage-tui", "beta");
17737
+ if (dep.hasUpdate && dep.latestVersion) {
17738
+ cam.send("ShowToast", {
17739
+ text: `camouflage-tui update available: ${dep.localVersion} \u2192 ${dep.latestVersion}. Run: npm update camouflage-tui`,
17740
+ kind: "success",
17741
+ ttl_ms: 5e3
17742
+ });
17743
+ } else if (dep.localVersion) {
17744
+ cam.send("ShowToast", { text: `camouflage-tui up to date (${dep.localVersion})`, kind: "info", ttl_ms: 2500 });
17745
+ } else {
17746
+ cam.send("ShowToast", { text: "camouflage-tui is not installed", kind: "info", ttl_ms: 2500 });
17747
+ }
17748
+ } catch (err) {
17749
+ cam.send("ShowToast", { text: `camouflage-tui update check failed: ${err instanceof Error ? err.message : String(err)}`, kind: "error", ttl_ms: 3e3 });
17750
+ }
17751
+ } else {
17752
+ cam.send("ShowToast", { text: "checking for updates\u2026", kind: "info", ttl_ms: 1500 });
17753
+ try {
17754
+ const r = await checkForUpdate(true);
17755
+ if (r.hasUpdate && r.latestVersion) {
17756
+ cam.send("ShowToast", {
17757
+ text: `update available: ${r.localVersion} \u2192 ${r.latestVersion}. Run: npm i -g kimiflare@latest`,
17758
+ kind: "success",
17759
+ ttl_ms: 5e3
17760
+ });
17761
+ } else {
17762
+ cam.send("ShowToast", { text: `up to date (${r.localVersion ?? "unknown"})`, kind: "info", ttl_ms: 2500 });
17763
+ }
17764
+ } catch (err) {
17765
+ cam.send("ShowToast", { text: `update check failed: ${err instanceof Error ? err.message : String(err)}`, kind: "error", ttl_ms: 3e3 });
17639
17766
  }
17640
- } catch (err) {
17641
- cam.send("ShowToast", { text: `update check failed: ${err instanceof Error ? err.message : String(err)}`, kind: "error", ttl_ms: 3e3 });
17642
17767
  }
17643
17768
  return true;
17644
17769
  }
@@ -17980,8 +18105,43 @@ Executor opened PR: ${prUrl}` : plan });
17980
18105
  ttl_ms: 4e3
17981
18106
  });
17982
18107
  return true;
18108
+ case "fresh": {
18109
+ if (currentPhase !== "idle") {
18110
+ cam.send("ShowToast", { text: "can't /fresh while model is running \u2014 press Esc to interrupt first", kind: "warn", ttl_ms: 2500 });
18111
+ return true;
18112
+ }
18113
+ const plan = distillSessionPlan(messages);
18114
+ if (!plan) {
18115
+ cam.send("ShowToast", { text: "No plan found to start fresh with.", kind: "error", ttl_ms: 2500 });
18116
+ return true;
18117
+ }
18118
+ const clipResult = writeToClipboard(plan);
18119
+ const systemMessages = messages.filter((m) => m.role === "system");
18120
+ messages.length = 0;
18121
+ messages.push(...systemMessages);
18122
+ sessionCostUsd = 0;
18123
+ promptTokens = 0;
18124
+ cachedTokens = 0;
18125
+ completionTokens = 0;
18126
+ cam.send("TranscriptCleared", {});
18127
+ cam.send("StatusUpdate", {
18128
+ segments: { tokens: "in 0", cost: "$0.00", elapsed: "" }
18129
+ });
18130
+ messages.push({ role: "user", content: plan });
18131
+ cam.send("ShowToast", {
18132
+ text: clipResult.success ? "Plan copied to clipboard. Starting fresh session with plan only\u2026" : "Clipboard unavailable. Starting fresh session with plan only\u2026",
18133
+ kind: "info",
18134
+ ttl_ms: 3e3
18135
+ });
18136
+ if (!clipResult.success) {
18137
+ cam.send("UserMessageCreated", { text: "--- Plan ---\n" + plan });
18138
+ }
18139
+ return true;
18140
+ }
17983
18141
  case "logout": {
17984
- cam.send("ShowToast", { text: "Cloud has been removed; nothing to log out of.", kind: "info", ttl_ms: 2500 });
18142
+ unlink4(configPath()).catch(() => {
18143
+ });
18144
+ cam.send("ShowToast", { text: `credentials cleared from ${configPath()}`, kind: "success", ttl_ms: 2500 });
17985
18145
  return true;
17986
18146
  }
17987
18147
  case "remote": {
@@ -18090,7 +18250,7 @@ function formatShortDate(iso) {
18090
18250
  }
18091
18251
  }
18092
18252
  function openBrowser(url) {
18093
- const cmd = platform3() === "darwin" ? "open" : platform3() === "win32" ? "start" : "xdg-open";
18253
+ const cmd = platform4() === "darwin" ? "open" : platform4() === "win32" ? "start" : "xdg-open";
18094
18254
  const child = spawn5(cmd, [url], { detached: true, stdio: "ignore" });
18095
18255
  child.unref();
18096
18256
  }
@@ -18110,7 +18270,7 @@ function formatElapsed2(secs) {
18110
18270
  }
18111
18271
  function tryGitBranch2() {
18112
18272
  try {
18113
- const out = execSync3("git rev-parse --abbrev-ref HEAD 2>/dev/null", {
18273
+ const out = execSync4("git rev-parse --abbrev-ref HEAD 2>/dev/null", {
18114
18274
  encoding: "utf8",
18115
18275
  timeout: 200
18116
18276
  }).trim();
@@ -18220,9 +18380,11 @@ var init_ui_mode = __esm({
18220
18380
  init_builtins();
18221
18381
  init_sessions();
18222
18382
  init_llm_summarize();
18383
+ init_distill();
18223
18384
  init_greetings();
18224
18385
  init_theme();
18225
18386
  init_update_check();
18387
+ init_clipboard();
18226
18388
  init_pricing();
18227
18389
  init_config();
18228
18390
  init_ai_gateway_api();
@@ -19645,12 +19807,19 @@ var init_chat = __esm({
19645
19807
  for (const [sig, count] of toolCounts) {
19646
19808
  if (count >= 3) repeatedSigs.add(sig);
19647
19809
  }
19810
+ let lastAssistantIndex = -1;
19811
+ for (let i = events.length - 1; i >= 0; i--) {
19812
+ if (events[i].kind === "assistant") {
19813
+ lastAssistantIndex = i;
19814
+ break;
19815
+ }
19816
+ }
19648
19817
  return /* @__PURE__ */ jsx6(Box5, { flexDirection: "column", children: events.map((e, i) => {
19649
19818
  const prev = events[i - 1];
19650
19819
  const showSeparator = !!(prev && (e.kind === "user" && prev.kind !== "user" || e.kind === "assistant" && prev.kind !== "assistant" && prev.kind !== "tool"));
19651
19820
  return /* @__PURE__ */ jsxs5(Box5, { flexDirection: "column", children: [
19652
19821
  showSeparator && /* @__PURE__ */ jsx6(Box5, { marginY: 1, children: /* @__PURE__ */ jsx6(Text5, { color: theme.info.color, children: "\u2500".repeat(40) }) }),
19653
- /* @__PURE__ */ jsx6(EventView, { evt: e, showReasoning, verbose, repeatedSigs, intentTier })
19822
+ /* @__PURE__ */ jsx6(EventView, { evt: e, showReasoning, verbose, repeatedSigs, intentTier, isLastAssistant: i === lastAssistantIndex })
19654
19823
  ] }, e.key);
19655
19824
  }) });
19656
19825
  });
@@ -19659,7 +19828,8 @@ var init_chat = __esm({
19659
19828
  showReasoning,
19660
19829
  verbose,
19661
19830
  repeatedSigs,
19662
- intentTier
19831
+ intentTier,
19832
+ isLastAssistant
19663
19833
  }) {
19664
19834
  const theme = useTheme();
19665
19835
  if (evt.kind === "user") {
@@ -19704,7 +19874,7 @@ var init_chat = __esm({
19704
19874
  evt.reasoning.length > 400 ? evt.reasoning.slice(0, 400) + "\u2026" : evt.reasoning
19705
19875
  ] }) }) : null,
19706
19876
  evt.text ? /* @__PURE__ */ jsx6(MD, { text: evt.text }) : null,
19707
- evt.streaming && /* @__PURE__ */ jsx6(Text5, { color: theme.spinner, children: /* @__PURE__ */ jsx6(Spinner2, { type: "dots" }) })
19877
+ evt.streaming && isLastAssistant && /* @__PURE__ */ jsx6(Text5, { color: theme.spinner, children: /* @__PURE__ */ jsx6(Spinner2, { type: "dots" }) })
19708
19878
  ] })
19709
19879
  ] }) });
19710
19880
  }
@@ -20405,7 +20575,7 @@ var init_source = __esm({
20405
20575
 
20406
20576
  // src/ui/text-input.tsx
20407
20577
  import { useState as useState3, useEffect as useEffect3, useRef } from "react";
20408
- import { Text as Text7, useInput } from "ink";
20578
+ import { Text as Text7, useInput, usePaste } from "ink";
20409
20579
  import { jsx as jsx8 } from "react/jsx-runtime";
20410
20580
  function shouldTreatAsPaste(input) {
20411
20581
  if (input.length >= PASTE_CHAR_THRESHOLD) return true;
@@ -20422,6 +20592,9 @@ function makePastePreview(input, lines, id) {
20422
20592
  function countLines(s) {
20423
20593
  return s.split("\n").length;
20424
20594
  }
20595
+ function sanitizeInput(input) {
20596
+ return input.replace(/\r\n/g, "\n").replace(/\r/g, "\n").replace(/\x1B(?:[@-Z\\-_]|\[[0-?]*[ -/]*[@-~])/g, "").replace(/\t/g, " ");
20597
+ }
20425
20598
  function findWordBoundaryForward(text, pos) {
20426
20599
  while (pos < text.length && /\w/.test(text[pos])) pos++;
20427
20600
  while (pos < text.length && !/\w/.test(text[pos])) pos++;
@@ -20459,13 +20632,51 @@ function CustomTextInput({
20459
20632
  };
20460
20633
  const pastesRef = useRef(/* @__PURE__ */ new Map());
20461
20634
  const pasteCounterRef = useRef(0);
20635
+ const prevValueRef = useRef(value);
20636
+ const cursorOffsetRef = useRef(cursorOffset);
20637
+ cursorOffsetRef.current = cursorOffset;
20462
20638
  useEffect3(() => {
20463
20639
  if (!focus) return;
20464
- const next = cursorOffset > value.length ? value.length : cursorOffset;
20465
- if (next !== cursorOffset) {
20640
+ const prevValue = prevValueRef.current;
20641
+ prevValueRef.current = value;
20642
+ if (value === prevValue) return;
20643
+ const currentCursor = cursorOffsetRef.current;
20644
+ if (currentCursor === prevValue.length) {
20645
+ if (currentCursor !== value.length) {
20646
+ setCursorOffset(value.length);
20647
+ }
20648
+ return;
20649
+ }
20650
+ const next = currentCursor > value.length ? value.length : currentCursor;
20651
+ if (next !== currentCursor) {
20466
20652
  setCursorOffset(next);
20467
20653
  }
20468
- }, [value, focus, cursorOffset]);
20654
+ }, [value, focus]);
20655
+ const handleInsert = (rawInput) => {
20656
+ let toInsert = sanitizeInput(rawInput);
20657
+ if (enablePaste && shouldTreatAsPaste(toInsert)) {
20658
+ const lines = countLines(toInsert);
20659
+ const id = ++pasteCounterRef.current;
20660
+ const placeholder = makePastePreview(toInsert, lines, id);
20661
+ pastesRef.current.set(placeholder, toInsert);
20662
+ toInsert = placeholder;
20663
+ }
20664
+ const nextValue = value.slice(0, cursorOffset) + toInsert + value.slice(cursorOffset);
20665
+ const nextCursor = cursorOffset + toInsert.length;
20666
+ if (nextCursor !== cursorOffset) {
20667
+ setCursorOffset(nextCursor);
20668
+ }
20669
+ if (nextValue !== value) {
20670
+ onChange(nextValue);
20671
+ }
20672
+ };
20673
+ usePaste(
20674
+ (input) => {
20675
+ if (!focus || !enablePaste) return;
20676
+ handleInsert(input);
20677
+ },
20678
+ { isActive: focus && enablePaste }
20679
+ );
20469
20680
  useInput(
20470
20681
  (input, key) => {
20471
20682
  if (!focus) return;
@@ -20590,12 +20801,13 @@ function CustomTextInput({
20590
20801
  didDelete = true;
20591
20802
  nextValue = value.slice(0, cursorOffset);
20592
20803
  } else if (input.length > 0 && !key.ctrl && !key.meta) {
20593
- let toInsert = input;
20594
- if (enablePaste && shouldTreatAsPaste(input)) {
20595
- const lines = countLines(input);
20804
+ const sanitized = sanitizeInput(input);
20805
+ let toInsert = sanitized;
20806
+ if (enablePaste && shouldTreatAsPaste(toInsert)) {
20807
+ const lines = countLines(toInsert);
20596
20808
  const id = ++pasteCounterRef.current;
20597
- const placeholder = makePastePreview(input, lines, id);
20598
- pastesRef.current.set(placeholder, input);
20809
+ const placeholder = makePastePreview(toInsert, lines, id);
20810
+ pastesRef.current.set(placeholder, toInsert);
20599
20811
  toInsert = placeholder;
20600
20812
  }
20601
20813
  nextValue = value.slice(0, cursorOffset) + toInsert + value.slice(cursorOffset);
@@ -20617,10 +20829,9 @@ function CustomTextInput({
20617
20829
  );
20618
20830
  const displayValue = mask ? mask.repeat(value.length) : value;
20619
20831
  let renderedValue = "";
20620
- let i = 0;
20621
- for (const char of displayValue) {
20832
+ for (let i = 0; i < displayValue.length; i++) {
20833
+ const char = displayValue[i];
20622
20834
  renderedValue += i === cursorOffset ? source_default.inverse(char) : char;
20623
- i++;
20624
20835
  }
20625
20836
  if (displayValue.length === 0) {
20626
20837
  renderedValue = source_default.inverse(" ");
@@ -20651,7 +20862,7 @@ var init_text_input = __esm({
20651
20862
  // src/ui/permission.tsx
20652
20863
  import { useState as useState4, useCallback } from "react";
20653
20864
  import { Box as Box7, Text as Text8, useInput as useInput2 } from "ink";
20654
- import { platform as platform4 } from "os";
20865
+ import { platform as platform5 } from "os";
20655
20866
  import { jsx as jsx9, jsxs as jsxs7 } from "react/jsx-runtime";
20656
20867
  function formatSelection(label, shortcut) {
20657
20868
  return `${label} [${MOD_KEY}+${shortcut}]`;
@@ -20815,7 +21026,7 @@ var init_permission = __esm({
20815
21026
  { value: { decision: "deny", scope: "once" }, label: "Something else", key: 3 }
20816
21027
  ];
20817
21028
  DENY = { decision: "deny", scope: "once" };
20818
- MOD_KEY = platform4() === "darwin" ? "\u2325" : "Alt";
21029
+ MOD_KEY = platform5() === "darwin" ? "\u2325" : "Alt";
20819
21030
  }
20820
21031
  });
20821
21032
 
@@ -23743,8 +23954,8 @@ function useModalHost() {
23743
23954
  const [showShellPicker, setShowShellPicker] = useState15(false);
23744
23955
  const [showPlanCompletePicker, setShowPlanCompletePicker] = useState15(false);
23745
23956
  const flags = useMemo4(() => {
23746
- const hasFullscreenModal = commandWizard !== null || commandPicker !== null || commandToDelete !== null || showCommandList || showLspWizard || showThemePicker || showUiPicker || showModelPicker || showModePicker || keyEntryFor !== null || billingChooserFor !== null || unifiedProbeFor !== null || showRemoteDashboard || showInboxModal || showMultiAgentModal || showHooksDashboard || showHelpMenu || showMemoryPicker || showGatewayPicker || showSkillsPicker || showShellPicker || showPlanCompletePicker;
23747
- const hasOverlayModal = limitModal !== null || loopModal !== null;
23957
+ const hasFullscreenModal = commandWizard !== null || commandPicker !== null || commandToDelete !== null || showCommandList || showLspWizard || showThemePicker || showUiPicker || showModelPicker || showModePicker || keyEntryFor !== null || billingChooserFor !== null || unifiedProbeFor !== null || showRemoteDashboard || showInboxModal || showMultiAgentModal || showHooksDashboard || showHelpMenu || showMemoryPicker || showGatewayPicker || showSkillsPicker || showShellPicker;
23958
+ const hasOverlayModal = limitModal !== null || loopModal !== null || showPlanCompletePicker;
23748
23959
  return {
23749
23960
  hasFullscreenModal,
23750
23961
  hasOverlayModal,
@@ -25707,10 +25918,10 @@ var init_inbox_modal = __esm({
25707
25918
  });
25708
25919
 
25709
25920
  // src/ui/app-helpers.ts
25710
- import { execSync as execSync4, spawn as spawn7 } from "child_process";
25921
+ import { execSync as execSync5, spawn as spawn7 } from "child_process";
25711
25922
  import { existsSync as existsSync5, readFileSync as readFileSync4, statSync as statSync5 } from "fs";
25712
25923
  import { join as join30 } from "path";
25713
- import { platform as platform5 } from "os";
25924
+ import { platform as platform6 } from "os";
25714
25925
  function buildFilePickerIgnoreList(cwd) {
25715
25926
  const hardcoded = [
25716
25927
  // Dependencies
@@ -25829,7 +26040,7 @@ function gatewayUsageLookupFromConfig(cfg, meta) {
25829
26040
  };
25830
26041
  }
25831
26042
  function openBrowser2(url) {
25832
- const cmd = platform5() === "darwin" ? "open" : platform5() === "win32" ? "start" : "xdg-open";
26043
+ const cmd = platform6() === "darwin" ? "open" : platform6() === "win32" ? "start" : "xdg-open";
25833
26044
  const child = spawn7(cmd, [url], { detached: true, stdio: "ignore" });
25834
26045
  child.unref();
25835
26046
  }
@@ -25839,7 +26050,7 @@ function detectGitHubRepo(cachedRepo) {
25839
26050
  if (parts.length === 2) return { owner: parts[0], name: parts[1] };
25840
26051
  }
25841
26052
  try {
25842
- const remoteUrl = execSync4("git remote get-url origin", { cwd: process.cwd(), encoding: "utf8" }).trim();
26053
+ const remoteUrl = execSync5("git remote get-url origin", { cwd: process.cwd(), encoding: "utf8" }).trim();
25843
26054
  const httpsMatch = remoteUrl.match(/github\.com\/([^\/]+)\/([^\/]+?)(?:\.git)?$/);
25844
26055
  if (httpsMatch) return { owner: httpsMatch[1], name: httpsMatch[2] };
25845
26056
  const sshMatch = remoteUrl.match(/github\.com:([^\/]+)\/([^\/]+?)(?:\.git)?$/);
@@ -25850,7 +26061,7 @@ function detectGitHubRepo(cachedRepo) {
25850
26061
  }
25851
26062
  function detectGitBranch() {
25852
26063
  try {
25853
- return execSync4("git branch --show-current", { cwd: process.cwd(), encoding: "utf8" }).trim() || null;
26064
+ return execSync5("git branch --show-current", { cwd: process.cwd(), encoding: "utf8" }).trim() || null;
25854
26065
  } catch {
25855
26066
  return null;
25856
26067
  }
@@ -26290,7 +26501,7 @@ var init_multi_agent_modal = __esm({
26290
26501
  });
26291
26502
 
26292
26503
  // src/hooks/recommended.ts
26293
- import { platform as platform6 } from "os";
26504
+ import { platform as platform7 } from "os";
26294
26505
  function getRecommendedHook(id) {
26295
26506
  return RECOMMENDED_HOOKS.find((r) => r.id === id);
26296
26507
  }
@@ -26298,7 +26509,7 @@ var isMac, RECOMMENDED_HOOKS;
26298
26509
  var init_recommended = __esm({
26299
26510
  "src/hooks/recommended.ts"() {
26300
26511
  "use strict";
26301
- isMac = platform6() === "darwin";
26512
+ isMac = platform7() === "darwin";
26302
26513
  RECOMMENDED_HOOKS = [
26303
26514
  // ── Stop notifications ───────────────────────────────────────────────
26304
26515
  {
@@ -27193,6 +27404,7 @@ var init_help_menu = __esm({
27193
27404
  { command: "/cost", description: "show cost report" },
27194
27405
  { command: "/model", description: "show current model" },
27195
27406
  { command: "/update", description: "check for updates" },
27407
+ { command: "/update camouflage", description: "check for camouflage-tui updates" },
27196
27408
  { command: "/hello", description: "send a voice note to the creator" }
27197
27409
  ]
27198
27410
  },
@@ -27229,42 +27441,10 @@ var init_help_menu = __esm({
27229
27441
  }
27230
27442
  });
27231
27443
 
27232
- // src/ui/plan-complete-picker.tsx
27444
+ // src/ui/modal-host.tsx
27233
27445
  import { Box as Box38, Text as Text39 } from "ink";
27234
27446
  import SelectInput21 from "ink-select-input";
27235
27447
  import { jsx as jsx40, jsxs as jsxs38 } from "react/jsx-runtime";
27236
- function PlanCompletePicker({ onPick }) {
27237
- const theme = useTheme();
27238
- const items = [
27239
- { label: "\u25B8 Execute this plan and accept changes (auto mode)", value: "auto", key: "auto" },
27240
- { label: "\u25B8 Start building and ask for permission (edit mode)", value: "edit", key: "edit" },
27241
- { label: "\u25B8 Continue planning / ask a question", value: "continue", key: "continue" }
27242
- ];
27243
- return /* @__PURE__ */ jsxs38(Box38, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
27244
- /* @__PURE__ */ jsx40(Text39, { color: theme.accent, bold: true, children: "Plan complete \u2014 what next?" }),
27245
- /* @__PURE__ */ jsx40(Text39, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to select, Esc to cancel." }),
27246
- /* @__PURE__ */ jsx40(Box38, { marginTop: 1, children: /* @__PURE__ */ jsx40(
27247
- SelectInput21,
27248
- {
27249
- items,
27250
- onSelect: (item) => onPick(item.value),
27251
- onHighlight: () => {
27252
- }
27253
- }
27254
- ) })
27255
- ] });
27256
- }
27257
- var init_plan_complete_picker = __esm({
27258
- "src/ui/plan-complete-picker.tsx"() {
27259
- "use strict";
27260
- init_theme_context();
27261
- }
27262
- });
27263
-
27264
- // src/ui/modal-host.tsx
27265
- import { Box as Box39, Text as Text40 } from "ink";
27266
- import SelectInput22 from "ink-select-input";
27267
- import { jsx as jsx41, jsxs as jsxs39 } from "react/jsx-runtime";
27268
27448
  function ModalHost(props) {
27269
27449
  const {
27270
27450
  modals,
@@ -27297,7 +27477,7 @@ function ModalHost(props) {
27297
27477
  onInboxOpen
27298
27478
  } = props;
27299
27479
  if (modals.showRemoteDashboard) {
27300
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: selectedRemoteSession ? /* @__PURE__ */ jsx41(
27480
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: selectedRemoteSession ? /* @__PURE__ */ jsx40(
27301
27481
  RemoteSessionDetail,
27302
27482
  {
27303
27483
  session: selectedRemoteSession,
@@ -27306,7 +27486,7 @@ function ModalHost(props) {
27306
27486
  void onCancelRemoteSession(session);
27307
27487
  }
27308
27488
  }
27309
- ) : /* @__PURE__ */ jsx41(
27489
+ ) : /* @__PURE__ */ jsx40(
27310
27490
  RemoteDashboard,
27311
27491
  {
27312
27492
  onSelect: (session) => onSelectRemoteSession(session),
@@ -27315,7 +27495,7 @@ function ModalHost(props) {
27315
27495
  ) }) });
27316
27496
  }
27317
27497
  if (modals.showInboxModal) {
27318
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27498
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27319
27499
  InboxModal,
27320
27500
  {
27321
27501
  onDone: () => modals.setShowInboxModal(false),
@@ -27324,7 +27504,7 @@ function ModalHost(props) {
27324
27504
  ) }) });
27325
27505
  }
27326
27506
  if (modals.showMultiAgentModal) {
27327
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27507
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27328
27508
  MultiAgentModal,
27329
27509
  {
27330
27510
  initial: props.multiAgentSettings ?? {},
@@ -27337,7 +27517,7 @@ function ModalHost(props) {
27337
27517
  ) }) });
27338
27518
  }
27339
27519
  if (modals.showHooksDashboard) {
27340
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27520
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27341
27521
  HooksDashboard,
27342
27522
  {
27343
27523
  getConfigured: props.getConfiguredHooks,
@@ -27348,7 +27528,7 @@ function ModalHost(props) {
27348
27528
  ) }) });
27349
27529
  }
27350
27530
  if (modals.showHelpMenu) {
27351
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27531
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27352
27532
  HelpMenu,
27353
27533
  {
27354
27534
  customCommands: customCommands.map((c) => ({ name: c.name, description: c.description })),
@@ -27362,10 +27542,10 @@ function ModalHost(props) {
27362
27542
  ) }) });
27363
27543
  }
27364
27544
  if (modals.showShellPicker) {
27365
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(ShellPicker, { current: props.currentShell, onPick: props.onPickShell }) }) });
27545
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(ShellPicker, { current: props.currentShell, onPick: props.onPickShell }) }) });
27366
27546
  }
27367
27547
  if (modals.showMemoryPicker) {
27368
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27548
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27369
27549
  MemoryPicker,
27370
27550
  {
27371
27551
  enabled: props.memoryEnabled,
@@ -27376,7 +27556,7 @@ function ModalHost(props) {
27376
27556
  ) }) });
27377
27557
  }
27378
27558
  if (modals.showGatewayPicker) {
27379
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27559
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27380
27560
  GatewayPicker,
27381
27561
  {
27382
27562
  gatewayId: props.gatewayId,
@@ -27389,10 +27569,10 @@ function ModalHost(props) {
27389
27569
  ) }) });
27390
27570
  }
27391
27571
  if (modals.showSkillsPicker) {
27392
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(SkillsPicker, { onAction: props.onSkillsAction, onDone: props.onSkillsDone }) }) });
27572
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(SkillsPicker, { onAction: props.onSkillsAction, onDone: props.onSkillsDone }) }) });
27393
27573
  }
27394
27574
  if (modals.showLspWizard) {
27395
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27575
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27396
27576
  LspWizard,
27397
27577
  {
27398
27578
  servers: lspServers,
@@ -27404,7 +27584,7 @@ function ModalHost(props) {
27404
27584
  ) }) });
27405
27585
  }
27406
27586
  if (modals.commandWizard) {
27407
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27587
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27408
27588
  CommandWizard,
27409
27589
  {
27410
27590
  mode: modals.commandWizard.mode,
@@ -27418,7 +27598,7 @@ function ModalHost(props) {
27418
27598
  }
27419
27599
  if (modals.commandPicker) {
27420
27600
  const pickerMode = modals.commandPicker.mode;
27421
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27601
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27422
27602
  CommandPicker,
27423
27603
  {
27424
27604
  commands: customCommands,
@@ -27437,15 +27617,15 @@ function ModalHost(props) {
27437
27617
  }
27438
27618
  if (modals.commandToDelete) {
27439
27619
  const cmd = modals.commandToDelete;
27440
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsxs39(Box39, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
27441
- /* @__PURE__ */ jsxs39(Text40, { color: theme.accent, bold: true, children: [
27620
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsxs38(Box38, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
27621
+ /* @__PURE__ */ jsxs38(Text39, { color: theme.accent, bold: true, children: [
27442
27622
  "Delete /",
27443
27623
  cmd.name,
27444
27624
  "?"
27445
27625
  ] }),
27446
- /* @__PURE__ */ jsx41(Text40, { color: theme.info.color, children: cmd.filepath }),
27447
- /* @__PURE__ */ jsx41(Box39, { marginTop: 1, children: /* @__PURE__ */ jsx41(
27448
- SelectInput22,
27626
+ /* @__PURE__ */ jsx40(Text39, { color: theme.info.color, children: cmd.filepath }),
27627
+ /* @__PURE__ */ jsx40(Box38, { marginTop: 1, children: /* @__PURE__ */ jsx40(
27628
+ SelectInput21,
27449
27629
  {
27450
27630
  items: [
27451
27631
  { label: "Yes, delete", value: "yes", key: "yes" },
@@ -27463,7 +27643,7 @@ function ModalHost(props) {
27463
27643
  ] }) });
27464
27644
  }
27465
27645
  if (modals.showCommandList) {
27466
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27646
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27467
27647
  CommandList,
27468
27648
  {
27469
27649
  commands: customCommands,
@@ -27472,27 +27652,24 @@ function ModalHost(props) {
27472
27652
  ) }) });
27473
27653
  }
27474
27654
  if (modals.showThemePicker) {
27475
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(ThemePicker, { themes, onPick: onPickTheme }) }) });
27655
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(ThemePicker, { themes, onPick: onPickTheme }) }) });
27476
27656
  }
27477
27657
  if (modals.showUiPicker) {
27478
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(UiPicker, { current: currentUiEngine, onPick: onPickUi }) }) });
27658
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(UiPicker, { current: currentUiEngine, onPick: onPickUi }) }) });
27479
27659
  }
27480
27660
  if (modals.showModelPicker) {
27481
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(ModelPicker, { current: currentModel, onPick: onPickModel }) }) });
27661
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(ModelPicker, { current: currentModel, onPick: onPickModel }) }) });
27482
27662
  }
27483
27663
  if (modals.showModePicker) {
27484
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(ModePicker, { current: props.currentMode, onPick: props.onPickMode, multiAgentEnabled: props.multiAgentEnabled }) }) });
27485
- }
27486
- if (modals.showPlanCompletePicker) {
27487
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(PlanCompletePicker, { onPick: props.onPlanCompletePick }) }) });
27664
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(ModePicker, { current: props.currentMode, onPick: props.onPickMode, multiAgentEnabled: props.multiAgentEnabled }) }) });
27488
27665
  }
27489
27666
  if (modals.billingChooserFor) {
27490
27667
  const model = modals.billingChooserFor;
27491
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(BillingChooser, { model, onPick: (choice) => onPickBilling(model, choice) }) }) });
27668
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(BillingChooser, { model, onPick: (choice) => onPickBilling(model, choice) }) }) });
27492
27669
  }
27493
27670
  if (modals.unifiedProbeFor) {
27494
27671
  const model = modals.unifiedProbeFor;
27495
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27672
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27496
27673
  UnifiedBillingStatus,
27497
27674
  {
27498
27675
  model,
@@ -27505,7 +27682,7 @@ function ModalHost(props) {
27505
27682
  }
27506
27683
  if (modals.keyEntryFor) {
27507
27684
  const model = modals.keyEntryFor;
27508
- return /* @__PURE__ */ jsx41(ThemeProvider, { theme, children: /* @__PURE__ */ jsx41(Box39, { flexDirection: "column", children: /* @__PURE__ */ jsx41(
27685
+ return /* @__PURE__ */ jsx40(ThemeProvider, { theme, children: /* @__PURE__ */ jsx40(Box38, { flexDirection: "column", children: /* @__PURE__ */ jsx40(
27509
27686
  KeyEntryModal,
27510
27687
  {
27511
27688
  model,
@@ -27526,7 +27703,7 @@ function ModalOverlay({
27526
27703
  }) {
27527
27704
  if (modals.limitModal) {
27528
27705
  const m = modals.limitModal;
27529
- return /* @__PURE__ */ jsx41(
27706
+ return /* @__PURE__ */ jsx40(
27530
27707
  LimitModal,
27531
27708
  {
27532
27709
  limit: m.limit,
@@ -27540,7 +27717,7 @@ function ModalOverlay({
27540
27717
  }
27541
27718
  if (modals.loopModal) {
27542
27719
  const m = modals.loopModal;
27543
- return /* @__PURE__ */ jsx41(
27720
+ return /* @__PURE__ */ jsx40(
27544
27721
  LimitModal,
27545
27722
  {
27546
27723
  limit: 50,
@@ -27585,7 +27762,38 @@ var init_modal_host = __esm({
27585
27762
  init_multi_agent_modal();
27586
27763
  init_hooks_dashboard();
27587
27764
  init_help_menu();
27588
- init_plan_complete_picker();
27765
+ }
27766
+ });
27767
+
27768
+ // src/ui/plan-complete-picker.tsx
27769
+ import { Box as Box39, Text as Text40 } from "ink";
27770
+ import SelectInput22 from "ink-select-input";
27771
+ import { jsx as jsx41, jsxs as jsxs39 } from "react/jsx-runtime";
27772
+ function PlanCompletePicker({ onPick }) {
27773
+ const theme = useTheme();
27774
+ const items = [
27775
+ { label: "\u25B8 Execute this plan and accept changes (auto mode)", value: "auto", key: "auto" },
27776
+ { label: "\u25B8 Start building and ask for permission (edit mode)", value: "edit", key: "edit" },
27777
+ { label: "\u25B8 Continue planning / ask a question", value: "continue", key: "continue" }
27778
+ ];
27779
+ return /* @__PURE__ */ jsxs39(Box39, { flexDirection: "column", borderStyle: "round", borderColor: theme.accent, paddingX: 1, children: [
27780
+ /* @__PURE__ */ jsx41(Text40, { color: theme.accent, bold: true, children: "Plan complete \u2014 what next?" }),
27781
+ /* @__PURE__ */ jsx41(Text40, { color: theme.info.color, dimColor: false, children: "Arrow keys to navigate, Enter to select, Esc to cancel." }),
27782
+ /* @__PURE__ */ jsx41(Box39, { marginTop: 1, children: /* @__PURE__ */ jsx41(
27783
+ SelectInput22,
27784
+ {
27785
+ items,
27786
+ onSelect: (item) => onPick(item.value),
27787
+ onHighlight: () => {
27788
+ }
27789
+ }
27790
+ ) })
27791
+ ] });
27792
+ }
27793
+ var init_plan_complete_picker = __esm({
27794
+ "src/ui/plan-complete-picker.tsx"() {
27795
+ "use strict";
27796
+ init_theme_context();
27589
27797
  }
27590
27798
  });
27591
27799
 
@@ -27925,64 +28133,6 @@ var init_input_handlers = __esm({
27925
28133
  }
27926
28134
  });
27927
28135
 
27928
- // src/agent/distill.ts
27929
- function distillSessionPlan(messages) {
27930
- for (let i = messages.length - 1; i >= 0; i--) {
27931
- const m = messages[i];
27932
- if (m?.role !== "assistant") continue;
27933
- let text = "";
27934
- if (typeof m.content === "string") {
27935
- text = m.content;
27936
- } else if (Array.isArray(m.content)) {
27937
- text = m.content.filter((p) => p.type === "text").map((p) => p.text).join("\n");
27938
- }
27939
- text = text.trim();
27940
- if (text.length > 20) {
27941
- return text;
27942
- }
27943
- }
27944
- return null;
27945
- }
27946
- var init_distill = __esm({
27947
- "src/agent/distill.ts"() {
27948
- "use strict";
27949
- }
27950
- });
27951
-
27952
- // src/util/clipboard.ts
27953
- import { execSync as execSync5 } from "child_process";
27954
- import { platform as platform7 } from "os";
27955
- function writeToClipboard(text) {
27956
- const os2 = platform7();
27957
- try {
27958
- if (os2 === "darwin") {
27959
- execSync5("pbcopy", { input: text, timeout: 5e3 });
27960
- return { success: true, message: "Copied to clipboard" };
27961
- }
27962
- if (os2 === "win32") {
27963
- execSync5("clip", { input: text, timeout: 5e3 });
27964
- return { success: true, message: "Copied to clipboard" };
27965
- }
27966
- try {
27967
- execSync5("xclip -selection clipboard", { input: text, timeout: 5e3 });
27968
- return { success: true, message: "Copied to clipboard" };
27969
- } catch {
27970
- execSync5("xsel --clipboard --input", { input: text, timeout: 5e3 });
27971
- return { success: true, message: "Copied to clipboard" };
27972
- }
27973
- } catch {
27974
- return {
27975
- success: false,
27976
- message: "Clipboard not available \u2014 plan will be shown below"
27977
- };
27978
- }
27979
- }
27980
- var init_clipboard = __esm({
27981
- "src/util/clipboard.ts"() {
27982
- "use strict";
27983
- }
27984
- });
27985
-
27986
28136
  // src/cost-attribution/tui-report.ts
27987
28137
  var tui_report_exports = {};
27988
28138
  __export(tui_report_exports, {
@@ -28066,7 +28216,7 @@ var init_tui_report = __esm({
28066
28216
 
28067
28217
  // src/ui/slash-commands.ts
28068
28218
  import { join as join32 } from "path";
28069
- import { unlink as unlink4 } from "fs/promises";
28219
+ import { unlink as unlink5 } from "fs/promises";
28070
28220
  import QRCode from "qrcode";
28071
28221
  function dispatchSlashCommand(ctx, cmd) {
28072
28222
  const raw = cmd.trim();
@@ -28932,8 +29082,27 @@ ${lines.join("\n")}` }]);
28932
29082
  void ctx.runInit();
28933
29083
  return true;
28934
29084
  };
28935
- handleUpdate = (ctx) => {
29085
+ handleUpdate = (ctx, _rest, arg) => {
28936
29086
  const { setEvents, mkKey: mkKey2 } = ctx;
29087
+ if (arg === "camouflage") {
29088
+ void checkOptionalDependency("camouflage-tui", "beta").then((dep) => {
29089
+ if (dep.hasUpdate && dep.latestVersion) {
29090
+ setEvents((e) => [
29091
+ ...e,
29092
+ { kind: "info", key: mkKey2(), text: `camouflage-tui update available: ${dep.localVersion} \u2192 ${dep.latestVersion}` }
29093
+ ]);
29094
+ setEvents((e) => [
29095
+ ...e,
29096
+ { kind: "info", key: mkKey2(), text: "run: npm update camouflage-tui" }
29097
+ ]);
29098
+ } else if (dep.localVersion) {
29099
+ setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: `camouflage-tui up to date (${dep.localVersion})` }]);
29100
+ } else {
29101
+ setEvents((e) => [...e, { kind: "info", key: mkKey2(), text: "camouflage-tui is not installed" }]);
29102
+ }
29103
+ });
29104
+ return true;
29105
+ }
28937
29106
  void checkForUpdate(true).then((result) => {
28938
29107
  if (result.hasUpdate) {
28939
29108
  ctx.setHasUpdate(true);
@@ -29188,7 +29357,7 @@ project: ${projectSettingsPath(cwd)}`
29188
29357
  return true;
29189
29358
  };
29190
29359
  handleLogout = (ctx) => {
29191
- unlink4(configPath()).catch(() => {
29360
+ unlink5(configPath()).catch(() => {
29192
29361
  });
29193
29362
  ctx.setEvents((e) => [
29194
29363
  ...e,
@@ -30986,6 +31155,26 @@ ${wcagWarnings.join("\n")}` }
30986
31155
  ]);
30987
31156
  }
30988
31157
  });
31158
+ void checkOptionalDependency("camouflage-tui", "beta").then((dep) => {
31159
+ if (dep.hasUpdate && dep.latestVersion) {
31160
+ setEvents((e) => [
31161
+ ...e,
31162
+ {
31163
+ kind: "info",
31164
+ key: mkKey(),
31165
+ text: `camouflage-tui update available: ${dep.localVersion} \u2192 ${dep.latestVersion}`
31166
+ }
31167
+ ]);
31168
+ setEvents((e) => [
31169
+ ...e,
31170
+ {
31171
+ kind: "info",
31172
+ key: mkKey(),
31173
+ text: "run: npm update camouflage-tui"
31174
+ }
31175
+ ]);
31176
+ }
31177
+ });
30989
31178
  }, [cfg, initialUpdateResult]);
30990
31179
  useEffect11(() => {
30991
31180
  modeRef.current = mode;
@@ -31045,6 +31234,26 @@ ${wcagWarnings.join("\n")}` }
31045
31234
  }
31046
31235
  }
31047
31236
  });
31237
+ void checkOptionalDependency("camouflage-tui", "beta").then((dep) => {
31238
+ if (dep.hasUpdate && dep.latestVersion) {
31239
+ setEvents((e) => [
31240
+ ...e,
31241
+ {
31242
+ kind: "info",
31243
+ key: mkKey(),
31244
+ text: `camouflage-tui update available: ${dep.localVersion} \u2192 ${dep.latestVersion}`
31245
+ }
31246
+ ]);
31247
+ setEvents((e) => [
31248
+ ...e,
31249
+ {
31250
+ kind: "info",
31251
+ key: mkKey(),
31252
+ text: "run: npm update camouflage-tui"
31253
+ }
31254
+ ]);
31255
+ }
31256
+ });
31048
31257
  }, 30 * 60 * 1e3);
31049
31258
  return () => clearInterval(id);
31050
31259
  }, [cfg]);
@@ -31488,7 +31697,6 @@ ${wcagWarnings.join("\n")}` }
31488
31697
  clearTaskTracking();
31489
31698
  compactSuggestedRef.current = false;
31490
31699
  updateNudgedRef.current = false;
31491
- messagesRef.current.push({ role: "user", content: plan });
31492
31700
  setEvents((e) => [
31493
31701
  ...e,
31494
31702
  {
@@ -31501,8 +31709,10 @@ ${wcagWarnings.join("\n")}` }
31501
31709
  setEvents((e) => [...e, { kind: "info", key: mkKey(), text: "--- Plan ---\n" + plan }]);
31502
31710
  }
31503
31711
  setMode(picked);
31712
+ modeRef.current = picked;
31713
+ submitRef.current(plan);
31504
31714
  },
31505
- [mkKey, setShowPlanCompletePicker, setMode, setEvents, setUsage, setSessionUsage, setGatewayMeta, clearTaskTracking, resetSession]
31715
+ [mkKey, setShowPlanCompletePicker, setMode, setEvents, setUsage, setSessionUsage, setGatewayMeta, clearTaskTracking, resetSession, submitRef]
31506
31716
  );
31507
31717
  const handleModelPick = useCallback10(
31508
31718
  (picked) => {
@@ -32200,6 +32410,7 @@ ${conflicts.join("\n")}` }
32200
32410
  clearPermissionResolveRef();
32201
32411
  limitResolveRef.current = null;
32202
32412
  loopResolveRef.current = null;
32413
+ setLimitModal(null);
32203
32414
  setLoopModal(null);
32204
32415
  pendingToolCallsRef.current.clear();
32205
32416
  clearTaskTracking();
@@ -32633,8 +32844,7 @@ ${conflicts.join("\n")}` }
32633
32844
  }
32634
32845
  setEvents((e) => [...e, { kind: "info", key: mkKey(), text: `Type /skills ${action} <name> to ${action} a skill.` }]);
32635
32846
  },
32636
- onSkillsDone: () => setShowSkillsPicker(false),
32637
- onPlanCompletePick: handlePlanCompletePick
32847
+ onSkillsDone: () => setShowSkillsPicker(false)
32638
32848
  }
32639
32849
  );
32640
32850
  }
@@ -32662,7 +32872,7 @@ ${conflicts.join("\n")}` }
32662
32872
  loopResolveRef.current = null;
32663
32873
  }
32664
32874
  }
32665
- ) : /* @__PURE__ */ jsxs40(Box40, { flexDirection: "column", marginTop: 1, children: [
32875
+ ) : showPlanCompletePicker ? /* @__PURE__ */ jsx42(PlanCompletePicker, { onPick: handlePlanCompletePick }) : /* @__PURE__ */ jsxs40(Box40, { flexDirection: "column", marginTop: 1, children: [
32666
32876
  (activeWorkers.length > 0 || coordinatorNarration) && /* @__PURE__ */ jsx42(WorkerList, { workers: activeWorkers, isSynthesizing, narration: coordinatorNarration }),
32667
32877
  tasks.length > 0 && /* @__PURE__ */ jsx42(
32668
32878
  TaskList,
@@ -32847,6 +33057,7 @@ var init_app = __esm({
32847
33057
  init_use_picker_controller();
32848
33058
  init_use_modal_host();
32849
33059
  init_modal_host();
33060
+ init_plan_complete_picker();
32850
33061
  init_use_session_manager();
32851
33062
  init_use_turn_controller();
32852
33063
  init_input_handlers();