chatccc 0.2.117 → 0.2.119

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 CHANGED
@@ -296,20 +296,28 @@ Codex 的默认模型和推理强度可继续由 `~/.codex/config.toml` 管理
296
296
 
297
297
  ## 可用指令
298
298
 
299
- | 指令 | 作用 |
300
- | --- | --- |
301
- | `/new` | 使用默认 Agent 创建新会话 |
302
- | `/new claude` | 创建 Claude Code 会话 |
303
- | `/new cursor` | 创建 Cursor 会话 |
304
- | `/new codex` | 创建 Codex 会话 |
305
- | `/newh` | 重置当前会话,保留工作目录 |
306
- | `/model` | 查看或切换当前会话的模型 |
307
- | `/stop` | 停止当前回复 |
308
- | `/state` | 查看当前会话状态 |
309
- | `/cd` | 查看或设置当前会话工作目录 |
310
- | `/sessions` | 查看所有会话状态 |
311
- | `/git <子命令>` | 在当前会话工作目录执行 `git ...` 并回传输出 |
312
- | `/restart` | 重启机器人进程 |
299
+ | 指令 | 作用 | 适用 Agent |
300
+ | --- | --- | --- |
301
+ | `/new` | 使用默认 Agent 创建新会话 | 全部 |
302
+ | `/new claude` | 创建 Claude Code 会话 | 全部 |
303
+ | `/new cursor` | 创建 Cursor 会话 | 全部 |
304
+ | `/new codex` | 创建 Codex 会话 | 全部 |
305
+ | `/newh` | 重置当前会话,保留工作目录 | 全部 |
306
+ | `/stop` | 停止当前回复 | 全部 |
307
+ | `/cancel` | 取消缓存队列中的消息 | 全部 |
308
+ | `/state` | 查看当前会话状态 | 全部 |
309
+ | `/sessions` | 查看所有会话状态 | 全部 |
310
+ | `/session <编号>` | 将当前群聊切换到指定编号的会话 | 全部 |
311
+ | `/model` | 查看当前会话的可用模型 | 全部 |
312
+ | `/model <名称>` | 模糊匹配切换当前会话模型 | 全部 |
313
+ | `/model clear` | 清除模型覆盖,恢复默认 | 全部 |
314
+ | `/cd` | 查看当前工作目录(含最近目录快捷按钮) | 全部 |
315
+ | `/cd <路径>` | 设置新建会话的默认工作目录 | 全部 |
316
+ | `/plan <消息>` | Claude Code 使用 CLI 只读/规划模式;其他 Agent 会收到包含 `/plan` 前缀的原始消息,可能通过消息内容自行支持 | Claude Code(CLI 模式)/ 全部(消息透传) |
317
+ | `/ask <消息>` | Cursor 使用 CLI 只读/问答模式;其他 Agent 会收到包含 `/ask` 前缀的原始消息,可能通过消息内容自行支持 | Cursor(CLI 模式)/ 全部(消息透传) |
318
+ | `/git <子命令>` | 在当前会话工作目录执行 git 命令并回传输出 | 全部 |
319
+ | `/deleteg` | 解散当前群聊,保留 Agent 会话 | 全部(仅群聊) |
320
+ | `/restart` | 重启 ChatCCC 服务进程 | 全部 |
313
321
 
314
322
  > **模型切换**:`/model` 查看可选模型清单,`/model <名称>` 模糊匹配切换,`/model clear` 恢复默认。当前仅 Claude Code 支持模型切换(需同时填写 `claude.model` 和 `claude.subagentModel`),Cursor / Codex 切换正在开发中。
315
323
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatccc",
3
- "version": "0.2.117",
3
+ "version": "0.2.119",
4
4
  "description": "Feishu bot bridge for Claude Code",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -154,30 +154,72 @@ describe("handleCommand WeChat processing ack", () => {
154
154
  expect(platform.sendCard).not.toHaveBeenCalled();
155
155
  });
156
156
 
