@renseiai/agentfactory 0.8.6 → 0.8.8

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/README.md +2 -2
  2. package/dist/src/config/repository-config.d.ts +14 -0
  3. package/dist/src/config/repository-config.d.ts.map +1 -1
  4. package/dist/src/config/repository-config.js +20 -0
  5. package/dist/src/governor/decision-engine.d.ts +7 -0
  6. package/dist/src/governor/decision-engine.d.ts.map +1 -1
  7. package/dist/src/governor/decision-engine.js +59 -1
  8. package/dist/src/governor/event-types.d.ts +18 -1
  9. package/dist/src/governor/event-types.d.ts.map +1 -1
  10. package/dist/src/governor/event-types.js +4 -0
  11. package/dist/src/governor/governor.d.ts +5 -1
  12. package/dist/src/governor/governor.d.ts.map +1 -1
  13. package/dist/src/governor/governor.js +6 -1
  14. package/dist/src/index.d.ts +1 -0
  15. package/dist/src/index.d.ts.map +1 -1
  16. package/dist/src/index.js +1 -0
  17. package/dist/src/merge-queue/adapters/github-native.d.ts +22 -0
  18. package/dist/src/merge-queue/adapters/github-native.d.ts.map +1 -0
  19. package/dist/src/merge-queue/adapters/github-native.js +243 -0
  20. package/dist/src/merge-queue/adapters/github-native.test.d.ts +2 -0
  21. package/dist/src/merge-queue/adapters/github-native.test.d.ts.map +1 -0
  22. package/dist/src/merge-queue/adapters/github-native.test.js +384 -0
  23. package/dist/src/merge-queue/index.d.ts +18 -0
  24. package/dist/src/merge-queue/index.d.ts.map +1 -0
  25. package/dist/src/merge-queue/index.js +28 -0
  26. package/dist/src/merge-queue/merge-queue.integration.test.d.ts +2 -0
  27. package/dist/src/merge-queue/merge-queue.integration.test.d.ts.map +1 -0
  28. package/dist/src/merge-queue/merge-queue.integration.test.js +128 -0
  29. package/dist/src/merge-queue/types.d.ts +48 -0
  30. package/dist/src/merge-queue/types.d.ts.map +1 -0
  31. package/dist/src/merge-queue/types.js +8 -0
  32. package/dist/src/orchestrator/activity-emitter.d.ts +3 -3
  33. package/dist/src/orchestrator/activity-emitter.d.ts.map +1 -1
  34. package/dist/src/orchestrator/activity-emitter.js +1 -1
  35. package/dist/src/orchestrator/artifact-tracker.d.ts +93 -0
  36. package/dist/src/orchestrator/artifact-tracker.d.ts.map +1 -0
  37. package/dist/src/orchestrator/artifact-tracker.js +235 -0
  38. package/dist/src/orchestrator/artifact-tracker.test.d.ts +2 -0
  39. package/dist/src/orchestrator/artifact-tracker.test.d.ts.map +1 -0
  40. package/dist/src/orchestrator/artifact-tracker.test.js +189 -0
  41. package/dist/src/orchestrator/context-manager.d.ts +72 -0
  42. package/dist/src/orchestrator/context-manager.d.ts.map +1 -0
  43. package/dist/src/orchestrator/context-manager.js +120 -0
  44. package/dist/src/orchestrator/context-manager.test.d.ts +2 -0
  45. package/dist/src/orchestrator/context-manager.test.d.ts.map +1 -0
  46. package/dist/src/orchestrator/context-manager.test.js +137 -0
  47. package/dist/src/orchestrator/detect-work-type.test.js +25 -16
  48. package/dist/src/orchestrator/index.d.ts +12 -2
  49. package/dist/src/orchestrator/index.d.ts.map +1 -1
  50. package/dist/src/orchestrator/index.js +9 -1
  51. package/dist/src/orchestrator/issue-tracker-client.d.ts +103 -0
  52. package/dist/src/orchestrator/issue-tracker-client.d.ts.map +1 -0
  53. package/dist/src/orchestrator/issue-tracker-client.js +8 -0
  54. package/dist/src/orchestrator/log-analyzer.d.ts +19 -4
  55. package/dist/src/orchestrator/log-analyzer.d.ts.map +1 -1
  56. package/dist/src/orchestrator/log-analyzer.js +26 -50
  57. package/dist/src/orchestrator/orchestrator-utils.test.js +3 -0
  58. package/dist/src/orchestrator/orchestrator.d.ts +16 -2
  59. package/dist/src/orchestrator/orchestrator.d.ts.map +1 -1
  60. package/dist/src/orchestrator/orchestrator.js +449 -115
  61. package/dist/src/orchestrator/parse-work-result.d.ts +1 -1
  62. package/dist/src/orchestrator/parse-work-result.d.ts.map +1 -1
  63. package/dist/src/orchestrator/parse-work-result.js +1 -1
  64. package/dist/src/orchestrator/session-logger.d.ts +1 -1
  65. package/dist/src/orchestrator/session-logger.d.ts.map +1 -1
  66. package/dist/src/orchestrator/state-recovery.d.ts +22 -3
  67. package/dist/src/orchestrator/state-recovery.d.ts.map +1 -1
  68. package/dist/src/orchestrator/state-recovery.js +55 -2
  69. package/dist/src/orchestrator/state-recovery.test.js +106 -2
  70. package/dist/src/orchestrator/state-types.d.ts +63 -1
  71. package/dist/src/orchestrator/state-types.d.ts.map +1 -1
  72. package/dist/src/orchestrator/state-types.js +5 -1
  73. package/dist/src/orchestrator/summary-builder.d.ts +47 -0
  74. package/dist/src/orchestrator/summary-builder.d.ts.map +1 -0
  75. package/dist/src/orchestrator/summary-builder.js +240 -0
  76. package/dist/src/orchestrator/summary-builder.test.d.ts +2 -0
  77. package/dist/src/orchestrator/summary-builder.test.d.ts.map +1 -0
  78. package/dist/src/orchestrator/summary-builder.test.js +236 -0
  79. package/dist/src/orchestrator/types.d.ts +24 -2
  80. package/dist/src/orchestrator/types.d.ts.map +1 -1
  81. package/dist/src/orchestrator/work-types.d.ts +50 -0
  82. package/dist/src/orchestrator/work-types.d.ts.map +1 -0
  83. package/dist/src/orchestrator/work-types.js +20 -0
  84. package/dist/src/templates/registry.d.ts +1 -1
  85. package/dist/src/templates/registry.test.js +2 -2
  86. package/dist/src/templates/renderer.d.ts +1 -1
  87. package/dist/src/templates/types.d.ts +6 -2
  88. package/dist/src/templates/types.d.ts.map +1 -1
  89. package/dist/src/templates/types.js +2 -0
  90. package/dist/src/templates/types.test.js +4 -3
  91. package/dist/src/tools/index.d.ts +0 -3
  92. package/dist/src/tools/index.d.ts.map +1 -1
  93. package/dist/src/tools/index.js +0 -2
  94. package/dist/src/workflow/branching-router.d.ts +38 -0
  95. package/dist/src/workflow/branching-router.d.ts.map +1 -0
  96. package/dist/src/workflow/branching-router.js +52 -0
  97. package/dist/src/workflow/branching-router.test.d.ts +2 -0
  98. package/dist/src/workflow/branching-router.test.d.ts.map +1 -0
  99. package/dist/src/workflow/branching-router.test.js +209 -0
  100. package/dist/src/workflow/duration.d.ts +28 -0
  101. package/dist/src/workflow/duration.d.ts.map +1 -0
  102. package/dist/src/workflow/duration.js +57 -0
  103. package/dist/src/workflow/duration.test.d.ts +2 -0
  104. package/dist/src/workflow/duration.test.d.ts.map +1 -0
  105. package/dist/src/workflow/duration.test.js +74 -0
  106. package/dist/src/workflow/expression/ast.d.ts +53 -0
  107. package/dist/src/workflow/expression/ast.d.ts.map +1 -0
  108. package/dist/src/workflow/expression/ast.js +8 -0
  109. package/dist/src/workflow/expression/context.d.ts +40 -0
  110. package/dist/src/workflow/expression/context.d.ts.map +1 -0
  111. package/dist/src/workflow/expression/context.js +37 -0
  112. package/dist/src/workflow/expression/evaluator.d.ts +28 -0
  113. package/dist/src/workflow/expression/evaluator.d.ts.map +1 -0
  114. package/dist/src/workflow/expression/evaluator.js +165 -0
  115. package/dist/src/workflow/expression/evaluator.test.d.ts +2 -0
  116. package/dist/src/workflow/expression/evaluator.test.d.ts.map +1 -0
  117. package/dist/src/workflow/expression/evaluator.test.js +792 -0
  118. package/dist/src/workflow/expression/expression.test.d.ts +2 -0
  119. package/dist/src/workflow/expression/expression.test.d.ts.map +1 -0
  120. package/dist/src/workflow/expression/expression.test.js +516 -0
  121. package/dist/src/workflow/expression/helpers.d.ts +21 -0
  122. package/dist/src/workflow/expression/helpers.d.ts.map +1 -0
  123. package/dist/src/workflow/expression/helpers.js +56 -0
  124. package/dist/src/workflow/expression/index.d.ts +55 -0
  125. package/dist/src/workflow/expression/index.d.ts.map +1 -0
  126. package/dist/src/workflow/expression/index.js +71 -0
  127. package/dist/src/workflow/expression/lexer.d.ts +37 -0
  128. package/dist/src/workflow/expression/lexer.d.ts.map +1 -0
  129. package/dist/src/workflow/expression/lexer.js +166 -0
  130. package/dist/src/workflow/expression/parser.d.ts +23 -0
  131. package/dist/src/workflow/expression/parser.d.ts.map +1 -0
  132. package/dist/src/workflow/expression/parser.js +181 -0
  133. package/dist/src/workflow/index.d.ts +21 -0
  134. package/dist/src/workflow/index.d.ts.map +1 -0
  135. package/dist/src/workflow/index.js +15 -0
  136. package/dist/src/workflow/retry-resolver.d.ts +51 -0
  137. package/dist/src/workflow/retry-resolver.d.ts.map +1 -0
  138. package/dist/src/workflow/retry-resolver.js +70 -0
  139. package/dist/src/workflow/retry-resolver.test.d.ts +2 -0
  140. package/dist/src/workflow/retry-resolver.test.d.ts.map +1 -0
  141. package/dist/src/workflow/retry-resolver.test.js +149 -0
  142. package/dist/src/workflow/transition-engine.d.ts +46 -0
  143. package/dist/src/workflow/transition-engine.d.ts.map +1 -0
  144. package/dist/src/workflow/transition-engine.js +113 -0
  145. package/dist/src/workflow/transition-engine.test.d.ts +2 -0
  146. package/dist/src/workflow/transition-engine.test.d.ts.map +1 -0
  147. package/dist/src/workflow/transition-engine.test.js +425 -0
  148. package/dist/src/workflow/workflow-loader.d.ts +21 -0
  149. package/dist/src/workflow/workflow-loader.d.ts.map +1 -0
  150. package/dist/src/workflow/workflow-loader.js +40 -0
  151. package/dist/src/workflow/workflow-loader.test.d.ts +2 -0
  152. package/dist/src/workflow/workflow-loader.test.d.ts.map +1 -0
  153. package/dist/src/workflow/workflow-loader.test.js +134 -0
  154. package/dist/src/workflow/workflow-registry.d.ts +97 -0
  155. package/dist/src/workflow/workflow-registry.d.ts.map +1 -0
  156. package/dist/src/workflow/workflow-registry.js +173 -0
  157. package/dist/src/workflow/workflow-registry.test.d.ts +2 -0
  158. package/dist/src/workflow/workflow-registry.test.d.ts.map +1 -0
  159. package/dist/src/workflow/workflow-registry.test.js +201 -0
  160. package/dist/src/workflow/workflow-types.d.ts +442 -0
  161. package/dist/src/workflow/workflow-types.d.ts.map +1 -0
  162. package/dist/src/workflow/workflow-types.js +113 -0
  163. package/dist/src/workflow/workflow-types.test.d.ts +2 -0
  164. package/dist/src/workflow/workflow-types.test.d.ts.map +1 -0
  165. package/dist/src/workflow/workflow-types.test.js +440 -0
  166. package/package.json +3 -4
  167. package/dist/src/linear-cli.d.ts +0 -38
  168. package/dist/src/linear-cli.d.ts.map +0 -1
  169. package/dist/src/linear-cli.js +0 -674
  170. package/dist/src/tools/linear-runner.d.ts +0 -34
  171. package/dist/src/tools/linear-runner.d.ts.map +0 -1
  172. package/dist/src/tools/linear-runner.js +0 -700
  173. package/dist/src/tools/plugins/linear.d.ts +0 -9
  174. package/dist/src/tools/plugins/linear.d.ts.map +0 -1
  175. package/dist/src/tools/plugins/linear.js +0 -138
