chatccc 0.2.43 → 0.2.45

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
@@ -232,7 +232,7 @@ function parseCardAction(data: unknown): CardActionResult | null {
232
232
  }
233
233
  if (!cmd) return null;
234
234
 
235
- const CMD_MAP: Record<string, string> = { stop: "/stop", new: "/new", "new claude": "/new claude", "new cursor": "/new cursor", "new codex": "/new codex", restart: "/restart", status: "/status", cd: "/cd", sessions: "/sessions", forget: "/forget" };
235
+ const CMD_MAP: Record<string, string> = { stop: "/stop", new: "/new", "new claude": "/new claude", "new cursor": "/new cursor", "new codex": "/new codex", restart: "/restart", status: "/status", cd: "/cd", sessions: "/sessions", newh: "/newh" };
236
236
  let text = CMD_MAP[cmd] ?? "";
237
237
  if (cmd === "cd" && typeof action.value === "object" && action.value !== null) {
238
238
  const path = (action.value as Record<string, string>).path;
@@ -608,7 +608,8 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
608
608
  logTrace(tid, "BRANCH", { cmd: "/sessions" });
609
609
  const allSessions = await getAllSessionsStatus();
610
610
  const now = Date.now();
611
- const cardData = allSessions.map(s => ({
611
+ const others = allSessions.filter(s => s.chatId !== chatId);
612
+ const cardData = others.map(s => ({
612
613
  sessionId: s.sessionId,
613
614
  chatName: s.chatName,
614
615
  active: s.active,
@@ -619,13 +620,13 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
619
620
  }));
620
621
  const card = buildSessionsCard(cardData);
621
622
  const ok = await sendRawCard(freshToken, chatId, card);
622
- console.log(`[${ts()}] [SESSIONS] card sent, ok=${ok}, count=${allSessions.length}`);
623
- logTrace(tid, "DONE", { outcome: "sessions", ok, count: allSessions.length });
623
+ console.log(`[${ts()}] [SESSIONS] card sent, ok=${ok}, count=${others.length}`);
624
+ logTrace(tid, "DONE", { outcome: "sessions", ok, count: others.length });
624
625
  return;
625
626
  }
626
627
 
627
- if (textLower === "/forget") {
628
- logTrace(tid, "BRANCH", { cmd: "/forget" });
628
+ if (textLower === "/newh") {
629
+ logTrace(tid, "BRANCH", { cmd: "/newh" });
629
630
  const adapter = getAdapterForTool(descriptionTool);
630
631
  let cwd: string;
631
632
  try {
@@ -652,7 +653,7 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
652
653
  const init = await initClaudeSession(descriptionTool, cwd);
653
654
  newSessionId = init.sessionId;
654
655
  } catch (err) {
655
- logTrace(tid, "DONE", { outcome: "forget_session_fail", error: (err as Error).message });
656
+ logTrace(tid, "DONE", { outcome: "newh_session_fail", error: (err as Error).message });
656
657
  await sendCardReply(freshToken, chatId, "Error", `Failed to create new session:\n${(err as Error).message}`, "red");
657
658
  return;
658
659
  }
@@ -660,7 +661,7 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
660
661
  const descPrefix = sessionPrefixForTool(descriptionTool);
661
662
  const newName = sessionChatName("新会话", cwd);
662
663
  await updateChatInfo(freshToken, chatId, newName, `${descPrefix} ${newSessionId}`);
663
- console.log(`[${ts()}] [FORGET] Group updated: name="${newName}" desc="${descPrefix} ${newSessionId}"`);
664
+ console.log(`[${ts()}] [NEWH] Group updated: name="${newName}" desc="${descPrefix} ${newSessionId}"`);
664
665
 
665
666
  sessionInfoMap.set(chatId, {
666
667
  sessionId: newSessionId,
@@ -691,8 +692,8 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
691
692
  "green"
692
693
  );
693
694
 
694
- console.log(`[${ts()}] [FORGET] Session ${sessionId} → ${newSessionId} (same cwd=${cwd})`);
695
- logTrace(tid, "DONE", { outcome: "forget", newSessionId, cwd });
695
+ console.log(`[${ts()}] [NEWH] Session ${sessionId} → ${newSessionId} (same cwd=${cwd})`);
696
+ logTrace(tid, "DONE", { outcome: "newh", newSessionId, cwd });
696
697
  return;
697
698
  }
698
699
 
@@ -702,17 +703,22 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
702
703
  const index = parseInt(sessionMatch[1], 10) - 1;
703
704
  logTrace(tid, "BRANCH", { cmd: "/session", index: index + 1 });
704
705
  const allSessions = await getAllSessionsStatus();
705
- if (allSessions.length === 0) {
706
+ // buildSessionsCard 保持一致的排序:Claude Code → Cursor → Codex,组内保持 updatedAt 降序
707
+ const claudeOrdered = allSessions.filter(s => s.tool !== "cursor" && s.tool !== "codex");
708
+ const cursorOrdered = allSessions.filter(s => s.tool === "cursor");
709
+ const codexOrdered = allSessions.filter(s => s.tool === "codex");
710
+ const ordered = [...claudeOrdered, ...cursorOrdered, ...codexOrdered].filter(s => s.chatId !== chatId);
711
+ if (ordered.length === 0) {
706
712
  await sendCardReply(freshToken, chatId, "/session", "暂无历史会话。", "yellow");
707
713
  logTrace(tid, "DONE", { outcome: "session_no_sessions" });
708
714
  return;
709
715
  }
710
- if (index < 0 || index >= allSessions.length) {
711
- await sendCardReply(freshToken, chatId, "/session", `序号超出范围,当前共 ${allSessions.length} 个会话。`, "yellow");
712
- logTrace(tid, "DONE", { outcome: "session_out_of_range", index: index + 1, total: allSessions.length });
716
+ if (index < 0 || index >= ordered.length) {
717
+ await sendCardReply(freshToken, chatId, "/session", `序号超出范围,当前共 ${ordered.length} 个会话。`, "yellow");
718
+ logTrace(tid, "DONE", { outcome: "session_out_of_range", index: index + 1, total: ordered.length });
713
719
  return;
714
720
  }
715
- const target = allSessions[index];
721
+ const target = ordered[index];
716
722
 
717
723
  const existing2 = chatSessionMap.get(chatId);
718
724
  if (existing2) {
@@ -736,8 +742,9 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
736
742
  }
737
743
 
738
744
  const descPrefix2 = sessionPrefixForTool(target.tool);
739
- const newName2 = sessionChatName("新会话", cwd2);
745
+ const newName2 = target.chatName || sessionChatName("新会话", cwd2);
740
746
  await updateChatInfo(freshToken, chatId, newName2, `${descPrefix2} ${target.sessionId}`);
747
+ console.log(`[${ts()}] [SESSION] Switched to session ${target.sessionId} (#${index + 1}), name="${newName2}"`);
741
748
 
742
749
  sessionInfoMap.set(chatId, {
743
750
  sessionId: target.sessionId,
@@ -767,7 +774,6 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
767
774
  "green"
768
775
  );
769
776
 
770
- console.log(`[${ts()}] [SESSION] Switched to session ${target.sessionId} (#${index + 1})`);
771
777
  logTrace(tid, "DONE", { outcome: "session_switch", sessionId: target.sessionId, index: index + 1, cwd: cwd2 });
772
778
  return;
773
779
  }
@@ -839,7 +845,7 @@ async function handleCommand(text: string, chatId: string, openId: string, msgTi
839
845
  console.log(`[${ts()}] [BLOCKED] allowInterrupt=false, ignoring message during generation. Hint sent to user.`);
840
846
  await sendCardReply(
841
847
  freshToken, chatId, "生成中",
842
- "AI 正在生成回复中,请先点击卡片上的「停止」按钮停止当前生成后,再发送新消息。",
848
+ "AI 正在生成回复中,Agent 不会遗忘当前进度,停止后仍可从断点继续。\n请先点击卡片上的「停止」按钮,再发送新消息。",
843
849
  "yellow"
844
850
  );
845
851
  return;
package/src/shared.ts CHANGED
@@ -1,17 +1,17 @@
1
1
  import { execSync } from "node:child_process";
2
- import {
3
- appendFileSync,
4
- existsSync,
5
- mkdirSync,
6
- readFileSync,
7
- unlinkSync,
8
- writeFileSync,
9
- } from "node:fs";
10
- import { createServer } from "node:http";
11
- import { homedir } from "node:os";
12
- import { join } from "node:path";
13
- import { inspect } from "node:util";
14
- import { WebSocketServer, WebSocket } from "ws";
2
+ import {
3
+ appendFileSync,
4
+ existsSync,
5
+ mkdirSync,
6
+ readFileSync,
7
+ unlinkSync,
8
+ writeFileSync,
9
+ } from "node:fs";
10
+ import { createServer } from "node:http";
11
+ import { homedir } from "node:os";
12
+ import { join } from "node:path";
13
+ import { inspect } from "node:util";
14
+ import { WebSocketServer, WebSocket } from "ws";
15
15
 
16
16
  import { printServiceDidNotStart } from "./exit-banner.ts";
17
17
 
@@ -355,56 +355,56 @@ export function installCrashLogging(opts: CrashHandlersOptions = {}): InstallCra
355
355
  // 文件日志:同时输出到控制台和日志文件
356
356
  // ---------------------------------------------------------------------------
357
357
 
358
- export function setupFileLogging(logDir: string, prefix: string): { logPath: string; flush: () => void } {
359
- mkdirSync(logDir, { recursive: true });
360
- const ts = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
361
- const logPath = join(logDir, `${prefix}-${ts}.log`);
362
- writeFileSync(logPath, "", { flag: "a", encoding: "utf8" });
363
- const origConsoleLog = console.log.bind(console);
364
- const origConsoleError = console.error.bind(console);
365
- const formatArg = (arg: unknown): string => {
366
- if (typeof arg === "string") return arg;
367
- if (arg instanceof Error) return arg.stack ?? arg.message;
368
- return inspect(arg, { depth: 6, breakLength: Infinity, maxArrayLength: 200 });
369
- };
370
- const writeLine = (level: string, args: unknown[]) => {
371
- try {
372
- const line = args.map(formatArg).join(" ");
373
- appendFileSync(logPath, `[${new Date().toISOString()}] [${level}] ${line}\n`, "utf8");
374
- } catch (err) {
375
- try {
376
- appendStartupTrace("fileLog write failed", { level, error: err instanceof Error ? err.message : String(err) });
377
- } catch {
378
- // 日志系统自身不能影响主流程
379
- }
380
- }
381
- };
382
- console.log = (...args: unknown[]) => {
383
- writeLine("LOG", args);
384
- try {
385
- origConsoleLog(...args);
386
- } catch {
387
- // 控制台输出失败也不能拖垮服务
388
- }
389
- };
390
- console.error = (...args: unknown[]) => {
391
- writeLine("ERR", args);
392
- try {
393
- origConsoleError(...args);
394
- } catch {
395
- // 控制台输出失败也不能拖垮服务
396
- }
397
- };
398
- const flush = () => {
399
- try {
400
- appendFileSync(logPath, "", "utf8");
401
- } catch (err) {
402
- appendStartupTrace("fileLog flush failed", { error: err instanceof Error ? err.message : String(err) });
403
- }
404
- };
405
- origConsoleLog(`Log file: ${logPath}`);
406
- return { logPath, flush };
407
- }
358
+ export function setupFileLogging(logDir: string, prefix: string): { logPath: string; flush: () => void } {
359
+ mkdirSync(logDir, { recursive: true });
360
+ const ts = new Date().toISOString().replace(/[:.]/g, "-").slice(0, 19);
361
+ const logPath = join(logDir, `${prefix}-${ts}.log`);
362
+ writeFileSync(logPath, "", { flag: "a", encoding: "utf8" });
363
+ const origConsoleLog = console.log.bind(console);
364
+ const origConsoleError = console.error.bind(console);
365
+ const formatArg = (arg: unknown): string => {
366
+ if (typeof arg === "string") return arg;
367
+ if (arg instanceof Error) return arg.stack ?? arg.message;
368
+ return inspect(arg, { depth: 6, breakLength: Infinity, maxArrayLength: 200 });
369
+ };
370
+ const writeLine = (level: string, args: unknown[]) => {
371
+ try {
372
+ const line = args.map(formatArg).join(" ");
373
+ appendFileSync(logPath, `[${new Date().toISOString()}] [${level}] ${line}\n`, "utf8");
374
+ } catch (err) {
375
+ try {
376
+ appendStartupTrace("fileLog write failed", { level, error: err instanceof Error ? err.message : String(err) });
377
+ } catch {
378
+ // 日志系统自身不能影响主流程
379
+ }
380
+ }
381
+ };
382
+ console.log = (...args: unknown[]) => {
383
+ writeLine("LOG", args);
384
+ try {
385
+ origConsoleLog(...args);
386
+ } catch {
387
+ // 控制台输出失败也不能拖垮服务
388
+ }
389
+ };
390
+ console.error = (...args: unknown[]) => {
391
+ writeLine("ERR", args);
392
+ try {
393
+ origConsoleError(...args);
394
+ } catch {
395
+ // 控制台输出失败也不能拖垮服务
396
+ }
397
+ };
398
+ const flush = () => {
399
+ try {
400
+ appendFileSync(logPath, "", "utf8");
401
+ } catch (err) {
402
+ appendStartupTrace("fileLog flush failed", { error: err instanceof Error ? err.message : String(err) });
403
+ }
404
+ };
405
+ origConsoleLog(`Log file: ${logPath}`);
406
+ return { logPath, flush };
407
+ }
408
408
 
409
409
  // ---------------------------------------------------------------------------
410
410
  // 本地 WebSocket 中继服务器(同一端口、多客户端广播)