chatccc 0.2.181 → 0.2.182

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.
@@ -38,14 +38,14 @@ import {
38
38
  import {
39
39
  buildHelpCard,
40
40
  buildModelCard,
41
- buildStatusCard,
42
- buildCdContent,
43
- buildCdCard,
44
- buildSessionsCard,
45
- buildQueuedCard,
46
- buildQueueFullCard,
47
- buildCodexUsageCard,
48
- } from "./cards.ts";
41
+ buildStatusCard,
42
+ buildCdContent,
43
+ buildCdCard,
44
+ buildSessionsCard,
45
+ buildQueuedCard,
46
+ buildQueueFullCard,
47
+ buildCodexUsageCard,
48
+ } from "./cards.ts";
49
49
  import {
50
50
  formatGitResult,
51
51
  gitResultHeaderTemplate,
@@ -79,10 +79,10 @@ import {
79
79
  enqueueMessage,
80
80
  cancelQueuedMessage,
81
81
  } from "./session-chat-binding.ts";
82
- import { getCodexUsageSummary, getTenantAccessToken, sendPostMessage } from "./feishu-platform.ts";
83
- import { getCursorUsageSummary, type CursorUsageSummary } from "./cursor-usage.ts";
84
- import { applySharedPrefix } from "./shared-prefix.ts";
85
- export { type PlatformAdapter } from "./platform-adapter.ts";
82
+ import { getCodexUsageSummary, getTenantAccessToken, sendPostMessage } from "./feishu-platform.ts";
83
+ import { getCursorUsageSummary, type CursorUsageSummary } from "./cursor-usage.ts";
84
+ import { applySharedPrefix } from "./shared-prefix.ts";
85
+ export { type PlatformAdapter } from "./platform-adapter.ts";
86
86
  import type { PlatformAdapter } from "./platform-adapter.ts";
87
87
  import type { CodexUsageSummary } from "./feishu-api.ts";
88
88
 
@@ -114,7 +114,7 @@ function findModelMatch(input: string, models: string[]): string | null {
114
114
  return candidates[0] ?? null;
115
115
  }
116
116
 
117
- function formatCodexUsageSummary(usage: CodexUsageSummary): string {
117
+ function formatCodexUsageSummary(usage: CodexUsageSummary): string {
118
118
  const progressBar = (usedPercent: number) => {
119
119
  const width = 20;
120
120
  const usedBlocks = Math.max(0, Math.min(width, Math.round((usedPercent / 100) * width)));
@@ -153,54 +153,54 @@ function formatCodexUsageSummary(usage: CodexUsageSummary): string {
153
153
  return `${absolute}${formatDuration(balance.resetAfterSeconds)}`;
154
154
  };
155
155
 
156
- const formatWindow = (label: string, balance: CodexUsageSummary["fiveHour"] | null) => {
157
- if (!balance) return `**${label}:** 暂无数据`;
158
- return [
159
- `**${label}:** 已用 ${balance.usedPercent}%,剩余 ${balance.remainingPercent}%,重置: ${formatResetTime(balance)}`,
160
- progressBar(balance.usedPercent),
161
- ].join("\n");
162
- };
163
-
164
- const formatResetCredits = () => {
165
- if (usage.rateLimitResetCreditsAvailable === null) return "**主动重置:** 暂无数据";
166
- const lines = [`**主动重置:** 剩余 ${usage.rateLimitResetCreditsAvailable} 次`];
167
- const credits = usage.rateLimitResetCredits ?? [];
168
- if (credits.length > 0) {
169
- const pad = (value: number) => String(value).padStart(2, "0");
170
- const formatExpiresAt = (value: string) => {
171
- const date = new Date(value);
172
- if (!Number.isFinite(date.getTime())) return value;
173
- return [
174
- date.getFullYear(),
175
- "-",
176
- pad(date.getMonth() + 1),
177
- "-",
178
- pad(date.getDate()),
179
- " ",
180
- pad(date.getHours()),
181
- ":",
182
- pad(date.getMinutes()),
183
- ":",
184
- pad(date.getSeconds()),
185
- ].join("");
186
- };
187
- lines.push("**过期时间:**");
188
- for (const credit of credits) {
189
- lines.push(`- ${formatExpiresAt(credit.expiresAt)}`);
190
- }
191
- }
192
- return lines.join("\n");
193
- };
194
-
195
- return [
196
- "Codex 用量:",
197
- "",
198
- formatResetCredits(),
199
- "",
200
- formatWindow("5h", usage.fiveHour),
201
- formatWindow("周", usage.weekly),
202
- ].join("\n");
203
- }
156
+ const formatWindow = (label: string, balance: CodexUsageSummary["fiveHour"] | null) => {
157
+ if (!balance) return `**${label}:** 暂无数据`;
158
+ return [
159
+ `**${label}:** 已用 ${balance.usedPercent}%,剩余 ${balance.remainingPercent}%,重置: ${formatResetTime(balance)}`,
160
+ progressBar(balance.usedPercent),
161
+ ].join("\n");
162
+ };
163
+
164
+ const formatResetCredits = () => {
165
+ if (usage.rateLimitResetCreditsAvailable === null) return "**主动重置:** 暂无数据";
166
+ const lines = [`**主动重置:** 剩余 ${usage.rateLimitResetCreditsAvailable} 次`];
167
+ const credits = usage.rateLimitResetCredits ?? [];
168
+ if (credits.length > 0) {
169
+ const pad = (value: number) => String(value).padStart(2, "0");
170
+ const formatExpiresAt = (value: string) => {
171
+ const date = new Date(value);
172
+ if (!Number.isFinite(date.getTime())) return value;
173
+ return [
174
+ date.getFullYear(),
175
+ "-",
176
+ pad(date.getMonth() + 1),
177
+ "-",
178
+ pad(date.getDate()),
179
+ " ",
180
+ pad(date.getHours()),
181
+ ":",
182
+ pad(date.getMinutes()),
183
+ ":",
184
+ pad(date.getSeconds()),
185
+ ].join("");
186
+ };
187
+ lines.push("**过期时间:**");
188
+ for (const credit of credits) {
189
+ lines.push(`- ${formatExpiresAt(credit.expiresAt)}`);
190
+ }
191
+ }
192
+ return lines.join("\n");
193
+ };
194
+
195
+ return [
196
+ "Codex 用量:",
197
+ "",
198
+ formatResetCredits(),
199
+ "",
200
+ formatWindow("5h", usage.fiveHour),
201
+ formatWindow("周", usage.weekly),
202
+ ].join("\n");
203
+ }
204
204
 
205
205
  function formatCursorUsageSummary(usage: CursorUsageSummary): string {
206
206
  const timeZone = Intl.DateTimeFormat().resolvedOptions().timeZone;
@@ -259,7 +259,7 @@ function formatCursorUsageSummary(usage: CursorUsageSummary): string {
259
259
  }
260
260
 
261
261
  function usageHelpLine(tool: string): string {
262
- if (tool === "codex") return "\n发送 **/usage** 查看 Codex 5h/周用量,以及查询/使用主动重置卡。";
262
+ if (tool === "codex") return "\n发送 **/usage** 查看 Codex 5h/周用量,以及查询/使用主动重置卡。";
263
263
  if (tool === "cursor") return "\n发送 **/usage** 查看 Cursor 用量。";
264
264
  return "";
265
265
  }
@@ -284,16 +284,16 @@ async function sendUsageSummary(platform: PlatformAdapter, chatId: string, tool:
284
284
  return;
285
285
  }
286
286
 
287
- const usage = await getCodexUsageSummary();
288
- const content = formatCodexUsageSummary(usage);
289
- if (platform.kind === "wechat") {
290
- await platform.sendText(chatId, content).catch(() => {});
291
- } else if (platform.kind === "feishu") {
292
- await platform.sendRawCard(chatId, buildCodexUsageCard(content, usage.rateLimitResetCreditsAvailable));
293
- } else {
294
- await platform.sendCard(chatId, "Codex Usage", content, "blue");
295
- }
296
- }
287
+ const usage = await getCodexUsageSummary();
288
+ const content = formatCodexUsageSummary(usage);
289
+ if (platform.kind === "wechat") {
290
+ await platform.sendText(chatId, content).catch(() => {});
291
+ } else if (platform.kind === "feishu") {
292
+ await platform.sendRawCard(chatId, buildCodexUsageCard(content, usage.rateLimitResetCreditsAvailable));
293
+ } else {
294
+ await platform.sendCard(chatId, "Codex Usage", content, "blue");
295
+ }
296
+ }
297
297
 
298
298
  async function sendUsageError(platform: PlatformAdapter, chatId: string, tool: "codex" | "cursor", err: unknown): Promise<void> {
299
299
  const toolLabel = tool === "cursor" ? "Cursor" : "Codex";
@@ -309,13 +309,13 @@ function isUntitledSessionChatName(name: string): boolean {
309
309
  return name === "新会话" || name.startsWith("新会话-");
310
310
  }
311
311
 
312
- function shouldSendWechatProcessingAck(
313
- platform: PlatformAdapter,
314
- isCommandText: boolean,
315
- chatType: string,
316
- ): boolean {
317
- return platform.kind === "wechat" && chatType === "p2p" && !isCommandText;
318
- }
312
+ function shouldSendWechatProcessingAck(
313
+ platform: PlatformAdapter,
314
+ isCommandText: boolean,
315
+ chatType: string,
316
+ ): boolean {
317
+ return platform.kind === "wechat" && chatType === "p2p" && !isCommandText;
318
+ }
319
319
 
320
320
  function isNonWechatP2p(platform: PlatformAdapter, chatType: string): boolean {
321
321
  return chatType === "p2p" && platform.kind !== "wechat";
@@ -445,17 +445,17 @@ export async function handleCommand(
445
445
  msgTimestamp: number,
446
446
  chatType = "group",
447
447
  traceId?: string,
448
- ): Promise<void> {
449
- const tid = traceId ?? makeTraceId();
450
- const sharedPrefix = applySharedPrefix(text);
451
- const promptText = sharedPrefix.text;
452
- text = sharedPrefix.body;
453
- const textLower = text.toLowerCase();
454
- const isCommandText = !sharedPrefix.matched && textLower.startsWith("/");
455
- recordChatPlatform(chatId, platform);
448
+ ): Promise<void> {
449
+ const tid = traceId ?? makeTraceId();
450
+ const sharedPrefix = applySharedPrefix(text);
451
+ const promptText = sharedPrefix.text;
452
+ text = sharedPrefix.body;
453
+ const textLower = text.toLowerCase();
454
+ const isCommandText = !sharedPrefix.matched && textLower.startsWith("/");
455
+ recordChatPlatform(chatId, platform);
456
456
  await cleanupNonWechatP2pBinding(platform, chatId, chatType, tid);
457
457
 
458
- if (isCommandText && textLower === "/restart") {
458
+ if (isCommandText && textLower === "/restart") {
459
459
  logTrace(tid, "BRANCH", { cmd: "/restart" });
460
460
  await platform.sendText(chatId, "重启中...请几秒后发消息唤醒我").catch(() => {});
461
461
  logTrace(tid, "DONE", { outcome: "restart" });
@@ -490,7 +490,7 @@ export async function handleCommand(
490
490
  return;
491
491
  }
492
492
 
493
- if (isCommandText && textLower === "/update") {
493
+ if (isCommandText && textLower === "/update") {
494
494
  logTrace(tid, "BRANCH", { cmd: "/update" });
495
495
  const isGlobal = isRunningFromGlobalNpm();
496
496
  appendStartupTrace("update: command received", { isGlobal, chatId });
@@ -507,7 +507,7 @@ export async function handleCommand(
507
507
  return;
508
508
  }
509
509
 
510
- if (isCommandText && textLower === "/usage") {
510
+ if (isCommandText && textLower === "/usage") {
511
511
  const usageTool = await resolveUsageTool(chatId);
512
512
  logTrace(tid, "BRANCH", { cmd: "/usage", tool: usageTool });
513
513
  try {
@@ -520,7 +520,7 @@ export async function handleCommand(
520
520
  return;
521
521
  }
522
522
 
523
- if (isCommandText && (textLower === "/cd" || textLower.startsWith("/cd "))) {
523
+ if (isCommandText && (textLower === "/cd" || textLower.startsWith("/cd "))) {
524
524
  logTrace(tid, "BRANCH", {
525
525
  cmd: "/cd",
526
526
  arg: text.slice(3).trim() || "(none)",
@@ -643,7 +643,7 @@ export async function handleCommand(
643
643
  return;
644
644
  }
645
645
 
646
- if (isCommandText && (textLower === "/new" || textLower.startsWith("/new "))) {
646
+ if (isCommandText && (textLower === "/new" || textLower.startsWith("/new "))) {
647
647
  const toolArg = text.slice(5).trim().toLowerCase();
648
648
  const tool = toolArg || resolveDefaultAgentTool();
649
649
  logTrace(tid, "BRANCH", { cmd: "/new", tool });
@@ -904,7 +904,7 @@ export async function handleCommand(
904
904
  if (sessionId && descriptionTool && toolLabel) {
905
905
  // 有会话上下文 — 路由到命令处理或 prompt
906
906
  logTrace(tid, "BRANCH", { sessionId, tool: descriptionTool });
907
- const routeKind = isCommandText ? "command" : "prompt";
907
+ const routeKind = isCommandText ? "command" : "prompt";
908
908
  const chatKind = chatType === "p2p" ? "p2p chat" : "session group";
909
909
  console.log(
910
910
  `[${ts()}] [ROUTE] ${toolLabel} ${chatKind} ${routeKind} detected, session=${sessionId} tool=${descriptionTool}`,
@@ -913,7 +913,7 @@ export async function handleCommand(
913
913
  if (
914
914
  chatType !== "p2p" &&
915
915
  isUntitledSessionChatName(chatInfo!.name) &&
916
- !isCommandText
916
+ !isCommandText
917
917
  ) {
918
918
  const MAX_PREFIX = 10;
919
919
  const prefix = text.slice(0, MAX_PREFIX);
@@ -948,7 +948,7 @@ export async function handleCommand(
948
948
  if (
949
949
  chatType === "p2p" &&
950
950
  platform.kind === "wechat" &&
951
- !isCommandText
951
+ !isCommandText
952
952
  ) {
953
953
  try {
954
954
  const reg = await loadSessionRegistryForBinding();
@@ -987,7 +987,7 @@ export async function handleCommand(
987
987
  }
988
988
  }
989
989
 
990
- if (isCommandText && textLower === "/stop") {
990
+ if (isCommandText && textLower === "/stop") {
991
991
  logTrace(tid, "BRANCH", { cmd: "/stop" });
992
992
  if (stopSession(sessionId)) {
993
993
  console.log(`[${ts()}] [STOP] User sent /stop, session=${sessionId}`);
@@ -1001,7 +1001,7 @@ export async function handleCommand(
1001
1001
  return;
1002
1002
  }
1003
1003
 
1004
- if (isCommandText && textLower === "/cancel") {
1004
+ if (isCommandText && textLower === "/cancel") {
1005
1005
  logTrace(tid, "BRANCH", { cmd: "/cancel" });
1006
1006
  if (cancelQueuedMessage(sessionId)) {
1007
1007
  console.log(`[${ts()}] [CANCEL] Queue cancelled for session=${sessionId}`);
@@ -1014,7 +1014,7 @@ export async function handleCommand(
1014
1014
  return;
1015
1015
  }
1016
1016
 
1017
- if (isCommandText && textLower === "/test") {
1017
+ if (isCommandText && textLower === "/test") {
1018
1018
  logTrace(tid, "BRANCH", { cmd: "/test" });
1019
1019
  const tableHeaders = ["名称", "版本", "状态"];
1020
1020
  const tableRows = [
@@ -1051,7 +1051,7 @@ export async function handleCommand(
1051
1051
  return;
1052
1052
  }
1053
1053
 
1054
- if (isCommandText && textLower === "/state") {
1054
+ if (isCommandText && textLower === "/state") {
1055
1055
  logTrace(tid, "BRANCH", { cmd: "/state" });
1056
1056
  const status = await getSessionStatus(chatId);
1057
1057
  const isActive = isSessionRunning(sessionId);
@@ -1090,7 +1090,7 @@ export async function handleCommand(
1090
1090
  return;
1091
1091
  }
1092
1092
 
1093
- if (isCommandText && textLower === "/sessions") {
1093
+ if (isCommandText && textLower === "/sessions") {
1094
1094
  logTrace(tid, "BRANCH", { cmd: "/sessions" });
1095
1095
  const allSessions = await getAllSessionsStatus();
1096
1096
  const now = Date.now();
@@ -1115,7 +1115,7 @@ export async function handleCommand(
1115
1115
  return;
1116
1116
  }
1117
1117
 
1118
- if (isCommandText && textLower === "/newh") {
1118
+ if (isCommandText && textLower === "/newh") {
1119
1119
  logTrace(tid, "BRANCH", { cmd: "/newh" });
1120
1120
  const adapter = getAdapterForTool(descriptionTool, sessionId);
1121
1121
  let cwd: string;
@@ -1202,7 +1202,7 @@ export async function handleCommand(
1202
1202
  return;
1203
1203
  }
1204
1204
 
1205
- if (isCommandText && textLower === "/deleteg") {
1205
+ if (isCommandText && textLower === "/deleteg") {
1206
1206
  logTrace(tid, "BRANCH", { cmd: "/deleteg" });
1207
1207
  if (chatType === "p2p") {
1208
1208
  await platform
@@ -1240,7 +1240,7 @@ export async function handleCommand(
1240
1240
  }
1241
1241
 
1242
1242
  // /session <number>:切换到 /sessions 列表中的指定会话
1243
- const sessionMatch = isCommandText ? textLower.match(/^\/session\s+(\d+)$/) : null;
1243
+ const sessionMatch = isCommandText ? textLower.match(/^\/session\s+(\d+)$/) : null;
1244
1244
  if (sessionMatch) {
1245
1245
  const index = parseInt(sessionMatch[1], 10) - 1;
1246
1246
  logTrace(tid, "BRANCH", { cmd: "/session", index: index + 1 });
@@ -1366,7 +1366,7 @@ export async function handleCommand(
1366
1366
  }
1367
1367
 
1368
1368
  // /model clear — 清除当前 session 的模型覆盖
1369
- if (isCommandText && textLower === "/model clear") {
1369
+ if (isCommandText && textLower === "/model clear") {
1370
1370
  logTrace(tid, "BRANCH", { cmd: "/model clear", sessionId });
1371
1371
  clearSessionModelOverride(sessionId);
1372
1372
  const defaultModel = getEffectiveModelForTool(descriptionTool);
@@ -1381,7 +1381,7 @@ export async function handleCommand(
1381
1381
  }
1382
1382
 
1383
1383
  // /model <name> — 切换当前 session 的模型(支持所有 agent,模糊匹配)
1384
- if (isCommandText && textLower.startsWith("/model ")) {
1384
+ if (isCommandText && textLower.startsWith("/model ")) {
1385
1385
  const modelArg = text.slice(7).trim();
1386
1386
  if (!modelArg) return; // 纯 "/model " 不处理,交给上面的 /model 分支
1387
1387
  logTrace(tid, "BRANCH", { cmd: "/model", arg: modelArg, sessionId, tool: descriptionTool });
@@ -1415,7 +1415,7 @@ export async function handleCommand(
1415
1415
  }
1416
1416
 
1417
1417
  // /model — 查看当前会话的可用模型(根据会话 Agent 类型)
1418
- if (isCommandText && textLower === "/model") {
1418
+ if (isCommandText && textLower === "/model") {
1419
1419
  logTrace(tid, "BRANCH", { cmd: "/model", sessionId, tool: descriptionTool });
1420
1420
  const models = getAllModelsForTool(descriptionTool as AgentTool);
1421
1421
  const currentModel = getEffectiveModelForTool(descriptionTool, sessionId);
@@ -1439,7 +1439,7 @@ export async function handleCommand(
1439
1439
  }
1440
1440
 
1441
1441
  // /git <args>:在「当前会话工作目录」执行 git 命令
1442
- if (isCommandText && (textLower.startsWith("/git ") || textLower === "/git")) {
1442
+ if (isCommandText && (textLower.startsWith("/git ") || textLower === "/git")) {
1443
1443
  const args = text === "/git" ? "" : text.slice(5).trim();
1444
1444
  logTrace(tid, "BRANCH", { cmd: "/git", args: args || "(none)" });
1445
1445
  if (!args) {
@@ -1508,9 +1508,9 @@ export async function handleCommand(
1508
1508
 
1509
1509
  // 并发检查:同一 session 只能有一个活跃 prompt,多余消息进入队列
1510
1510
  if (isSessionRunning(sessionId)) {
1511
- const queued = enqueueMessage(sessionId, {
1512
- text: promptText, chatId, openId, msgTimestamp, chatType, traceId: tid,
1513
- });
1511
+ const queued = enqueueMessage(sessionId, {
1512
+ text: promptText, chatId, openId, msgTimestamp, chatType, traceId: tid,
1513
+ });
1514
1514
  if (queued) {
1515
1515
  logTrace(tid, "QUEUED", { sessionId });
1516
1516
  console.log(
@@ -1535,16 +1535,16 @@ export async function handleCommand(
1535
1535
  return;
1536
1536
  }
1537
1537
 
1538
- if (shouldSendWechatProcessingAck(platform, isCommandText, chatType)) {
1538
+ if (shouldSendWechatProcessingAck(platform, isCommandText, chatType)) {
1539
1539
  await platform.sendText(chatId, "生成中...").catch(() => {});
1540
1540
  }
1541
1541
 
1542
1542
  try {
1543
1543
  logTrace(tid, "RESUME", { sessionId, tool: descriptionTool });
1544
1544
  await resumeAndPrompt(
1545
- sessionId,
1546
- promptText,
1547
- platform,
1545
+ sessionId,
1546
+ promptText,
1547
+ platform,
1548
1548
  chatId,
1549
1549
  msgTimestamp,
1550
1550
  descriptionTool,
@@ -1570,7 +1570,7 @@ export async function handleCommand(
1570
1570
  }
1571
1571
 
1572
1572
  // 无会话上下文 → 检查是否是 /model 查询
1573
- if (isCommandText && textLower === "/model") {
1573
+ if (isCommandText && textLower === "/model") {
1574
1574
  const defaultTool = resolveDefaultAgentTool();
1575
1575
  const models = getAllModelsForTool(defaultTool);
1576
1576
  let currentModel = "";
@@ -1597,7 +1597,7 @@ export async function handleCommand(
1597
1597
  }
1598
1598
 
1599
1599
  // 无会话上下文 → /sessions 仍是有效指令,不触发飞书私聊自动建群。
1600
- if (isCommandText && textLower === "/sessions") {
1600
+ if (isCommandText && textLower === "/sessions") {
1601
1601
  logTrace(tid, "BRANCH", { cmd: "/sessions", scope: "global" });
1602
1602
  const allSessions = await getAllSessionsStatus();
1603
1603
  const now = Date.now();
@@ -1623,7 +1623,7 @@ export async function handleCommand(
1623
1623
  }
1624
1624
 
1625
1625
  // 飞书私聊普通消息:不再绑定私聊本身,而是自动创建会话群并把私聊内容作为首轮 prompt。
1626
- if (isNonWechatP2p(platform, chatType) && !isCommandText) {
1626
+ if (isNonWechatP2p(platform, chatType) && !isCommandText) {
1627
1627
  const tool = resolveDefaultAgentTool();
1628
1628
  const toolLabel = toolDisplayName(tool);
1629
1629
  logTrace(tid, "BRANCH", { cmd: "auto_new_from_p2p", tool });
@@ -1741,9 +1741,9 @@ export async function handleCommand(
1741
1741
  try {
1742
1742
  logTrace(tid, "RESUME", { sessionId, tool, trigger: "auto_new_from_p2p" });
1743
1743
  await resumeAndPrompt(
1744
- sessionId,
1745
- promptText,
1746
- platform,
1744
+ sessionId,
1745
+ promptText,
1746
+ platform,
1747
1747
  newChatId,
1748
1748
  msgTimestamp,
1749
1749
  tool,
@@ -1,29 +1,29 @@
1
- export const ABD_PREFIX = "/abd";
2
-
3
- export const ABD_APPEND_PROMPT =
4
- "请从第一性原理出发挖掘我的真实需求。你觉得我的需求合理吗?开始实现之前有什么问题要问我的?";
5
-
6
- export const ABD_HELP_LINE =
7
- `发送 **${ABD_PREFIX}** 前缀消息,将以第一性原理追问真实需求、合理性和实现前问题`;
8
-
9
- export interface SharedPrefixResult {
10
- matched: boolean;
11
- text: string;
12
- body: string;
13
- prefix?: typeof ABD_PREFIX;
14
- }
15
-
16
- export function applySharedPrefix(text: string): SharedPrefixResult {
17
- if (!text.toLowerCase().startsWith(ABD_PREFIX)) {
18
- return { matched: false, text, body: text };
19
- }
20
-
21
- const body = text.slice(ABD_PREFIX.length).replace(/^\s+/, "");
22
- const appendix = ["---", ABD_APPEND_PROMPT].join("\n");
23
- return {
24
- matched: true,
25
- prefix: ABD_PREFIX,
26
- body,
27
- text: body ? [body, appendix].join("\n\n") : appendix,
28
- };
29
- }
1
+ export const ABD_PREFIX = "/abd";
2
+
3
+ export const ABD_APPEND_PROMPT =
4
+ "请从第一性原理出发挖掘我的真实需求。你觉得我的需求合理吗?开始实现之前有什么问题要问我的?";
5
+
6
+ export const ABD_HELP_LINE =
7
+ `发送 **${ABD_PREFIX}** 前缀消息,将以第一性原理追问真实需求、合理性和实现前问题`;
8
+
9
+ export interface SharedPrefixResult {
10
+ matched: boolean;
11
+ text: string;
12
+ body: string;
13
+ prefix?: typeof ABD_PREFIX;
14
+ }
15
+
16
+ export function applySharedPrefix(text: string): SharedPrefixResult {
17
+ if (!text.toLowerCase().startsWith(ABD_PREFIX)) {
18
+ return { matched: false, text, body: text };
19
+ }
20
+
21
+ const body = text.slice(ABD_PREFIX.length).replace(/^\s+/, "");
22
+ const appendix = ["---", ABD_APPEND_PROMPT].join("\n");
23
+ return {
24
+ matched: true,
25
+ prefix: ABD_PREFIX,
26
+ body,
27
+ text: body ? [body, appendix].join("\n\n") : appendix,
28
+ };
29
+ }
@@ -123,25 +123,25 @@ export const SimulatedPlatform: FeishuPlatform = {
123
123
  },
124
124
 
125
125
  // ---- 头像 ----
126
- async setChatAvatar(_token, _chatId, _tool, _status) {
127
- // 模拟模式不需要头像
128
- },
129
-
130
- async getCodexUsageSummary() {
131
- return {
132
- fiveHour: { usedPercent: 0, remainingPercent: 100, resetAtEpochSeconds: null, resetAfterSeconds: null },
133
- weekly: { usedPercent: 0, remainingPercent: 100, resetAtEpochSeconds: null, resetAfterSeconds: null },
134
- rateLimitResetCreditsAvailable: null,
135
- rateLimitResetCredits: null,
136
- };
137
- },
138
-
139
- async consumeCodexRateLimitResetCredit(_redeemRequestId) {
140
- return { code: "no_credit" as const, windowsReset: 0 };
141
- },
142
-
143
- // ---- 图片下载 ----
144
- async getOrDownloadImage(_token, _messageId, fileKey) {
126
+ async setChatAvatar(_token, _chatId, _tool, _status) {
127
+ // 模拟模式不需要头像
128
+ },
129
+
130
+ async getCodexUsageSummary() {
131
+ return {
132
+ fiveHour: { usedPercent: 0, remainingPercent: 100, resetAtEpochSeconds: null, resetAfterSeconds: null },
133
+ weekly: { usedPercent: 0, remainingPercent: 100, resetAtEpochSeconds: null, resetAfterSeconds: null },
134
+ rateLimitResetCreditsAvailable: null,
135
+ rateLimitResetCredits: null,
136
+ };
137
+ },
138
+
139
+ async consumeCodexRateLimitResetCredit(_redeemRequestId) {
140
+ return { code: "no_credit" as const, windowsReset: 0 };
141
+ },
142
+
143
+ // ---- 图片下载 ----
144
+ async getOrDownloadImage(_token, _messageId, fileKey) {
145
145
  return join(SIM_DIR, "images", fileKey);
146
146
  },
147
147
 
@@ -174,4 +174,4 @@ export const SimulatedPlatform: FeishuPlatform = {
174
174
  };
175
175
 
176
176
  /** 模拟模式下的默认 chat_id(重新导出以保持向后兼容) */
177
- export { SIM_DEFAULT_CHAT_ID } from "./sim-store.ts";
177
+ export { SIM_DEFAULT_CHAT_ID } from "./sim-store.ts";