@@ -0,0 +1,440 @@
1
+ import { describe, it, expect } from 'vitest';
2
+ import { PhaseDefinitionSchema, TransitionDefinitionSchema, EscalationLadderRungSchema, EscalationConfigSchema, GateDefinitionSchema, ParallelismGroupDefinitionSchema, WorkflowDefinitionSchema, validateWorkflowDefinition, } from './workflow-types.js';
3
+ describe('PhaseDefinitionSchema', () => {
4
+ it('validates a minimal phase', () => {
5
+ const result = PhaseDefinitionSchema.parse({
6
+ name: 'development',
7
+ template: 'development',
8
+ });
9
+ expect(result.name).toBe('development');
10
+ expect(result.template).toBe('development');
11
+ expect(result.description).toBeUndefined();
12
+ expect(result.variants).toBeUndefined();
13
+ });
14
+ it('validates a phase with all optional fields', () => {
15
+ const result = PhaseDefinitionSchema.parse({
16
+ name: 'refinement',
17
+ description: 'Address rejection feedback',
18
+ template: 'refinement',
19
+ variants: {
20
+ 'context-enriched': 'refinement-context-enriched',
21
+ 'decompose': 'refinement-decompose',
22
+ },
23
+ });
24
+ expect(result.variants).toEqual({
25
+ 'context-enriched': 'refinement-context-enriched',
26
+ 'decompose': 'refinement-decompose',
27
+ });
28
+ });
29
+ it('rejects phase without name', () => {
30
+ expect(() => PhaseDefinitionSchema.parse({
31
+ template: 'development',
32
+ })).toThrow();
33
+ });
34
+ it('rejects phase without template', () => {
35
+ expect(() => PhaseDefinitionSchema.parse({
36
+ name: 'development',
37
+ })).toThrow();
38
+ });
39
+ it('rejects empty name', () => {
40
+ expect(() => PhaseDefinitionSchema.parse({
41
+ name: '',
42
+ template: 'development',
43
+ })).toThrow();
44
+ });
45
+ it('rejects empty template', () => {
46
+ expect(() => PhaseDefinitionSchema.parse({
47
+ name: 'development',
48
+ template: '',
49
+ })).toThrow();
50
+ });
51
+ });
52
+ describe('TransitionDefinitionSchema', () => {
53
+ it('validates a minimal transition', () => {
54
+ const result = TransitionDefinitionSchema.parse({
55
+ from: 'Backlog',
56
+ to: 'development',
57
+ });
58
+ expect(result.from).toBe('Backlog');
59
+ expect(result.to).toBe('development');
60
+ expect(result.condition).toBeUndefined();
61
+ expect(result.priority).toBeUndefined();
62
+ });
63
+ it('validates a transition with condition and priority', () => {
64
+ const result = TransitionDefinitionSchema.parse({
65
+ from: 'Icebox',
66
+ to: 'research',
67
+ condition: '{{ not researchCompleted }}',
68
+ priority: 10,
69
+ });
70
+ expect(result.condition).toBe('{{ not researchCompleted }}');
71
+ expect(result.priority).toBe(10);
72
+ });
73
+ it('rejects transition without from', () => {
74
+ expect(() => TransitionDefinitionSchema.parse({
75
+ to: 'development',
76
+ })).toThrow();
77
+ });
78
+ it('rejects transition without to', () => {
79
+ expect(() => TransitionDefinitionSchema.parse({
80
+ from: 'Backlog',
81
+ })).toThrow();
82
+ });
83
+ it('rejects empty from', () => {
84
+ expect(() => TransitionDefinitionSchema.parse({
85
+ from: '',
86
+ to: 'development',
87
+ })).toThrow();
88
+ });
89
+ it('rejects empty to', () => {
90
+ expect(() => TransitionDefinitionSchema.parse({
91
+ from: 'Backlog',
92
+ to: '',
93
+ })).toThrow();
94
+ });
95
+ it('rejects non-integer priority', () => {
96
+ expect(() => TransitionDefinitionSchema.parse({
97
+ from: 'Backlog',
98
+ to: 'development',
99
+ priority: 1.5,
100
+ })).toThrow();
101
+ });
102
+ });
103
+ describe('EscalationLadderRungSchema', () => {
104
+ it('validates a ladder rung', () => {
105
+ const result = EscalationLadderRungSchema.parse({
106
+ cycle: 1,
107
+ strategy: 'normal',
108
+ });
109
+ expect(result.cycle).toBe(1);
110
+ expect(result.strategy).toBe('normal');
111
+ });
112
+ it('accepts cycle 0', () => {
113
+ const result = EscalationLadderRungSchema.parse({
114
+ cycle: 0,
115
+ strategy: 'normal',
116
+ });
117
+ expect(result.cycle).toBe(0);
118
+ });
119
+ it('rejects negative cycle', () => {
120
+ expect(() => EscalationLadderRungSchema.parse({
121
+ cycle: -1,
122
+ strategy: 'normal',
123
+ })).toThrow();
124
+ });
125
+ it('rejects empty strategy', () => {
126
+ expect(() => EscalationLadderRungSchema.parse({
127
+ cycle: 1,
128
+ strategy: '',
129
+ })).toThrow();
130
+ });
131
+ });
132
+ describe('EscalationConfigSchema', () => {
133
+ it('validates a full escalation config', () => {
134
+ const result = EscalationConfigSchema.parse({
135
+ ladder: [
136
+ { cycle: 1, strategy: 'normal' },
137
+ { cycle: 2, strategy: 'context-enriched' },
138
+ { cycle: 3, strategy: 'decompose' },
139
+ { cycle: 4, strategy: 'escalate-human' },
140
+ ],
141
+ circuitBreaker: {
142
+ maxSessionsPerIssue: 8,
143
+ maxSessionsPerPhase: 3,
144
+ },
145
+ });
146
+ expect(result.ladder).toHaveLength(4);
147
+ expect(result.circuitBreaker.maxSessionsPerIssue).toBe(8);
148
+ expect(result.circuitBreaker.maxSessionsPerPhase).toBe(3);
149
+ });
150
+ it('validates config without optional maxSessionsPerPhase', () => {
151
+ const result = EscalationConfigSchema.parse({
152
+ ladder: [{ cycle: 1, strategy: 'normal' }],
153
+ circuitBreaker: {
154
+ maxSessionsPerIssue: 5,
155
+ },
156
+ });
157
+ expect(result.circuitBreaker.maxSessionsPerPhase).toBeUndefined();
158
+ });
159
+ it('rejects empty ladder', () => {
160
+ expect(() => EscalationConfigSchema.parse({
161
+ ladder: [],
162
+ circuitBreaker: { maxSessionsPerIssue: 8 },
163
+ })).toThrow();
164
+ });
165
+ it('rejects zero maxSessionsPerIssue', () => {
166
+ expect(() => EscalationConfigSchema.parse({
167
+ ladder: [{ cycle: 1, strategy: 'normal' }],
168
+ circuitBreaker: { maxSessionsPerIssue: 0 },
169
+ })).toThrow();
170
+ });
171
+ it('rejects negative maxSessionsPerIssue', () => {
172
+ expect(() => EscalationConfigSchema.parse({
173
+ ladder: [{ cycle: 1, strategy: 'normal' }],
174
+ circuitBreaker: { maxSessionsPerIssue: -1 },
175
+ })).toThrow();
176
+ });
177
+ it('rejects zero maxSessionsPerPhase', () => {
178
+ expect(() => EscalationConfigSchema.parse({
179
+ ladder: [{ cycle: 1, strategy: 'normal' }],
180
+ circuitBreaker: { maxSessionsPerIssue: 8, maxSessionsPerPhase: 0 },
181
+ })).toThrow();
182
+ });
183
+ });
184
+ describe('GateDefinitionSchema', () => {
185
+ it('validates a signal gate', () => {
186
+ const result = GateDefinitionSchema.parse({
187
+ name: 'human-review',
188
+ description: 'Wait for human approval',
189
+ type: 'signal',
190
+ trigger: { source: 'comment', match: 'RESUME' },
191
+ timeout: { duration: '4h', action: 'escalate' },
192
+ appliesTo: ['refinement'],
193
+ });
194
+ expect(result.name).toBe('human-review');
195
+ expect(result.type).toBe('signal');
196
+ expect(result.timeout?.action).toBe('escalate');
197
+ expect(result.appliesTo).toEqual(['refinement']);
198
+ });
199
+ it('validates a timer gate', () => {
200
+ const result = GateDefinitionSchema.parse({
201
+ name: 'scheduled-release',
202
+ type: 'timer',
203
+ trigger: { cron: '0 9 * * 1-5' },
204
+ });
205
+ expect(result.type).toBe('timer');
206
+ expect(result.timeout).toBeUndefined();
207
+ });
208
+ it('validates a webhook gate', () => {
209
+ const result = GateDefinitionSchema.parse({
210
+ name: 'external-approval',
211
+ type: 'webhook',
212
+ trigger: { endpoint: '/api/approve' },
213
+ timeout: { duration: '24h', action: 'fail' },
214
+ });
215
+ expect(result.type).toBe('webhook');
216
+ });
217
+ it('rejects invalid gate type', () => {
218
+ expect(() => GateDefinitionSchema.parse({
219
+ name: 'test',
220
+ type: 'invalid',
221
+ trigger: {},
222
+ })).toThrow();
223
+ });
224
+ it('rejects invalid timeout action', () => {
225
+ expect(() => GateDefinitionSchema.parse({
226
+ name: 'test',
227
+ type: 'signal',
228
+ trigger: {},
229
+ timeout: { duration: '1h', action: 'invalid' },
230
+ })).toThrow();
231
+ });
232
+ });
233
+ describe('ParallelismGroupDefinitionSchema', () => {
234
+ it('validates a fan-out group', () => {
235
+ const result = ParallelismGroupDefinitionSchema.parse({
236
+ name: 'sub-issue-fan-out',
237
+ description: 'Coordinate sub-issues in parallel',
238
+ phases: ['development'],
239
+ strategy: 'fan-out',
240
+ maxConcurrent: 5,
241
+ waitForAll: true,
242
+ });
243
+ expect(result.name).toBe('sub-issue-fan-out');
244
+ expect(result.strategy).toBe('fan-out');
245
+ expect(result.maxConcurrent).toBe(5);
246
+ expect(result.waitForAll).toBe(true);
247
+ });
248
+ it('validates a minimal group', () => {
249
+ const result = ParallelismGroupDefinitionSchema.parse({
250
+ name: 'parallel-qa',
251
+ phases: ['qa'],
252
+ strategy: 'race',
253
+ });
254
+ expect(result.maxConcurrent).toBeUndefined();
255
+ expect(result.waitForAll).toBeUndefined();
256
+ });
257
+ it('rejects empty phases array', () => {
258
+ expect(() => ParallelismGroupDefinitionSchema.parse({
259
+ name: 'test',
260
+ phases: [],
261
+ strategy: 'fan-out',
262
+ })).toThrow();
263
+ });
264
+ it('rejects invalid strategy', () => {
265
+ expect(() => ParallelismGroupDefinitionSchema.parse({
266
+ name: 'test',
267
+ phases: ['dev'],
268
+ strategy: 'invalid',
269
+ })).toThrow();
270
+ });
271
+ it('rejects zero maxConcurrent', () => {
272
+ expect(() => ParallelismGroupDefinitionSchema.parse({
273
+ name: 'test',
274
+ phases: ['dev'],
275
+ strategy: 'fan-out',
276
+ maxConcurrent: 0,
277
+ })).toThrow();
278
+ });
279
+ });
280
+ describe('WorkflowDefinitionSchema', () => {
281
+ it('validates a minimal workflow definition', () => {
282
+ const result = WorkflowDefinitionSchema.parse({
283
+ apiVersion: 'v1.1',
284
+ kind: 'WorkflowDefinition',
285
+ metadata: { name: 'test-workflow' },
286
+ phases: [
287
+ { name: 'development', template: 'development' },
288
+ ],
289
+ transitions: [
290
+ { from: 'Backlog', to: 'development' },
291
+ ],
292
+ });
293
+ expect(result.apiVersion).toBe('v1.1');
294
+ expect(result.kind).toBe('WorkflowDefinition');
295
+ expect(result.metadata.name).toBe('test-workflow');
296
+ expect(result.phases).toHaveLength(1);
297
+ expect(result.transitions).toHaveLength(1);
298
+ expect(result.escalation).toBeUndefined();
299
+ expect(result.gates).toBeUndefined();
300
+ expect(result.parallelism).toBeUndefined();
301
+ });
302
+ it('validates a full workflow definition with all sections', () => {
303
+ const result = WorkflowDefinitionSchema.parse({
304
+ apiVersion: 'v1.1',
305
+ kind: 'WorkflowDefinition',
306
+ metadata: {
307
+ name: 'full-workflow',
308
+ description: 'Complete workflow with all features',
309
+ },
310
+ phases: [
311
+ { name: 'dev', template: 'development' },
312
+ {
313
+ name: 'refinement',
314
+ template: 'refinement',
315
+ variants: { 'context-enriched': 'refinement-context-enriched' },
316
+ },
317
+ ],
318
+ transitions: [
319
+ { from: 'Backlog', to: 'dev' },
320
+ { from: 'Rejected', to: 'refinement', condition: '{{ true }}', priority: 5 },
321
+ ],
322
+ escalation: {
323
+ ladder: [
324
+ { cycle: 1, strategy: 'normal' },
325
+ { cycle: 4, strategy: 'escalate-human' },
326
+ ],
327
+ circuitBreaker: { maxSessionsPerIssue: 8, maxSessionsPerPhase: 3 },
328
+ },
329
+ gates: [
330
+ {
331
+ name: 'approval',
332
+ type: 'signal',
333
+ trigger: { source: 'comment' },
334
+ timeout: { duration: '4h', action: 'escalate' },
335
+ },
336
+ ],
337
+ parallelism: [
338
+ {
339
+ name: 'fan-out',
340
+ phases: ['dev'],
341
+ strategy: 'fan-out',
342
+ maxConcurrent: 3,
343
+ },
344
+ ],
345
+ });
346
+ expect(result.phases).toHaveLength(2);
347
+ expect(result.transitions).toHaveLength(2);
348
+ expect(result.escalation?.ladder).toHaveLength(2);
349
+ expect(result.gates).toHaveLength(1);
350
+ expect(result.parallelism).toHaveLength(1);
351
+ });
352
+ it('accepts empty phases and transitions arrays', () => {
353
+ const result = WorkflowDefinitionSchema.parse({
354
+ apiVersion: 'v1.1',
355
+ kind: 'WorkflowDefinition',
356
+ metadata: { name: 'empty' },
357
+ phases: [],
358
+ transitions: [],
359
+ });
360
+ expect(result.phases).toHaveLength(0);
361
+ expect(result.transitions).toHaveLength(0);
362
+ });
363
+ it('rejects invalid apiVersion', () => {
364
+ expect(() => WorkflowDefinitionSchema.parse({
365
+ apiVersion: 'v1',
366
+ kind: 'WorkflowDefinition',
367
+ metadata: { name: 'test' },
368
+ phases: [],
369
+ transitions: [],
370
+ })).toThrow();
371
+ });
372
+ it('rejects invalid kind', () => {
373
+ expect(() => WorkflowDefinitionSchema.parse({
374
+ apiVersion: 'v1.1',
375
+ kind: 'WorkflowTemplate',
376
+ metadata: { name: 'test' },
377
+ phases: [],
378
+ transitions: [],
379
+ })).toThrow();
380
+ });
381
+ it('rejects missing metadata.name', () => {
382
+ expect(() => WorkflowDefinitionSchema.parse({
383
+ apiVersion: 'v1.1',
384
+ kind: 'WorkflowDefinition',
385
+ metadata: {},
386
+ phases: [],
387
+ transitions: [],
388
+ })).toThrow();
389
+ });
390
+ it('rejects empty metadata.name', () => {
391
+ expect(() => WorkflowDefinitionSchema.parse({
392
+ apiVersion: 'v1.1',
393
+ kind: 'WorkflowDefinition',
394
+ metadata: { name: '' },
395
+ phases: [],
396
+ transitions: [],
397
+ })).toThrow();
398
+ });
399
+ it('rejects missing phases', () => {
400
+ expect(() => WorkflowDefinitionSchema.parse({
401
+ apiVersion: 'v1.1',
402
+ kind: 'WorkflowDefinition',
403
+ metadata: { name: 'test' },
404
+ transitions: [],
405
+ })).toThrow();
406
+ });
407
+ it('rejects missing transitions', () => {
408
+ expect(() => WorkflowDefinitionSchema.parse({
409
+ apiVersion: 'v1.1',
410
+ kind: 'WorkflowDefinition',
411
+ metadata: { name: 'test' },
412
+ phases: [],
413
+ })).toThrow();
414
+ });
415
+ });
416
+ describe('validateWorkflowDefinition', () => {
417
+ it('returns validated workflow for valid input', () => {
418
+ const result = validateWorkflowDefinition({
419
+ apiVersion: 'v1.1',
420
+ kind: 'WorkflowDefinition',
421
+ metadata: { name: 'test' },
422
+ phases: [{ name: 'dev', template: 'development' }],
423
+ transitions: [{ from: 'Backlog', to: 'dev' }],
424
+ });
425
+ expect(result.metadata.name).toBe('test');
426
+ });
427
+ it('includes file path in error message when provided', () => {
428
+ try {
429
+ validateWorkflowDefinition({ invalid: true }, '/path/to/workflow.yaml');
430
+ expect.fail('should throw');
431
+ }
432
+ catch (error) {
433
+ expect(error.message).toContain('/path/to/workflow.yaml');
434
+ expect(error.message).toContain('Invalid workflow definition');
435
+ }
436
+ });
437
+ it('throws ZodError directly when no file path provided', () => {
438
+ expect(() => validateWorkflowDefinition({ invalid: true })).toThrow();
439
+ });
440
+ });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@renseiai/agentfactory",
3
- "version": "0.8.6",
3
+ "version": "0.8.8",
4
4
  "type": "module",
