chatccc 0.2.3 → 0.2.5

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/src/cards.ts CHANGED
@@ -42,35 +42,37 @@ export function truncateContent(text: string, maxLines = 20, maxChars = 8000): s
42
42
  return displayText;
43
43
  }
44
44
 
45
+ const TOOL_EMOJI_MAP: Record<string, string> = {
46
+ Read: "\u{1F4D6}", // 📖
47
+ Write: "\u{270D}\u{FE0F}", // ✍️
48
+ Edit: "\u{270F}\u{FE0F}", // ✏️
49
+ Grep: "\u{1F50E}", // 🔎
50
+ Glob: "\u{1F4C2}", // 📂
51
+ Bash: "\u{1F5A5}\u{FE0F}", // 🖥️
52
+ WebSearch: "\u{1F310}", // 🌐
53
+ WebFetch: "\u{1F4E5}", // 📥
54
+ TodoWrite: "\u{2705}", // ✅
55
+ Agent: "\u{1F916}", // 🤖
56
+ NotebookEdit: "\u{1F4D3}", // 📓
57
+ AskUserQuestion: "\u{2753}",// ❓
58
+ };
59
+
45
60
  export function getToolEmoji(name: string): string {
46
- const n = name.toLowerCase();
47
- if (n.includes("read") || n.includes("cat")) return "\u{1F4D6}"; // 📖
48
- if (n.includes("write")) return "\u{270D}\u{FE0F}"; // ✍️
49
- if (n.includes("edit")) return "\u{270F}\u{FE0F}"; // ✏️
50
- if (n.includes("grep") || n.includes("search")) return "\u{1F50E}"; // 🔎
51
- if (n.includes("glob") || n.includes("find") || n.includes("ls")) return "\u{1F4C2}"; // 📂
52
- if (n.includes("bash") || n.includes("shell") || n.includes("exec")) return "\u{1F5A5}\u{FE0F}"; // 🖥️
53
- if (n.includes("websearch") || n.includes("web_search")) return "\u{1F310}"; // 🌐
54
- if (n.includes("webfetch") || n.includes("web_fetch") || n.includes("fetch")) return "\u{1F4E5}"; // 📥
55
- if (n.includes("todo") || n.includes("task")) return "\u{2705}"; // ✅
56
- if (n.includes("agent")) return "\u{1F916}"; // 🤖
57
- if (n.includes("notebook")) return "\u{1F4D3}"; // 📓
58
- if (n.includes("ask") || n.includes("question")) return "\u{2753}"; // ❓
59
- return "\u{1F527}"; // 🔧
61
+ return TOOL_EMOJI_MAP[name] ?? "\u{1F527}"; // 🔧
60
62
  }
61
63
 
62
64
  // ---------------------------------------------------------------------------
63
65
  // Card builders
64
66
  // ---------------------------------------------------------------------------
65
67
 
