chatccc 0.2.139 → 0.2.140

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,13 +1,3 @@
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
1
  ## Repeated Successful Command Guard
12
2
 
13
3
  When working in this project through Claude Agent SDK, repeated successful shell commands are a completion signal, not a reason to keep using tools.
@@ -1 +0,0 @@
1
- 如果计划写好了等待你的审阅,将计划文件通过当前聊天软件发给用户
@@ -1 +0,0 @@
1
- 如果计划写好了等待你的审阅,将计划文件通过当前聊天软件发给用户
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "chatccc",
3
- "version": "0.2.139",
3
+ "version": "0.2.140",
4
4
  "description": "Feishu bot bridge for Claude Code",
5
5
  "license": "Apache-2.0",
6
6
  "type": "module",
@@ -467,7 +467,37 @@ describe("buildClaudePromptText", () => {
467
467
 
468
468
  describe("buildSdkEnv", () => {
469
469
  it("returns undefined when no SDK env override is configured", () => {
470
- expect(buildSdkEnv("", " ", undefined)).toBeUndefined();
470
+ const originalPath = process.env.PATH;
471
+ try {
472
+ process.env.PATH = process.platform === "win32"
473
+ ? "C:\\Program Files\\Git\\usr\\bin;C:\\Windows\\System32"
474
+ : "/usr/bin:/bin";
475
+ expect(buildSdkEnv("", " ", undefined)).toBeUndefined();
476
+ } finally {
477
+ process.env.PATH = originalPath;
478
+ }
479
+ });
480
+
481
+ it("prefers Git Bash before WindowsApps for Claude SDK subprocesses on Windows", () => {
482
+ if (process.platform !== "win32") return;
483
+ const originalPath = process.env.PATH;
484
+ try {
485
+ process.env.PATH = [
486
+ "C:\\Users\\weizhangjian\\AppData\\Local\\Microsoft\\WindowsApps",
487
+ "C:\\Program Files\\Git\\usr\\bin",
488
+ "%PATH%",
489
+ "C:\\Windows\\System32",
490
+ ].join(";");
491
+
492
+ const env = buildSdkEnv("", "", "");
493
+
494
+ expect(env).toBeDefined();
495
+ const parts = env!.PATH!.split(";");
496
+ expect(parts[0]).toBe("C:\\Program Files\\Git\\usr\\bin");
497
+ expect(parts).not.toContain("%PATH%");
498
+ } finally {
499
+ process.env.PATH = originalPath;
500
+ }
471
501
  });
472
502
 
473
503
  it("sets requested SDK env overrides and removes conflicting Claude auth/model vars", () => {
@@ -1,6 +1,6 @@
1
1
  import { existsSync, readFileSync } from "node:fs";
2
2
  import { homedir } from "node:os";
3
- import { dirname, join } from "node:path";
3
+ import { delimiter, dirname, join } from "node:path";
4
4
  import { fileURLToPath } from "node:url";
5
5
 
6
6
  import {
@@ -82,23 +82,73 @@ export function buildSdkEnv(
82
82
  const apiKeyTrim = (apiKey ?? "").trim();
83
83
  const baseUrlTrim = (baseUrl ?? "").trim();
84
84
 
85
- if (!subagentModelTrim && !apiKeyTrim && !baseUrlTrim) return undefined;
86
-
87
85
  const env: Record<string, string | undefined> = { ...process.env };
88
- delete env.ANTHROPIC_AUTH_TOKEN;
89
- delete env.CLAUDE_CODE_OAUTH_TOKEN;
90
- delete env.ANTHROPIC_MODEL;
91
- delete env.ANTHROPIC_DEFAULT_OPUS_MODEL;
92
- delete env.ANTHROPIC_DEFAULT_SONNET_MODEL;
93
- delete env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
94
- delete env.CLAUDE_CODE_EFFORT_LEVEL;
95
- delete env.CLAUDE_CODE_SUBAGENT_MODEL;
86
+ let mutated = preferGitBashOnWindows(env);
87
+
88
+ if (subagentModelTrim || apiKeyTrim || baseUrlTrim) {
89
+ delete env.ANTHROPIC_AUTH_TOKEN;
90
+ delete env.CLAUDE_CODE_OAUTH_TOKEN;
91
+ delete env.ANTHROPIC_MODEL;
92
+ delete env.ANTHROPIC_DEFAULT_OPUS_MODEL;
93
+ delete env.ANTHROPIC_DEFAULT_SONNET_MODEL;
94
+ delete env.ANTHROPIC_DEFAULT_HAIKU_MODEL;
95
+ delete env.CLAUDE_CODE_EFFORT_LEVEL;
96
+ delete env.CLAUDE_CODE_SUBAGENT_MODEL;
97
+ mutated = true;
98
+ }
96
99
 
97
100
  if (subagentModelTrim) env.CLAUDE_CODE_SUBAGENT_MODEL = subagentModelTrim;
98
101
  if (apiKeyTrim) env.ANTHROPIC_API_KEY = apiKeyTrim;
99
102
  if (baseUrlTrim) env.ANTHROPIC_BASE_URL = baseUrlTrim;
100
103
 
101
- return env;
104
+ return mutated ? env : undefined;
105
+ }
106
+
107
+ function preferGitBashOnWindows(env: Record<string, string | undefined>): boolean {
108
+ if (process.platform !== "win32") return false;
109
+
110
+ const pathKey = Object.keys(env).find((key) => key.toLowerCase() === "path") ?? "PATH";
111
+ const rawPath = env[pathKey];
112
+ if (!rawPath) return false;
113
+
114
+ const parts = rawPath.split(delimiter).filter((part) => part && part !== "%PATH%");
115
+ const preferred = findPreferredGitBashPath(parts);
116
+ if (!preferred) {
117
+ const nextPath = parts.join(delimiter);
118
+ if (nextPath !== rawPath) {
119
+ env[pathKey] = nextPath;
120
+ return true;
121
+ }
122
+ return false;
123
+ }
124
+
125
+ const preferredLower = preferred.toLowerCase();
126
+ const reordered = [
127
+ preferred,
128
+ ...parts.filter((part) => part.toLowerCase() !== preferredLower),
129
+ ];
130
+ const nextPath = reordered.join(delimiter);
131
+ if (nextPath === rawPath) return false;
132
+ env[pathKey] = nextPath;
133
+ return true;
134
+ }
135
+
136
+ function findPreferredGitBashPath(pathParts: string[]): string | undefined {
137
+ const programFilesGit = join(
138
+ process.env.ProgramFiles ?? "C:\\Program Files",
139
+ "Git",
140
+ "usr",
141
+ "bin",
142
+ );
143
+ if (existsSync(join(programFilesGit, "bash.exe"))) return programFilesGit;
144
+
145
+ return pathParts.find((part) => {
146
+ if (!/(\\|\/)(Git)(\\|\/)usr(\\|\/)bin$/i.test(part) &&
147
+ !/(\\|\/)Fork(\\|\/)gitInstance(\\|\/)[^\\/]+(\\|\/)bin$/i.test(part)) {
148
+ return false;
149
+ }
150
+ return existsSync(join(part, "bash.exe"));
151
+ });
102
152
  }
103
153
 
104
154
  function readMcpServersConfig(): Record<string, unknown> | undefined {
@@ -1235,11 +1235,10 @@ export async function handleCommand(
1235
1235
  text,
1236
1236
  platform,
1237
1237
  chatId,
1238
- msgTimestamp,
1239
- descriptionTool,
1240
- tid,
1241
- false,
1242
- );
1238
+ msgTimestamp,
1239
+ descriptionTool,
1240
+ tid,
1241
+ );
1243
1242
  logTrace(tid, "DONE", { outcome: "resume_done", sessionId });
1244
1243
  console.log(`[${ts()}] [RESUME] Session ${sessionId} done`);
1245
1244
  } catch (err) {
@@ -1435,11 +1434,10 @@ export async function handleCommand(
1435
1434
  text,
1436
1435
  platform,
1437
1436
  newChatId,
1438
- msgTimestamp,
1439
- tool,
1440
- tid,
1441
- false,
1442
- );
1437
+ msgTimestamp,
1438
+ tool,
1439
+ tid,
1440
+ );
1443
1441
  console.log(`[${ts()}] [AUTO-P2P 5/5] First prompt sent → OK`);
1444
1442
  logTrace(tid, "DONE", { outcome: "auto_new_p2p_prompt_done", newChatId, sessionId, tool });
1445
1443
  } catch (err) {
package/src/session.ts CHANGED
@@ -1013,7 +1013,9 @@ export async function runAgentSession(
1013
1013
  const { title: headerTitle, template: headerTemplate } = formatTerminalHeader(prevState.status);
1014
1014
  const cardContent = truncateContent(prevState.accumulatedContent + prevState.finalReply) || " ";
1015
1015
  const doneCard = buildProgressCard(cardContent, { showStop: false, headerTitle, headerTemplate });
1016
- await pp.cardUpdate(display.cardId, doneCard, nextSeq).catch(() => {});
1016
+ await pp.cardUpdate(display.cardId, doneCard, nextSeq).catch(err => {
1017
+ console.error(`[${ts()}] [DISPLAY] prevState final cardUpdate failed: ${(err as Error).message}`);
1018
+ });
1017
1019
  // cardUpdate IO 期间统一 loop 可能也已处理此 display → 删前检查引用
1018
1020
  const stillOursAfterUpdate = displayCards.get(displayChatId) === display;
1019
1021
  displayCards.delete(displayChatId);
@@ -1344,7 +1346,9 @@ export function startUnifiedDisplayLoop(): void {
1344
1346
  const { title: headerTitle, template: headerTemplate } = formatTerminalHeader(state.status);
1345
1347
  const cardContent = truncateContent(state.accumulatedContent + state.finalReply) || " ";
1346
1348
  const doneCard = buildProgressCard(cardContent, { showStop: false, headerTitle, headerTemplate });
1347
- await p.cardUpdate(display.cardId, doneCard, nextSeq).catch(() => {});
1349
+ await p.cardUpdate(display.cardId, doneCard, nextSeq).catch(err => {
1350
+ console.error(`[${ts()}] [DISPLAY] terminal cardUpdate failed: ${(err as Error).message}`);
1351
+ });
1348
1352
 
1349
1353
  // 若 session 仍在 activePrompts 中,说明 runAgentSession 的 finally
1350
1354
  // 还没执行,当前 stream state 可能是 stopSession fire-and-forget
@@ -1419,7 +1423,9 @@ export function startUnifiedDisplayLoop(): void {
1419
1423
  const oldSeqBase = display.sequence;
1420
1424
  const oldContent = state.accumulatedContent + state.finalReply;
1421
1425
  const oldCard = buildProgressCard(truncateContent(oldContent) || " ", { showStop: false, headerTitle: "生成中(上轮)" });
1422
- await p.cardUpdate(display.cardId, oldCard, oldSeqBase + 1).catch(() => {});
1426
+ await p.cardUpdate(display.cardId, oldCard, oldSeqBase + 1).catch(err => {
1427
+ console.error(`[${ts()}] [DISPLAY] rotation old cardUpdate failed: ${(err as Error).message}`);
1428
+ });
1423
1429
  markCardDone(sessionId, display.turnCount, display.cardId).catch(() => {});
1424
1430
  const newCardId = await p.cardCreate(buildProgressCard(
1425
1431
  "",