@haoyiyin/workflow 0.2.2 → 0.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +9 -8
- package/src/agents/contracts.ts +559 -0
- package/src/agents/dispatcher-enhanced.ts +350 -0
- package/src/agents/dispatcher.ts +680 -0
- package/src/agents/index.ts +48 -0
- package/src/agents/resilience.ts +255 -0
- package/src/agents/token-budget.ts +83 -0
- package/src/agents/types.ts +73 -0
- package/src/guard/main-agent.ts +245 -0
- package/src/hooks/builtin/index.ts +8 -0
- package/src/hooks/builtin/on-error.ts +23 -0
- package/src/hooks/builtin/post-execute.ts +40 -0
- package/src/hooks/builtin/post-plan.ts +23 -0
- package/src/hooks/builtin/pre-execute.ts +30 -0
- package/src/hooks/builtin/pre-plan.ts +26 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/loader.ts +98 -0
- package/src/hooks/manager.ts +99 -0
- package/src/hooks/types-enhanced.ts +38 -0
- package/src/hooks/types.ts +35 -0
- package/src/index.ts +127 -0
- package/src/persistence/index.ts +17 -0
- package/src/persistence/plan-md.ts +141 -0
- package/src/persistence/state-md.ts +167 -0
- package/src/persistence/types.ts +89 -0
- package/src/router/classifier.ts +610 -0
- package/src/router/guard.ts +483 -0
- package/src/router/index.ts +22 -0
- package/src/router/router.ts +108 -0
- package/src/router/types.ts +127 -0
- package/src/skills/agents-md/SKILL.md +45 -0
- package/src/skills/agents-md/index.ts +33 -0
- package/src/skills/execute-plan/SKILL.md +60 -0
- package/src/skills/execute-plan/index.ts +970 -0
- package/src/skills/index.ts +13 -0
- package/src/skills/quick-task/SKILL.md +54 -0
- package/src/skills/quick-task/index.ts +346 -0
- package/src/skills/registry.ts +59 -0
- package/src/skills/review-diff/SKILL.md +53 -0
- package/src/skills/review-diff/index.ts +394 -0
- package/src/skills/skill.ts +59 -0
- package/src/skills/systematic-debugging/SKILL.md +56 -0
- package/src/skills/systematic-debugging/index.ts +404 -0
- package/src/skills/tdd/SKILL.md +52 -0
- package/src/skills/tdd/index.ts +409 -0
- package/src/skills/to-plan/SKILL.md +56 -0
- package/src/skills/to-plan/index-enhanced.ts +551 -0
- package/src/skills/to-plan/index.ts +586 -0
- package/src/skills/types.ts +47 -0
- package/src/state/cleanup.ts +118 -0
- package/src/state/index.ts +8 -0
- package/src/state/manager.ts +96 -0
- package/src/state/persistence.ts +77 -0
- package/src/state/types.ts +30 -0
- package/src/state/validator.ts +78 -0
- package/src/types.ts +102 -0
- package/src/utils/compress.ts +347 -0
- package/src/utils/git.ts +82 -0
- package/src/utils/index.ts +6 -0
- package/src/utils/logger.ts +23 -0
- package/src/utils/paths.ts +55 -0
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State cleanup utilities
|
|
3
|
+
*/
|
|
4
|
+
import { readdir, rmdir } from 'fs/promises'
|
|
5
|
+
import { join } from 'path'
|
|
6
|
+
import type { PersistenceLayer } from './types.js'
|
|
7
|
+
|
|
8
|
+
export interface CleanupOptions {
|
|
9
|
+
maxAgeDays: number
|
|
10
|
+
maxStates?: number
|
|
11
|
+
dryRun?: boolean
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface CleanupResult {
|
|
15
|
+
deleted: number
|
|
16
|
+
archived: number
|
|
17
|
+
errors: string[]
|
|
18
|
+
paths: string[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export async function cleanupOldStates(
|
|
22
|
+
persistence: PersistenceLayer,
|
|
23
|
+
options: CleanupOptions
|
|
24
|
+
): Promise<CleanupResult> {
|
|
25
|
+
const result: CleanupResult = {
|
|
26
|
+
deleted: 0,
|
|
27
|
+
archived: 0,
|
|
28
|
+
errors: [],
|
|
29
|
+
paths: [],
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
const ids = await persistence.list()
|
|
34
|
+
const now = Date.now()
|
|
35
|
+
const maxAgeMs = options.maxAgeDays * 24 * 60 * 60 * 1000
|
|
36
|
+
|
|
37
|
+
for (const id of ids) {
|
|
38
|
+
try {
|
|
39
|
+
const state = await persistence.read(id)
|
|
40
|
+
if (!state) continue
|
|
41
|
+
|
|
42
|
+
const age = now - new Date(state.updatedAt).getTime()
|
|
43
|
+
|
|
44
|
+
if (age > maxAgeMs) {
|
|
45
|
+
if (!options.dryRun) {
|
|
46
|
+
await persistence.archive(id)
|
|
47
|
+
}
|
|
48
|
+
result.archived++
|
|
49
|
+
result.paths.push(id)
|
|
50
|
+
}
|
|
51
|
+
} catch (error) {
|
|
52
|
+
result.errors.push(`Failed to process state ${id}: ${error}`)
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return result
|
|
57
|
+
} catch (error) {
|
|
58
|
+
result.errors.push(`Cleanup failed: ${error}`)
|
|
59
|
+
return result
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export async function cleanupEmptyDirs(basePath: string): Promise<void> {
|
|
64
|
+
try {
|
|
65
|
+
const entries = await readdir(basePath, { withFileTypes: true })
|
|
66
|
+
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
if (entry.isDirectory()) {
|
|
69
|
+
const fullPath = join(basePath, entry.name)
|
|
70
|
+
const files = await readdir(fullPath)
|
|
71
|
+
|
|
72
|
+
if (files.length === 0) {
|
|
73
|
+
await rmdir(fullPath)
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
} catch {
|
|
78
|
+
// Ignore errors
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export async function getStateStats(basePath: string): Promise<{
|
|
83
|
+
total: number
|
|
84
|
+
active: number
|
|
85
|
+
archived: number
|
|
86
|
+
oldest?: Date
|
|
87
|
+
newest?: Date
|
|
88
|
+
}> {
|
|
89
|
+
const stats = {
|
|
90
|
+
total: 0,
|
|
91
|
+
active: 0,
|
|
92
|
+
archived: 0,
|
|
93
|
+
oldest: undefined as Date | undefined,
|
|
94
|
+
newest: undefined as Date | undefined,
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
// Count active states
|
|
99
|
+
const activePath = join(basePath, 'active')
|
|
100
|
+
const activeFiles = await readdir(activePath)
|
|
101
|
+
stats.active = activeFiles.filter((f) => f.endsWith('.json')).length
|
|
102
|
+
|
|
103
|
+
// Count archived states
|
|
104
|
+
const archivePath = join(basePath, 'archive')
|
|
105
|
+
const archiveDirs = await readdir(archivePath)
|
|
106
|
+
for (const dir of archiveDirs) {
|
|
107
|
+
const dirPath = join(archivePath, dir)
|
|
108
|
+
const files = await readdir(dirPath)
|
|
109
|
+
stats.archived += files.filter((f) => f.endsWith('.json')).length
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
stats.total = stats.active + stats.archived
|
|
113
|
+
|
|
114
|
+
return stats
|
|
115
|
+
} catch {
|
|
116
|
+
return stats
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State module index
|
|
3
|
+
*/
|
|
4
|
+
export type { ExecutionState, StateManager, PersistenceLayer, StateOptions } from './types.js'
|
|
5
|
+
export { StateManagerImpl, createStateManager } from './manager.js'
|
|
6
|
+
export { FilePersistence, createPersistence } from './persistence.js'
|
|
7
|
+
export { validateState, isValidState, sanitizeState } from './validator.js'
|
|
8
|
+
export { cleanupOldStates, cleanupEmptyDirs, getStateStats } from './cleanup.js'
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State manager implementation
|
|
3
|
+
*/
|
|
4
|
+
import { nanoid } from 'nanoid'
|
|
5
|
+
import type {
|
|
6
|
+
ExecutionState,
|
|
7
|
+
StateManager,
|
|
8
|
+
PersistenceLayer,
|
|
9
|
+
} from './types.js'
|
|
10
|
+
import type { WorkflowConfig } from '../types.js'
|
|
11
|
+
import { createPersistence } from './persistence.js'
|
|
12
|
+
|
|
13
|
+
export class StateManagerImpl implements StateManager {
|
|
14
|
+
private persistence: PersistenceLayer
|
|
15
|
+
|
|
16
|
+
constructor(persistence: PersistenceLayer) {
|
|
17
|
+
this.persistence = persistence
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
async load(id: string): Promise<ExecutionState | null> {
|
|
21
|
+
return this.persistence.read(id)
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
async save(state: ExecutionState): Promise<void> {
|
|
25
|
+
const updated = {
|
|
26
|
+
...state,
|
|
27
|
+
updatedAt: new Date(),
|
|
28
|
+
}
|
|
29
|
+
await this.persistence.write(updated)
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
async create(planId: string, _config: WorkflowConfig): Promise<ExecutionState> {
|
|
33
|
+
const now = new Date()
|
|
34
|
+
const state: ExecutionState = {
|
|
35
|
+
id: nanoid(),
|
|
36
|
+
planId,
|
|
37
|
+
completedTasks: [],
|
|
38
|
+
failedTasks: [],
|
|
39
|
+
startedAt: now,
|
|
40
|
+
updatedAt: now,
|
|
41
|
+
status: 'running',
|
|
42
|
+
context: {},
|
|
43
|
+
}
|
|
44
|
+
await this.persistence.write(state)
|
|
45
|
+
return state
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async update(
|
|
49
|
+
id: string,
|
|
50
|
+
updates: Partial<ExecutionState>
|
|
51
|
+
): Promise<ExecutionState> {
|
|
52
|
+
const existing = await this.persistence.read(id)
|
|
53
|
+
if (!existing) {
|
|
54
|
+
throw new Error(`State not found: ${id}`)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const updated: ExecutionState = {
|
|
58
|
+
...existing,
|
|
59
|
+
...updates,
|
|
60
|
+
id: existing.id,
|
|
61
|
+
updatedAt: new Date(),
|
|
62
|
+
}
|
|
63
|
+
await this.persistence.write(updated)
|
|
64
|
+
return updated
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
async archive(id: string): Promise<void> {
|
|
68
|
+
await this.persistence.archive(id)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
async list(): Promise<ExecutionState[]> {
|
|
72
|
+
const ids = await this.persistence.list()
|
|
73
|
+
const states: ExecutionState[] = []
|
|
74
|
+
for (const id of ids) {
|
|
75
|
+
const state = await this.persistence.read(id)
|
|
76
|
+
if (state) {
|
|
77
|
+
states.push(state)
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
return states.sort(
|
|
81
|
+
(a, b) => b.updatedAt.getTime() - a.updatedAt.getTime()
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async getActive(): Promise<ExecutionState | null> {
|
|
86
|
+
const states = await this.list()
|
|
87
|
+
return states.find((s) => s.status === 'running') || null
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
export function createStateManager(
|
|
92
|
+
config: WorkflowConfig
|
|
93
|
+
): StateManager {
|
|
94
|
+
const persistence = createPersistence(config.statePath)
|
|
95
|
+
return new StateManagerImpl(persistence)
|
|
96
|
+
}
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File-based persistence layer for state
|
|
3
|
+
*/
|
|
4
|
+
import { join } from 'path'
|
|
5
|
+
import { readFile, writeFile, mkdir, readdir, rename } from 'fs/promises'
|
|
6
|
+
import { existsSync } from 'fs'
|
|
7
|
+
import type { PersistenceLayer, ExecutionState } from './types.js'
|
|
8
|
+
import { validateState, sanitizeState } from './validator.js'
|
|
9
|
+
|
|
10
|
+
export class FilePersistence implements PersistenceLayer {
|
|
11
|
+
private basePath: string
|
|
12
|
+
private archivePath: string
|
|
13
|
+
|
|
14
|
+
constructor(basePath: string) {
|
|
15
|
+
this.basePath = join(basePath, 'active')
|
|
16
|
+
this.archivePath = join(basePath, 'archive')
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
async read(id: string): Promise<ExecutionState | null> {
|
|
20
|
+
const path = join(this.basePath, `${id}.json`)
|
|
21
|
+
try {
|
|
22
|
+
const data = await readFile(path, 'utf-8')
|
|
23
|
+
const parsed = JSON.parse(data)
|
|
24
|
+
|
|
25
|
+
// Validate state structure
|
|
26
|
+
const validation = validateState(parsed)
|
|
27
|
+
if (!validation.valid) {
|
|
28
|
+
console.warn(`[State] Invalid state file ${id}:`, validation.errors)
|
|
29
|
+
return null
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Sanitize dates and arrays
|
|
33
|
+
return sanitizeState(parsed)
|
|
34
|
+
} catch {
|
|
35
|
+
return null
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
async write(state: ExecutionState): Promise<void> {
|
|
40
|
+
await mkdir(this.basePath, { recursive: true })
|
|
41
|
+
const path = join(this.basePath, `${state.id}.json`)
|
|
42
|
+
await writeFile(path, JSON.stringify(state, null, 2))
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
async delete(id: string): Promise<void> {
|
|
46
|
+
const path = join(this.basePath, `${id}.json`)
|
|
47
|
+
try {
|
|
48
|
+
await import('fs/promises').then((fs) => fs.unlink(path))
|
|
49
|
+
} catch {
|
|
50
|
+
// Ignore if file doesn't exist
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
async list(): Promise<string[]> {
|
|
55
|
+
await mkdir(this.basePath, { recursive: true })
|
|
56
|
+
const files = await readdir(this.basePath)
|
|
57
|
+
return files
|
|
58
|
+
.filter((f) => f.endsWith('.json'))
|
|
59
|
+
.map((f) => f.replace('.json', ''))
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async archive(id: string): Promise<void> {
|
|
63
|
+
const sourcePath = join(this.basePath, `${id}.json`)
|
|
64
|
+
const date = new Date().toISOString().split('T')[0]
|
|
65
|
+
const archiveDir = join(this.archivePath, date)
|
|
66
|
+
await mkdir(archiveDir, { recursive: true })
|
|
67
|
+
const targetPath = join(archiveDir, `${id}.json`)
|
|
68
|
+
|
|
69
|
+
if (existsSync(sourcePath)) {
|
|
70
|
+
await rename(sourcePath, targetPath)
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export function createPersistence(basePath: string): PersistenceLayer {
|
|
76
|
+
return new FilePersistence(basePath)
|
|
77
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State management types
|
|
3
|
+
*/
|
|
4
|
+
import type { ExecutionState as CoreExecutionState, WorkflowConfig } from '../types.js'
|
|
5
|
+
|
|
6
|
+
// Use alias to avoid naming conflicts
|
|
7
|
+
export type ExecutionState = CoreExecutionState
|
|
8
|
+
|
|
9
|
+
export interface StateManager {
|
|
10
|
+
load(id: string): Promise<ExecutionState | null>
|
|
11
|
+
save(state: ExecutionState): Promise<void>
|
|
12
|
+
create(planId: string, config: WorkflowConfig): Promise<ExecutionState>
|
|
13
|
+
update(id: string, updates: Partial<ExecutionState>): Promise<ExecutionState>
|
|
14
|
+
archive(id: string): Promise<void>
|
|
15
|
+
list(): Promise<ExecutionState[]>
|
|
16
|
+
getActive(): Promise<ExecutionState | null>
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface PersistenceLayer {
|
|
20
|
+
read(id: string): Promise<ExecutionState | null>
|
|
21
|
+
write(state: ExecutionState): Promise<void>
|
|
22
|
+
delete(id: string): Promise<void>
|
|
23
|
+
list(): Promise<string[]>
|
|
24
|
+
archive(id: string): Promise<void>
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface StateOptions {
|
|
28
|
+
statePath: string
|
|
29
|
+
autoArchive: boolean
|
|
30
|
+
}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State validation utilities
|
|
3
|
+
*/
|
|
4
|
+
import type { ExecutionState } from '../types.js'
|
|
5
|
+
|
|
6
|
+
export interface ValidationResult {
|
|
7
|
+
valid: boolean
|
|
8
|
+
errors: string[]
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function validateState(state: unknown): ValidationResult {
|
|
12
|
+
const errors: string[] = []
|
|
13
|
+
|
|
14
|
+
if (!state || typeof state !== 'object') {
|
|
15
|
+
return { valid: false, errors: ['State must be an object'] }
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const s = state as Record<string, unknown>
|
|
19
|
+
|
|
20
|
+
// Required fields
|
|
21
|
+
if (!s.id || typeof s.id !== 'string') {
|
|
22
|
+
errors.push('State must have a string id')
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
if (!s.planId || typeof s.planId !== 'string') {
|
|
26
|
+
errors.push('State must have a string planId')
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
if (!s.status || !['running', 'paused', 'completed', 'failed', 'cancelled'].includes(s.status as string)) {
|
|
30
|
+
errors.push('State must have a valid status')
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
if (!s.startedAt) {
|
|
34
|
+
errors.push('State must have startedAt')
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (!s.updatedAt) {
|
|
38
|
+
errors.push('State must have updatedAt')
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Optional arrays
|
|
42
|
+
if (s.completedTasks && !Array.isArray(s.completedTasks)) {
|
|
43
|
+
errors.push('completedTasks must be an array')
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (s.failedTasks && !Array.isArray(s.failedTasks)) {
|
|
47
|
+
errors.push('failedTasks must be an array')
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
if (s.context && typeof s.context !== 'object') {
|
|
51
|
+
errors.push('context must be an object')
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
valid: errors.length === 0,
|
|
56
|
+
errors,
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function isValidState(state: unknown): state is ExecutionState {
|
|
61
|
+
return validateState(state).valid
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function sanitizeState(state: ExecutionState): ExecutionState {
|
|
65
|
+
return {
|
|
66
|
+
...state,
|
|
67
|
+
id: String(state.id),
|
|
68
|
+
planId: String(state.planId),
|
|
69
|
+
completedTasks: Array.isArray(state.completedTasks) ? state.completedTasks : [],
|
|
70
|
+
failedTasks: Array.isArray(state.failedTasks) ? state.failedTasks : [],
|
|
71
|
+
status: state.status || 'running',
|
|
72
|
+
context: typeof state.context === 'object' && state.context !== null
|
|
73
|
+
? state.context
|
|
74
|
+
: {},
|
|
75
|
+
startedAt: state.startedAt ? new Date(state.startedAt) : new Date(),
|
|
76
|
+
updatedAt: state.updatedAt ? new Date(state.updatedAt) : new Date(),
|
|
77
|
+
}
|
|
78
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Global type definitions for yi-workflow
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Core workflow types
|
|
6
|
+
export interface WorkflowConfig {
|
|
7
|
+
planPath: string
|
|
8
|
+
statePath: string
|
|
9
|
+
autoCleanup: boolean
|
|
10
|
+
autoMerge: boolean
|
|
11
|
+
defaultModel: string
|
|
12
|
+
maxConcurrent: number
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface Plan {
|
|
16
|
+
id: string
|
|
17
|
+
title: string
|
|
18
|
+
description: string
|
|
19
|
+
tasks: Task[]
|
|
20
|
+
createdAt: Date
|
|
21
|
+
updatedAt: Date
|
|
22
|
+
status: PlanStatus
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export type PlanStatus = 'draft' | 'approved' | 'in_progress' | 'completed' | 'cancelled'
|
|
26
|
+
|
|
27
|
+
export interface Task {
|
|
28
|
+
id: string
|
|
29
|
+
title: string
|
|
30
|
+
description: string
|
|
31
|
+
status: TaskStatus
|
|
32
|
+
dependencies: string[]
|
|
33
|
+
estimatedHours?: number
|
|
34
|
+
actualHours?: number
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export type TaskStatus = 'pending' | 'in_progress' | 'completed' | 'blocked'
|
|
38
|
+
|
|
39
|
+
// Execution state
|
|
40
|
+
export interface ExecutionState {
|
|
41
|
+
id: string
|
|
42
|
+
planId: string
|
|
43
|
+
currentTaskId?: string
|
|
44
|
+
completedTasks: string[]
|
|
45
|
+
failedTasks: string[]
|
|
46
|
+
startedAt: Date
|
|
47
|
+
updatedAt: Date
|
|
48
|
+
status: ExecutionStatus
|
|
49
|
+
context: Record<string, unknown>
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export type ExecutionStatus = 'running' | 'paused' | 'completed' | 'failed' | 'cancelled'
|
|
53
|
+
|
|
54
|
+
// Hook types
|
|
55
|
+
export type HookType = 'pre-plan' | 'post-plan' | 'pre-execute' | 'post-execute' | 'on-error'
|
|
56
|
+
|
|
57
|
+
export interface HookContext {
|
|
58
|
+
config: WorkflowConfig
|
|
59
|
+
state?: ExecutionState
|
|
60
|
+
plan?: Plan
|
|
61
|
+
error?: Error
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export interface HookDefinition {
|
|
65
|
+
type: HookType
|
|
66
|
+
name: string
|
|
67
|
+
execute: (context: HookContext) => Promise<void> | void
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Skill types
|
|
71
|
+
export interface SkillDefinition {
|
|
72
|
+
name: string
|
|
73
|
+
description: string
|
|
74
|
+
requires?: string[]
|
|
75
|
+
hooks?: {
|
|
76
|
+
pre?: HookDefinition[]
|
|
77
|
+
post?: HookDefinition[]
|
|
78
|
+
error?: HookDefinition[]
|
|
79
|
+
}
|
|
80
|
+
execute: (input: unknown, context: SkillContext) => Promise<unknown>
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
export interface SkillContext {
|
|
84
|
+
config: WorkflowConfig
|
|
85
|
+
state: ExecutionState
|
|
86
|
+
logger: Logger
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
// Logger interface
|
|
90
|
+
export interface Logger {
|
|
91
|
+
info: (message: string, ...args: unknown[]) => void
|
|
92
|
+
warn: (message: string, ...args: unknown[]) => void
|
|
93
|
+
error: (message: string, ...args: unknown[]) => void
|
|
94
|
+
debug: (message: string, ...args: unknown[]) => void
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// CLI types
|
|
98
|
+
export interface CLIOptions {
|
|
99
|
+
verbose?: boolean
|
|
100
|
+
config?: string
|
|
101
|
+
model?: string
|
|
102
|
+
}
|