@haoyiyin/workflow 0.2.11 → 0.3.1

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.
Files changed (175) hide show
  1. package/dist/src/agents/contracts/implementer.d.ts +29 -0
  2. package/dist/src/agents/contracts/implementer.d.ts.map +1 -0
  3. package/dist/src/agents/contracts/implementer.js +94 -0
  4. package/dist/src/agents/contracts/implementer.js.map +1 -0
  5. package/dist/src/agents/contracts/index.d.ts +11 -0
  6. package/dist/src/agents/contracts/index.d.ts.map +1 -0
  7. package/dist/src/agents/contracts/index.js +11 -0
  8. package/dist/src/agents/contracts/index.js.map +1 -0
  9. package/dist/src/agents/contracts/planner.d.ts +25 -0
  10. package/dist/src/agents/contracts/planner.d.ts.map +1 -0
  11. package/dist/src/agents/contracts/planner.js +107 -0
  12. package/dist/src/agents/contracts/planner.js.map +1 -0
  13. package/dist/src/agents/contracts/router.d.ts +24 -0
  14. package/dist/src/agents/contracts/router.d.ts.map +1 -0
  15. package/dist/src/agents/contracts/router.js +137 -0
  16. package/dist/src/agents/contracts/router.js.map +1 -0
  17. package/dist/src/agents/contracts/verifier.d.ts +27 -0
  18. package/dist/src/agents/contracts/verifier.d.ts.map +1 -0
  19. package/dist/src/agents/contracts/verifier.js +115 -0
  20. package/dist/src/agents/contracts/verifier.js.map +1 -0
  21. package/dist/src/agents/dispatcher.d.ts +94 -51
  22. package/dist/src/agents/dispatcher.d.ts.map +1 -1
  23. package/dist/src/agents/dispatcher.js +207 -164
  24. package/dist/src/agents/dispatcher.js.map +1 -1
  25. package/dist/src/persistence/index.d.ts +4 -2
  26. package/dist/src/persistence/index.d.ts.map +1 -1
  27. package/dist/src/persistence/index.js +4 -1
  28. package/dist/src/persistence/index.js.map +1 -1
  29. package/dist/src/persistence/plan-md.d.ts +3 -2
  30. package/dist/src/persistence/plan-md.d.ts.map +1 -1
  31. package/dist/src/persistence/plan-md.js +47 -15
  32. package/dist/src/persistence/plan-md.js.map +1 -1
  33. package/dist/src/persistence/state-md.d.ts +2 -0
  34. package/dist/src/persistence/state-md.d.ts.map +1 -1
  35. package/dist/src/persistence/state-md.js +40 -22
  36. package/dist/src/persistence/state-md.js.map +1 -1
  37. package/dist/src/persistence/types.d.ts +35 -39
  38. package/dist/src/persistence/types.d.ts.map +1 -1
  39. package/dist/src/pi-extension.d.ts +4 -3
  40. package/dist/src/pi-extension.d.ts.map +1 -1
  41. package/dist/src/pi-extension.js +36 -67
  42. package/dist/src/pi-extension.js.map +1 -1
  43. package/dist/src/router/namespace/core/intent-router.d.ts +24 -0
  44. package/dist/src/router/namespace/core/intent-router.d.ts.map +1 -0
  45. package/dist/src/router/namespace/core/intent-router.js +190 -0
  46. package/dist/src/router/namespace/core/intent-router.js.map +1 -0
  47. package/dist/src/router/namespace/core/lifecycle-router.d.ts +28 -0
  48. package/dist/src/router/namespace/core/lifecycle-router.d.ts.map +1 -0
  49. package/dist/src/router/namespace/core/lifecycle-router.js +132 -0
  50. package/dist/src/router/namespace/core/lifecycle-router.js.map +1 -0
  51. package/dist/src/router/namespace/core/state-router.d.ts +32 -0
  52. package/dist/src/router/namespace/core/state-router.d.ts.map +1 -0
  53. package/dist/src/router/namespace/core/state-router.js +157 -0
  54. package/dist/src/router/namespace/core/state-router.js.map +1 -0
  55. package/dist/src/router/namespace/domain/code-router.d.ts +26 -0
  56. package/dist/src/router/namespace/domain/code-router.d.ts.map +1 -0
  57. package/dist/src/router/namespace/domain/code-router.js +171 -0
  58. package/dist/src/router/namespace/domain/code-router.js.map +1 -0
  59. package/dist/src/router/namespace/domain/debug-router.d.ts +25 -0
  60. package/dist/src/router/namespace/domain/debug-router.d.ts.map +1 -0
  61. package/dist/src/router/namespace/domain/debug-router.js +139 -0
  62. package/dist/src/router/namespace/domain/debug-router.js.map +1 -0
  63. package/dist/src/router/namespace/domain/plan-router.d.ts +29 -0
  64. package/dist/src/router/namespace/domain/plan-router.d.ts.map +1 -0
  65. package/dist/src/router/namespace/domain/plan-router.js +160 -0
  66. package/dist/src/router/namespace/domain/plan-router.js.map +1 -0
  67. package/dist/src/router/namespace/domain/review-router.d.ts +24 -0
  68. package/dist/src/router/namespace/domain/review-router.d.ts.map +1 -0
  69. package/dist/src/router/namespace/domain/review-router.js +116 -0
  70. package/dist/src/router/namespace/domain/review-router.js.map +1 -0
  71. package/dist/src/router/namespace/index.d.ts +19 -0
  72. package/dist/src/router/namespace/index.d.ts.map +1 -0
  73. package/dist/src/router/namespace/index.js +22 -0
  74. package/dist/src/router/namespace/index.js.map +1 -0
  75. package/dist/src/router/namespace/registry.d.ts +67 -0
  76. package/dist/src/router/namespace/registry.d.ts.map +1 -0
  77. package/dist/src/router/namespace/registry.js +197 -0
  78. package/dist/src/router/namespace/registry.js.map +1 -0
  79. package/dist/src/router/namespace/types.d.ts +124 -0
  80. package/dist/src/router/namespace/types.d.ts.map +1 -0
  81. package/dist/src/router/namespace/types.js +20 -0
  82. package/dist/src/router/namespace/types.js.map +1 -0
  83. package/dist/src/router/namespace/utility/fallback-router.d.ts +28 -0
  84. package/dist/src/router/namespace/utility/fallback-router.d.ts.map +1 -0
  85. package/dist/src/router/namespace/utility/fallback-router.js +88 -0
  86. package/dist/src/router/namespace/utility/fallback-router.js.map +1 -0
  87. package/dist/src/router/namespace/utility/quick-task-router.d.ts +28 -0
  88. package/dist/src/router/namespace/utility/quick-task-router.d.ts.map +1 -0
  89. package/dist/src/router/namespace/utility/quick-task-router.js +99 -0
  90. package/dist/src/router/namespace/utility/quick-task-router.js.map +1 -0
  91. package/dist/src/router/namespace/utility/research-router.d.ts +24 -0
  92. package/dist/src/router/namespace/utility/research-router.d.ts.map +1 -0
  93. package/dist/src/router/namespace/utility/research-router.js +84 -0
  94. package/dist/src/router/namespace/utility/research-router.js.map +1 -0
  95. package/dist/src/skills/agents-md/index.js +2 -2
  96. package/dist/src/skills/agents-md/index.js.map +1 -1
  97. package/dist/src/skills/execute-plan/index.d.ts +45 -65
  98. package/dist/src/skills/execute-plan/index.d.ts.map +1 -1
  99. package/dist/src/skills/execute-plan/index.js +325 -551
  100. package/dist/src/skills/execute-plan/index.js.map +1 -1
  101. package/dist/src/skills/index.d.ts +1 -0
  102. package/dist/src/skills/index.d.ts.map +1 -1
  103. package/dist/src/skills/index.js +1 -0
  104. package/dist/src/skills/index.js.map +1 -1
  105. package/dist/src/skills/quick-task/index.d.ts +4 -4
  106. package/dist/src/skills/quick-task/index.js +1 -1
  107. package/dist/src/skills/quick-task/index.js.map +1 -1
  108. package/dist/src/skills/review-diff/index.d.ts +6 -6
  109. package/dist/src/skills/review-diff/index.js +1 -1
  110. package/dist/src/skills/review-diff/index.js.map +1 -1
  111. package/dist/src/skills/router/index.d.ts +101 -0
  112. package/dist/src/skills/router/index.d.ts.map +1 -0
  113. package/dist/src/skills/router/index.js +450 -0
  114. package/dist/src/skills/router/index.js.map +1 -0
  115. package/dist/src/skills/router/types.d.ts +79 -0
  116. package/dist/src/skills/router/types.d.ts.map +1 -0
  117. package/dist/src/skills/router/types.js +8 -0
  118. package/dist/src/skills/router/types.js.map +1 -0
  119. package/dist/src/skills/systematic-debugging/index.js +1 -1
  120. package/dist/src/skills/systematic-debugging/index.js.map +1 -1
  121. package/dist/src/skills/tdd/index.d.ts +14 -14
  122. package/dist/src/skills/tdd/index.js +1 -1
  123. package/dist/src/skills/tdd/index.js.map +1 -1
  124. package/dist/src/skills/to-plan/index-enhanced.d.ts +4 -4
  125. package/dist/src/skills/to-plan/index-enhanced.d.ts.map +1 -1
  126. package/dist/src/skills/to-plan/index-enhanced.js +3 -5
  127. package/dist/src/skills/to-plan/index-enhanced.js.map +1 -1
  128. package/dist/src/skills/to-plan/index.d.ts +24 -91
  129. package/dist/src/skills/to-plan/index.d.ts.map +1 -1
  130. package/dist/src/skills/to-plan/index.js +214 -409
  131. package/dist/src/skills/to-plan/index.js.map +1 -1
  132. package/package.json +3 -5
  133. package/scripts/postinstall.js +42 -47
  134. package/src/agents/contracts/implementer.ts +122 -0
  135. package/src/agents/contracts/index.ts +27 -0
  136. package/src/agents/contracts/planner.ts +129 -0
  137. package/src/agents/contracts/router.ts +168 -0
  138. package/src/agents/contracts/verifier.ts +137 -0
  139. package/src/agents/dispatcher.ts +387 -362
  140. package/src/persistence/index.ts +10 -4
  141. package/src/persistence/plan-md.ts +52 -18
  142. package/src/persistence/state-md.ts +45 -23
  143. package/src/persistence/types.ts +37 -40
  144. package/src/pi-extension.ts +38 -76
  145. package/src/router/namespace/README.md +127 -0
  146. package/src/router/namespace/core/intent-router.ts +221 -0
  147. package/src/router/namespace/core/lifecycle-router.ts +156 -0
  148. package/src/router/namespace/core/state-router.ts +192 -0
  149. package/src/router/namespace/domain/code-router.ts +202 -0
  150. package/src/router/namespace/domain/debug-router.ts +167 -0
  151. package/src/router/namespace/domain/plan-router.ts +196 -0
  152. package/src/router/namespace/domain/review-router.ts +142 -0
  153. package/src/router/namespace/index.ts +84 -0
  154. package/src/router/namespace/registry.ts +242 -0
  155. package/src/router/namespace/types.ts +182 -0
  156. package/src/router/namespace/utility/fallback-router.ts +107 -0
  157. package/src/router/namespace/utility/quick-task-router.ts +121 -0
  158. package/src/router/namespace/utility/research-router.ts +105 -0
  159. package/src/skills/agents-md/index.ts +2 -2
  160. package/src/skills/execute-plan/index.ts +419 -673
  161. package/src/skills/index.ts +1 -0
  162. package/src/skills/quick-task/index.ts +1 -1
  163. package/src/skills/review-diff/index.ts +1 -1
  164. package/src/skills/router/SKILL.md +81 -0
  165. package/src/skills/router/index.ts +577 -0
  166. package/src/skills/router/types.ts +90 -0
  167. package/src/skills/systematic-debugging/index.ts +1 -1
  168. package/src/skills/tdd/index.ts +1 -1
  169. package/src/skills/to-plan/index-enhanced.ts +3 -5
  170. package/src/skills/to-plan/index.ts +231 -502
  171. package/dist/src/extension/classifier.d.ts +0 -18
  172. package/dist/src/extension/classifier.d.ts.map +0 -1
  173. package/dist/src/extension/classifier.js +0 -143
  174. package/dist/src/extension/classifier.js.map +0 -1
  175. package/src/extension/classifier.ts +0 -160
