larkcc 0.12.4 → 0.12.6

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/CHANGELOG.md CHANGED
@@ -7,6 +7,30 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.12.6] - 2026-05-09
11
+
12
+ ### Fixed
13
+
14
+ - Fix `Claude Code native binary not found` error on Windows: cross-platform claude binary detection (`where` on Windows, `which` on POSIX)
15
+ - Add `claude.path` config field for manual override of Claude Code binary location
16
+ - Fix `ensureEnv()` and `ensureClaudeInPath()` for Windows: skip bash PATH injection, use Windows common paths (`APPDATA/npm`), correct PATH separator (`;` vs `:`)
17
+
18
+ ## [0.12.5] - 2026-05-09
19
+
20
+ ### Changed
21
+
22
+ - Sub-agent card: swap header title/subtitle (description as title, "Sub Agent" as subtitle), add header tags (elapsed time + tokens)
23
+ - Sub-agent card: display tool call sequence with arrow display; terminal state merges consecutive duplicates with counts
24
+ - Replace sub-agent footer column_set with header text_tag_list, removing duplicate info between header and footer
25
+ - Accumulate tool call history from progress events in TaskPanelController
26
+ - Logger timestamp color: `chalk.gray` → `chalk.blue`
27
+
28
+ ### Fixed
29
+
30
+ - Tool result truncation: remove 500-char early truncation in agent.ts (both CardKit and non-CardKit paths), let `buildToolResultPanel` handle it uniformly at 2000 chars
31
+ - Extract `STREAMING_TRUNCATE` (4000) and `TASK_SUMMARY_TRUNCATE` (3000) as shared constants, eliminating duplicated `TRUNCATE_LIMIT` in cardkit.ts and streaming.ts
32
+ - Remove unused `truncate()` helper and `THINKING_OVERFLOW_TRUNCATE` import in cardkit.ts
33
+
10
34
  ## [0.12.4] - 2026-05-09
11
35
 
12
36
  ### Changed