66
- // CardKit schema 2.0 思考卡片(带停止按钮,支持打字机流式更新)
67
- export function buildThinkingCardV2(
68
- thinkingText: string,
68
+ // CardKit schema 2.0 进度卡片(带停止按钮,支持流式更新)
69
+ export function buildProgressCard(
70
+ text: string,
69
71
  opts: { showStop?: boolean; headerTitle?: string; headerTemplate?: string } = {}
70
72
  ): string {
71
- const { showStop = true, headerTitle = "思考中...", headerTemplate = "blue" } = opts;
73
+ const { showStop = true, headerTitle = "生成中...", headerTemplate = "blue" } = opts;
72
74
  const elements: object[] = [
73
- { tag: "markdown", content: truncateContent(thinkingText), element_id: "main_content" },
75
+ { tag: "markdown", content: truncateContent(text), element_id: "main_content" },
74
76
  ];
75
77
  if (showStop) {
76
78
  elements.push({ tag: "hr" });
package/src/config.ts CHANGED
@@ -62,6 +62,9 @@ export const CLAUDE_MODEL = process.env.CHATCCC_ANTHROPIC_MODEL?.trim() || "defa
62
62
 
63
63
  export const CLAUDE_EFFORT = process.env.CHATCCC_ANTHROPIC_EFFORT?.trim() || "default";
64
64
 
65
+ /** AI 工具选择:claude | cursor | codex(未设置时默认 claude) */
66
+ export const AI_TOOL = process.env.CHATCCC_AI_TOOL?.trim().toLowerCase() || "claude";
67
+
65
68
  // 新建会话的默认工作路径(/cd 命令设置,持久化到本地文件)
66
69
  // 该路径仅影响通过 /new 新建的 Claude 会话,不影响已有会话的 resume。
67
70
  export const DEFAULT_CWD_FILE = join(PROJECT_ROOT, ".claude", "working_dir.txt");
@@ -224,4 +227,4 @@ export function explainMissingFeishuCredentialsAndExit(): never {
224
227
  process.exit(1);
225
228
  }
226
229
 
227
- export const SESSION_DESC_PREFIX = "Claude Session:";
230
+ export let SESSION_DESC_PREFIX: string = "Claude Session:";
package/src/index.ts CHANGED
@@ -13,7 +13,7 @@
13
13
  * session ID, resumes the session via SDK, sends the user's text, and
14
14
  * streams the response to the session's jsonl file.
15
15
  *
16
- * Buttons: thinking cards have a 停止 button; help messages have /new and /restart buttons.
16
+ * Buttons: progress cards have a 停止 button; help messages have /new and /restart buttons.
17
17
  *
18
18
  * Usage:
19
19
  * npm run dev
@@ -22,7 +22,6 @@
22
22
  */
23
23
 
24
24
  import { spawn } from "node:child_process";
25
- import { getSessionInfo } from "@anthropic-ai/claude-agent-sdk";
26
25
  import { readdir, stat } from "node:fs/promises";
27
26
  import { resolve, dirname, basename } from "node:path";
28
27
 
@@ -67,7 +66,7 @@ import {
67
66
  verifyAllPermissions,
68
67
  reportPermissionResults,
69
68
  } from "./feishu-api.ts";
70
- import { buildHelpCard, buildStatusCard, buildThinkingCardV2, buildCdContent, buildSessionsCard } from "./cards.ts";
69
+ import { buildHelpCard, buildStatusCard, buildProgressCard, buildCdContent, buildSessionsCard } from "./cards.ts";
71
70
  import { updateCardKitCard } from "./cardkit.ts";
72
71
  import {
73
72
  MAX_PROCESSED,
@@ -79,6 +78,8 @@ import {
79
78
  resetState,
80
79
  resumeAndPrompt,
81
80
  sessionInfoMap,
81
+ initAdapter,
82
+ getAdapter,
82
83
  } from "./session.ts";
83
84
 
84
85
  // ---------------------------------------------------------------------------
@@ -190,7 +191,7 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
190
191
  const chatInfo = await getChatInfo(cdToken, chatId);
191
192
  const sid = extractSessionId(chatInfo.description);
192
193
  if (sid) {
193
- const info = await getSessionInfo(sid);
194
+ const info = await getAdapter().getSessionInfo(sid);
194
195
  sessionCwd = info?.cwd;
195
196
  }
196
197
  } catch { /* 非会话群或获取失败,不显示 */ }
@@ -420,9 +421,9 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
420
421
  await new Promise(r => setTimeout(r, 20));
421
422
  }
422
423
  const cardId = existing.cardId;
423
- const thinking = existing.accumulatedThinking;
424
- const interruptedCard = buildThinkingCardV2(
425
- thinking || "新问题已提交,当前回复已中断。",
424
+ const currentContent = existing.accumulatedContent;
425
+ const interruptedCard = buildProgressCard(
426
+ currentContent || "新问题已提交,当前回复已中断。",
426
427
  { showStop: false, headerTitle: "已中断", headerTemplate: "yellow" }
427
428
  );
428
429
  let nextSeq = existing.sequence + 1;
@@ -648,6 +649,7 @@ async function main(): Promise<void> {
648
649
  });
649
650
 
650
651
  if (USE_LOCAL) {
652
+ initAdapter();
651
653
  console.log(`\n[启动 6/7] 本地区 relay 模式:正在连接 ${LOCAL_RELAY_URL} …`);
652
654
  console.log(" 若失败:请先在 SDK 模式下启动主进程,或确认本机中继已在该地址监听。");
653
655
  let localRelayOpened = false;
@@ -693,12 +695,13 @@ async function main(): Promise<void> {
693
695
  });
694
696
  } else {
695
697
  resetState();
698
+ initAdapter();
696
699
 
697
700
  const wsClient = new WSClient({
698
701
  appId: APP_ID,
699
702
  appSecret: APP_SECRET,
700
- onReady: () => resetState(),
701
- onReconnected: () => resetState(),
703
+ onReady: () => { resetState(); initAdapter(); },
704
+ onReconnected: () => { resetState(); initAdapter(); },
702
705
  });
703
706
 
704
707
  console.log(`\n[启动 6/7] 飞书长连接:正在通过 SDK 建立 WebSocket …`);