mirror of
https://github.com/claude-code-best/claude-code.git
synced 2026-06-23 08:45:50 +00:00
feat(workflow): add workflow engine, /workflows panel, /ultracode skill
将 feat/sdk-backend 分支中 workflow 相关的 20 个 commit 压缩为单 commit: - 工作流引擎核心:phase / agent / parallel / pipeline 编排原语(packages/workflow-engine/) - /workflows 面板:三区焦点布局(顶部 run tabs + 左侧 phase 侧栏 + 右侧 agent 列表) - /ultracode skill:多 agent workflow 编排入口 - 进度存储 / journal / notification 系统 - WorkflowService 生命周期管理 + SentryErrorBoundary - 脚本沙箱:禁用 dynamic import()、JSON args 防御性归一化 - journal 与 named-workflow 路径统一在 projectRoot - 错误处理:parallel/pipeline hooks 错误日志、failure routing、semaphore abort - workflow 工具升级为 core 工具 + PascalCase 命名 Co-Authored-By: glm-5.1 <zai-org@claude-code-best.win>
This commit is contained in:
91
src/skills/bundled/__tests__/ultracode.test.ts
Normal file
91
src/skills/bundled/__tests__/ultracode.test.ts
Normal file
@@ -0,0 +1,91 @@
|
||||
import { afterEach, describe, expect, test } from 'bun:test'
|
||||
|
||||
import type { PromptCommand } from '../../../types/command.js'
|
||||
import { clearBundledSkills, getBundledSkills } from '../../bundledSkills.js'
|
||||
import { registerUltracodeSkill } from '../ultracode.js'
|
||||
|
||||
// Command is a union; source/getPromptForCommand only exist on the prompt
|
||||
// variant. Narrow via type assertion once we've confirmed type === 'prompt'.
|
||||
function asPrompt(c: { type: string }): PromptCommand {
|
||||
return c as unknown as PromptCommand
|
||||
}
|
||||
|
||||
// bundledSkills is a process-global registry (per CLAUDE.md mock/state rules,
|
||||
// module-level singletons leak across test files in one bun test process).
|
||||
// Clear after each test so `ultracode` never leaks into other suites that
|
||||
// enumerate registered skills (e.g. skill-search prefetch discovery).
|
||||
afterEach(() => {
|
||||
clearBundledSkills()
|
||||
})
|
||||
|
||||
describe('registerUltracodeSkill', () => {
|
||||
test('registers a user-invocable prompt command named ultracode', () => {
|
||||
clearBundledSkills()
|
||||
registerUltracodeSkill()
|
||||
|
||||
const skills = getBundledSkills()
|
||||
const ultracode = skills.find(s => s.name === 'ultracode')
|
||||
expect(ultracode).toBeDefined()
|
||||
expect(ultracode!.type).toBe('prompt')
|
||||
expect(ultracode!.userInvocable).toBe(true)
|
||||
expect(ultracode!.whenToUse).toBeTruthy()
|
||||
expect(ultracode!.description).toContain('workflow')
|
||||
const promptCmd = asPrompt(ultracode!)
|
||||
expect(promptCmd.source).toBe('bundled')
|
||||
})
|
||||
|
||||
test('getPromptForCommand injects the orchestration playbook with key sections', async () => {
|
||||
clearBundledSkills()
|
||||
registerUltracodeSkill()
|
||||
|
||||
const ultracode = getBundledSkills().find(s => s.name === 'ultracode')!
|
||||
const blocks = await asPrompt(ultracode).getPromptForCommand(
|
||||
'',
|
||||
{} as never,
|
||||
)
|
||||
expect(blocks).toHaveLength(1)
|
||||
expect(blocks[0]!.type).toBe('text')
|
||||
|
||||
const text = (blocks[0] as { type: 'text'; text: string }).text
|
||||
expect(text).toContain('编排原语')
|
||||
expect(text).toContain('parallel')
|
||||
expect(text).toContain('pipeline')
|
||||
expect(text).toContain('resumeFromRunId')
|
||||
expect(text).toContain('AgentAdapterRegistry')
|
||||
expect(text).toContain('确定性约束')
|
||||
// 脚本执行模型约束(非 ESM / 禁 import / 禁 TS / 单 export / 顶层 return)
|
||||
expect(text).toContain('脚本编写约束')
|
||||
expect(text).toContain('不转译 TS')
|
||||
expect(text).toContain('禁 `import`')
|
||||
})
|
||||
|
||||
test('appends user-provided args to the prompt when given', async () => {
|
||||
clearBundledSkills()
|
||||
registerUltracodeSkill()
|
||||
|
||||
const ultracode = getBundledSkills().find(s => s.name === 'ultracode')!
|
||||
const blocks = await asPrompt(ultracode).getPromptForCommand(
|
||||
'迁移 auth 模块',
|
||||
{} as never,
|
||||
)
|
||||
const text = (blocks[0] as { type: 'text'; text: string }).text
|
||||
expect(text.endsWith('迁移 auth 模块\n')).toBe(true)
|
||||
expect(text).toContain('用户输入')
|
||||
})
|
||||
|
||||
test('is not gated behind USER_TYPE — registers with no env set', () => {
|
||||
// No USER_TYPE env is configured in this test process. If the skill were
|
||||
// ant-gated (like stuck.ts), it would not appear here.
|
||||
const previousUserType = process.env.USER_TYPE
|
||||
delete process.env.USER_TYPE
|
||||
clearBundledSkills()
|
||||
registerUltracodeSkill()
|
||||
|
||||
const skills = getBundledSkills()
|
||||
expect(skills.some(s => s.name === 'ultracode')).toBe(true)
|
||||
|
||||
// Restore so we never mutate the process env for other test files.
|
||||
if (previousUserType === undefined) delete process.env.USER_TYPE
|
||||
else process.env.USER_TYPE = previousUserType
|
||||
})
|
||||
})
|
||||
Reference in New Issue
Block a user