chatccc 0.2.6 → 0.2.7
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/README.md +50 -11
- package/package.json +1 -1
- package/src/__tests__/adapter-interface.test.ts +151 -151
- package/src/__tests__/cards.test.ts +417 -413
- package/src/__tests__/claude-adapter.test.ts +528 -528
- package/src/__tests__/config.test.ts +123 -0
- package/src/__tests__/cursor-adapter.test.ts +662 -249
- package/src/__tests__/cursor-session-meta-store.test.ts +212 -0
- package/src/__tests__/fixtures/cursor_partial_only.jsonl +5 -0
- package/src/__tests__/fixtures/cursor_partial_with_final.jsonl +13 -0
- package/src/__tests__/fixtures/cursor_with_tool_call.jsonl +12 -0
- package/src/__tests__/git-command.test.ts +288 -0
- package/src/__tests__/session.test.ts +475 -296
- package/src/adapters/adapter-interface.ts +151 -126
- package/src/adapters/claude-adapter.ts +257 -257
- package/src/adapters/cursor-adapter.ts +401 -228
- package/src/adapters/cursor-session-meta-store.ts +154 -0
- package/src/cards.ts +33 -21
- package/src/config.ts +92 -2
- package/src/feishu-api.ts +1 -1
- package/src/git-command.ts +202 -0
- package/src/index.ts +58 -4
- package/src/session.ts +620 -513
package/src/index.ts
CHANGED
|
@@ -36,6 +36,7 @@ import {
|
|
|
36
36
|
BASE_URL,
|
|
37
37
|
CLAUDE_EFFORT,
|
|
38
38
|
CLAUDE_MODEL,
|
|
39
|
+
GIT_TIMEOUT_MS,
|
|
39
40
|
anthropicConfigDisplay,
|
|
40
41
|
LOCAL_RELAY_URL,
|
|
41
42
|
PID_FILE,
|
|
@@ -72,6 +73,7 @@ import {
|
|
|
72
73
|
} from "./feishu-api.ts";
|
|
73
74
|
import { buildHelpCard, buildStatusCard, buildProgressCard, buildCdContent, buildCdCard, buildSessionsCard } from "./cards.ts";
|
|
74
75
|
import { updateCardKitCard } from "./cardkit.ts";
|
|
76
|
+
import { formatGitResult, gitResultHeaderTemplate, runGitCommand } from "./git-command.ts";
|
|
75
77
|
import {
|
|
76
78
|
MAX_PROCESSED,
|
|
77
79
|
chatSessionMap,
|
|
@@ -371,7 +373,12 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
371
373
|
const adapter = getAdapterForTool(tool);
|
|
372
374
|
await sendCardReply(
|
|
373
375
|
freshToken, newChatId, `${toolLabel} Session Ready`,
|
|
374
|
-
`群聊已创建,这是你的 **${toolLabel}** 会话群。\n\n
|
|
376
|
+
`群聊已创建,这是你的 **${toolLabel}** 会话群。\n\n` +
|
|
377
|
+
`**Session ID:** ${sessionId}\n` +
|
|
378
|
+
`**工作目录:** \`${cwd}\`\n\n` +
|
|
379
|
+
`直接在这里发消息即可与 ${toolLabel} 对话。\n\n` +
|
|
380
|
+
`发送 **/sessions** 查看所有会话状态。\n` +
|
|
381
|
+
`发送 \`/git <子命令>\` 在本会话工作目录执行 git,例如 \`/git status\`、\`/git log --oneline -n 5\`。`,
|
|
375
382
|
"green"
|
|
376
383
|
);
|
|
377
384
|
|
|
@@ -421,7 +428,7 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
421
428
|
}
|
|
422
429
|
|
|
423
430
|
if (text === "/status") {
|
|
424
|
-
const status = getSessionStatus(chatId);
|
|
431
|
+
const status = await getSessionStatus(chatId);
|
|
425
432
|
const running = chatSessionMap.get(chatId);
|
|
426
433
|
const isActive = running && !running.stopped;
|
|
427
434
|
const statusText = [
|
|
@@ -430,8 +437,12 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
430
437
|
`**状态:** ${isActive ? "🟢 运行中" : "⚪ 空闲"}`,
|
|
431
438
|
`**已对话轮数:** ${status?.turnCount ?? 0}`,
|
|
432
439
|
`**模型:** ${status?.model ?? anthropicConfigDisplay(CLAUDE_MODEL)}`,
|
|
433
|
-
`**Effort:** ${status?.effort ?? anthropicConfigDisplay(CLAUDE_EFFORT)}`,
|
|
434
440
|
];
|
|
441
|
+
// effort 仅在该工具有此概念时显示(status?.effort 为 null 表示
|
|
442
|
+
// 当前工具没有 effort,如 Cursor,应隐藏整行避免误导)
|
|
443
|
+
if (status?.effort != null) {
|
|
444
|
+
statusText.push(`**Effort:** ${status.effort}`);
|
|
445
|
+
}
|
|
435
446
|
if (isActive) {
|
|
436
447
|
const elapsed = Math.floor((Date.now() - (status!.startTime)) / 1000);
|
|
437
448
|
const mins = Math.floor(elapsed / 60);
|
|
@@ -457,7 +468,7 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
457
468
|
}
|
|
458
469
|
|
|
459
470
|
if (text === "/sessions") {
|
|
460
|
-
const allSessions = getAllSessionsStatus();
|
|
471
|
+
const allSessions = await getAllSessionsStatus();
|
|
461
472
|
const now = Date.now();
|
|
462
473
|
const cardData = allSessions.map(s => ({
|
|
463
474
|
sessionId: s.sessionId,
|
|
@@ -481,6 +492,49 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
|
|
|
481
492
|
return;
|
|
482
493
|
}
|
|
483
494
|
|
|
495
|
+
// /git <args>:在「当前会话工作目录」执行 git 命令,把输出回发到群里。
|
|
496
|
+
// 注意 cwd 必须取自 adapter.getSessionInfo(会话真实 cwd),而非
|
|
497
|
+
// getDefaultCwd(下一次 /new 才会使用的默认路径)。
|
|
498
|
+
if (text.startsWith("/git ") || text === "/git") {
|
|
499
|
+
const args = text === "/git" ? "" : text.slice(5).trim();
|
|
500
|
+
if (!args) {
|
|
501
|
+
await sendCardReply(
|
|
502
|
+
freshToken, chatId, "/git",
|
|
503
|
+
"用法:`/git <子命令> [参数]`,例如 `/git status`、`/git log --oneline -n 5`。",
|
|
504
|
+
"yellow"
|
|
505
|
+
);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
const adapter = getAdapterForTool(descriptionTool);
|
|
510
|
+
let cwd: string | undefined;
|
|
511
|
+
try {
|
|
512
|
+
const info = await adapter.getSessionInfo(sessionId);
|
|
513
|
+
cwd = info?.cwd;
|
|
514
|
+
} catch (err) {
|
|
515
|
+
console.error(`[${ts()}] [GIT] getSessionInfo FAIL: ${(err as Error).message}`);
|
|
516
|
+
}
|
|
517
|
+
if (!cwd) {
|
|
518
|
+
// Cursor 会话的 cwd 依赖 .claude/cursor-session-cwd.json 持久化映射;
|
|
519
|
+
// 升级前创建的旧会话或映射文件丢失时,向会话发送一次普通消息即可触发
|
|
520
|
+
// adapter 自动学习并补全(resume 流首条 init 事件携带 cwd)。
|
|
521
|
+
const isCursor = descriptionTool === "cursor";
|
|
522
|
+
const hint = isCursor
|
|
523
|
+
? "无法获取当前 Cursor 会话的工作目录(缺少 sessionId→cwd 持久化映射)。请先在本群发送一条普通消息(让 adapter 从 cursor-agent 流中自动补回 cwd),然后再试 /git;若仍失败,可用 /new 重建会话。"
|
|
524
|
+
: `无法获取当前会话的工作目录(${toolLabel} adapter 未返回 cwd)。请先与 AI 对话一次再试,或检查会话是否仍存在。`;
|
|
525
|
+
await sendCardReply(freshToken, chatId, "/git", hint, "red");
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
console.log(`[${ts()}] [GIT] chat=${chatId} cwd=${cwd} cmd="git ${args}" timeoutMs=${GIT_TIMEOUT_MS}`);
|
|
530
|
+
const result = await runGitCommand(args, cwd, { timeoutMs: GIT_TIMEOUT_MS });
|
|
531
|
+
console.log(`[${ts()}] [GIT] exitCode=${result.exitCode}, durationMs=${result.durationMs}, truncated=${result.truncated}, timedOut=${result.timedOut}`);
|
|
532
|
+
const content = formatGitResult(args, cwd, result);
|
|
533
|
+
const template = gitResultHeaderTemplate(result);
|
|
534
|
+
await sendCardReply(freshToken, chatId, "/git 输出", content, template);
|
|
535
|
+
return;
|
|
536
|
+
}
|
|
537
|
+
|
|
484
538
|
const existing = chatSessionMap.get(chatId);
|
|
485
539
|
if (existing && !existing.stopped) {
|
|
486
540
|
if (msgTimestamp <= existing.msgTimestamp) {
|