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/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**Session ID:** ${sessionId}\n**工作目录:** \`${cwd}\`\n\n直接在这里发消息即可与 ${toolLabel} 对话。\n\n发送 **/sessions** 查看所有会话状态。`,
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) {