157
- it("sends the WeChat processing ack after the busy check for normal prompts", async () => {
158
- const platform = mockPlatform();
159
- await recordSessionRegistry({
160
- chatId: "wx-chat",
161
- sessionId: "sid-wechat",
157
+ it("sends the WeChat processing ack after the busy check for normal prompts", async () => {
158
+ const platform = mockPlatform();
159
+ await recordSessionRegistry({
160
+ chatId: "wx-chat",
161
+ sessionId: "sid-wechat",
162
162
  tool: "claude",
163
163
  chatName: "ready-session",
164
164
  running: false,
165
165
  });
166
166
 
167
167
  await handleCommand(platform, "继续说明", "wx-chat", "wx-user", Date.now(), "p2p");
168
-
169
- expect(platform.sendText).toHaveBeenCalledWith("wx-chat", "生成中...");
170
- });
171
-
172
- it("cleans stale Feishu p2p binding, creates a group, and sends the private message as first prompt", async () => {
173
- const platform = mockPlatform("feishu");
174
- const prompt = vi.fn(async function* (_sessionId: string, userText: string) {
175
- yield {
168
+
169
+ expect(platform.sendText).toHaveBeenCalledWith("wx-chat", "生成中...");
170
+ });
171
+
172
+ it.each([
173
+ { tool: "claude" as const, sessionId: "sid-claude", text: "/ask 只回答不要改文件" },
174
+ { tool: "cursor" as const, sessionId: "sid-cursor", text: "/plan 先给我计划" },
175
+ ])("passes unsupported mode commands through as raw prompts for $tool", async ({ tool, sessionId, text }) => {
176
+ const platform = mockPlatform();
177
+ const prompt = vi.fn(async function* (_sessionId: string, userText: string) {
178
+ yield {
179
+ type: "assistant" as const,
180
+ blocks: [{ type: "text" as const, text: `收到: ${userText}` }],
181
+ };
182
+ });
183
+ _setAdapterForToolForTest(tool, {
184
+ displayName: tool,
185
+ sessionDescPrefix: `${tool} Session:`,
186
+ createSession: vi.fn(async () => ({ sessionId })),
187
+ prompt,
188
+ getSessionInfo: async (sessionId: string): Promise<SessionInfo> => ({
189
+ sessionId,
190
+ cwd: "F:\\repo",
191
+ }),
192
+ closeSession: async () => {},
193
+ });
194
+ await recordSessionRegistry({
195
+ chatId: "wx-chat",
196
+ sessionId,
197
+ tool,
198
+ chatName: "ready-session",
199
+ running: false,
200
+ });
201
+
202
+ await handleCommand(platform, text, "wx-chat", "wx-user", Date.now(), "p2p");
203
+
204
+ expect(platform.sendText).not.toHaveBeenCalledWith("wx-chat", expect.stringContaining("仅在以下 Agent 会话中可用"));
205
+ expect(prompt).toHaveBeenCalledWith(
206
+ sessionId,
207
+ expect.stringContaining(text),
208
+ "F:\\repo",
209
+ expect.any(AbortSignal),
210
+ expect.objectContaining({ permissionMode: undefined }),
211
+ );
212
+ });
213
+
214
+ it("cleans stale Feishu p2p binding, creates a group, and sends the private message as first prompt", async () => {
215
+ const platform = mockPlatform("feishu");
216
+ const prompt = vi.fn(async function* (_sessionId: string, userText: string) {
217
+ yield {
176
218
  type: "assistant" as const,
177
219
  blocks: [{ type: "text" as const, text: `收到: ${userText}` }],
178
220
  };
179
221
  });
180
- _setAdapterForToolForTest("cursor", {
222
+ _setAdapterForToolForTest("claude", {
181
223
  displayName: "Claude",
182
224
  sessionDescPrefix: "Claude Session:",
183
225
  createSession: vi.fn(async () => ({ sessionId: "sid-feishu-new" })),
@@ -128,6 +128,13 @@ export interface ToolPromptOptions {
128
128
  onProcessStart?: (info: ToolProcessInfo) => void;
129
129
  /** Called when the adapter leaves the prompt process scope normally or by abort. */
130
130
  onProcessExit?: (info: ToolProcessInfo) => void;
131
+ /**
132
+ * 权限模式覆盖:
133
+ * - "plan": Claude Code --permission-mode plan(只读/规划)
134
+ * - "ask": Cursor Agent --mode ask(只读/问答)
135
+ * 不传或为 undefined 时使用适配器默认行为(正常编辑模式)。
136
+ */
137
+ permissionMode?: "plan" | "ask";
131
138
  }
132
139
 
133
140
  // ---------------------------------------------------------------------------