package/dist/index.js CHANGED
@@ -6665,7 +6665,8 @@ function loadConfig(cwd2, profile2) {
6665
6665
  claude: {
6666
6666
  permission_mode: claude.permission_mode ?? "acceptEdits",
6667
6667
  allowed_tools: claude.allowed_tools ?? DEFAULT_TOOLS,
6668
- thinking: claude.thinking ?? { type: "adaptive" }
6668
+ thinking: claude.thinking ?? { type: "adaptive" },
6669
+ path: claude.path
6669
6670
  },
6670
6671
  overflow: {
6671
6672
  mode: overflow.mode ?? DEFAULT_OVERFLOW.mode,
@@ -6868,7 +6869,7 @@ var init_logger = __esm({
6868
6869
  "src/logger.ts"() {
6869
6870
  "use strict";
6870
6871
  init_source();
6871
- ts = () => source_default.gray((/* @__PURE__ */ new Date()).toLocaleTimeString());
6872
+ ts = () => source_default.blue((/* @__PURE__ */ new Date()).toLocaleTimeString());
6872
6873
  logger = {
6873
6874
  info: (msg) => console.log(`${ts()} ${source_default.cyan("\u2139")} ${msg}`),
6874
6875
  success: (msg) => console.log(`${ts()} ${source_default.green("\u2705")} ${msg}`),
@@ -135962,13 +135963,15 @@ function buildToolPanels(results) {
135962
135963
  if (results.length === 0) return [];
135963
135964
  return results.map((r2) => buildToolResultPanel(r2));
135964
135965
  }
135965
- var THINKING_OVERFLOW_TRUNCATE, TOOL_RESULT_TRUNCATE, EXT_LANG_MAP;
135966
+ var THINKING_OVERFLOW_TRUNCATE, TOOL_RESULT_TRUNCATE, STREAMING_TRUNCATE, TASK_SUMMARY_TRUNCATE, EXT_LANG_MAP;
135966
135967
  var init_duration = __esm({
135967
135968
  "src/format/duration.ts"() {
135968
135969
  "use strict";
135969
135970
  init_card_optimize();
135970
135971
  THINKING_OVERFLOW_TRUNCATE = 3e3;
135971
135972
  TOOL_RESULT_TRUNCATE = 2e3;
135973
+ STREAMING_TRUNCATE = 4e3;
135974
+ TASK_SUMMARY_TRUNCATE = 3e3;
135972
135975
  EXT_LANG_MAP = {
135973
135976
  ts: "typescript",
135974
135977
  tsx: "typescript",
@@ -137823,56 +137826,57 @@ var init_download = __esm({
137823
137826
  function fmtDur(sec) {
137824
137827
  return sec < 60 ? `${sec.toFixed(0)}s` : `${Math.floor(sec / 60)}m ${Math.round(sec % 60)}s`;
137825
137828
  }
137829
+ function formatToolSequence(toolsUsed, isTerminal) {
137830
+ if (toolsUsed.length === 0) return "";
137831
+ if (!isTerminal) {
137832
+ return toolsUsed.map((t) => TOOL_DISPLAY[t] ?? `\u{1F527} ${t}`).join(" \u2192 ");
137833
+ }
137834
+ const merged = [];
137835
+ let current = toolsUsed[0];
137836
+ let count = 1;
137837
+ for (let i2 = 1; i2 < toolsUsed.length; i2++) {
137838
+ if (toolsUsed[i2] === current) {
137839
+ count++;
137840
+ } else {
137841
+ const label = TOOL_DISPLAY[current] ?? `\u{1F527} ${current}`;
137842
+ merged.push(count > 1 ? `${label} \xD7${count}` : label);
137843
+ current = toolsUsed[i2];
137844
+ count = 1;
137845
+ }
137846
+ }
137847
+ const lastLabel = TOOL_DISPLAY[current] ?? `\u{1F527} ${current}`;
137848
+ merged.push(count > 1 ? `${lastLabel} \xD7${count}` : lastLabel);
137849
+ return merged.join(" \u2192 ");
137850
+ }
137826
137851
  function buildTaskPanelCard(options) {
137827
- const { description, status, summary, lastToolName, elapsedSeconds, tokens, headerIconImgKey } = options;
137852
+ const { description, status, summary, lastToolName, toolsUsed, elapsedSeconds, tokens, headerIconImgKey } = options;
137828
137853
  const st = STATUS_TEMPLATE[status];
137854
+ const isTerminal = status !== "running";
137829
137855
  const elements = [];
137830
137856
  const statusParts = [`**${st.icon} ${st.label}**`];
137831
- if (lastToolName && status === "running") statusParts.push(`Tool: \`${lastToolName}\``);
137857
+ if (lastToolName && status === "running") statusParts.push(`\`${lastToolName}\``);
137832
137858
  elements.push({ tag: "markdown", content: statusParts.join(" \xB7 ") });
137833
- const hasContent = summary && summary !== "Done" && summary !== "Aborted";
137834
- if (hasContent) {
137835
- elements.push({ tag: "markdown", content: summary.length > 3e3 ? summary.slice(0, 3e3) + "..." : summary });
137859
+ if (toolsUsed && toolsUsed.length > 0) {
137860
+ elements.push({ tag: "markdown", content: formatToolSequence(toolsUsed, isTerminal), text_size: "notation" });
137861
+ }
137862
+ if (summary && summary !== "Done" && summary !== "Aborted") {
137863
+ elements.push({ tag: "markdown", content: summary.length > TASK_SUMMARY_TRUNCATE ? summary.slice(0, TASK_SUMMARY_TRUNCATE) + "..." : summary });
137836
137864
  } else if (status === "running") {
137837
137865
  elements.push({ tag: "markdown", content: "Processing..." });
137838
137866
  }
137839
- const footerColumns = [];
137840
- if (elapsedSeconds != null) {
137841
- footerColumns.push({
137842
- tag: "column",
137843
- width: "weighted",
137844
- weight: 1,
137845
- vertical_align: "center",
137846
- elements: [{ tag: "markdown", content: `<font color='grey'>\u23F1 ${fmtDur(elapsedSeconds)}</font>`, text_size: "notation" }]
137847
- });
137848
- }
137849
- if (status !== "running" && tokens != null) {
137850
- footerColumns.push({
137851
- tag: "column",
137852
- width: "weighted",
137853
- weight: 1,
137854
- vertical_align: "center",
137855
- elements: [{ tag: "markdown", content: `<font color='grey'>\u{1FA99} ${tokens.toLocaleString()} tokens</font>`, text_size: "notation" }]
137856
- });
137857
- }
137858
- if (footerColumns.length > 0) {
137859
- elements.push({
137860
- tag: "column_set",
137861
- flex_mode: "none",
137862
- background_style: "default",
137863
- columns: footerColumns
137864
- });
137865
- }
137866
- const icon = headerIconImgKey ? { tag: "custom_icon", img_key: headerIconImgKey } : { tag: "standard_icon", token: "larkcommunity_colorful" };
137867
+ const tags = [];
137868
+ if (elapsedSeconds != null) tags.push({ text: fmtDur(elapsedSeconds), color: "turquoise" });
137869
+ if (isTerminal && tokens != null) tags.push({ text: `${tokens.toLocaleString()} tokens`, color: "blue" });
137867
137870
  return {
137868
137871
  schema: "2.0",
137869
137872
  config: { wide_screen_mode: true },
137870
- header: {
137871
- title: { tag: "plain_text", content: "\u{1F916} Sub Agent" },
137872
- subtitle: { tag: "plain_text", content: description },
137873
+ header: buildHeader({
137874
+ title: description,
137875
+ subtitle: "\u{1F916} Sub Agent",
137873
137876
  template: st.color,
137874
- icon
137875
- },
137877
+ iconImgKey: headerIconImgKey,
137878
+ tags
137879
+ }),
137876
137880
  body: { elements }
137877
137881
  };
137878
137882
  }
@@ -137891,16 +137895,27 @@ async function updateTaskCard(client, msgId, options) {
137891
137895
  data: { content: JSON.stringify(card) }
137892
137896
  });
137893
137897
  }
137894
- var STATUS_TEMPLATE;
137898
+ var STATUS_TEMPLATE, TOOL_DISPLAY;
137895
137899
  var init_task_panel = __esm({
137896
137900
  "src/feishu/task-panel.ts"() {
137897
137901
  "use strict";
137902
+ init_card();
137903
+ init_duration();
137898
137904
  STATUS_TEMPLATE = {
137899
137905
  running: { color: "turquoise", icon: "\u{1F504}", label: "Running" },
137900
137906
  completed: { color: "green", icon: "\u2705", label: "Completed" },
137901
137907
  failed: { color: "red", icon: "\u274C", label: "Failed" },
137902
137908
  stopped: { color: "grey", icon: "\u23F9", label: "Stopped" }
137903
137909
  };
137910
+ TOOL_DISPLAY = {
137911
+ Read: "\u{1F4C2} Read",
137912
+ Write: "\u{1F4DD} Write",
137913
+ Edit: "\u270F\uFE0F Edit",
137914
+ Bash: "\u26A1 Bash",
137915
+ Glob: "\u{1F4C2} Glob",
137916
+ Grep: "\u{1F50D} Grep",
137917
+ LS: "\u{1F4C1} LS"
137918
+ };
137904
137919
  }
137905
137920
  });
137906
137921
 
@@ -137980,7 +137995,7 @@ var VERSION3;
137980
137995
  var init_version = __esm({
137981
137996
  "src/version.ts"() {
137982
137997
  "use strict";
137983
- VERSION3 = "0.12.4";
137998
+ VERSION3 = "0.12.6";
137984
137999
  }
137985
138000
  });
137986
138001
 
@@ -154740,12 +154755,13 @@ var init_streaming = __esm({
154740
154755
  "src/streaming.ts"() {
154741
154756
  "use strict";
154742
154757
  init_card_optimize();
154758
+ init_duration();
154743
154759
  init_thinking();
154744
154760
  init_feishu();
154745
154761
  LONG_GAP_MS = 2e3;
154746
154762
  GAP_CHECK_INTERVAL = 500;
154747
154763
  HEARTBEAT_MS = 15e3;
154748
- TRUNCATE_LIMIT = 4e3;
154764
+ TRUNCATE_LIMIT = STREAMING_TRUNCATE;
154749
154765
  FlushController = class {
154750
154766
  minIntervalMs;
154751
154767
  onFlush;
@@ -155026,7 +155042,7 @@ var init_cardkit = __esm({
155026
155042
  init_feishu();
155027
155043
  init_streaming();
155028
155044
  init_format();
155029
- TRUNCATE_LIMIT2 = 4e3;
155045
+ TRUNCATE_LIMIT2 = STREAMING_TRUNCATE;
155030
155046
  CardKitApiError = class extends Error {
155031
155047
  code;
155032
155048
  constructor(code, msg, context) {
@@ -155529,7 +155545,8 @@ var init_task_panel2 = __esm({
155529
155545
  msgId,
155530
155546
  description: event.description,
155531
155547
  status: "running",
155532
- startTime: Date.now()
155548
+ startTime: Date.now(),
155549
+ toolsUsed: []
155533
155550
  });
155534
155551
  logger.dim(`[task-panel] started: ${event.description.slice(0, 40)}`);
155535
155552
  } catch (err) {
@@ -155541,7 +155558,12 @@ var init_task_panel2 = __esm({
155541
155558
  const task = this.tasks.get(event.task_id);
155542
155559
  if (!task || task.status !== "running") return;
155543
155560
  if (event.summary) task.summary = event.summary;
155544
- if (event.last_tool_name) task.lastToolName = event.last_tool_name;
155561
+ if (event.last_tool_name) {
155562
+ task.lastToolName = event.last_tool_name;
155563
+ if (task.toolsUsed[task.toolsUsed.length - 1] !== event.last_tool_name) {
155564
+ task.toolsUsed.push(event.last_tool_name);
155565
+ }
155566
+ }
155545
155567
  if (event.usage) {
155546
155568
  task.tokens = event.usage.total_tokens;
155547
155569
  }
@@ -155551,6 +155573,7 @@ var init_task_panel2 = __esm({
155551
155573
  status: "running",
155552
155574
  summary: task.summary,
155553
155575
  lastToolName: task.lastToolName,
155576
+ toolsUsed: task.toolsUsed,
155554
155577
  elapsedSeconds: (Date.now() - task.startTime) / 1e3,
155555
155578
  tokens: task.tokens,
155556
155579
  headerIconImgKey: this.headerIconImgKey
@@ -155572,6 +155595,7 @@ var init_task_panel2 = __esm({
155572
155595
  description: task.description,
155573
155596
  status: event.status,
155574
155597
  summary: event.summary,
155598
+ toolsUsed: task.toolsUsed,
155575
155599
  elapsedSeconds,
155576
155600
  tokens: task.tokens,
155577
155601
  headerIconImgKey: this.headerIconImgKey
@@ -155591,6 +155615,7 @@ var init_task_panel2 = __esm({
155591
155615
  description: task.description,
155592
155616
  status: "stopped",
155593
155617
  summary: task.summary ?? "Aborted",
155618
+ toolsUsed: task.toolsUsed,
155594
155619
  elapsedSeconds: (Date.now() - task.startTime) / 1e3,
155595
155620
  tokens: task.tokens
155596
155621
  });
@@ -155609,6 +155634,7 @@ var init_task_panel2 = __esm({
155609
155634
  description: task.description,
155610
155635
  status: "completed",
155611
155636
  summary: task.summary ?? "Done",
155637
+ toolsUsed: task.toolsUsed,
155612
155638
  elapsedSeconds: (Date.now() - task.startTime) / 1e3,
155613
155639
  tokens: task.tokens
155614
155640
  });
@@ -155640,6 +155666,15 @@ var init_task_panel2 = __esm({
155640
155666
 
155641
155667
  // src/agent.ts
155642
155668
  import { execSync } from "child_process";
155669
+ function findClaudeBinary() {
155670
+ const cmd = process.platform === "win32" ? "where claude 2>nul" : "which claude 2>/dev/null || command -v claude 2>/dev/null";
155671
+ try {
155672
+ const result = execSync(cmd, { encoding: "utf8", timeout: 5e3 }).trim();
155673
+ if (result) return result.split(/[\r\n]/)[0];
155674
+ } catch {
155675
+ }
155676
+ return void 0;
155677
+ }
155643
155678
  function formatInput(name, input) {
155644
155679
  if (["Read", "Write", "Edit"].includes(name))
155645
155680
  return String(input.file_path ?? input.path ?? "");
@@ -155649,9 +155684,6 @@ function formatInput(name, input) {
155649
155684
  if (name === "Glob") return String(input.pattern ?? "");
155650
155685
  return JSON.stringify(input).slice(0, 100);
155651
155686
  }
155652
- function truncate(str2, len) {
155653
- return str2.length > len ? str2.slice(0, len) + "..." : str2;
155654
- }
155655
155687
  function buildReplyContext(config2, profile2, cwd2, chatId, rootMsgId) {
155656
155688
  return {
155657
155689
  profile: profile2 ?? "default",
@@ -155754,7 +155786,7 @@ async function runAgent(prompt2, cwd2, config2, client, chatId, rootMsgId, image
155754
155786
  thinking: config2.claude.thinking,
155755
155787
  abortController,
155756
155788
  agentProgressSummaries: true,
155757
- pathToClaudeCodeExecutable: execSync("which claude 2>/dev/null || echo ''", { encoding: "utf8" }).trim() || void 0,
155789
+ pathToClaudeCodeExecutable: config2.claude.path || findClaudeBinary(),
155758
155790
  ...systemPrompt ? { systemPrompt: { type: "preset", preset: "claude_code", append: systemPrompt } } : {}
155759
155791
  }
155760
155792
  })) {
@@ -155809,13 +155841,13 @@ async function runAgent(prompt2, cwd2, config2, client, chatId, rootMsgId, image
155809
155841
  await cardkitCtrl?.clearStatus();
155810
155842
  const raw = typeof block.content === "string" ? block.content : JSON.stringify(block.content ?? "");
155811
155843
  const toolEntry = cardkitToolResults.find((t) => t.id === block.tool_use_id);
155812
- if (toolEntry) toolEntry.resultPreview = truncate(raw, 500);
155844
+ if (toolEntry) toolEntry.resultPreview = raw;
155813
155845
  break;
155814
155846
  }
155815
155847
  const toolInfo = toolMsgMap.get(block.tool_use_id);
155816
155848
  if (toolInfo) {
155817
155849
  const raw = typeof block.content === "string" ? block.content : JSON.stringify(block.content ?? "");
155818
- await updateToolCard(client, toolInfo.msgId, toolInfo.label, toolInfo.detail, truncate(raw, 500));
155850
+ await updateToolCard(client, toolInfo.msgId, toolInfo.label, toolInfo.detail, raw.length > TOOL_RESULT_TRUNCATE ? truncateSafely(raw, TOOL_RESULT_TRUNCATE) : raw);
155819
155851
  }
155820
155852
  }
155821
155853
  }
@@ -155945,6 +155977,7 @@ var init_agent = __esm({
155945
155977
  init_guide();
155946
155978
  init_thinking();
155947
155979
  init_duration();
155980
+ init_card_optimize();
155948
155981
  init_image_resolver();
155949
155982
  init_streaming();
155950
155983
  init_cardkit();
@@ -156931,6 +156964,7 @@ function ensureClaudeOnboarding() {
156931
156964
  }
156932
156965
  function ensureEnv() {
156933
156966
  if (!process.env.HOME) process.env.HOME = os8.homedir();
156967
+ if (IS_WIN) return;
156934
156968
  try {
156935
156969
  const shellPath = execSync3("bash -lc 'echo $PATH' 2>/dev/null", { timeout: 3e3 }).toString().trim();
156936
156970
  if (shellPath) process.env.PATH = shellPath;
@@ -156938,7 +156972,10 @@ function ensureEnv() {
156938
156972
  }
156939
156973
  }
156940
156974
  function ensureClaudeInPath() {
156941
- const commonPaths = [
156975
+ const commonPaths = IS_WIN ? [
156976
+ path7.join(process.env.APPDATA ?? "", "npm"),
156977
+ path7.join(process.env.LOCALAPPDATA ?? "", "Programs", "claude")
156978
+ ] : [
156942
156979
  "/usr/local/bin",
156943
156980
  "/usr/bin",
156944
156981
  `${os8.homedir()}/.npm-global/bin`,
@@ -156946,23 +156983,29 @@ function ensureClaudeInPath() {
156946
156983
  "/opt/homebrew/bin",
156947
156984
  "/home/linuxbrew/.linuxbrew/bin"
156948
156985
  ];
156986
+ const findCmd = IS_WIN ? "where claude 2>nul" : "which claude 2>/dev/null || command -v claude 2>/dev/null";
156987
+ const shellOpt = IS_WIN ? {} : { shell: "/bin/bash" };
156949
156988
  try {
156950
- const claudePath = execSync3("which claude 2>/dev/null || command -v claude 2>/dev/null", {
156951
- shell: "/bin/bash",
156952
- env: { ...process.env, PATH: [...commonPaths, process.env.PATH ?? ""].join(":") }
156953
- }).toString().trim();
156989
+ const claudePath = execSync3(findCmd, {
156990
+ ...shellOpt,
156991
+ encoding: "utf8",
156992
+ timeout: 5e3,
156993
+ env: { ...process.env, PATH: [...commonPaths, process.env.PATH ?? ""].join(PATH_SEP) }
156994
+ }).trim();
156954
156995
  if (claudePath) {
156955
- const dir = path7.dirname(claudePath);
156956
- if (!process.env.PATH?.includes(dir)) process.env.PATH = `${dir}:${process.env.PATH}`;
156957
- logger.dim(`claude found: ${claudePath}`);
156996
+ const resolved = claudePath.split(/[\r\n]/)[0];
156997
+ const dir = path7.dirname(resolved);
156998
+ if (!process.env.PATH?.includes(dir)) process.env.PATH = `${dir}${PATH_SEP}${process.env.PATH}`;
156999
+ logger.dim(`claude found: ${resolved}`);
156958
157000
  return;
156959
157001
  }
156960
157002
  } catch {
156961
157003
  }
156962
157004
  for (const dir of commonPaths) {
156963
- if (fs8.existsSync(path7.join(dir, "claude"))) {
156964
- if (!process.env.PATH?.includes(dir)) process.env.PATH = `${dir}:${process.env.PATH}`;
156965
- logger.dim(`claude found: ${dir}/claude`);
157005
+ const binName = IS_WIN ? "claude.cmd" : "claude";
157006
+ if (fs8.existsSync(path7.join(dir, binName)) || fs8.existsSync(path7.join(dir, "claude"))) {
157007
+ if (!process.env.PATH?.includes(dir)) process.env.PATH = `${dir}${PATH_SEP}${process.env.PATH}`;
157008
+ logger.dim(`claude found: ${dir}`);
156966
157009
  return;
156967
157010
  }
156968
157011
  }
@@ -157093,7 +157136,7 @@ async function startApp(cwd2, config2, profile2, continueSession = false, force
157093
157136
  process.on("SIGINT", shutdown);
157094
157137
  process.on("SIGTERM", shutdown);
157095
157138
  }
157096
- var CLAUDE_SETTINGS_PATH, LOCK_DIR;
157139
+ var CLAUDE_SETTINGS_PATH, LOCK_DIR, IS_WIN, PATH_SEP;
157097
157140
  var init_app = __esm({
157098
157141
  "src/app.ts"() {
157099
157142
  "use strict";
@@ -157107,6 +157150,8 @@ var init_app = __esm({
157107
157150
  init_message_handler();
157108
157151
  CLAUDE_SETTINGS_PATH = path7.join(os8.homedir(), ".claude", "settings.json");
157109
157152
  LOCK_DIR = path7.join(os8.homedir(), ".larkcc");
157153
+ IS_WIN = process.platform === "win32";
157154
+ PATH_SEP = IS_WIN ? ";" : ":";
157110
157155
  }
157111
157156
  });
157112
157157