claude-code-best/src/jobs/classifier.ts
claude-code-best c8d08d235b
Feat/integrate lint preview (#285)
* feat: 适配 zed acp 协议

* docs: 完善 acp 文档

* feat: integrate feature branches + daemon/job 命令层级化 + 跨平台后台引擎

Cherry-picked from origin/lint/preview (637c908), excluding lint-only changes.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: correct detectMimeFromBase64 to decode raw bytes from base64

Cherry-picked from origin/lint/preview (ee36954).

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: daemon 子进程 spawn 跨平台修复 + CliLaunchSpec 集中化重构

Cherry-picked from origin/lint/preview (c5f52cd), excluding lint-only formatting changes.

- 新建 src/utils/cliLaunch.ts: 集中化 CLI 子进程启动层
- 修复 --daemon-worker=kind 等号格式解析
- 修复 daemon/bg fast path 缺少 setShellIfWindows()
- 修复 checkPathExists 用 existsSync 替代 execSync('dir')
- 7 个 spawn 站点迁移到 CliLaunchSpec

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: merge tsconfig.base.json into tsconfig.json with full compiler options

The cherry-pick from 637c908 dropped jsx/strict/etc settings when removing
tsconfig.base.json. This commit restores them in a single tsconfig.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

* fix: merge tsconfig.base.json into tsconfig.json with full compiler options

The cherry-pick from 637c908 dropped jsx/strict/etc settings when removing
tsconfig.base.json. This commit restores them in a single tsconfig.json.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-16 20:59:29 +08:00

68 lines
2.0 KiB
TypeScript

import { readFileSync, writeFileSync } from 'fs'
import { join } from 'path'
import type { AssistantMessage } from '../types/message.js'
/**
* Classify the job status from the turn's assistant messages and update state.json.
*
* Called by stopHooks.ts after each repl_main_thread turn when CLAUDE_JOB_DIR is set.
* Only the main thread calls this (not subagents).
*
* @param jobDir - Path to the job directory (from CLAUDE_JOB_DIR env)
* @param assistantMessages - Assistant messages from this turn
*/
export async function classifyAndWriteState(
jobDir: string,
assistantMessages: AssistantMessage[],
): Promise<void> {
const stateFile = join(jobDir, 'state.json')
let state: Record<string, unknown>
try {
state = JSON.parse(readFileSync(stateFile, 'utf-8'))
} catch {
// No state file or corrupt — not a valid job directory
return
}
const newStatus = classifyStatus(assistantMessages)
state.status = newStatus
state.updatedAt = new Date().toISOString()
writeFileSync(stateFile, JSON.stringify(state, null, 2), 'utf-8')
}
/**
* Determine job status from assistant messages.
*
* - Has tool_use blocks → still running (tools executing)
* - stop_reason === 'end_turn' → completed (model finished)
* - Otherwise → running
*/
function classifyStatus(messages: AssistantMessage[]): string {
if (messages.length === 0) return 'running'
const lastMessage = messages[messages.length - 1]!
const content = lastMessage.message?.content
// Check if the last message has tool_use blocks (still executing)
if (Array.isArray(content)) {
const hasToolUse = content.some(
block =>
typeof block === 'object' &&
block !== null &&
'type' in block &&
block.type === 'tool_use',
)
if (hasToolUse) return 'running'
}
// Check stop_reason via index signature
const stopReason = (lastMessage.message as Record<string, unknown>)
?.stop_reason
if (stopReason === 'end_turn') return 'completed'
if (stopReason === 'max_tokens') return 'running'
return 'running'
}