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/package.json +6 -3
- package/src/__tests__/adapter-interface.test.ts +152 -0
- package/src/__tests__/cards.test.ts +297 -0
- package/src/__tests__/claude-adapter.test.ts +529 -0
- package/src/__tests__/session.test.ts +296 -0
- package/src/adapters/adapter-interface.ts +127 -0
- package/src/adapters/claude-adapter.ts +257 -0
- package/src/cards.ts +21 -19
- package/src/config.ts +4 -1
- package/src/index.ts +12 -9
- package/src/session.ts +450 -455
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
|
-
|
|
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
|
|
68
|
-
|
|
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 = "
|
|
73
|
+
const { showStop = true, headerTitle = "生成中...", headerTemplate = "blue" } = opts;
|
|
72
74
|
const elements: object[] = [
|
|
73
|
-
{ tag: "markdown", content: truncateContent(
|
|
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
|
|
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:
|
|
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,
|
|
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
|
|
424
|
-
const interruptedCard =
|
|
425
|
-
|
|
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 …`);
|