5
5
  "description": "Multi-agent fleet management for coding agents — orchestrator, providers, crash recovery",
6
6
  "author": "Rensei AI (https://rensei.ai)",
@@ -49,14 +49,13 @@
49
49
  "dotenv": "^17.2.3",
50
50
  "handlebars": "^4.7.8",
51
51
  "yaml": "^2.8.2",
52
- "zod": "^4.3.6",
53
- "@renseiai/agentfactory-linear": "0.8.6"
52
+ "zod": "^4.3.6"
54
53
  },
55
54
  "devDependencies": {
56
55
  "@types/node": "^22.5.4",
57
56
  "typescript": "^5.7.3",
58
57
  "vitest": "^3.2.3",
59
- "@renseiai/create-agentfactory-app": "0.8.6"
58
+ "@renseiai/create-agentfactory-app": "0.8.8"
60
59
  },
61
60
  "scripts": {
62
61
  "build": "tsc",
@@ -1,38 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * @deprecated Use `af-linear` from `@renseiai/agentfactory-cli` instead.
4
- * This file is kept for backwards compatibility. The canonical implementation
5
- * is in packages/cli/src/lib/linear-runner.ts.
6
- *
7
- * Linear CLI - Command-line interface for Linear Agent SDK
8
- *
9
- * Usage:
10
- * pnpm af-linear <command> [options]
11
- *
12
- * Commands:
13
- * get-issue <id> Get issue details
14
- * create-issue Create a new issue
15
- * update-issue <id> Update an existing issue
16
- * list-comments <issueId> List comments on an issue
17
- * create-comment <issueId> Create a comment on an issue
18
- * list-backlog-issues List backlog issues for a project
19
- * list-unblocked-backlog List unblocked backlog issues
20
- * check-blocked <id> Check if an issue is blocked
21
- * add-relation <id> <id> Create relation between issues
22
- * list-relations <id> List relations for an issue
23
- * remove-relation <id> Remove a relation by ID
24
- * list-sub-issues <id> List sub-issues of a parent issue
25
- * list-sub-issue-statuses <id> List sub-issue statuses (lightweight)
26
- * update-sub-issue <id> Update sub-issue status with comment
27
- * check-deployment <PR> Check Vercel deployment status for a PR
28
- *
29
- * Array Values:
30
- * --labels accepts comma-separated: --labels "Bug,Feature"
31
- * For values with commas, use JSON: --labels '["Bug", "UI, UX"]'
32
- * Text fields (--description, --title, --body) preserve commas.
33
- *
34
- * Environment:
35
- * LINEAR_API_KEY Required API key for authentication
36
- */
37
- import 'dotenv/config';
38
- //# sourceMappingURL=linear-cli.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"linear-cli.d.ts","sourceRoot":"","sources":["../../src/linear-cli.ts"],"names":[],"mappings":";AACA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,eAAe,CAAA"}