@@ -1,8 +1,14 @@
1
1
  /**
2
- * Persistence layer exports
2
+ * Persistence layer entry point
3
+ *
4
+ * Provides STATE.md and PLAN.md management for workflow state persistence.
3
5
  */
6
+
7
+ // Managers
4
8
  export { StateMdManager } from './state-md.js'
5
9
  export { PlanMdManager } from './plan-md.js'
10
+
11
+ // Types
6
12
  export type {
7
13
  Task,
8
14
  Wave,
@@ -11,7 +17,7 @@ export type {
11
17
  VerificationResult,
12
18
  DimensionResult,
13
19
  WorkflowState,
14
- StateMdManager as IStateMdManager,
15
- PlanMdManager as IPlanMdManager,
16
- PersistenceManager
20
+ StateMdManager as StateMdManagerInterface,
21
+ PlanMdManager as PlanMdManagerInterface,
22
+ PersistenceManager,
17
23
  } from './types.js'
@@ -3,7 +3,7 @@
3
3
  */
4
4
  import { promises as fs } from 'fs'
5
5
  import { dirname } from 'path'
6
- import type { Plan, Task, PlanMdManager as IPlanMdManager } from './types.js'
6
+ import type { Plan, Task, Wave, PlanMdManager as IPlanMdManager } from './types.js'
7
7
 
8
8
  export class PlanMdManager implements IPlanMdManager {
9
9
  private planPath: string
@@ -20,9 +20,16 @@ export class PlanMdManager implements IPlanMdManager {
20
20
  await fs.writeFile(this.planPath, content, 'utf-8')
21
21
  }
22
22
 
23
- async readPlan(): Promise<Plan> {
24
- const content = await fs.readFile(this.planPath, 'utf-8')
25
- return this.parsePlan(content)
23
+ async readPlan(): Promise<Plan | null> {
24
+ try {
25
+ const content = await fs.readFile(this.planPath, 'utf-8')
26
+ return this.parsePlan(content)
27
+ } catch (error) {
28
+ if ((error as NodeJS.ErrnoException).code === 'ENOENT') {
29
+ return null
30
+ }
31
+ throw error
32
+ }
26
33
  }
27
34
 
28
35
  async updateTaskStatus(taskId: string, status: 'pending' | 'in_progress' | 'complete'): Promise<void> {
@@ -35,14 +42,37 @@ export class PlanMdManager implements IPlanMdManager {
35
42
  await fs.writeFile(this.planPath, updated, 'utf-8')
36
43
  }
37
44
 
45
+ async getNextPendingWave(): Promise<Wave | null> {
46
+ const plan = await this.readPlan()
47
+ if (!plan) {
48
+ return null
49
+ }
50
+
51
+ // Find first wave with pending tasks
52
+ for (const wave of plan.waves) {
53
+ const hasPendingTask = wave.tasks.some(task => task.status === 'pending')
54
+ if (hasPendingTask) {
55
+ return wave
56
+ }
57
+ }
58
+
59
+ return null
60
+ }
61
+
38
62
  private generatePlanMarkdown(plan: Plan): string {
63
+ const getStatusChar = (status: Task['status']) => {
64
+ if (status === 'completed') return 'x'
65
+ if (status === 'in_progress') return '~'
66
+ return ' '
67
+ }
68
+
39
69
  return `# Execution Plan
40
70
 
41
71
  ## Goal
42
72
  ${plan.goal}
43
73
 
44
74
  ## Tasks
45
- ${plan.tasks.map(t => `- [ ] ${t.id}: ${t.description} (${t.estimatedComplexity})`).join('\n')}
75
+ ${plan.tasks.map(t => `- [${getStatusChar(t.status)}] ${t.id}: ${t.description} (${t.estimatedComplexity})`).join('\n')}
46
76
 
47
77
  ## Dependencies
48
78
  ${Object.entries(plan.dependencies).length > 0
@@ -51,7 +81,7 @@ ${Object.entries(plan.dependencies).length > 0
51
81
  }
52
82
 
53
83
  ## Waves
54
- ${plan.waves.map(w => `- ${w.id}: ${w.tasks.join(', ')}`).join('\n')}
84
+ ${plan.waves.map(w => `- ${w.id}: ${w.tasks.map(t => t.id).join(', ')}`).join('\n')}
55
85
 
56
86
  ## Verification Criteria
57
87
  ${plan.verificationCriteria.map(c => `- ${c}`).join('\n')}
@@ -60,9 +90,6 @@ ${plan.verificationCriteria.map(c => `- ${c}`).join('\n')}
60
90
  - Total Tasks: ${plan.tasks.length}
61
91
  - Parallel Waves: ${plan.waves.length}
62
92
  - Estimated Duration: ${plan.estimatedDuration}ms
63
-
64
- ## Rationale
65
- ${plan.rationale}
66
93
  `
67
94
  }
68
95
 
@@ -73,19 +100,22 @@ ${plan.rationale}
73
100
  const wavesMatch = content.match(/## Waves\n([\s\S]*?)(?=\n## |$)/)
74
101
  const criteriaMatch = content.match(/## Verification Criteria\n([\s\S]*?)(?=\n## |$)/)
75
102
  const durationMatch = content.match(/- Estimated Duration: (\d+)/)
76
- const rationaleMatch = content.match(/## Rationale\n([\s\S]*?)(?=\n## |$)/)
77
103
 
78
104
  // Parse tasks
79
105
  const tasks: Task[] = []
80
106
  if (tasksMatch) {
81
107
  const taskLines = tasksMatch[1].split('\n').filter(l => l.trim().startsWith('- ['))
82
108
  for (const line of taskLines) {
83
- const match = line.match(/- \[[x\s~]]\s+(\w+):\s+(.+)\s+\((\w+)\)/)
109
+ const match = line.match(/- \[(.)\]\s+(\w+):\s+(.+)\s+\((\w+)\)/)
84
110
  if (match) {
111
+ const statusChar = match[1]
112
+ const status = statusChar === 'x' ? 'completed' : statusChar === '~' ? 'in_progress' : 'pending'
85
113
  tasks.push({
86
- id: match[1],
87
- description: match[2],
88
- estimatedComplexity: match[3] as 'low' | 'medium' | 'high'
114
+ id: match[2],
115
+ description: match[3],
116
+ status,
117
+ dependencies: [],
118
+ estimatedComplexity: match[4] as 'low' | 'medium' | 'high'
89
119
  })
90
120
  }
91
121
  }
@@ -103,16 +133,21 @@ ${plan.rationale}
103
133
  }
104
134
  }
105
135
 
136
+ // Build task map for wave parsing
137
+ const taskMap = new Map<string, Task>(tasks.map(t => [t.id, t]))
138
+
106
139
  // Parse waves
107
- const waves = []
140
+ const waves: Wave[] = []
108
141
  if (wavesMatch) {
109
142
  const waveLines = wavesMatch[1].split('\n').filter(l => l.trim().startsWith('- '))
110
143
  for (const line of waveLines) {
111
144
  const match = line.match(/- (\w+):\s*(.+)/)
112
145
  if (match) {
146
+ const taskIds = match[2].split(',').map(s => s.trim()).filter(s => s)
147
+ const waveTasks = taskIds.map(id => taskMap.get(id)).filter((t): t is Task => t !== undefined)
113
148
  waves.push({
114
149
  id: match[1],
115
- tasks: match[2].split(',').map(s => s.trim()).filter(s => s)
150
+ tasks: waveTasks
116
151
  })
117
152
  }
118
153
  }
@@ -134,8 +169,7 @@ ${plan.rationale}
134
169
  dependencies,
135
170
  waves,
136
171
  verificationCriteria,
137
- estimatedDuration: durationMatch ? parseInt(durationMatch[1], 10) : 0,
138
- rationale: rationaleMatch?.[1]?.trim() || ''
172
+ estimatedDuration: durationMatch ? parseInt(durationMatch[1], 10) : 0
139
173
  }
140
174
  }
141
175
  }
@@ -36,10 +36,7 @@ ${goal}
36
36
  <!-- Populated by verify phase -->
37
37
 
38
38
  ## Token Usage
39
- - Plan Phase: 0
40
- - Execute Phase: 0
41
- - Verify Phase: 0
42
- - Total: 0 / 1000000
39
+ 0
43
40
  `
44
41
  await fs.writeFile(this.statePath, content, 'utf-8')
45
42
  }
@@ -65,7 +62,14 @@ ${goal}
65
62
 
66
63
  async recordTaskComplete(taskId: string, result: TaskResult): Promise<void> {
67
64
  const content = await fs.readFile(this.statePath, 'utf-8')
68
- const entry = `- [x] ${taskId}: ${result.success ? '' : '✗'} (${result.duration}ms) - ${result.notes || ''}\n`
65
+ const entry = `- [x] ${taskId}: ✓ (${result.duration}ms) - ${result.notes || ''}\n`
66
+ const updated = this.appendToSection(content, 'Completed Tasks', entry)
67
+ await fs.writeFile(this.statePath, updated, 'utf-8')
68
+ }
69
+
70
+ async recordTaskFailed(taskId: string, error?: string): Promise<void> {
71
+ const content = await fs.readFile(this.statePath, 'utf-8')
72
+ const entry = `- [x] ${taskId}: ✗ FAILED${error ? ` - ${error}` : ''}\n`
69
73
  const updated = this.appendToSection(content, 'Completed Tasks', entry)
70
74
  await fs.writeFile(this.statePath, updated, 'utf-8')
71
75
  }
@@ -77,6 +81,15 @@ ${goal}
77
81
  await fs.writeFile(this.statePath, updated, 'utf-8')
78
82
  }
79
83
 
84
+ async updateTokenUsage(tokens: number): Promise<void> {
85
+ const content = await fs.readFile(this.statePath, 'utf-8')
86
+ const updated = content.replace(
87
+ /## Token Usage\n\d+/,
88
+ `## Token Usage\n${tokens}`
89
+ )
90
+ await fs.writeFile(this.statePath, updated, 'utf-8')
91
+ }
92
+
80
93
  async readCurrentState(): Promise<WorkflowState> {
81
94
  const content = await fs.readFile(this.statePath, 'utf-8')
82
95
  return this.parseState(content)
@@ -91,7 +104,7 @@ ${plan.goal}
91
104
  ${plan.tasks.map(t => `- ${t.id}: ${t.description} (${t.estimatedComplexity})`).join('\n')}
92
105
 
93
106
  ### Waves (${plan.waves.length})
94
- ${plan.waves.map(w => `- ${w.id}: ${w.tasks.join(', ')}`).join('\n')}
107
+ ${plan.waves.map(w => `- ${w.id}: ${w.tasks.map(t => t.id).join(', ')}`).join('\n')}
95
108
 
96
109
  ### Verification Criteria
97
110
  ${plan.verificationCriteria.map(c => `- ${c}`).join('\n')}
@@ -99,8 +112,8 @@ ${plan.verificationCriteria.map(c => `- ${c}`).join('\n')}
99
112
  ### Estimated Duration
100
113
  ${plan.estimatedDuration}ms
101
114
 
102
- ### Rationale
103
- ${plan.rationale}
115
+ ### Dependencies
116
+ ${Object.entries(plan.dependencies).map(([taskId, deps]) => `- ${taskId}: ${deps.join(', ') || 'none'}`).join('\n')}
104
117
  `
105
118
  }
106
119
 
@@ -141,27 +154,36 @@ ${results.join('\n')}
141
154
  const phaseMatch = content.match(/- Phase: (\w+)/)
142
155
  const goalMatch = content.match(/## Goal\n(.+)/)
143
156
  const startedMatch = content.match(/- Started: (.+)/)
144
- const totalMatch = content.match(/### Tasks \((\d+)\)/)
157
+ const lastUpdatedMatch = content.match(/- Last Updated: (.+)/)
158
+ const tokenMatch = content.match(/## Token Usage\n(\d+)/)
145
159
  const completedMatch = content.match(/## Completed Tasks\n([\s\S]*?)(?=\n## |$)/)
146
160
 
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
161
+ const completedTasks: string[] = []
162
+ const failedTasks: string[] = []
163
+
164
+ if (completedMatch) {
165
+ const lines = completedMatch[1].split('\n').filter(l => l.trim().startsWith('- [x]'))
166
+ for (const line of lines) {
167
+ const taskIdMatch = line.match(/- \[x\] ([^:]+):/)
168
+ if (taskIdMatch) {
169
+ if (line.includes('✗ FAILED')) {
170
+ failedTasks.push(taskIdMatch[1])
171
+ } else {
172
+ completedTasks.push(taskIdMatch[1])
173
+ }
174
+ }
175
+ }
176
+ }
152
177
 
153
178
  return {
154
179
  goal: goalMatch?.[1] || '',
155
180
  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
- }
181
+ startedAt: startedMatch?.[1] || new Date().toISOString(),
182
+ lastUpdated: lastUpdatedMatch?.[1] || new Date().toISOString(),
183
+ completedTasks,
184
+ failedTasks,
185
+ totalTasks: 0,
186
+ tokenUsage: { used: tokenMatch ? parseInt(tokenMatch[1], 10) : 0, limit: 1000000, percentage: 0 }
165
187
  }
166
188
  }
167
189
  }
@@ -5,67 +5,45 @@
5
5
  export interface Task {
6
6
  id: string
7
7
  description: string
8
+ status: 'pending' | 'in_progress' | 'completed' | 'failed'
9
+ dependencies: string[]
8
10
  estimatedComplexity: 'low' | 'medium' | 'high'
9
11
  relatedFiles?: string[]
10
- verificationCriteria?: string[]
11
12
  }
12
13
 
13
14
  export interface Wave {
14
15
  id: string
15
- tasks: string[]
16
+ tasks: Task[]
16
17
  }
17
18
 
18
19
  export interface Plan {
19
20
  goal: string
20
21
  tasks: Task[]
21
- dependencies: Record<string, string[]>
22
22
  waves: Wave[]
23
+ dependencies: Record<string, string[]>
23
24
  verificationCriteria: string[]
24
25
  estimatedDuration: number
25
- rationale: string
26
+ }
27
+
28
+ export interface WorkflowState {
29
+ goal: string
30
+ phase: 'discovery' | 'planning' | 'executing' | 'verifying' | 'completed' | 'failed' | 'idle'
31
+ startedAt: string
32
+ lastUpdated: string
33
+ completedTasks: string[]
34
+ failedTasks: string[]
35
+ totalTasks: number
36
+ tokenUsage: { used: number; limit: number; percentage: number }
26
37
  }
27
38
 
28
39
  export interface TaskResult {
29
40
  taskId: string
30
41
  success: boolean
31
- filesModified: string[]
32
- testsAdded: string[]
33
42
  duration: number
34
43
  notes: string
44
+ filesModified?: string[]
45
+ testsAdded?: string[]
35
46
  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
47
  }
70
48
 
71
49
  export interface StateMdManager {
@@ -73,17 +51,36 @@ export interface StateMdManager {
73
51
  updatePhase(phase: WorkflowState['phase']): Promise<void>
74
52
  recordPlan(plan: Plan): Promise<void>
75
53
  recordTaskComplete(taskId: string, result: TaskResult): Promise<void>
54
+ recordTaskFailed(taskId: string, error?: string): Promise<void>
76
55
  recordVerification(verification: VerificationResult): Promise<void>
56
+ updateTokenUsage(tokens: number): Promise<void>
77
57
  readCurrentState(): Promise<WorkflowState>
78
58
  }
79
59
 
80
60
  export interface PlanMdManager {
81
61
  writePlan(plan: Plan): Promise<void>
82
- readPlan(): Promise<Plan>
62
+ readPlan(): Promise<Plan | null>
83
63
  updateTaskStatus(taskId: string, status: 'pending' | 'in_progress' | 'complete'): Promise<void>
64
+ getNextPendingWave(): Promise<Wave | null>
84
65
  }
85
66
 
86
67
  export interface PersistenceManager {
87
68
  state: StateMdManager
88
69
  plan: PlanMdManager
89
70
  }
71
+
72
+ export interface VerificationResult {
73
+ success: boolean
74
+ dimensions: {
75
+ goals?: DimensionResult
76
+ quality?: DimensionResult
77
+ tests?: DimensionResult
78
+ }
79
+ }
80
+
81
+ export interface DimensionResult {
82
+ success: boolean
83
+ details?: unknown
84
+ evidence?: string[]
85
+ gaps?: string[]
86
+ }
@@ -1,111 +1,73 @@
1
1
  /**
2
- * PI Extension - yi-workflow dispatcher
2
+ * PI Extension - yi-workflow router interceptor
3
3
  *
4
- * Intercepts regular chat and dispatches to appropriate skills.
5
- * Ensures THIN DISPATCHER pattern: main agent NEVER does work.
4
+ * ALL user input /skill:router
5
+ * Router skill handles ALL routing logic (5-layer GSD).
6
+ * Extension does NOT classify intent — that's the router's job.
6
7
  */
7
8
 
8
- import { classifyIntent } from "./extension/classifier.js";
9
-
10
- // Extension API is provided by PI at runtime
11
- // We use 'any' to avoid compile-time dependency
12
9
  export default function (pi: any) {
13
- // Store transformed input for before_agent_start to detect
14
10
  let transformedInput: string | null = null;
15
11
 
16
- // Intercept user input before processing
12
+ // Intercept ALL user input route to router skill
17
13
  pi.on("input", async (event: any, ctx: any) => {
18
- // Skip if user already used a slash command
14
+ // Skip slash commands (user explicitly calling a skill)
19
15
  if (event.text.startsWith("/")) {
20
16
  return { action: "continue" };
21
17
  }
22
18
 
23
- // Classify intent
24
- const intent = classifyIntent(event.text);
25
- const targetSkill = intent.skill || "quick-task";
26
-
27
- // Determine reason for dispatch
28
- let reason = intent.reason;
29
- if (!intent.skill) {
30
- const needsExternal = /^(what|how|why|when|where|who|search|find|look|weather|time|price|stock|translate)/i.test(event.text);
31
- if (needsExternal) {
32
- reason = "External query - needs subagent";
33
- } else {
34
- reason = "Default dispatch - no direct execution allowed";
35
- }
36
- }
37
-
38
- ctx.ui.notify(
39
- `[yi-workflow] Dispatching to /skill:${targetSkill} (${reason})`,
40
- "info"
41
- );
42
-
43
- // Transform to explicit skill command with instruction not to analyze
44
- const transformedText = `=== SKILL COMMAND ===\n/skill:${targetSkill}\n\nINPUT:\n${event.text}\n\n=== DO NOT ANALYZE ===\nYou MUST call the skill above immediately. NO thinking. NO planning. NO analysis. Just call the skill.`;
19
+ // Store for before_agent_start
20
+ transformedInput = event.text;
21
+
22
+ // Transform ALL input to router skill call
23
+ // The router skill does 5-layer routing internally
24
+ const transformedText = [
25
+ "=== ROUTER DISPATCH ===",
26
+ "/skill:router",
27
+ "",
28
+ "USER REQUEST:",
29
+ event.text,
30
+ "",
31
+ "=== INSTRUCTIONS ===",
32
+ "Call the router skill above IMMEDIATELY.",
33
+ "Do NOT analyze, plan, or execute the request yourself.",
34
+ "Do NOT use any tools (Read, Edit, Bash, etc.).",
35
+ "The router skill will determine the correct target skill.",
36
+ ].join("\n");
37
+
38
+ ctx.ui.notify("[yi-workflow] → router", "info");
45
39
 
46
- // Store for before_agent_start hook
47
- transformedInput = transformedText;
48
-
49
- // Transform input - this will be passed to main agent
50
40
  return {
51
41
  action: "transform",
52
42
  text: transformedText,
53
43
  };
54
44
  });
55
45
 
56
- // Log when session starts
57
46
  pi.on("session_start", async (event: any, ctx: any) => {
58
- ctx.ui.notify("yi-workflow extension loaded", "info");
47
+ ctx.ui.notify("yi-workflow router active", "info");
59
48
  });
60
49
 
61
- // CRITICAL: Inject THIN DISPATCHER rules into system prompt
62
- // This ensures main agent NEVER executes work directly
50
+ // Minimal system prompt injection just reinforce router dispatch
63
51
  pi.on("before_agent_start", async (event: any, ctx: any) => {
64
52
  const systemPrompt = event.systemPrompt || "";
65
-
66
- // Get the user input (either from event or from transformed global)
67
53
  const userInput = transformedInput || event.userInput || "";
68
54
 
69
- // Detect if this input was transformed to a skill command
70
- const isSkillCommand = userInput.startsWith("/skill:");
71
-
72
- // Prepend THIN DISPATCHER constraints
73
- const thinDispatcherRules = `
74
- ╔══════════════════════════════════════════════════════════════════╗
75
- ║ THIN DISPATCHER PROTOCOL [ABSOLUTE] ║
76
- ╚══════════════════════════════════════════════════════════════════╝
55
+ const isRouterCommand = userInput.includes("/skill:router");
77
56
 
78
- YOU ARE A THIN DISPATCHER. YOUR ONLY JOB IS TO DISPATCH TO SKILLS.
57
+ const rules = `
58
+ ## ROUTER DISPATCH RULE (MANDATORY)
79
59
 
80
- ⚠️ ABSOLUTE RULE #1: If user input starts with "/skill:", you MUST call that skill.
81
- ⚠️ ABSOLUTE RULE #2: You NEVER execute work yourself - only dispatch.
82
- ⚠️ ABSOLUTE RULE #3: Tools (Read, Edit, Bash, etc.) are FORBIDDEN unless in subagent.
60
+ You MUST follow this rule for ALL user messages:
83
61
 
84
- FORBIDDEN ACTIONS (VIOLATION = PROTOCOL BREACH):
85
- - Reading files (use /skill:review-diff)
86
- - Writing/editing files (use /skill:execute-plan)
87
- - Running commands (use /skill:quick-task)
88
- - Writing code directly (use /skill:tdd or /skill:execute-plan)
89
- - Using any tool directly (use /skill:quick-task to dispatch subagent)
62
+ 1. If input contains "/skill:router" → CALL THE ROUTER SKILL IMMEDIATELY
63
+ 2. You MUST NOT use any tools directly (Read, Edit, Bash, Grep, etc.)
64
+ 3. You MUST NOT analyze, plan, or execute user requests yourself
65
+ 4. Your ONLY action is: call /skill:router and return its output
90
66
 
91
- CURRENT INPUT: "${userInput}"
92
- IS_SKILL_COMMAND: ${isSkillCommand ? "YES - MUST CALL SKILL" : "NO"}
93
-
94
- ${isSkillCommand ? `
95
- ╔══════════════════════════════════════════════════════════════════╗
96
- ║ ⚠️ MANDATORY: CALL THE SKILL BELOW ⚠️ ║
97
- ╚══════════════════════════════════════════════════════════════════╝
98
- The input starts with "/skill:". You MUST call that skill.
99
- DO NOT process this input as a regular conversation.
100
- DO NOT use tools directly.
101
- CALL THE SKILL: "${userInput.split(" ")[0]}"
102
- ` : ""}
67
+ CURRENT REQUEST: ${isRouterCommand ? "ROUTER DISPATCH — CALL SKILL NOW" : "Normal message"}
103
68
  `;
104
69
 
105
- // Prepend to system prompt
106
- event.systemPrompt = thinDispatcherRules + "\n\n" + systemPrompt;
107
-
108
- // Reset transformed input for next turn
70
+ event.systemPrompt = rules + "\n\n" + systemPrompt;
109
71
  transformedInput = null;
110
72
 
111
73
  return { action: "continue" };