chatccc 0.2.136 → 0.2.138
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.
|
@@ -1 +1,55 @@
|
|
|
1
|
+
# Claude-Specific Injection Prompt
|
|
2
|
+
|
|
3
|
+
Use this prompt as the Claude Agent SDK injection prompt for this project.
|
|
4
|
+
|
|
5
|
+
Project workspace:
|
|
6
|
+
|
|
7
|
+
```text
|
|
8
|
+
f:\users\weizhangjian\feishuclauderprivate
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
## Repeated Successful Command Guard
|
|
12
|
+
|
|
13
|
+
When working in this project through Claude Agent SDK, repeated successful shell commands are a completion signal, not a reason to keep using tools.
|
|
14
|
+
|
|
15
|
+
A shell command is considered the same command when all of these are true:
|
|
16
|
+
|
|
17
|
+
- The command text is effectively the same.
|
|
18
|
+
- The working directory is the same.
|
|
19
|
+
- The current task goal has not changed.
|
|
20
|
+
- There is no new user input that changes the task.
|
|
21
|
+
- There is no new error output that explains why the command must be run again.
|
|
22
|
+
|
|
23
|
+
If the same shell command succeeds more than once consecutively, do not call it again.
|
|
24
|
+
|
|
25
|
+
After the second consecutive successful execution of the same command, the next assistant action must be a final user-facing response. Do not call another tool unless the next command is materially different and has a clear reason based on the latest output.
|
|
26
|
+
|
|
27
|
+
This guard applies to all shell commands, including but not limited to:
|
|
28
|
+
|
|
29
|
+
- test commands
|
|
30
|
+
- build commands
|
|
31
|
+
- git commands
|
|
32
|
+
- install commands
|
|
33
|
+
- formatting commands
|
|
34
|
+
- project scripts
|
|
35
|
+
- status or inspection commands
|
|
36
|
+
|
|
37
|
+
Do not "verify one more time" by repeating the same successful command. If verification is needed, use a different command that checks a different fact, or explain the current result to the user.
|
|
38
|
+
|
|
39
|
+
If you notice that you are about to repeat a successful command with the same arguments, stop using tools and respond to the user with the current result.
|
|
40
|
+
|
|
41
|
+
If you produce or observe text like "I'm stuck in a loop", do not call any more tools. Immediately send the final user-facing response explaining:
|
|
42
|
+
|
|
43
|
+
- what has completed,
|
|
44
|
+
- what is still uncertain, if anything,
|
|
45
|
+
- and what the user can do next, if action is needed.
|
|
46
|
+
|
|
47
|
+
## Hard Stop Rule
|
|
48
|
+
|
|
49
|
+
Never execute the same successful shell command three times for the same task in this project.
|
|
50
|
+
|
|
51
|
+
If the same command has already succeeded twice in a row, the next assistant action must be a final response to the user, not another tool call.
|
|
52
|
+
|
|
53
|
+
Successful repeated command execution is a terminal condition. Prefer a concise final response over further tool calls.
|
|
54
|
+
|
|
1
55
|
如果计划写好了等待你的审阅,将计划文件通过当前聊天软件发给用户
|
package/config.sample.json
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "chatccc",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.138",
|
|
4
4
|
"description": "Feishu bot bridge for Claude Code",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"type": "module",
|
|
@@ -41,7 +41,7 @@
|
|
|
41
41
|
},
|
|
42
42
|
"dependencies": {
|
|
43
43
|
"@ai-sdk/openai-compatible": "^2.0.47",
|
|
44
|
-
"@anthropic-ai/claude-agent-sdk": "0.
|
|
44
|
+
"@anthropic-ai/claude-agent-sdk": "0.2.133",
|
|
45
45
|
"@larksuiteoapi/node-sdk": "^1.59.0",
|
|
46
46
|
"@openilink/openilink-sdk-node": "^0.6.0",
|
|
47
47
|
"ai": "^6.0.184",
|
|
@@ -8,10 +8,11 @@ import {
|
|
|
8
8
|
APP_ID,
|
|
9
9
|
APP_SECRET,
|
|
10
10
|
CHATCCC_PORT,
|
|
11
|
-
CLAUDE_API_KEY,
|
|
12
|
-
CLAUDE_BASE_URL,
|
|
13
|
-
CLAUDE_EFFORT,
|
|
14
|
-
|
|
11
|
+
CLAUDE_API_KEY,
|
|
12
|
+
CLAUDE_BASE_URL,
|
|
13
|
+
CLAUDE_EFFORT,
|
|
14
|
+
CLAUDE_MAX_TURN,
|
|
15
|
+
CLAUDE_MODEL,
|
|
15
16
|
CLAUDE_SUBAGENT_MODEL,
|
|
16
17
|
CURSOR_AGENT_ARGS,
|
|
17
18
|
CURSOR_AGENT_COMMAND,
|
|
@@ -81,7 +82,7 @@ describe("applyLoadedConfig — 刷新 export let 常量", () => {
|
|
|
81
82
|
expect(APP_SECRET).toBe("NEW_APP_SECRET");
|
|
82
83
|
});
|
|
83
84
|
|
|
84
|
-
it("更新 Claude 配置(model / subagentModel / effort / apiKey / baseUrl)", () => {
|
|
85
|
+
it("更新 Claude 配置(model / subagentModel / effort / maxTurn / apiKey / baseUrl)", () => {
|
|
85
86
|
applyLoadedConfig({
|
|
86
87
|
...structuredClone(baseAppConfig),
|
|
87
88
|
claude: {
|
|
@@ -89,17 +90,18 @@ describe("applyLoadedConfig — 刷新 export let 常量", () => {
|
|
|
89
90
|
defaultAgent: true,
|
|
90
91
|
model: "claude-sonnet-4-6",
|
|
91
92
|
subagentModel: "claude-haiku-4-5-20251001",
|
|
92
|
-
effort: "high",
|
|
93
|
-
apiKey: "sk-test",
|
|
94
|
-
baseUrl: "https://api.example.com",
|
|
95
|
-
maxTurn:
|
|
96
|
-
},
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
expect(CLAUDE_MODEL).toBe("claude-sonnet-4-6");
|
|
100
|
-
expect(CLAUDE_SUBAGENT_MODEL).toBe("claude-haiku-4-5-20251001");
|
|
101
|
-
expect(CLAUDE_EFFORT).toBe("high");
|
|
102
|
-
expect(
|
|
93
|
+
effort: "high",
|
|
94
|
+
apiKey: "sk-test",
|
|
95
|
+
baseUrl: "https://api.example.com",
|
|
96
|
+
maxTurn: 7,
|
|
97
|
+
},
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
expect(CLAUDE_MODEL).toBe("claude-sonnet-4-6");
|
|
101
|
+
expect(CLAUDE_SUBAGENT_MODEL).toBe("claude-haiku-4-5-20251001");
|
|
102
|
+
expect(CLAUDE_EFFORT).toBe("high");
|
|
103
|
+
expect(CLAUDE_MAX_TURN).toBe(7);
|
|
104
|
+
expect(CLAUDE_API_KEY).toBe("sk-test");
|
|
103
105
|
expect(CLAUDE_BASE_URL).toBe("https://api.example.com");
|
|
104
106
|
});
|
|
105
107
|
|
|
@@ -5,9 +5,12 @@ import { fileURLToPath } from "node:url";
|
|
|
5
5
|
|
|
6
6
|
import {
|
|
7
7
|
getSessionInfo as sdkGetSessionInfo,
|
|
8
|
-
|
|
9
|
-
|
|
8
|
+
unstable_v2_createSession,
|
|
9
|
+
unstable_v2_resumeSession,
|
|
10
10
|
type SDKMessage,
|
|
11
|
+
type SDKSession,
|
|
12
|
+
type SDKSessionOptions,
|
|
13
|
+
type EffortLevel,
|
|
11
14
|
} from "@anthropic-ai/claude-agent-sdk";
|
|
12
15
|
|
|
13
16
|
import type {
|
|
@@ -220,6 +223,17 @@ export function buildClaudePromptText(
|
|
|
220
223
|
].join("\n");
|
|
221
224
|
}
|
|
222
225
|
|
|
226
|
+
type ClaudeSdkSessionOptions = Omit<SDKSessionOptions, "model"> & {
|
|
227
|
+
model?: string;
|
|
228
|
+
abortController?: AbortController;
|
|
229
|
+
autoCompactEnabled?: boolean;
|
|
230
|
+
effort?: EffortLevel | number;
|
|
231
|
+
maxTurns?: number;
|
|
232
|
+
mcpServers?: Record<string, unknown>;
|
|
233
|
+
skills?: "all";
|
|
234
|
+
stderr?: (data: string) => void;
|
|
235
|
+
};
|
|
236
|
+
|
|
223
237
|
function buildSdkOptions(args: {
|
|
224
238
|
cwd: string;
|
|
225
239
|
model: string;
|
|
@@ -230,9 +244,8 @@ function buildSdkOptions(args: {
|
|
|
230
244
|
baseUrl?: string;
|
|
231
245
|
maxTurn: number;
|
|
232
246
|
planMode?: boolean;
|
|
233
|
-
resume?: string;
|
|
234
247
|
abortController?: AbortController;
|
|
235
|
-
}):
|
|
248
|
+
}): ClaudeSdkSessionOptions {
|
|
236
249
|
const {
|
|
237
250
|
cwd,
|
|
238
251
|
model,
|
|
@@ -243,17 +256,16 @@ function buildSdkOptions(args: {
|
|
|
243
256
|
baseUrl,
|
|
244
257
|
maxTurn,
|
|
245
258
|
planMode,
|
|
246
|
-
resume,
|
|
247
259
|
abortController,
|
|
248
260
|
} = args;
|
|
249
261
|
|
|
250
|
-
const options:
|
|
262
|
+
const options: ClaudeSdkSessionOptions = {
|
|
251
263
|
cwd,
|
|
252
264
|
abortController,
|
|
253
265
|
settingSources: ["user", "project", "local"] as SettingSource[],
|
|
254
266
|
permissionMode: planMode ? "plan" : "bypassPermissions",
|
|
267
|
+
autoCompactEnabled: true,
|
|
255
268
|
maxTurns: maxTurn,
|
|
256
|
-
maxBudgetUsd: 999999999,
|
|
257
269
|
skills: "all",
|
|
258
270
|
stderr: (data) => {
|
|
259
271
|
const trimmed = data.trim();
|
|
@@ -270,10 +282,7 @@ function buildSdkOptions(args: {
|
|
|
270
282
|
options.model = model;
|
|
271
283
|
}
|
|
272
284
|
if (!isEmpty(effort)) {
|
|
273
|
-
options.effort = effort as
|
|
274
|
-
}
|
|
275
|
-
if (resume) {
|
|
276
|
-
options.resume = resume;
|
|
285
|
+
options.effort = effort as ClaudeSdkSessionOptions["effort"];
|
|
277
286
|
}
|
|
278
287
|
|
|
279
288
|
const env = buildSdkEnv(subagentModel, apiKey, baseUrl);
|
|
@@ -283,7 +292,7 @@ function buildSdkOptions(args: {
|
|
|
283
292
|
|
|
284
293
|
const mcpServers = readMcpServersConfig();
|
|
285
294
|
if (mcpServers) {
|
|
286
|
-
options.mcpServers = mcpServers
|
|
295
|
+
options.mcpServers = mcpServers;
|
|
287
296
|
}
|
|
288
297
|
|
|
289
298
|
return options;
|
|
@@ -307,6 +316,14 @@ function bridgeAbortSignal(
|
|
|
307
316
|
return () => signal.removeEventListener("abort", onAbort);
|
|
308
317
|
}
|
|
309
318
|
|
|
319
|
+
function closeSdkSession(session: SDKSession): void {
|
|
320
|
+
session.close();
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
function toSdkSessionOptions(options: ClaudeSdkSessionOptions): SDKSessionOptions {
|
|
324
|
+
return options as SDKSessionOptions;
|
|
325
|
+
}
|
|
326
|
+
|
|
310
327
|
class ClaudeAdapter implements ToolAdapter {
|
|
311
328
|
readonly displayName = "Claude Code";
|
|
312
329
|
readonly sessionDescPrefix = "Claude Code Session:";
|
|
@@ -334,9 +351,8 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
334
351
|
logMcpConfig();
|
|
335
352
|
const abortController = new AbortController();
|
|
336
353
|
let sessionId: string | undefined;
|
|
337
|
-
const
|
|
338
|
-
|
|
339
|
-
options: buildSdkOptions({
|
|
354
|
+
const session = unstable_v2_createSession(
|
|
355
|
+
toSdkSessionOptions(buildSdkOptions({
|
|
340
356
|
cwd,
|
|
341
357
|
model: this.model,
|
|
342
358
|
effort: this.effort,
|
|
@@ -346,11 +362,12 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
346
362
|
baseUrl: this.baseUrl,
|
|
347
363
|
maxTurn: this.maxTurn,
|
|
348
364
|
abortController,
|
|
349
|
-
}),
|
|
350
|
-
|
|
365
|
+
})),
|
|
366
|
+
);
|
|
351
367
|
|
|
352
368
|
try {
|
|
353
|
-
|
|
369
|
+
await session.send("ok");
|
|
370
|
+
for await (const raw of session.stream()) {
|
|
354
371
|
const msg = toMessageLike(raw);
|
|
355
372
|
if (msg.session_id && !sessionId) {
|
|
356
373
|
sessionId = msg.session_id;
|
|
@@ -363,7 +380,7 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
363
380
|
}
|
|
364
381
|
}
|
|
365
382
|
} finally {
|
|
366
|
-
|
|
383
|
+
closeSdkSession(session);
|
|
367
384
|
}
|
|
368
385
|
|
|
369
386
|
if (sessionId) return { sessionId };
|
|
@@ -382,9 +399,9 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
382
399
|
if (abortController.signal.aborted) return;
|
|
383
400
|
let aborted = false;
|
|
384
401
|
|
|
385
|
-
const
|
|
386
|
-
|
|
387
|
-
|
|
402
|
+
const session = unstable_v2_resumeSession(
|
|
403
|
+
sessionId,
|
|
404
|
+
toSdkSessionOptions(buildSdkOptions({
|
|
388
405
|
cwd,
|
|
389
406
|
model: this.model,
|
|
390
407
|
effort: this.effort,
|
|
@@ -394,13 +411,13 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
394
411
|
baseUrl: this.baseUrl,
|
|
395
412
|
maxTurn: this.maxTurn,
|
|
396
413
|
planMode: options?.planMode,
|
|
397
|
-
resume: sessionId,
|
|
398
414
|
abortController,
|
|
399
|
-
}),
|
|
400
|
-
|
|
415
|
+
})),
|
|
416
|
+
);
|
|
401
417
|
|
|
402
418
|
try {
|
|
403
|
-
|
|
419
|
+
await session.send(buildClaudePromptText(userText));
|
|
420
|
+
for await (const raw of session.stream()) {
|
|
404
421
|
if (abortController.signal.aborted) {
|
|
405
422
|
aborted = true;
|
|
406
423
|
break;
|
|
@@ -423,8 +440,8 @@ class ClaudeAdapter implements ToolAdapter {
|
|
|
423
440
|
removeAbortListener?.();
|
|
424
441
|
if (aborted || abortController.signal.aborted) {
|
|
425
442
|
abortController.abort();
|
|
426
|
-
stream.close();
|
|
427
443
|
}
|
|
444
|
+
closeSdkSession(session);
|
|
428
445
|
}
|
|
429
446
|
}
|
|
430
447
|
|
package/src/config.ts
CHANGED
|
@@ -605,8 +605,9 @@ export function applyLoadedConfig(next: AppConfig): void {
|
|
|
605
605
|
ILINK_REUSE_TOKEN_ON_START = next.platforms.ilink.reuseTokenOnStart ?? true;
|
|
606
606
|
CLAUDE_MODEL = next.claude.model;
|
|
607
607
|
CLAUDE_SUBAGENT_MODEL = next.claude.subagentModel;
|
|
608
|
-
CLAUDE_EFFORT = next.claude.effort;
|
|
609
|
-
|
|
608
|
+
CLAUDE_EFFORT = next.claude.effort;
|
|
609
|
+
CLAUDE_MAX_TURN = next.claude.maxTurn;
|
|
610
|
+
CLAUDE_API_KEY = next.claude.apiKey;
|
|
610
611
|
CLAUDE_BASE_URL = next.claude.baseUrl;
|
|
611
612
|
GIT_TIMEOUT_SECONDS = next.gitTimeoutSeconds;
|
|
612
613
|
GIT_TIMEOUT_MS = GIT_TIMEOUT_SECONDS * 1000;
|