feat(workflow): 复刻 ultracode 手册并修复 worktree/inline/opt-in 三处缺口

围绕 ultracode skill 审查 agent 系统一致性后:
- ultracode.ts: 用系统提示版完整 Workflow 编排手册替换中文精简版
- HIGH#1 isolation:'worktree': claudeCodeBackend.run() 用 createAgentWorktree +
  runWithCwdOverride 包裹 runAgent + finally 清理实现真正的 cwd 隔离;slug 用
  sha256(runId:agentId) 派生以匹配 cleanupStaleAgentWorktrees 清理正则
  (修 runId 为 w+base36 非 UUID 导致的泄漏盲区);worktree.ts 注释同步修正
- HIGH#2 inline 持久化: 新增 persistInlineScript,WorkflowTool + service 两条
  inline 路径对称持久化到 .claude/workflow-runs/<runId>/script.js,返回可复用
  scriptPath(闭环 inline→编辑→scriptPath 重提迭代循环)
- HIGH#3 opt-in 分工: ultracode/WorkflowTool/effort 注明 session reminder 由
  harness 注入,repo 内无 ultracode 信号,保持 feature('WORKFLOW_SCRIPTS') +
  isEnabled 两层 gate,不自造注入
- 测试: 新增 persistInline.test.ts;扩展 claudeCodeBackend(isolation 4 用例)/
  WorkflowTool(inline)/service(scriptPath)/ultracode(harness)

含配套 workflow engine/panel 完善与 run-state-persistence design doc。

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
claude-code-best
2026-06-13 23:04:33 +08:00
parent d236880bc3
commit 54d2bf6f12
32 changed files with 2253 additions and 196 deletions

View File

@@ -3,12 +3,15 @@ import type { AgentProgress, RunProgress } from '../progress/store.js'
import {
STATUS_DOT,
RUN_STATUS_COLOR,
RUN_STATUS_TEXT,
PHASE_MARK,
PHASE_COLOR,
agentVisual,
formatTokenCount,
agentMetaText,
} from '../panel/status.js'
test('STATUS_DOT / RUN_STATUS_COLOR 覆盖四种 run 状态且为非空字符', () => {
test('STATUS_DOT / RUN_STATUS_COLOR / RUN_STATUS_TEXT 覆盖四种 run 状态', () => {
const statuses: RunProgress['status'][] = [
'running',
'completed',
@@ -18,11 +21,14 @@ test('STATUS_DOT / RUN_STATUS_COLOR 覆盖四种 run 状态且为非空字符',
for (const s of statuses) {
expect(STATUS_DOT[s].length).toBeGreaterThan(0)
expect(RUN_STATUS_COLOR[s]).toBeTruthy()
expect(RUN_STATUS_TEXT[s].length).toBeGreaterThan(0)
}
expect(STATUS_DOT.running).toBe('●')
expect(STATUS_DOT.completed).toBe('✓')
expect(STATUS_DOT.failed).toBe('✗')
expect(STATUS_DOT.killed).toBe('■')
expect(RUN_STATUS_TEXT.completed).toBe('done')
expect(RUN_STATUS_TEXT.running).toBe('running')
})
test('PHASE_MARK / PHASE_COLOR 覆盖 running/done/pending', () => {
@@ -32,44 +38,51 @@ test('PHASE_MARK / PHASE_COLOR 覆盖 running/done/pending', () => {
expect(PHASE_COLOR.pending).toBe('subtle')
})
test('agentVisualrunning → ● warning running', () => {
test('agentVisualrunning → ● warning', () => {
const a: AgentProgress = { id: 1, status: 'running' }
expect(agentVisual(a)).toEqual({
mark: '●',
color: 'warning',
suffix: 'running',
})
expect(agentVisual(a)).toEqual({ mark: '●', color: 'warning' })
})
test('agentVisualdone·object → ✓ success object', () => {
test('agentVisualdone·ok → ✓ success(不再带 outputShape 后缀)', () => {
const a: AgentProgress = {
id: 1,
status: 'done',
resultKind: 'ok',
outputShape: 'object',
}
expect(agentVisual(a)).toEqual({
mark: '✓',
color: 'success',
suffix: 'object',
})
expect(agentVisual(a)).toEqual({ mark: '✓', color: 'success' })
})
test('agentVisualdone·text → ✓ success text', () => {
test('agentVisualdead → ✗ error', () => {
const a: AgentProgress = { id: 1, status: 'done', resultKind: 'dead' }
expect(agentVisual(a)).toEqual({ mark: '✗', color: 'error' })
})
test('formatTokenCount<1000 原值≥1000 保留 1 位小数 + k', () => {
expect(formatTokenCount(undefined)).toBe('0')
expect(formatTokenCount(0)).toBe('0')
expect(formatTokenCount(42)).toBe('42')
expect(formatTokenCount(1000)).toBe('1.0k')
expect(formatTokenCount(22900)).toBe('22.9k')
})
test('agentMetaTextmodel · Nk tok · N tool', () => {
const a: AgentProgress = {
id: 1,
status: 'done',
resultKind: 'ok',
outputShape: 'text',
model: 'glm-5.2',
tokenCount: 22900,
toolCount: 1,
}
expect(agentVisual(a)).toEqual({
mark: '✓',
color: 'success',
suffix: 'text',
})
expect(agentMetaText(a)).toBe('glm-5.2 · 22.9k tok · 1 tool')
})
test('agentVisualdead → ✗ error dead', () => {
const a: AgentProgress = { id: 1, status: 'done', resultKind: 'dead' }
expect(agentVisual(a)).toEqual({ mark: '✗', color: 'error', suffix: 'dead' })
test('agentMetaText无 model 时省略前段', () => {
const a: AgentProgress = {
id: 1,
status: 'running',
tokenCount: 500,
toolCount: 2,
}
expect(agentMetaText(a)).toBe('500 tok · 2 tool')
})