@haoyiyin/workflow 0.2.2 → 0.2.3
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,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PLAN.md manager for execution plan persistence
|
|
3
|
+
*/
|
|
4
|
+
import { promises as fs } from 'fs'
|
|
5
|
+
import { dirname } from 'path'
|
|
6
|
+
import type { Plan, Task, PlanMdManager as IPlanMdManager } from './types.js'
|
|
7
|
+
|
|
8
|
+
export class PlanMdManager implements IPlanMdManager {
|
|
9
|
+
private planPath: string
|
|
10
|
+
|
|
11
|
+
constructor(planPath: string = '.pi/state/PLAN.md') {
|
|
12
|
+
this.planPath = planPath
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async writePlan(plan: Plan): Promise<void> {
|
|
16
|
+
// Ensure directory exists
|
|
17
|
+
await fs.mkdir(dirname(this.planPath), { recursive: true })
|
|
18
|
+
|
|
19
|
+
const content = this.generatePlanMarkdown(plan)
|
|
20
|
+
await fs.writeFile(this.planPath, content, 'utf-8')
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
async readPlan(): Promise<Plan> {
|
|
24
|
+
const content = await fs.readFile(this.planPath, 'utf-8')
|
|
25
|
+
return this.parsePlan(content)
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
async updateTaskStatus(taskId: string, status: 'pending' | 'in_progress' | 'complete'): Promise<void> {
|
|
29
|
+
const content = await fs.readFile(this.planPath, 'utf-8')
|
|
30
|
+
const statusChar = status === 'complete' ? 'x' : status === 'in_progress' ? '~' : ' '
|
|
31
|
+
const updated = content.replace(
|
|
32
|
+
new RegExp(`- \\[.] ${taskId}:`),
|
|
33
|
+
`- [${statusChar}] ${taskId}:`
|
|
34
|
+
)
|
|
35
|
+
await fs.writeFile(this.planPath, updated, 'utf-8')
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
private generatePlanMarkdown(plan: Plan): string {
|
|
39
|
+
return `# Execution Plan
|
|
40
|
+
|
|
41
|
+
## Goal
|
|
42
|
+
${plan.goal}
|
|
43
|
+
|
|
44
|
+
## Tasks
|
|
45
|
+
${plan.tasks.map(t => `- [ ] ${t.id}: ${t.description} (${t.estimatedComplexity})`).join('\n')}
|
|
46
|
+
|
|
47
|
+
## Dependencies
|
|
48
|
+
${Object.entries(plan.dependencies).length > 0
|
|
49
|
+
? Object.entries(plan.dependencies).map(([task, deps]) => `- ${task}: ${deps.join(', ')}`).join('\n')
|
|
50
|
+
: '- No dependencies'
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
## Waves
|
|
54
|
+
${plan.waves.map(w => `- ${w.id}: ${w.tasks.join(', ')}`).join('\n')}
|
|
55
|
+
|
|
56
|
+
## Verification Criteria
|
|
57
|
+
${plan.verificationCriteria.map(c => `- ${c}`).join('\n')}
|
|
58
|
+
|
|
59
|
+
## Estimated Effort
|
|
60
|
+
- Total Tasks: ${plan.tasks.length}
|
|
61
|
+
- Parallel Waves: ${plan.waves.length}
|
|
62
|
+
- Estimated Duration: ${plan.estimatedDuration}ms
|
|
63
|
+
|
|
64
|
+
## Rationale
|
|
65
|
+
${plan.rationale}
|
|
66
|
+
`
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private parsePlan(content: string): Plan {
|
|
70
|
+
const goalMatch = content.match(/## Goal\n(.+)/)
|
|
71
|
+
const tasksMatch = content.match(/## Tasks\n([\s\S]*?)(?=\n## |$)/)
|
|
72
|
+
const depsMatch = content.match(/## Dependencies\n([\s\S]*?)(?=\n## |$)/)
|
|
73
|
+
const wavesMatch = content.match(/## Waves\n([\s\S]*?)(?=\n## |$)/)
|
|
74
|
+
const criteriaMatch = content.match(/## Verification Criteria\n([\s\S]*?)(?=\n## |$)/)
|
|
75
|
+
const durationMatch = content.match(/- Estimated Duration: (\d+)/)
|
|
76
|
+
const rationaleMatch = content.match(/## Rationale\n([\s\S]*?)(?=\n## |$)/)
|
|
77
|
+
|
|
78
|
+
// Parse tasks
|
|
79
|
+
const tasks: Task[] = []
|
|
80
|
+
if (tasksMatch) {
|
|
81
|
+
const taskLines = tasksMatch[1].split('\n').filter(l => l.trim().startsWith('- ['))
|
|
82
|
+
for (const line of taskLines) {
|
|
83
|
+
const match = line.match(/- \[[x\s~]]\s+(\w+):\s+(.+)\s+\((\w+)\)/)
|
|
84
|
+
if (match) {
|
|
85
|
+
tasks.push({
|
|
86
|
+
id: match[1],
|
|
87
|
+
description: match[2],
|
|
88
|
+
estimatedComplexity: match[3] as 'low' | 'medium' | 'high'
|
|
89
|
+
})
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
// Parse dependencies
|
|
95
|
+
const dependencies: Record<string, string[]> = {}
|
|
96
|
+
if (depsMatch && depsMatch[1].trim() !== '- No dependencies') {
|
|
97
|
+
const depLines = depsMatch[1].split('\n').filter(l => l.trim().startsWith('- '))
|
|
98
|
+
for (const line of depLines) {
|
|
99
|
+
const match = line.match(/- (\w+):\s*(.+)/)
|
|
100
|
+
if (match) {
|
|
101
|
+
dependencies[match[1]] = match[2].split(',').map(s => s.trim()).filter(s => s)
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Parse waves
|
|
107
|
+
const waves = []
|
|
108
|
+
if (wavesMatch) {
|
|
109
|
+
const waveLines = wavesMatch[1].split('\n').filter(l => l.trim().startsWith('- '))
|
|
110
|
+
for (const line of waveLines) {
|
|
111
|
+
const match = line.match(/- (\w+):\s*(.+)/)
|
|
112
|
+
if (match) {
|
|
113
|
+
waves.push({
|
|
114
|
+
id: match[1],
|
|
115
|
+
tasks: match[2].split(',').map(s => s.trim()).filter(s => s)
|
|
116
|
+
})
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
// Parse verification criteria
|
|
122
|
+
const verificationCriteria: string[] = []
|
|
123
|
+
if (criteriaMatch) {
|
|
124
|
+
const criteriaLines = criteriaMatch[1].split('\n').filter(l => l.trim().startsWith('- '))
|
|
125
|
+
for (const line of criteriaLines) {
|
|
126
|
+
const criterion = line.replace(/^- /, '').trim()
|
|
127
|
+
if (criterion) verificationCriteria.push(criterion)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return {
|
|
132
|
+
goal: goalMatch?.[1] || '',
|
|
133
|
+
tasks,
|
|
134
|
+
dependencies,
|
|
135
|
+
waves,
|
|
136
|
+
verificationCriteria,
|
|
137
|
+
estimatedDuration: durationMatch ? parseInt(durationMatch[1], 10) : 0,
|
|
138
|
+
rationale: rationaleMatch?.[1]?.trim() || ''
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* STATE.md manager for workflow state persistence
|
|
3
|
+
*/
|
|
4
|
+
import { promises as fs } from 'fs'
|
|
5
|
+
import { dirname } from 'path'
|
|
6
|
+
import type { WorkflowState, Plan, TaskResult, VerificationResult, StateMdManager as IStateMdManager } from './types.js'
|
|
7
|
+
|
|
8
|
+
export class StateMdManager implements IStateMdManager {
|
|
9
|
+
private statePath: string
|
|
10
|
+
|
|
11
|
+
constructor(statePath: string = '.pi/state/STATE.md') {
|
|
12
|
+
this.statePath = statePath
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
async initialize(goal: string): Promise<void> {
|
|
16
|
+
// Ensure directory exists
|
|
17
|
+
await fs.mkdir(dirname(this.statePath), { recursive: true })
|
|
18
|
+
|
|
19
|
+
const content = `# Workflow State
|
|
20
|
+
|
|
21
|
+
## Goal
|
|
22
|
+
${goal}
|
|
23
|
+
|
|
24
|
+
## Status
|
|
25
|
+
- Phase: planning
|
|
26
|
+
- Started: ${new Date().toISOString()}
|
|
27
|
+
- Last Updated: ${new Date().toISOString()}
|
|
28
|
+
|
|
29
|
+
## Plan
|
|
30
|
+
<!-- Populated by plan skill -->
|
|
31
|
+
|
|
32
|
+
## Completed Tasks
|
|
33
|
+
<!-- Updated during execution -->
|
|
34
|
+
|
|
35
|
+
## Verification Results
|
|
36
|
+
<!-- Populated by verify phase -->
|
|
37
|
+
|
|
38
|
+
## Token Usage
|
|
39
|
+
- Plan Phase: 0
|
|
40
|
+
- Execute Phase: 0
|
|
41
|
+
- Verify Phase: 0
|
|
42
|
+
- Total: 0 / 1000000
|
|
43
|
+
`
|
|
44
|
+
await fs.writeFile(this.statePath, content, 'utf-8')
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async updatePhase(phase: WorkflowState['phase']): Promise<void> {
|
|
48
|
+
const content = await fs.readFile(this.statePath, 'utf-8')
|
|
49
|
+
const updated = content.replace(
|
|
50
|
+
/- Phase: .*/,
|
|
51
|
+
`- Phase: ${phase}`
|
|
52
|
+
).replace(
|
|
53
|
+
/- Last Updated: .*/,
|
|
54
|
+
`- Last Updated: ${new Date().toISOString()}`
|
|
55
|
+
)
|
|
56
|
+
await fs.writeFile(this.statePath, updated, 'utf-8')
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async recordPlan(plan: Plan): Promise<void> {
|
|
60
|
+
const content = await fs.readFile(this.statePath, 'utf-8')
|
|
61
|
+
const planSection = this.formatPlanSection(plan)
|
|
62
|
+
const updated = this.replaceSection(content, 'Plan', planSection)
|
|
63
|
+
await fs.writeFile(this.statePath, updated, 'utf-8')
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async recordTaskComplete(taskId: string, result: TaskResult): Promise<void> {
|
|
67
|
+
const content = await fs.readFile(this.statePath, 'utf-8')
|
|
68
|
+
const entry = `- [x] ${taskId}: ${result.success ? '✓' : '✗'} (${result.duration}ms) - ${result.notes || ''}\n`
|
|
69
|
+
const updated = this.appendToSection(content, 'Completed Tasks', entry)
|
|
70
|
+
await fs.writeFile(this.statePath, updated, 'utf-8')
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
async recordVerification(verification: VerificationResult): Promise<void> {
|
|
74
|
+
const content = await fs.readFile(this.statePath, 'utf-8')
|
|
75
|
+
const verificationSection = this.formatVerificationSection(verification)
|
|
76
|
+
const updated = this.replaceSection(content, 'Verification Results', verificationSection)
|
|
77
|
+
await fs.writeFile(this.statePath, updated, 'utf-8')
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async readCurrentState(): Promise<WorkflowState> {
|
|
81
|
+
const content = await fs.readFile(this.statePath, 'utf-8')
|
|
82
|
+
return this.parseState(content)
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
private formatPlanSection(plan: Plan): string {
|
|
86
|
+
return `
|
|
87
|
+
### Goal
|
|
88
|
+
${plan.goal}
|
|
89
|
+
|
|
90
|
+
### Tasks (${plan.tasks.length})
|
|
91
|
+
${plan.tasks.map(t => `- ${t.id}: ${t.description} (${t.estimatedComplexity})`).join('\n')}
|
|
92
|
+
|
|
93
|
+
### Waves (${plan.waves.length})
|
|
94
|
+
${plan.waves.map(w => `- ${w.id}: ${w.tasks.join(', ')}`).join('\n')}
|
|
95
|
+
|
|
96
|
+
### Verification Criteria
|
|
97
|
+
${plan.verificationCriteria.map(c => `- ${c}`).join('\n')}
|
|
98
|
+
|
|
99
|
+
### Estimated Duration
|
|
100
|
+
${plan.estimatedDuration}ms
|
|
101
|
+
|
|
102
|
+
### Rationale
|
|
103
|
+
${plan.rationale}
|
|
104
|
+
`
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
private formatVerificationSection(verification: VerificationResult): string {
|
|
108
|
+
const results: string[] = []
|
|
109
|
+
if (verification.dimensions.goals) {
|
|
110
|
+
results.push(`- Goals: ${verification.dimensions.goals.success ? '✓ PASS' : '✗ FAIL'}`)
|
|
111
|
+
}
|
|
112
|
+
if (verification.dimensions.quality) {
|
|
113
|
+
results.push(`- Quality: ${verification.dimensions.quality.success ? '✓ PASS' : '✗ FAIL'}`)
|
|
114
|
+
}
|
|
115
|
+
if (verification.dimensions.tests) {
|
|
116
|
+
results.push(`- Tests: ${verification.dimensions.tests.success ? '✓ PASS' : '✗ FAIL'}`)
|
|
117
|
+
}
|
|
118
|
+
results.push(`- Overall: ${verification.success ? '✓ PASSED' : '✗ FAILED'}`)
|
|
119
|
+
|
|
120
|
+
return `
|
|
121
|
+
${results.join('\n')}
|
|
122
|
+
`
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
private replaceSection(content: string, sectionName: string, newContent: string): string {
|
|
126
|
+
const pattern = new RegExp(`(## ${sectionName}\\n)([\\s\\S]*?)(?=\\n## |$)`, 'g')
|
|
127
|
+
return content.replace(pattern, `## ${sectionName}\n${newContent}\n`)
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private appendToSection(content: string, sectionName: string, entry: string): string {
|
|
131
|
+
const pattern = new RegExp(`(## ${sectionName}\\n)([\\s\\S]*?)(?=\\n## |$)`, 'g')
|
|
132
|
+
const match = pattern.exec(content)
|
|
133
|
+
if (match) {
|
|
134
|
+
const current = match[2] || ''
|
|
135
|
+
return content.replace(pattern, `## ${sectionName}\n${current}${entry}`)
|
|
136
|
+
}
|
|
137
|
+
return content
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
private parseState(content: string): WorkflowState {
|
|
141
|
+
const phaseMatch = content.match(/- Phase: (\w+)/)
|
|
142
|
+
const goalMatch = content.match(/## Goal\n(.+)/)
|
|
143
|
+
const startedMatch = content.match(/- Started: (.+)/)
|
|
144
|
+
const totalMatch = content.match(/### Tasks \((\d+)\)/)
|
|
145
|
+
const completedMatch = content.match(/## Completed Tasks\n([\s\S]*?)(?=\n## |$)/)
|
|
146
|
+
|
|
147
|
+
const completedTasks = completedMatch
|
|
148
|
+
? completedMatch[1].split('\n').filter(l => l.trim().startsWith('- [x]')).length
|
|
149
|
+
: 0
|
|
150
|
+
|
|
151
|
+
const totalTasks = totalMatch ? parseInt(totalMatch[1], 10) : 0
|
|
152
|
+
|
|
153
|
+
return {
|
|
154
|
+
goal: goalMatch?.[1] || '',
|
|
155
|
+
phase: (phaseMatch?.[1] as WorkflowState['phase']) || 'planning',
|
|
156
|
+
started: startedMatch?.[1] || new Date().toISOString(),
|
|
157
|
+
lastUpdated: new Date().toISOString(),
|
|
158
|
+
totalTasks,
|
|
159
|
+
completedTasks: [],
|
|
160
|
+
tokenUsage: {
|
|
161
|
+
used: 0,
|
|
162
|
+
limit: 1000000,
|
|
163
|
+
percentage: 0
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Persistence layer types for STATE.md and PLAN.md
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
export interface Task {
|
|
6
|
+
id: string
|
|
7
|
+
description: string
|
|
8
|
+
estimatedComplexity: 'low' | 'medium' | 'high'
|
|
9
|
+
relatedFiles?: string[]
|
|
10
|
+
verificationCriteria?: string[]
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface Wave {
|
|
14
|
+
id: string
|
|
15
|
+
tasks: string[]
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export interface Plan {
|
|
19
|
+
goal: string
|
|
20
|
+
tasks: Task[]
|
|
21
|
+
dependencies: Record<string, string[]>
|
|
22
|
+
waves: Wave[]
|
|
23
|
+
verificationCriteria: string[]
|
|
24
|
+
estimatedDuration: number
|
|
25
|
+
rationale: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface TaskResult {
|
|
29
|
+
taskId: string
|
|
30
|
+
success: boolean
|
|
31
|
+
filesModified: string[]
|
|
32
|
+
testsAdded: string[]
|
|
33
|
+
duration: number
|
|
34
|
+
notes: string
|
|
35
|
+
error?: string
|
|
36
|
+
phases?: { phase: string; status: string; command?: string; evidence?: string }[]
|
|
37
|
+
worktreePath?: string
|
|
38
|
+
branchName?: string
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface VerificationResult {
|
|
42
|
+
success: boolean
|
|
43
|
+
dimensions: {
|
|
44
|
+
goals?: DimensionResult
|
|
45
|
+
quality?: DimensionResult
|
|
46
|
+
tests?: DimensionResult
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface DimensionResult {
|
|
51
|
+
success: boolean
|
|
52
|
+
details?: unknown
|
|
53
|
+
evidence?: string[]
|
|
54
|
+
gaps?: string[]
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
export interface WorkflowState {
|
|
58
|
+
goal: string
|
|
59
|
+
phase: 'planning' | 'executing' | 'verifying' | 'complete'
|
|
60
|
+
started: string
|
|
61
|
+
lastUpdated: string
|
|
62
|
+
totalTasks: number
|
|
63
|
+
completedTasks: TaskResult[]
|
|
64
|
+
tokenUsage: {
|
|
65
|
+
used: number
|
|
66
|
+
limit: number
|
|
67
|
+
percentage: number
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface StateMdManager {
|
|
72
|
+
initialize(goal: string): Promise<void>
|
|
73
|
+
updatePhase(phase: WorkflowState['phase']): Promise<void>
|
|
74
|
+
recordPlan(plan: Plan): Promise<void>
|
|
75
|
+
recordTaskComplete(taskId: string, result: TaskResult): Promise<void>
|
|
76
|
+
recordVerification(verification: VerificationResult): Promise<void>
|
|
77
|
+
readCurrentState(): Promise<WorkflowState>
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface PlanMdManager {
|
|
81
|
+
writePlan(plan: Plan): Promise<void>
|
|
82
|
+
readPlan(): Promise<Plan>
|
|
83
|
+
updateTaskStatus(taskId: string, status: 'pending' | 'in_progress' | 'complete'): Promise<void>
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface PersistenceManager {
|
|
87
|
+
state: StateMdManager
|
|
88
|
+
plan: PlanMdManager
|
|
89
|
+
}
|