clixad 0.0.1-beta.43 → 0.0.1-beta.45

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.
@@ -607,16 +607,16 @@ async function httpFailure(res, what) {
607
607
  return detail ? `${what} failed: ${res.status} \u2014 ${detail}` : `${what} failed: ${res.status}`;
608
608
  }
609
609
  function sleep(ms, signal) {
610
- return new Promise((resolve4) => {
610
+ return new Promise((resolve5) => {
611
611
  if (!signal) {
612
- setTimeout(resolve4, ms);
612
+ setTimeout(resolve5, ms);
613
613
  return;
614
614
  }
615
- if (signal.aborted) return resolve4();
615
+ if (signal.aborted) return resolve5();
616
616
  const done = () => {
617
617
  clearTimeout(timer);
618
618
  signal.removeEventListener("abort", done);
619
- resolve4();
619
+ resolve5();
620
620
  };
621
621
  const timer = setTimeout(done, ms);
622
622
  signal.addEventListener("abort", done, { once: true });
@@ -1939,16 +1939,16 @@ async function devLogin(client, config, email, invite) {
1939
1939
  };
1940
1940
  }
1941
1941
  function sleep2(ms, signal) {
1942
- return new Promise((resolve4) => {
1942
+ return new Promise((resolve5) => {
1943
1943
  if (!signal) {
1944
- setTimeout(resolve4, ms);
1944
+ setTimeout(resolve5, ms);
1945
1945
  return;
1946
1946
  }
1947
- if (signal.aborted) return resolve4();
1947
+ if (signal.aborted) return resolve5();
1948
1948
  const done = () => {
1949
1949
  clearTimeout(timer);
1950
1950
  signal.removeEventListener("abort", done);
1951
- resolve4();
1951
+ resolve5();
1952
1952
  };
1953
1953
  const timer = setTimeout(done, ms);
1954
1954
  signal.addEventListener("abort", done, { once: true });
@@ -3084,7 +3084,7 @@ var StdioTransport = class {
3084
3084
  if (this.exited) return Promise.reject(new McpError(this.exited));
3085
3085
  const id = ++this.seq;
3086
3086
  const payload = JSON.stringify({ jsonrpc: "2.0", id, method, params }) + "\n";
3087
- return new Promise((resolve4, reject) => {
3087
+ return new Promise((resolve5, reject) => {
3088
3088
  const settle = (fn) => {
3089
3089
  clearTimeout(timer);
3090
3090
  signal?.removeEventListener("abort", onAbort);
@@ -3098,7 +3098,7 @@ var StdioTransport = class {
3098
3098
  const onAbort = () => settle(() => reject(new McpError("cancelled")));
3099
3099
  signal?.addEventListener("abort", onAbort, { once: true });
3100
3100
  this.pending.set(id, {
3101
- resolve: (v) => settle(() => resolve4(v)),
3101
+ resolve: (v) => settle(() => resolve5(v)),
3102
3102
  reject: (e) => settle(() => reject(e))
3103
3103
  });
3104
3104
  this.child.stdin?.write(payload, (err) => {
@@ -5486,79 +5486,92 @@ ${NO_SEARCH_INSTRUCTION}`,
5486
5486
  ...turn.spendable ? { spendable: turn.spendable } : {}
5487
5487
  });
5488
5488
  }
5489
- let retriedEmpty = false;
5490
- for (let step = 0; step < maxSteps; step++) {
5491
- if (opts.signal?.aborted) return stop("aborted");
5492
- let turn;
5493
- try {
5494
- turn = await client.chatStream(messages, opts.model, {
5495
- effort: opts.effort,
5496
- tools: opts.tools ? opts.tools() : TOOL_SCHEMA,
5497
- signal: opts.signal,
5498
- onDelta: (text) => emit({ type: "delta", text })
5499
- });
5500
- } catch (err) {
5501
- if (isAbort(err) || opts.signal?.aborted) return stop("aborted");
5502
- throw err;
5503
- }
5504
- creditsCharged += turn.creditsCharged;
5505
- creditsWaived += turn.creditsWaived ?? 0;
5506
- if (turn.freeCreditsLeft !== void 0) freeCreditsLeft = turn.freeCreditsLeft;
5507
- balance = turn.balance;
5508
- emitUsage(turn);
5509
- const content = messageText(turn.message);
5510
- const calls = turn.message.tool_calls ?? [];
5511
- if (calls.length === 0 && !content.trim() && !retriedEmpty) {
5512
- retriedEmpty = true;
5513
- continue;
5514
- }
5515
- retriedEmpty = false;
5516
- messages.push(turn.message);
5517
- if (content) lastText = content;
5518
- if (calls.length === 0) {
5519
- emit({ type: "final", content, creditsCharged, balance });
5520
- return {
5521
- content,
5522
- messages: withoutReasoning(messages),
5523
- creditsCharged,
5524
- balance,
5525
- creditsWaived,
5526
- freeCreditsLeft,
5527
- searchCredits,
5528
- steps: step + 1
5529
- };
5489
+ const base = messages.length;
5490
+ try {
5491
+ return await loop();
5492
+ } catch (err) {
5493
+ if (typeof err === "object" && err !== null) {
5494
+ if (messages.length > base) interrupted.set(err, closeOpenCalls(withoutReasoning(messages)));
5495
+ else interrupted.delete(err);
5530
5496
  }
5531
- if (content) emit({ type: "message", content });
5532
- pendingImages = [];
5533
- for (const [i, call] of calls.entries()) {
5534
- if (opts.signal?.aborted) {
5535
- for (const rest of calls.slice(i)) messages.push(toolMessage(rest, "cancelled by the user"));
5536
- return stop("aborted");
5497
+ throw err;
5498
+ }
5499
+ async function loop() {
5500
+ let retriedEmpty = false;
5501
+ for (let step = 0; step < maxSteps; step++) {
5502
+ if (opts.signal?.aborted) return stop("aborted");
5503
+ let turn;
5504
+ try {
5505
+ turn = await client.chatStream(messages, opts.model, {
5506
+ effort: opts.effort,
5507
+ tools: opts.tools ? opts.tools() : TOOL_SCHEMA,
5508
+ signal: opts.signal,
5509
+ onDelta: (text) => emit({ type: "delta", text })
5510
+ });
5511
+ } catch (err) {
5512
+ if (isAbort(err) || opts.signal?.aborted) return stop("aborted");
5513
+ throw err;
5537
5514
  }
5538
- const result = await executeToolCall(ctx, call, emit);
5539
- messages.push(toolMessage(call, result));
5540
- }
5541
- if (pendingImages.length) {
5542
- messages.push(imageMessage(pendingImages));
5543
- emit({
5544
- type: "attachment",
5545
- labels: pendingImages.map((image) => image.label)
5546
- });
5515
+ creditsCharged += turn.creditsCharged;
5516
+ creditsWaived += turn.creditsWaived ?? 0;
5517
+ if (turn.freeCreditsLeft !== void 0) freeCreditsLeft = turn.freeCreditsLeft;
5518
+ balance = turn.balance;
5519
+ emitUsage(turn);
5520
+ const content = messageText(turn.message);
5521
+ const calls = turn.message.tool_calls ?? [];
5522
+ if (calls.length === 0 && !content.trim() && !retriedEmpty) {
5523
+ retriedEmpty = true;
5524
+ continue;
5525
+ }
5526
+ retriedEmpty = false;
5527
+ messages.push(turn.message);
5528
+ if (content) lastText = content;
5529
+ if (calls.length === 0) {
5530
+ emit({ type: "final", content, creditsCharged, balance });
5531
+ return {
5532
+ content,
5533
+ messages: withoutReasoning(messages),
5534
+ creditsCharged,
5535
+ balance,
5536
+ creditsWaived,
5537
+ freeCreditsLeft,
5538
+ searchCredits,
5539
+ steps: step + 1
5540
+ };
5541
+ }
5542
+ if (content) emit({ type: "message", content });
5547
5543
  pendingImages = [];
5544
+ for (const [i, call] of calls.entries()) {
5545
+ if (opts.signal?.aborted) {
5546
+ for (const rest of calls.slice(i)) messages.push(toolMessage(rest, "cancelled by the user"));
5547
+ return stop("aborted");
5548
+ }
5549
+ const result = await executeToolCall(ctx, call, emit);
5550
+ messages.push(toolMessage(call, result));
5551
+ }
5552
+ if (pendingImages.length) {
5553
+ messages.push(imageMessage(pendingImages));
5554
+ emit({
5555
+ type: "attachment",
5556
+ labels: pendingImages.map((image) => image.label)
5557
+ });
5558
+ pendingImages = [];
5559
+ }
5560
+ opts.onCheckpoint?.(withoutReasoning(messages));
5548
5561
  }
5562
+ emit({ type: "max_steps" });
5563
+ return {
5564
+ content: lastText || "(stopped: reached max steps)",
5565
+ messages: withoutReasoning(messages),
5566
+ creditsCharged,
5567
+ balance,
5568
+ creditsWaived,
5569
+ freeCreditsLeft,
5570
+ searchCredits,
5571
+ steps: maxSteps,
5572
+ stopped: "max_steps"
5573
+ };
5549
5574
  }
5550
- emit({ type: "max_steps" });
5551
- return {
5552
- content: lastText || "(stopped: reached max steps)",
5553
- messages: withoutReasoning(messages),
5554
- creditsCharged,
5555
- balance,
5556
- creditsWaived,
5557
- freeCreditsLeft,
5558
- searchCredits,
5559
- steps: maxSteps,
5560
- stopped: "max_steps"
5561
- };
5562
5575
  function stop(reason) {
5563
5576
  emit({ type: reason });
5564
5577
  return {
@@ -5574,6 +5587,21 @@ ${NO_SEARCH_INSTRUCTION}`,
5574
5587
  };
5575
5588
  }
5576
5589
  }
5590
+ var interrupted = /* @__PURE__ */ new WeakMap();
5591
+ function interruptedConversation(err) {
5592
+ return typeof err === "object" && err !== null ? interrupted.get(err) : void 0;
5593
+ }
5594
+ var INTERRUPTED_CALL = "not completed: the turn stopped with an error before this call finished";
5595
+ function closeOpenCalls(messages) {
5596
+ const out = [...messages];
5597
+ const last = out.findLastIndex((m) => m.role === "assistant");
5598
+ const calls = last >= 0 ? out[last].tool_calls ?? [] : [];
5599
+ const answered = new Set(out.slice(last + 1).map((m) => m.tool_call_id));
5600
+ for (const call of calls) {
5601
+ if (!answered.has(call.id)) out.push(toolMessage(call, INTERRUPTED_CALL));
5602
+ }
5603
+ return out;
5604
+ }
5577
5605
  function toolMessage(call, content) {
5578
5606
  return { role: "tool", tool_call_id: call.id, name: call.function.name, content };
5579
5607
  }
@@ -5944,12 +5972,20 @@ import {
5944
5972
  writeFileSync as writeFileSync3
5945
5973
  } from "node:fs";
5946
5974
  import { homedir as homedir5 } from "node:os";
5947
- import { join as join5 } from "node:path";
5975
+ import { join as join5, resolve as resolve4 } from "node:path";
5948
5976
  var ROOT = join5(homedir5(), ".clixad");
5949
5977
  var SESSION_DIR = join5(ROOT, "sessions");
5950
5978
  var HISTORY_FILE = join5(ROOT, "history");
5951
5979
  var MAX_HISTORY = 200;
5952
- var MAX_SESSIONS = 50;
5980
+ var MAX_SESSIONS = 200;
5981
+ var SESSION_ID = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
5982
+ function isSessionId(id) {
5983
+ return SESSION_ID.test(id);
5984
+ }
5985
+ function sameDirectory(a, b) {
5986
+ const norm = (p) => process.platform === "win32" ? resolve4(p).toLowerCase() : resolve4(p);
5987
+ return norm(a) === norm(b);
5988
+ }
5953
5989
  function newSessionId() {
5954
5990
  const now = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/g, "").replace(/\.\d+Z$/, "");
5955
5991
  return `${now}-${Math.random().toString(16).slice(2, 6)}`;
@@ -5964,12 +6000,25 @@ function saveSession(session) {
5964
6000
  }
5965
6001
  }
5966
6002
  function loadSession(id) {
6003
+ if (!isSessionId(id)) return void 0;
5967
6004
  try {
5968
- return JSON.parse(readFileSync5(join5(SESSION_DIR, `${id}.json`), "utf8"));
6005
+ const s = JSON.parse(readFileSync5(join5(SESSION_DIR, `${id}.json`), "utf8"));
6006
+ return s && Array.isArray(s.messages) && typeof s.id === "string" ? s : void 0;
5969
6007
  } catch {
5970
6008
  return void 0;
5971
6009
  }
5972
6010
  }
6011
+ function deleteSession(id) {
6012
+ if (!isSessionId(id)) return false;
6013
+ const path = join5(SESSION_DIR, `${id}.json`);
6014
+ try {
6015
+ if (!existsSync4(path)) return false;
6016
+ rmSync(path);
6017
+ return true;
6018
+ } catch {
6019
+ return false;
6020
+ }
6021
+ }
5973
6022
  function listSessions(cwd, limit = 20) {
5974
6023
  if (!existsSync4(SESSION_DIR)) return [];
5975
6024
  const metas = [];
@@ -5977,16 +6026,15 @@ function listSessions(cwd, limit = 20) {
5977
6026
  if (!name.endsWith(".json")) continue;
5978
6027
  const s = loadSession(name.slice(0, -5));
5979
6028
  if (!s) continue;
5980
- if (cwd && s.cwd !== cwd) continue;
5981
- const { messages: _messages, ...meta } = s;
5982
- metas.push(meta);
6029
+ if (cwd && !sameDirectory(s.cwd ?? "", cwd)) continue;
6030
+ const { messages, ...meta } = s;
6031
+ metas.push({ ...meta, title: meta.title ?? "", updated: meta.updated ?? "", messageCount: messages.length });
5983
6032
  }
5984
6033
  metas.sort((a, b) => b.updated.localeCompare(a.updated));
5985
6034
  return metas.slice(0, limit);
5986
6035
  }
5987
6036
  function hasSessionIn(cwd) {
5988
6037
  if (!existsSync4(SESSION_DIR)) return false;
5989
- const needle = `"cwd":${JSON.stringify(cwd)}`;
5990
6038
  try {
5991
6039
  for (const name of readdirSync3(SESSION_DIR)) {
5992
6040
  if (!name.endsWith(".json")) continue;
@@ -5999,8 +6047,14 @@ function hasSessionIn(cwd) {
5999
6047
  } finally {
6000
6048
  closeSync2(fd);
6001
6049
  }
6002
- if (head.includes(needle)) return true;
6003
- if (!head.includes('"cwd":') && loadSession(name.slice(0, -5))?.cwd === cwd) return true;
6050
+ const field = /"cwd":("(?:[^"\\]|\\.)*")/.exec(head)?.[1];
6051
+ let stored;
6052
+ try {
6053
+ stored = field ? JSON.parse(field) : loadSession(name.slice(0, -5))?.cwd;
6054
+ } catch {
6055
+ stored = void 0;
6056
+ }
6057
+ if (stored && sameDirectory(stored, cwd)) return true;
6004
6058
  }
6005
6059
  } catch {
6006
6060
  }
@@ -6045,6 +6099,7 @@ var CONTEXT_TOKENS = {
6045
6099
  "claude-haiku-5.5": 24e3,
6046
6100
  "deepseek-v4-flash": 1048576,
6047
6101
  "gemini-3.5-flash-lite": 1048576,
6102
+ "glm-5.3-flash": 1048576,
6048
6103
  "kimi-k2.5": 262144,
6049
6104
  "gemini-3.8-flash": 1048576,
6050
6105
  "kimi-k2.7-code": 262144,
@@ -6132,14 +6187,14 @@ function runKimiCode(config, task, opts = {}) {
6132
6187
  OPENAI_BASE_URL: `${config.gatewayUrl.replace(/\/+$/, "")}/v1`,
6133
6188
  OPENAI_API_KEY: config.token
6134
6189
  };
6135
- return new Promise((resolve4) => {
6190
+ return new Promise((resolve5) => {
6136
6191
  const child = process.platform === "win32" ? spawn5([bin, ...args].map(quoteWin).join(" "), { stdio: "inherit", env, shell: true }) : spawn5(bin, args, { stdio: "inherit", env });
6137
6192
  child.on("error", (err) => {
6138
6193
  if (err.code === "ENOENT") console.log("\n" + INSTALL_HELP);
6139
6194
  else console.error(`failed to launch Kimi CLI: ${err.message}`);
6140
- resolve4(127);
6195
+ resolve5(127);
6141
6196
  });
6142
- child.on("exit", (code) => resolve4(code ?? 0));
6197
+ child.on("exit", (code) => resolve5(code ?? 0));
6143
6198
  });
6144
6199
  }
6145
6200
  function quoteWin(s) {
@@ -6399,6 +6454,8 @@ function cutPoint(messages, keepTurns = 2) {
6399
6454
  }
6400
6455
  return 0;
6401
6456
  }
6457
+ var SUMMARY_LEAD = "Summary of the earlier conversation:\n\n";
6458
+ var SUMMARY_ACK = "Understood \u2014 continuing from that summary.";
6402
6459
  var SUMMARY_PROMPT = "Summarise the conversation above for your future self, which will continue the work without seeing it. Cover: what the user asked for, what was already changed (files and the reasoning), what was learned about the codebase, and what is still open. Be specific about file paths and names. No preamble.";
6403
6460
  async function compact(client, model, messages, opts = {}) {
6404
6461
  const cut2 = cutPoint(messages, opts.keepTurns ?? 2);
@@ -6414,10 +6471,8 @@ async function compact(client, model, messages, opts = {}) {
6414
6471
  const summary = messageText(turn.message).trim() || "(no summary returned)";
6415
6472
  const compacted = [
6416
6473
  messages[0],
6417
- { role: "user", content: `Summary of the earlier conversation:
6418
-
6419
- ${summary}` },
6420
- { role: "assistant", content: "Understood \u2014 continuing from that summary." },
6474
+ { role: "user", content: `${SUMMARY_LEAD}${summary}` },
6475
+ { role: "assistant", content: SUMMARY_ACK },
6421
6476
  ...messages.slice(cut2)
6422
6477
  ];
6423
6478
  return {
@@ -6533,6 +6588,7 @@ export {
6533
6588
  referLines,
6534
6589
  redeemMessage,
6535
6590
  ledgerReasonLabel,
6591
+ messageText,
6536
6592
  PaywallError,
6537
6593
  PremiumLockedError,
6538
6594
  LimitRefusalError,
@@ -6574,6 +6630,8 @@ export {
6574
6630
  pickReviewModel,
6575
6631
  NO_SEARCH_INSTRUCTION,
6576
6632
  runAgent,
6633
+ interruptedConversation,
6634
+ INTERRUPTED_CALL,
6577
6635
  collectContext,
6578
6636
  INIT_PROMPT,
6579
6637
  INIT_HINT,
@@ -6586,9 +6644,12 @@ export {
6586
6644
  switchMode,
6587
6645
  createPermit,
6588
6646
  denyAll,
6647
+ MAX_SESSIONS,
6648
+ sameDirectory,
6589
6649
  newSessionId,
6590
6650
  saveSession,
6591
6651
  loadSession,
6652
+ deleteSession,
6592
6653
  listSessions,
6593
6654
  hasSessionIn,
6594
6655
  latestSession,
@@ -6613,6 +6674,8 @@ export {
6613
6674
  estimateTokens,
6614
6675
  conversationTokens,
6615
6676
  shouldCompact,
6677
+ SUMMARY_LEAD,
6678
+ SUMMARY_ACK,
6616
6679
  compact,
6617
6680
  VERSION,
6618
6681
  VERSION_LABEL,
package/dist/clixad.mjs CHANGED
@@ -43,6 +43,7 @@ import {
43
43
  rowTaskHint,
44
44
  runAgent,
45
45
  runKimiCode,
46
+ sameDirectory,
46
47
  saveConfig,
47
48
  savedEffort,
48
49
  shells,
@@ -53,7 +54,7 @@ import {
53
54
  takeConfigNotices,
54
55
  toolSchemaFor,
55
56
  usd
56
- } from "./chunk-T7SUUKJC.mjs";
57
+ } from "./chunk-Y55DBUP6.mjs";
57
58
 
58
59
  // src/tui/load.ts
59
60
  import * as nodeModule from "node:module";
@@ -132,7 +133,7 @@ async function withProductionReact(load) {
132
133
  }
133
134
  function loadTui() {
134
135
  boundInkCaches();
135
- return withProductionReact(() => import("./tui-773NJ4FQ.mjs"));
136
+ return withProductionReact(() => import("./tui-KH7DNN65.mjs"));
136
137
  }
137
138
 
138
139
  // src/title.ts
@@ -615,13 +616,18 @@ async function codeCmd(client, config, rest) {
615
616
  }
616
617
  async function repl(client, config, opts = {}) {
617
618
  if (opts.resume === "pick") {
618
- const sessions = listSessions(void 0, 10);
619
+ const sessions = listSessions(void 0, 20);
619
620
  if (!sessions.length) return console.log(c.yellow("no saved sessions yet"));
620
- console.log(c.bold("Sessions:"));
621
+ const here = process.cwd();
622
+ console.log(c.bold("Sessions (newest first, * = this directory):"));
621
623
  for (const s of sessions) {
622
- console.log(` ${c.cyan(s.id)} ${s.updated.slice(0, 16).replace("T", " ")} ${s.title}`);
624
+ const mark = sameDirectory(s.cwd, here) ? "*" : " ";
625
+ console.log(
626
+ `${mark} ${c.cyan(s.id)} ${s.updated.slice(0, 16).replace("T", " ")} ${String(s.messageCount).padStart(4)} msgs ${s.title}`
627
+ );
628
+ if (mark === " ") console.log(c.dim(` in ${s.cwd}`));
623
629
  }
624
- console.log(c.dim("\n resume one with: clixad --resume <id>"));
630
+ console.log(c.dim("\n resume one with: clixad --resume <id> \xB7 delete one in the REPL: /resume, then d twice"));
625
631
  return;
626
632
  }
627
633
  if (!process.stdin.isTTY) {
@@ -639,9 +645,10 @@ async function repl(client, config, opts = {}) {
639
645
  if (!config.token) return;
640
646
  }
641
647
  let session;
648
+ const notices = [...opts.notices ?? []];
642
649
  if (opts.resume === "latest") {
643
650
  session = latestSession(process.cwd());
644
- if (!session) console.log(c.dim(" no previous session here \u2014 starting a new one"));
651
+ if (!session) notices.push("no saved session in this directory, so this is a new one (/resume all lists every directory's)");
645
652
  } else if (opts.resume) {
646
653
  session = loadSession(opts.resume);
647
654
  if (!session) return console.log(c.red(`no such session: ${opts.resume}`));
@@ -661,7 +668,7 @@ async function repl(client, config, opts = {}) {
661
668
  newAccount,
662
669
  preferences,
663
670
  initialTask: opts.task,
664
- ...opts.notices?.length ? { notices: opts.notices } : {},
671
+ ...notices.length ? { notices } : {},
665
672
  ...initHint ? { initHint } : {}
666
673
  });
667
674
  }
@@ -6,7 +6,9 @@ import {
6
6
  EXTRA_AD_FORMATS,
7
7
  INIT_HINT,
8
8
  INIT_PROMPT,
9
+ INTERRUPTED_CALL,
9
10
  LimitRefusalError,
11
+ MAX_SESSIONS,
10
12
  MIN_BANNER_WIDTH,
11
13
  MODES,
12
14
  MODE_LABEL,
@@ -17,6 +19,8 @@ import {
17
19
  PaywallError,
18
20
  PremiumLockedError,
19
21
  SPINNER_TICKS_PER_FRAME,
22
+ SUMMARY_ACK,
23
+ SUMMARY_LEAD,
20
24
  TERMS_NOTICE,
21
25
  TERMS_URL,
22
26
  TOOLS,
@@ -35,6 +39,7 @@ import {
35
39
  conversationTokens,
36
40
  createPermit,
37
41
  createState,
42
+ deleteSession,
38
43
  diffTint,
39
44
  effortLabel,
40
45
  effortRaised,
@@ -45,12 +50,14 @@ import {
45
50
  freeHelpLine,
46
51
  freeTierNotice,
47
52
  grantLabel,
53
+ interruptedConversation,
48
54
  isNewerVersion,
49
55
  listSessions,
50
56
  listWorkspaceFiles,
51
57
  loadHistory,
52
58
  loadMcpConfig,
53
59
  loadSession,
60
+ messageText,
54
61
  modeInstruction,
55
62
  newSessionId,
56
63
  nextMode,
@@ -97,7 +104,7 @@ import {
97
104
  visibleLength,
98
105
  webSearchCostLine,
99
106
  welcomeBanner
100
- } from "./chunk-T7SUUKJC.mjs";
107
+ } from "./chunk-Y55DBUP6.mjs";
101
108
 
102
109
  // src/tui/index.tsx
103
110
  import "react";
@@ -110,6 +117,7 @@ import { Box as Box2, Static, Text as Text2, useApp, useInput, usePaste, useStdo
110
117
  // src/mentions.ts
111
118
  var TRAILING = /[),;:]+$/;
112
119
  var MAX_MENTION_CHARS = 12e4;
120
+ var MENTIONS_HEADING = "\n\n# Files the user referenced\n";
113
121
  function findMentions(text) {
114
122
  const out = [];
115
123
  for (const match of text.matchAll(/(?:^|\s)@([^\s]+)/g)) {
@@ -144,10 +152,7 @@ ${clipped}`);
144
152
  }
145
153
  if (blocks.length === 0) return { text, attached: [], images: [] };
146
154
  return {
147
- text: `${text}
148
-
149
- # Files the user referenced
150
- Their contents are below, already read for you \u2014 do not call read_file on them again.
155
+ text: `${text}${MENTIONS_HEADING}Their contents are below, already read for you \u2014 do not call read_file on them again.
151
156
 
152
157
  ` + blocks.join("\n\n"),
153
158
  attached,
@@ -420,8 +425,8 @@ var COMMANDS = [
420
425
  { name: "clear", desc: "clear the screen and start a new session" },
421
426
  // Deliberately *no* `args` on /resume, for /login's reason: an arg hint makes
422
427
  // enter complete the command instead of running it, and the picker is the
423
- // point. `/resume <id>` still works when typed in full.
424
- { name: "resume", desc: "pick up an earlier session in this directory" },
428
+ // point. `/resume <id>`, `/resume all` and `/resume delete <id>` work typed in full.
429
+ { name: "resume", desc: "pick up or delete an earlier session (all: every directory)" },
425
430
  { name: "init", desc: "write a CLIXAD.md for this project" },
426
431
  // Deliberately *no* `args` on either, for /login's reason: an arg hint makes
427
432
  // enter complete the command instead of running it, and listing is what both
@@ -1461,9 +1466,9 @@ function presentResult(input, width, tint = diffTint()) {
1461
1466
  const [notes = "", ...rest] = result.split("\n\n");
1462
1467
  const out = nonEmpty(rest.join("\n\n")).filter((l) => l !== "(nothing new since the last read)");
1463
1468
  const { body, more } = rows(out);
1464
- const failed = /\] (exited with code (?!0\b)\d+|failed|killed)/.test(notes);
1469
+ const failed2 = /\] (exited with code (?!0\b)\d+|failed|killed)/.test(notes);
1465
1470
  return {
1466
- status: failed ? "error" : "ok",
1471
+ status: failed2 ? "error" : "ok",
1467
1472
  line: fit(nonEmpty(notes)[0] ?? "read"),
1468
1473
  body,
1469
1474
  more,
@@ -1603,6 +1608,69 @@ function askWords(req) {
1603
1608
  }
1604
1609
  }
1605
1610
 
1611
+ // src/tui/replay.ts
1612
+ function shellNote(command2, output) {
1613
+ return `I ran \`${command2}\` in the workspace myself. Its output:
1614
+
1615
+ ${output}`;
1616
+ }
1617
+ var SHELL_NOTE = /^I ran `([^\n]*)` in the workspace myself\. Its output:\n\n([\s\S]*)$/;
1618
+ var IMAGE_NOTE = /^Here (?:is the image|are the images) you read \(/;
1619
+ function failed(result) {
1620
+ return result.startsWith("error: ") || result === INTERRUPTED_CALL;
1621
+ }
1622
+ function parseArgs(raw) {
1623
+ if (!raw) return {};
1624
+ try {
1625
+ const parsed = JSON.parse(raw);
1626
+ return parsed && typeof parsed === "object" && !Array.isArray(parsed) ? parsed : {};
1627
+ } catch {
1628
+ return {};
1629
+ }
1630
+ }
1631
+ function replayItems(messages) {
1632
+ const answers = /* @__PURE__ */ new Map();
1633
+ for (const m of messages) {
1634
+ if (m.role === "tool" && m.tool_call_id) answers.set(m.tool_call_id, messageText(m));
1635
+ }
1636
+ const out = [];
1637
+ for (const m of messages) {
1638
+ if (m.role === "user") {
1639
+ const text = messageText(m);
1640
+ if (text.startsWith(SUMMARY_LEAD)) {
1641
+ out.push({ kind: "notice", text: "earlier messages were summarised to save context; the summary is what the model has" });
1642
+ continue;
1643
+ }
1644
+ if (IMAGE_NOTE.test(text)) continue;
1645
+ const shell = SHELL_NOTE.exec(text);
1646
+ if (shell) {
1647
+ out.push({ kind: "user", text: `!${shell[1]}` });
1648
+ out.push({ kind: "tool", name: "!", args: { command: shell[1] }, result: shell[2] ?? "", ok: true });
1649
+ continue;
1650
+ }
1651
+ const cut = text.indexOf(MENTIONS_HEADING);
1652
+ const typed = (cut >= 0 ? text.slice(0, cut) : text).trim();
1653
+ if (typed) out.push({ kind: "user", text: typed });
1654
+ continue;
1655
+ }
1656
+ if (m.role === "assistant") {
1657
+ const text = messageText(m).trim();
1658
+ if (text && text !== SUMMARY_ACK) out.push({ kind: "assistant", text });
1659
+ for (const call of m.tool_calls ?? []) {
1660
+ const result = answers.get(call.id) ?? INTERRUPTED_CALL;
1661
+ out.push({
1662
+ kind: "tool",
1663
+ name: call.function.name,
1664
+ args: parseArgs(call.function.arguments),
1665
+ result,
1666
+ ok: !failed(result)
1667
+ });
1668
+ }
1669
+ }
1670
+ }
1671
+ return out;
1672
+ }
1673
+
1606
1674
  // src/tui/app.tsx
1607
1675
  import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
1608
1676
  var WORKING = "working\u2026";
@@ -1615,6 +1683,7 @@ var SENDING_FEEDBACK = "sending\u2026";
1615
1683
  var WAITING_FOR_PAYMENT = "waiting for the payment\u2026";
1616
1684
  var FEEDBACK_TURN_INTERVAL = 50;
1617
1685
  var NOT_SIGNED_IN = " Not signed in, or the stored token isn't valid for this gateway.\n Run /login to sign in.";
1686
+ var KEPT_AFTER_ERROR = " The work done before this error is kept. Send a message to carry on from there,\n now or after a restart with clixad --continue.";
1618
1687
  var LIVE_OUTPUT_LINES = 5;
1619
1688
  var DELTA_FLUSH_MS = 50;
1620
1689
  var PASTE_ENTER_MS = 30;
@@ -1713,6 +1782,7 @@ var INIT_HINT_AFTER_TURNS = 3;
1713
1782
  var freshStream = () => ({ text: "", md: {}, committed: false, streamed: false });
1714
1783
  var EARN_ROW = "\0earn";
1715
1784
  var BUY_ROW = "\0buy";
1785
+ var PICKER_DELETE_CONFIRM = "Press d again to delete it for good \xB7 any other key keeps it";
1716
1786
  function App({
1717
1787
  client,
1718
1788
  config,
@@ -1803,6 +1873,8 @@ function App({
1803
1873
  const [plan, setPlan] = useState(null);
1804
1874
  const [picker, setPicker] = useState(null);
1805
1875
  const [pickerSel, setPickerSel] = useState(0);
1876
+ const [pickerArmed, setPickerArmed] = useState(null);
1877
+ const [pickerNote, setPickerNote] = useState(null);
1806
1878
  const [buy, setBuy] = useState(null);
1807
1879
  const [tick, setTick] = useState(0);
1808
1880
  const [startedAt, setStartedAt] = useState(0);
@@ -2111,9 +2183,6 @@ function App({
2111
2183
  if (freeLine) {
2112
2184
  push({ kind: "notice", tone: (wallet?.balance ?? 0) <= 0 ? "warn" : "good", text: ` ${freeLine}` });
2113
2185
  }
2114
- if (session?.messages.length) {
2115
- push({ kind: "notice", text: ` resumed session ${session.id} (${session.messages.length} messages)` });
2116
- }
2117
2186
  if (initHint?.returning) {
2118
2187
  initHintedRef.current = true;
2119
2188
  push({ kind: "notice", text: ` ${INIT_HINT}` });
@@ -2158,14 +2227,14 @@ function App({
2158
2227
  if (cancelled) return void hub.close();
2159
2228
  mcpRef.current = hub;
2160
2229
  const ready = hub.status().filter((s) => s.state === "ready");
2161
- const failed = hub.status().filter((s) => s.state === "failed");
2230
+ const failed2 = hub.status().filter((s) => s.state === "failed");
2162
2231
  if (ready.length) {
2163
2232
  push({
2164
2233
  kind: "notice",
2165
2234
  text: ` mcp: ${ready.map((s) => `${s.name} (${s.toolCount} tools)`).join(", ")}`
2166
2235
  });
2167
2236
  }
2168
- for (const server of failed) {
2237
+ for (const server of failed2) {
2169
2238
  push({ kind: "notice", tone: "warn", text: ` mcp: ${server.name} did not start \u2014 ${server.error}` });
2170
2239
  }
2171
2240
  });
@@ -2245,7 +2314,7 @@ function App({
2245
2314
  const fitting = AD_FORMATS.filter((format) => adFitsFormat(format, colsRef.current));
2246
2315
  void (async () => {
2247
2316
  let direct = [];
2248
- let failed = false;
2317
+ let failed2 = false;
2249
2318
  if (directSupportedRef.current && fitting.length > 0) {
2250
2319
  const ask2 = sponsorDirect ?? ((formats, cols2, signal) => client.directSponsor(formats, cols2, signal));
2251
2320
  let answer = { supported: true, lines: [] };
@@ -2255,9 +2324,9 @@ function App({
2255
2324
  answer = { supported: true, lines: [], failed: true };
2256
2325
  }
2257
2326
  if (!answer.supported) directSupportedRef.current = false;
2258
- failed = answer.failed === true;
2327
+ failed2 = answer.failed === true;
2259
2328
  direct = answer.lines;
2260
- if (!failed) knownBookedRef.current = new Set(direct.map((line2) => line2.format));
2329
+ if (!failed2) knownBookedRef.current = new Set(direct.map((line2) => line2.format));
2261
2330
  }
2262
2331
  if (call !== sponsorCallRef.current) return;
2263
2332
  for (const format of ["top_bar", ...EXTRA_AD_FORMATS]) {
@@ -2265,7 +2334,7 @@ function App({
2265
2334
  const booked = direct.find((line2) => line2.format === format);
2266
2335
  if (booked && "hold" in booked) continue;
2267
2336
  if (booked) paintDirect(booked);
2268
- else if (failed && knownBookedRef.current.has(format)) continue;
2337
+ else if (failed2 && knownBookedRef.current.has(format)) continue;
2269
2338
  else fetchNetwork(format);
2270
2339
  }
2271
2340
  })();
@@ -2362,6 +2431,26 @@ function App({
2362
2431
  },
2363
2432
  [cols, keepOutput, push]
2364
2433
  );
2434
+ const replayConversation = useCallback(
2435
+ (stored) => {
2436
+ const title = stored.title ? ` \xB7 ${stored.title}` : "";
2437
+ push({
2438
+ kind: "notice",
2439
+ tone: "good",
2440
+ text: ` resumed session ${stored.id} (${stored.messages.length} messages)${title}`
2441
+ });
2442
+ for (const item of replayItems(stored.messages)) {
2443
+ if (item.kind === "tool") commitTool(item.name, item.args, { result: item.result, ok: item.ok });
2444
+ else if (item.kind === "notice") push({ kind: "notice", text: ` ${item.text}` });
2445
+ else push(item);
2446
+ }
2447
+ push({ kind: "notice", text: " the agent has all of this conversation; carry on below" });
2448
+ },
2449
+ [commitTool, push]
2450
+ );
2451
+ useEffect(() => {
2452
+ if (session?.messages.length) replayConversation(session);
2453
+ }, []);
2365
2454
  const handleEvent = useCallback(
2366
2455
  (event) => {
2367
2456
  if (event.type === "delta") {
@@ -2481,6 +2570,18 @@ function App({
2481
2570
  setLive({ text: "" });
2482
2571
  nextSponsor(model2);
2483
2572
  let history = messagesRef.current;
2573
+ const persist = (conversation) => {
2574
+ sessionRef.current.title ??= (task.split("\n")[0] || task).slice(0, 80);
2575
+ saveSession({
2576
+ id: sessionRef.current.id,
2577
+ started: sessionRef.current.started,
2578
+ updated: (/* @__PURE__ */ new Date()).toISOString(),
2579
+ cwd: root,
2580
+ model: model2,
2581
+ title: sessionRef.current.title,
2582
+ messages: conversation
2583
+ });
2584
+ };
2484
2585
  try {
2485
2586
  const window = contextWindow(model2);
2486
2587
  if (shouldCompact([{ role: "system", content: contextRef.current.systemPrompt }, ...history], window)) {
@@ -2546,20 +2647,13 @@ ${preferencesText}` : ""),
2546
2647
  webSearch: searchRef.current
2547
2648
  }),
2548
2649
  signal: ac.signal,
2549
- onEvent: handleEvent
2650
+ onEvent: handleEvent,
2651
+ // Without the system prompt, which is rebuilt per run.
2652
+ onCheckpoint: (conversation) => persist(conversation.slice(1))
2550
2653
  });
2551
2654
  const next = result.messages.slice(1);
2552
2655
  setMessages(next);
2553
- sessionRef.current.title ??= (task.split("\n")[0] || task).slice(0, 80);
2554
- saveSession({
2555
- id: sessionRef.current.id,
2556
- started: sessionRef.current.started,
2557
- updated: (/* @__PURE__ */ new Date()).toISOString(),
2558
- cwd: root,
2559
- model: model2,
2560
- title: sessionRef.current.title,
2561
- messages: next
2562
- });
2656
+ persist(next);
2563
2657
  if (result.stopped === "aborted") {
2564
2658
  if (streamRef.current.streamed) finishStream();
2565
2659
  push({ kind: "notice", tone: "warn", text: " (stopped)" });
@@ -2647,6 +2741,11 @@ ${preferencesText}` : ""),
2647
2741
  }
2648
2742
  } catch (err) {
2649
2743
  if (streamRef.current.streamed) finishStream();
2744
+ const kept = interruptedConversation(err)?.slice(1);
2745
+ if (kept) {
2746
+ setMessages(kept);
2747
+ persist(kept);
2748
+ }
2650
2749
  if ((err instanceof LimitRefusalError || err instanceof PremiumLockedError) && err.spendable) {
2651
2750
  balanceSeqRef.current++;
2652
2751
  takeSpendable(err.spendable.figures, err.spendable.balance);
@@ -2706,6 +2805,9 @@ ${preferencesText}` : ""),
2706
2805
  } else {
2707
2806
  push({ kind: "notice", tone: "error", text: ` error: ${errorNotice(err.message, void 0, " ")}` });
2708
2807
  }
2808
+ if (kept && !(err instanceof PaywallError) && !(err instanceof PremiumLockedError)) {
2809
+ push({ kind: "notice", text: KEPT_AFTER_ERROR });
2810
+ }
2709
2811
  } finally {
2710
2812
  abortRef.current = null;
2711
2813
  dropDelta();
@@ -3334,11 +3436,23 @@ ${preferencesText}` : ""),
3334
3436
  // earlier conversation was to quit the one you were in.
3335
3437
  case "resume":
3336
3438
  case "sessions": {
3337
- const metas = listSessions(root, 10);
3338
- if (!metas.length) {
3339
- push({ kind: "notice", tone: "warn", text: " no saved sessions in this directory yet" });
3439
+ const [verb, target] = arg.split(/\s+/);
3440
+ if (verb === "delete" || verb === "rm") {
3441
+ if (!target) {
3442
+ push({ kind: "notice", tone: "warn", text: " /resume delete <id>, or press d twice on a row in /resume" });
3443
+ return;
3444
+ }
3445
+ if (target === sessionRef.current.id) {
3446
+ push({ kind: "notice", tone: "warn", text: ` ${target} is the conversation you are in. /clear starts a new one first.` });
3447
+ return;
3448
+ }
3449
+ push(
3450
+ deleteSession(target) ? { kind: "notice", tone: "good", text: ` deleted session ${target}` } : { kind: "notice", tone: "error", text: ` no such session: ${target}` }
3451
+ );
3340
3452
  return;
3341
3453
  }
3454
+ const everywhere = verb === "all";
3455
+ const metas = listSessions(everywhere ? void 0 : root, MAX_SESSIONS);
3342
3456
  const load = (id) => {
3343
3457
  const found = loadSession(id);
3344
3458
  if (!found) {
@@ -3347,26 +3461,42 @@ ${preferencesText}` : ""),
3347
3461
  }
3348
3462
  setMessages(found.messages);
3349
3463
  sessionRef.current = { id: found.id, started: found.started, title: found.title };
3464
+ todosRef.current = new TodoList();
3465
+ lastTurnRef.current = null;
3466
+ replayConversation(found);
3467
+ };
3468
+ if (arg && !everywhere) return load(arg);
3469
+ if (!metas.length) {
3470
+ const elsewhere = everywhere ? 0 : listSessions(void 0, MAX_SESSIONS).length;
3350
3471
  push({
3351
3472
  kind: "notice",
3352
- tone: "good",
3353
- text: ` resumed ${found.id} \u2014 ${found.messages.length} messages
3354
- ${found.title}`
3473
+ tone: "warn",
3474
+ text: elsewhere ? ` no saved sessions in this directory yet. ${elsewhere} in other directories: /resume all lists them.` : " no saved sessions yet"
3355
3475
  });
3356
- };
3357
- if (arg) return load(arg);
3476
+ return;
3477
+ }
3358
3478
  const items = metas.map((m) => ({
3359
3479
  value: m.id,
3360
3480
  label: m.updated.slice(0, 16).replace("T", " "),
3361
- hint: m.title.slice(0, 60),
3481
+ hint: `${m.title.slice(0, 60)} \xB7 ${m.messageCount} messages${everywhere ? ` \xB7 ${m.cwd}` : ""}`,
3362
3482
  current: m.id === sessionRef.current.id
3363
3483
  }));
3364
3484
  setPickerSel(0);
3485
+ setPickerArmed(null);
3486
+ setPickerNote(null);
3365
3487
  setPicker({
3366
- title: "Resume a session",
3367
- subtitle: "Replaces the current conversation; nothing on screen is lost.",
3488
+ title: everywhere ? "Resume a session from any directory" : "Resume a session",
3489
+ subtitle: "Replaces the current conversation; nothing on screen is lost. d twice deletes one.",
3368
3490
  items,
3369
- onPick: (choice) => load(choice.value)
3491
+ onPick: (choice) => load(choice.value),
3492
+ onDelete: (choice) => {
3493
+ if (choice.value === sessionRef.current.id) {
3494
+ return "That is the conversation you are in. /clear starts a new one first.";
3495
+ }
3496
+ if (!deleteSession(choice.value)) return "It could not be deleted.";
3497
+ push({ kind: "notice", tone: "good", text: ` deleted session ${choice.value}` });
3498
+ return void 0;
3499
+ }
3370
3500
  });
3371
3501
  return;
3372
3502
  }
@@ -3465,7 +3595,7 @@ ${preferencesText}` : ""),
3465
3595
  push({ kind: "notice", tone: "warn", text: ` unknown command: /${cmd} \u2014 try /help` });
3466
3596
  }
3467
3597
  },
3468
- [client, cols, config, cycleMode, exit, model, modelTaskHints, premium, push, root, runAdWall, runBusy, runBuy, runLogin, runTurn, setPremium, writeToStdout]
3598
+ [client, cols, config, cycleMode, exit, model, modelTaskHints, premium, push, replayConversation, root, runAdWall, runBusy, runBuy, runLogin, runTurn, setPremium, writeToStdout]
3469
3599
  );
3470
3600
  const runShell = useCallback(
3471
3601
  async (command2) => {
@@ -3492,9 +3622,7 @@ ${preferencesText}` : ""),
3492
3622
  if (output) {
3493
3623
  setMessages((prev) => [
3494
3624
  ...prev,
3495
- { role: "user", content: `I ran \`${command2}\` in the workspace myself. Its output:
3496
-
3497
- ${output}` }
3625
+ { role: "user", content: shellNote(command2, output) }
3498
3626
  ]);
3499
3627
  }
3500
3628
  },
@@ -3703,6 +3831,22 @@ ${output}` }
3703
3831
  }
3704
3832
  if (picker) {
3705
3833
  const n = picker.items.length;
3834
+ if (picker.onDelete && (ch === "d" || ch === "D") && !key.ctrl && !key.meta) {
3835
+ const item = picker.items[Math.min(pickerSel, n - 1)];
3836
+ if (!item) return;
3837
+ setPickerNote(null);
3838
+ if (pickerArmed !== item.value) return setPickerArmed(item.value);
3839
+ setPickerArmed(null);
3840
+ const refused = picker.onDelete(item);
3841
+ if (refused) return setPickerNote(refused);
3842
+ const rest = picker.items.filter((i) => i !== item);
3843
+ if (!rest.length) return setPicker(null);
3844
+ setPicker({ ...picker, items: rest });
3845
+ setPickerSel((sel2) => Math.min(sel2, rest.length - 1));
3846
+ return;
3847
+ }
3848
+ if (pickerArmed !== null) setPickerArmed(null);
3849
+ if (pickerNote !== null) setPickerNote(null);
3706
3850
  if (key.escape) {
3707
3851
  const cancel = picker.onCancel;
3708
3852
  setPicker(null);
@@ -3917,16 +4061,17 @@ ${dropped.map((l) => ` \xB7 ${l}`).join("\n")}`
3917
4061
  };
3918
4062
  });
3919
4063
  const pickerTitle = picker ? fitRow(picker.title, inner) : "";
3920
- const pickerSubtitle = picker ? fitRow(picker.subtitle, inner) : "";
4064
+ const pickerSubtitle = picker ? fitRow(pickerArmed !== null ? PICKER_DELETE_CONFIRM : pickerNote ?? picker.subtitle, inner) : "";
4065
+ const pickerDelete = picker?.onDelete ? " \xB7 d delete" : "";
3921
4066
  const pickerHint = picker ? pickHint(
3922
4067
  picker.items.length > pickerItems.length ? [
3923
- `\u2191\u2193 choose (${pickerFrom + 1}-${pickerFrom + pickerItems.length} of ${picker.items.length}) \xB7 1-${pickerItems.length} jump \xB7 \u23CE confirm \xB7 esc cancel`,
3924
- `\u2191\u2193 ${pickerFrom + 1}-${pickerFrom + pickerItems.length}/${picker.items.length} \xB7 \u23CE confirm \xB7 esc cancel`,
3925
- "\u2191\u2193 \xB7 \u23CE \xB7 esc"
4068
+ `\u2191\u2193 choose (${pickerFrom + 1}-${pickerFrom + pickerItems.length} of ${picker.items.length}) \xB7 1-${pickerItems.length} jump \xB7 \u23CE confirm${pickerDelete} \xB7 esc cancel`,
4069
+ `\u2191\u2193 ${pickerFrom + 1}-${pickerFrom + pickerItems.length}/${picker.items.length} \xB7 \u23CE confirm${pickerDelete} \xB7 esc cancel`,
4070
+ picker.onDelete ? "\u2191\u2193 \xB7 \u23CE \xB7 d \xB7 esc" : "\u2191\u2193 \xB7 \u23CE \xB7 esc"
3926
4071
  ] : [
3927
- `\u2191\u2193 choose \xB7 1-${Math.min(9, pickerItems.length)} jump straight to a row \xB7 \u23CE confirm \xB7 esc cancel`,
3928
- `\u2191\u2193 choose \xB7 1-${Math.min(9, pickerItems.length)} jump \xB7 \u23CE confirm \xB7 esc cancel`,
3929
- "\u2191\u2193 \xB7 \u23CE \xB7 esc"
4072
+ `\u2191\u2193 choose \xB7 1-${Math.min(9, pickerItems.length)} jump straight to a row \xB7 \u23CE confirm${pickerDelete} \xB7 esc cancel`,
4073
+ `\u2191\u2193 choose \xB7 1-${Math.min(9, pickerItems.length)} jump \xB7 \u23CE confirm${pickerDelete} \xB7 esc cancel`,
4074
+ picker.onDelete ? "\u2191\u2193 \xB7 \u23CE \xB7 d \xB7 esc" : "\u2191\u2193 \xB7 \u23CE \xB7 esc"
3930
4075
  ],
3931
4076
  inner
3932
4077
  ) : "";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "clixad",
3
- "version": "0.0.1-beta.43",
3
+ "version": "0.0.1-beta.45",
4
4
  "description": "Free AI coding agent in your terminal, funded by rewarded ads.",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",