mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-17 13:55:50 +00:00
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:
@@ -1,5 +1,5 @@
|
||||
import { expect, test } from 'bun:test'
|
||||
import { mkdtemp, mkdir, rm, writeFile } from 'node:fs/promises'
|
||||
import { mkdtemp, mkdir, readFile, rm, writeFile } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
import { createWorkflowTool } from '../tool/WorkflowTool.js'
|
||||
@@ -74,6 +74,34 @@ test('call 返回 launch 消息并在后台完成', async () => {
|
||||
}
|
||||
})
|
||||
|
||||
test('inline script 持久化到 run 目录,返回真实 scriptPath', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'wf-tool-'))
|
||||
try {
|
||||
const { ports } = mockPorts(
|
||||
dir,
|
||||
new Map([['x', { kind: 'ok', output: 'x', usage: { outputTokens: 1 } }]]),
|
||||
)
|
||||
const tool = createWorkflowTool(ports)
|
||||
const res = await tool.call(
|
||||
{ script: `return agent('x')` },
|
||||
undefined,
|
||||
undefined,
|
||||
undefined,
|
||||
)
|
||||
const expectedPath = join(
|
||||
dir,
|
||||
'.claude',
|
||||
'workflow-runs',
|
||||
'run-x',
|
||||
'script.js',
|
||||
)
|
||||
expect(res.data.output).toContain(expectedPath)
|
||||
expect(await readFile(expectedPath, 'utf-8')).toBe(`return agent('x')`)
|
||||
} finally {
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('缺少 script/name/scriptPath → 返回错误(不进后台)', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'wf-tool-'))
|
||||
try {
|
||||
|
||||
41
packages/workflow-engine/src/__tests__/persistInline.test.ts
Normal file
41
packages/workflow-engine/src/__tests__/persistInline.test.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import { expect, test } from 'bun:test'
|
||||
import { mkdtemp, readFile, rm } from 'node:fs/promises'
|
||||
import { tmpdir } from 'node:os'
|
||||
import { join } from 'node:path'
|
||||
|
||||
import { persistInlineScript } from '../tool/persistInline.js'
|
||||
|
||||
test('持久化到 <cwd>/.claude/workflow-runs/<runId>/script.js 并返回路径', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'wf-pi-'))
|
||||
try {
|
||||
const path = await persistInlineScript('return 1', 'r1', dir)
|
||||
expect(path).toBe(join(dir, '.claude', 'workflow-runs', 'r1', 'script.js'))
|
||||
expect(await readFile(path, 'utf-8')).toBe('return 1')
|
||||
} finally {
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('同 runId 重复写覆盖(mkdir 幂等,不抛错)', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'wf-pi-'))
|
||||
try {
|
||||
await persistInlineScript('first', 'r2', dir)
|
||||
const path = await persistInlineScript('second', 'r2', dir)
|
||||
expect(await readFile(path, 'utf-8')).toBe('second')
|
||||
} finally {
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
|
||||
test('不同 runId 互不干扰(各自独立子目录)', async () => {
|
||||
const dir = await mkdtemp(join(tmpdir(), 'wf-pi-'))
|
||||
try {
|
||||
const p1 = await persistInlineScript('a', 'run-a', dir)
|
||||
const p2 = await persistInlineScript('b', 'run-b', dir)
|
||||
expect(p1).not.toBe(p2)
|
||||
expect(await readFile(p1, 'utf-8')).toBe('a')
|
||||
expect(await readFile(p2, 'utf-8')).toBe('b')
|
||||
} finally {
|
||||
await rm(dir, { recursive: true, force: true })
|
||||
}
|
||||
})
|
||||
Reference in New Issue
Block a user