diff --git a/CHANGELOG.md b/CHANGELOG.md index f2649e6..9e3b0b9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,19 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 --- +## [1.7.97] - 2026-03-26 + +### 🐛 修复 + +- **Gemini `-p -` 显示修正**:`Command:` 行现在显示真实的任务文本而非 `-p -`,与实际执行一致,消除误导 +- **Session-ID 早期输出**:wrapper 在 backend 返回 `session_started` 事件时立即输出 `Session-ID: xxx` 到 stderr,不再等到任务完成才输出。即使任务超时/失败,Claude 也能拿到真正的 session ID 来 resume,不再误用 PID + +### 🔄 变更 + +- **Binary 版本升级**:codeagent-wrapper `5.8.0` → `5.9.0` + +--- + ## [1.7.92] - 2026-03-25 ### ✨ 新功能 diff --git a/CLAUDE.md b/CLAUDE.md index 0d4c056..fc735a5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -2,7 +2,7 @@ > [根目录](../CLAUDE.md) > **skills-v2** -**Last Updated**: 2026-03-25 (v1.7.92) +**Last Updated**: 2026-03-26 (v1.7.97) --- @@ -10,6 +10,11 @@ > 完整变更历史请查看 [CHANGELOG.md](./CHANGELOG.md) +### 2026-03-26 (v1.7.97) +- 🐛 **Gemini `-p -` 显示修正**:`Command:` 行显示真实任务文本而非 `-p -`,消除误导 +- 🐛 **Session-ID 早期输出**:wrapper 在 `session_started` 时立即输出 `Session-ID:` 到 stderr,防止超时后 Claude 误用 PID resume +- 🔄 **Binary 版本升级**:`5.8.0` → `5.9.0` + ### 2026-03-25 (v1.7.92) - ✨ **初始化交互重构**:3 步流程(API 提供方 → MCP 多选 → 性能模式),赞助商预留位,MCP 多选共存 - 🐛 **第三方 API 修复**:`ANTHROPIC_API_KEY` → `ANTHROPIC_AUTH_TOKEN`,修复 `/login` 问题 diff --git a/codeagent-wrapper/executor.go b/codeagent-wrapper/executor.go index 3a5c2c4..87d0edc 100644 --- a/codeagent-wrapper/executor.go +++ b/codeagent-wrapper/executor.go @@ -1069,6 +1069,23 @@ func runCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe } } + // Emit Session-ID to stderr as soon as the backend reports it. + // This lets Claude Code capture the real session ID even if the + // task later times out or fails (where the final SESSION_ID: line + // in stdout would never be printed). + // Skip in silent mode (parallel tasks) to avoid polluting stderr. + var sessionIDEmitted bool + var onSessionStartedCallback func(string) + if !silent { + onSessionStartedCallback = func(id string) { + if sessionIDEmitted || id == "" { + return + } + sessionIDEmitted = true + fmt.Fprintf(os.Stderr, " Session-ID: %s\n", id) + } + } + go func() { msg, tid := parseJSONStreamInternalWithContent(stdoutReader, logWarnFn, logInfoFn, func() { select { @@ -1084,7 +1101,7 @@ func runCodexTaskWithContext(parentCtx context.Context, taskSpec TaskSpec, backe if globalWebServer != nil && webSessionID != "" { globalWebServer.EndSession(webSessionID, cfg.Backend) } - }, onContentCallback, onProgressCallback) + }, onContentCallback, onProgressCallback, onSessionStartedCallback) select { case completeSeen <- struct{}{}: default: diff --git a/codeagent-wrapper/main.go b/codeagent-wrapper/main.go index a77eea0..a2dba15 100644 --- a/codeagent-wrapper/main.go +++ b/codeagent-wrapper/main.go @@ -14,7 +14,7 @@ import ( ) const ( - version = "5.8.0" + version = "5.9.0" defaultWorkdir = "." defaultTimeout = 7200 // seconds (2 hours) defaultCoverageTarget = 90.0 @@ -417,7 +417,10 @@ func run() (exitCode int) { useStdin := cfg.ExplicitStdin || shouldUseStdin(taskText, piped) targetArg := taskText - if useStdin { + // Gemini CLI does not support "-" as stdin marker for -p flag. + // Match the geminiDirect logic in executor.go so the display is accurate. + geminiDirect := useStdin && cfg.Backend == "gemini" + if useStdin && !geminiDirect { targetArg = "-" } codexArgs := buildCodexArgsFn(cfg, targetArg) diff --git a/codeagent-wrapper/main_test.go b/codeagent-wrapper/main_test.go index 5b98c88..bfa55d7 100644 --- a/codeagent-wrapper/main_test.go +++ b/codeagent-wrapper/main_test.go @@ -2601,13 +2601,14 @@ func TestRunSilentMode(t *testing.T) { verbose := capture(false) quiet := capture(true) - // After refactoring, logs are only written to file, not stderr - // Both silent and non-silent modes should produce no stderr output + // Silent mode (parallel tasks): no stderr output at all if quiet != "" { t.Fatalf("silent mode should suppress stderr, got: %q", quiet) } - if verbose != "" { - t.Fatalf("non-silent mode should also suppress stderr (logs go to file), got: %q", verbose) + // Non-silent mode (single task): Session-ID is emitted to stderr + // so Claude Code can capture it even if the task later fails/times out + if !strings.Contains(verbose, "Session-ID: silent-session") { + t.Fatalf("non-silent mode should emit Session-ID to stderr, got: %q", verbose) } } @@ -3051,7 +3052,7 @@ func TestVersionFlag(t *testing.T) { } }) - want := "codeagent-wrapper version 5.8.0\n" + want := "codeagent-wrapper version 5.9.0\n" if output != want { t.Fatalf("output = %q, want %q", output, want) @@ -3067,7 +3068,7 @@ func TestVersionShortFlag(t *testing.T) { } }) - want := "codeagent-wrapper version 5.8.0\n" + want := "codeagent-wrapper version 5.9.0\n" if output != want { t.Fatalf("output = %q, want %q", output, want) @@ -3083,7 +3084,7 @@ func TestVersionLegacyAlias(t *testing.T) { } }) - want := "codex-wrapper version 5.8.0\n" + want := "codex-wrapper version 5.9.0\n" if output != want { t.Fatalf("output = %q, want %q", output, want) diff --git a/codeagent-wrapper/parser.go b/codeagent-wrapper/parser.go index 2c81c29..3e4f789 100644 --- a/codeagent-wrapper/parser.go +++ b/codeagent-wrapper/parser.go @@ -97,10 +97,10 @@ type ItemContent struct { } func parseJSONStreamInternal(r io.Reader, warnFn func(string), infoFn func(string), onMessage func(), onComplete func()) (message, threadID string) { - return parseJSONStreamInternalWithContent(r, warnFn, infoFn, onMessage, onComplete, nil, nil) + return parseJSONStreamInternalWithContent(r, warnFn, infoFn, onMessage, onComplete, nil, nil, nil) } -func parseJSONStreamInternalWithContent(r io.Reader, warnFn func(string), infoFn func(string), onMessage func(), onComplete func(), onContent func(content, contentType string), onProgress func(line string)) (message, threadID string) { +func parseJSONStreamInternalWithContent(r io.Reader, warnFn func(string), infoFn func(string), onMessage func(), onComplete func(), onContent func(content, contentType string), onProgress func(line string), onSessionStarted func(id string)) (message, threadID string) { reader := bufio.NewReaderSize(r, jsonLineReaderSize) if warnFn == nil { @@ -168,6 +168,9 @@ func parseJSONStreamInternalWithContent(r io.Reader, warnFn func(string), infoFn // Extract session_id early (works for all backends) if event.SessionID != "" && threadID == "" { threadID = event.SessionID + if onSessionStarted != nil { + onSessionStarted(threadID) + } } // Detect backend type by field presence @@ -205,6 +208,9 @@ func parseJSONStreamInternalWithContent(r io.Reader, warnFn func(string), infoFn threadID = event.ThreadID infoFn(fmt.Sprintf("thread.started event thread_id=%s", threadID)) emitProgress(formatProgressLine("session_started", map[string]string{"id": threadID})) + if onSessionStarted != nil { + onSessionStarted(threadID) + } case "turn.started": emitProgress(formatProgressLine("turn_started", nil)) diff --git a/codeagent-wrapper/parser_unknown_event_test.go b/codeagent-wrapper/parser_unknown_event_test.go index 055002f..65a28fc 100644 --- a/codeagent-wrapper/parser_unknown_event_test.go +++ b/codeagent-wrapper/parser_unknown_event_test.go @@ -52,6 +52,7 @@ func TestParseJSONStreamInternalWithContent_EmitsProgressLines(t *testing.T) { nil, nil, func(line string) { progress = append(progress, line) }, + nil, ) if message != "Done with changes" { diff --git a/package.json b/package.json index 7fd1496..9f4df0f 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "ccg-workflow", - "version": "1.7.96", + "version": "1.7.97", "description": "Claude + Codex + Gemini multi-model collaboration system - smart routing development workflow", "type": "module", "packageManager": "pnpm@10.17.1", diff --git a/src/utils/installer.ts b/src/utils/installer.ts index b3f7a57..200c0d0 100644 --- a/src/utils/installer.ts +++ b/src/utils/installer.ts @@ -50,7 +50,7 @@ export { * Must match the `version` constant in codeagent-wrapper/main.go. * When this differs from the installed binary, update triggers re-download. */ -const EXPECTED_BINARY_VERSION = '5.8.0' +const EXPECTED_BINARY_VERSION = '5.9.0' // ═══════════════════════════════════════════════════════ // Install context — shared across sub-functions