hone-ai 0.5.0 → 0.10.0

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/src/run.ts CHANGED
@@ -1,16 +1,16 @@
1
- import { existsSync } from 'fs';
2
- import { resolve } from 'path';
3
- import type { AgentType } from './config';
4
- import { loadConfig, resolveModelForPhase } from './config';
5
- import { spawnAgent, isAgentAvailable } from './agent';
6
- import { constructPrompt, type PromptPhase } from './prompt';
7
- import { exitWithError, ErrorMessages, parseAgentError } from './errors';
1
+ import { existsSync } from 'fs'
2
+ import { resolve } from 'path'
3
+ import type { AgentType } from './config'
4
+ import { loadConfig, resolveModelForPhase } from './config'
5
+ import { spawnAgent, isAgentAvailable } from './agent'
6
+ import { constructPrompt, type PromptPhase } from './prompt'
7
+ import { exitWithError, ErrorMessages, parseAgentError } from './errors'
8
8
 
9
9
  export interface ExecuteTasksOptions {
10
- tasksFile: string;
11
- iterations: number;
12
- agent: AgentType;
13
- skipPhase?: 'review';
10
+ tasksFile: string
11
+ iterations: number
12
+ agent: AgentType
13
+ skipPhase?: 'review'
14
14
  }
15
15
 
16
16
  /**
@@ -18,226 +18,226 @@ export interface ExecuteTasksOptions {
18
18
  * Runs for the specified number of iterations or until all tasks complete.
19
19
  */
20
20
  export async function executeTasks(options: ExecuteTasksOptions): Promise<void> {
21
- const { tasksFile, iterations, agent, skipPhase } = options;
22
-
21
+ const { tasksFile, iterations, agent, skipPhase } = options
22
+
23
23
  // Validate tasks file exists
24
- const tasksPath = resolve(tasksFile);
24
+ const tasksPath = resolve(tasksFile)
25
25
  if (!existsSync(tasksPath)) {
26
- const { message, details } = ErrorMessages.FILE_NOT_FOUND(tasksPath);
27
- exitWithError(message, details);
26
+ const { message, details } = ErrorMessages.FILE_NOT_FOUND(tasksPath)
27
+ exitWithError(message, details)
28
28
  }
29
-
29
+
30
30
  // Validate iterations is a positive integer
31
31
  if (!Number.isInteger(iterations) || iterations < 1) {
32
- throw new Error('Iterations must be a positive integer');
32
+ throw new Error('Iterations must be a positive integer')
33
33
  }
34
-
34
+
35
35
  // Check if agent is available
36
- const available = await isAgentAvailable(agent);
36
+ const available = await isAgentAvailable(agent)
37
37
  if (!available) {
38
- const { message, details } = ErrorMessages.AGENT_NOT_FOUND(agent);
39
- exitWithError(message, details);
38
+ const { message, details } = ErrorMessages.AGENT_NOT_FOUND(agent)
39
+ exitWithError(message, details)
40
40
  }
41
-
41
+
42
42
  // Extract feature name from tasks file name
43
- const featureName = extractFeatureName(tasksPath);
43
+ const featureName = extractFeatureName(tasksPath)
44
44
  if (!featureName) {
45
- throw new Error(`Could not extract feature name from tasks file: ${tasksFile}`);
45
+ throw new Error(`Could not extract feature name from tasks file: ${tasksFile}`)
46
46
  }
47
-
47
+
48
48
  // Load config
49
- const config = await loadConfig();
50
-
51
- console.log(`Using agent: ${agent}`);
52
- console.log(`Tasks file: ${tasksFile}`);
53
- console.log(`Feature: ${featureName}`);
54
- console.log(`Iterations: ${iterations}`);
49
+ const config = await loadConfig()
50
+
51
+ console.log(`Using agent: ${agent}`)
52
+ console.log(`Tasks file: ${tasksFile}`)
53
+ console.log(`Feature: ${featureName}`)
54
+ console.log(`Iterations: ${iterations}`)
55
55
  if (skipPhase) {
56
- console.log(`Skipping phase: ${skipPhase}`);
56
+ console.log(`Skipping phase: ${skipPhase}`)
57
57
  }
58
- console.log('');
59
-
58
+ console.log('')
59
+
60
60
  // Main iteration loop
61
61
  for (let i = 1; i <= iterations; i++) {
62
- console.log(`\n${'='.repeat(80)}`);
63
- console.log(`ITERATION ${i}/${iterations}`);
64
- console.log('='.repeat(80));
65
- console.log('');
66
-
62
+ console.log(`\n${'='.repeat(80)}`)
63
+ console.log(`ITERATION ${i}/${iterations}`)
64
+ console.log('='.repeat(80))
65
+ console.log('')
66
+
67
67
  // Phase 1: Implement
68
- console.log('Phase 1: Implement');
69
- console.log('-'.repeat(80));
68
+ console.log('Phase 1: Implement')
69
+ console.log('-'.repeat(80))
70
70
  const implementPrompt = constructPrompt({
71
71
  phase: 'implement',
72
72
  featureName,
73
- config
74
- });
75
-
76
- const implementModel = resolveModelForPhase(config, 'implement', agent);
77
-
73
+ config,
74
+ })
75
+
76
+ const implementModel = resolveModelForPhase(config, 'implement', agent)
77
+
78
78
  const implementResult = await spawnAgent({
79
79
  agent,
80
80
  prompt: implementPrompt,
81
81
  workingDir: process.cwd(),
82
- model: implementModel
83
- });
84
-
82
+ model: implementModel,
83
+ })
84
+
85
85
  if (implementResult.exitCode !== 0) {
86
86
  // Parse error type for better user feedback
87
- const errorInfo = parseAgentError(implementResult.stderr, implementResult.exitCode);
88
-
87
+ const errorInfo = parseAgentError(implementResult.stderr, implementResult.exitCode)
88
+
89
89
  if (errorInfo.type === 'model_unavailable') {
90
- const { message, details } = ErrorMessages.MODEL_UNAVAILABLE(implementModel, agent);
91
- exitWithError(message, details);
90
+ const { message, details } = ErrorMessages.MODEL_UNAVAILABLE(implementModel, agent)
91
+ exitWithError(message, details)
92
92
  } else if (errorInfo.type === 'rate_limit') {
93
- const { message, details } = ErrorMessages.RATE_LIMIT_ERROR(agent, errorInfo.retryAfter);
94
- exitWithError(message, details);
93
+ const { message, details } = ErrorMessages.RATE_LIMIT_ERROR(agent, errorInfo.retryAfter)
94
+ exitWithError(message, details)
95
95
  } else if (errorInfo.type === 'spawn_failed') {
96
- const { message, details } = ErrorMessages.AGENT_SPAWN_FAILED(agent, implementResult.stderr);
97
- exitWithError(message, details);
96
+ const { message, details } = ErrorMessages.AGENT_SPAWN_FAILED(agent, implementResult.stderr)
97
+ exitWithError(message, details)
98
98
  }
99
-
99
+
100
100
  // Generic error message for other failures
101
- console.error('\n✗ Implement phase failed');
102
- console.error('\nThe agent encountered an error during task implementation.');
103
- console.error('The task has NOT been marked as completed.');
104
- console.error('When you run hone again, it will retry the same task.');
105
- console.error(`\nAgent exit code: ${implementResult.exitCode}`);
101
+ console.error('\n✗ Implement phase failed')
102
+ console.error('\nThe agent encountered an error during task implementation.')
103
+ console.error('The task has NOT been marked as completed.')
104
+ console.error('When you run hone again, it will retry the same task.')
105
+ console.error(`\nAgent exit code: ${implementResult.exitCode}`)
106
106
  if (implementResult.stderr) {
107
- console.error('\nError output:');
108
- console.error(implementResult.stderr);
107
+ console.error('\nError output:')
108
+ console.error(implementResult.stderr)
109
109
  }
110
- throw new Error(`Implement phase failed with exit code ${implementResult.exitCode}`);
110
+ throw new Error(`Implement phase failed with exit code ${implementResult.exitCode}`)
111
111
  }
112
-
112
+
113
113
  // Extract task ID from output
114
- const completedTaskId = extractTaskId(implementResult.stdout, 'TASK_COMPLETED');
114
+ const completedTaskId = extractTaskId(implementResult.stdout, 'TASK_COMPLETED')
115
115
  if (completedTaskId) {
116
- console.log(`\n✓ Task ${completedTaskId} implementation complete`);
116
+ console.log(`\n✓ Task ${completedTaskId} implementation complete`)
117
117
  } else {
118
- console.warn('\n⚠ Warning: No TASK_COMPLETED marker found in output');
118
+ console.warn('\n⚠ Warning: No TASK_COMPLETED marker found in output')
119
119
  }
120
-
120
+
121
121
  // Check if all tasks are complete
122
122
  if (implementResult.stdout.includes('<promise>COMPLETE</promise>')) {
123
- console.log('\n✓ All tasks completed!');
124
- return;
123
+ console.log('\n✓ All tasks completed!')
124
+ return
125
125
  }
126
-
126
+
127
127
  // Phase 2: Review (unless skipped)
128
- let reviewFeedback: string | undefined;
128
+ let reviewFeedback: string | undefined
129
129
  if (skipPhase !== 'review') {
130
- console.log('\nPhase 2: Review');
131
- console.log('-'.repeat(80));
130
+ console.log('\nPhase 2: Review')
131
+ console.log('-'.repeat(80))
132
132
  const reviewPrompt = constructPrompt({
133
133
  phase: 'review',
134
134
  featureName,
135
135
  config,
136
- taskId: completedTaskId
137
- });
138
-
139
- const reviewModel = resolveModelForPhase(config, 'review', agent);
140
-
136
+ taskId: completedTaskId,
137
+ })
138
+
139
+ const reviewModel = resolveModelForPhase(config, 'review', agent)
140
+
141
141
  const reviewResult = await spawnAgent({
142
142
  agent,
143
143
  prompt: reviewPrompt,
144
144
  workingDir: process.cwd(),
145
- model: reviewModel
146
- });
147
-
145
+ model: reviewModel,
146
+ })
147
+
148
148
  if (reviewResult.exitCode !== 0) {
149
149
  // Parse error type for better user feedback
150
- const errorInfo = parseAgentError(reviewResult.stderr, reviewResult.exitCode);
151
-
150
+ const errorInfo = parseAgentError(reviewResult.stderr, reviewResult.exitCode)
151
+
152
152
  if (errorInfo.type === 'model_unavailable') {
153
- const { message, details } = ErrorMessages.MODEL_UNAVAILABLE(reviewModel, agent);
154
- exitWithError(message, details);
153
+ const { message, details } = ErrorMessages.MODEL_UNAVAILABLE(reviewModel, agent)
154
+ exitWithError(message, details)
155
155
  } else if (errorInfo.type === 'rate_limit') {
156
- const { message, details } = ErrorMessages.RATE_LIMIT_ERROR(agent, errorInfo.retryAfter);
157
- exitWithError(message, details);
156
+ const { message, details } = ErrorMessages.RATE_LIMIT_ERROR(agent, errorInfo.retryAfter)
157
+ exitWithError(message, details)
158
158
  } else if (errorInfo.type === 'spawn_failed') {
159
- const { message, details } = ErrorMessages.AGENT_SPAWN_FAILED(agent, reviewResult.stderr);
160
- exitWithError(message, details);
159
+ const { message, details } = ErrorMessages.AGENT_SPAWN_FAILED(agent, reviewResult.stderr)
160
+ exitWithError(message, details)
161
161
  }
162
-
162
+
163
163
  // Generic error message for other failures
164
- console.error('\n✗ Review phase failed');
165
- console.error('\nThe agent encountered an error during task review.');
166
- console.error('The task has NOT been marked as completed.');
167
- console.error('When you run hone again, it will retry the same task.');
168
- console.error(`\nAgent exit code: ${reviewResult.exitCode}`);
164
+ console.error('\n✗ Review phase failed')
165
+ console.error('\nThe agent encountered an error during task review.')
166
+ console.error('The task has NOT been marked as completed.')
167
+ console.error('When you run hone again, it will retry the same task.')
168
+ console.error(`\nAgent exit code: ${reviewResult.exitCode}`)
169
169
  if (reviewResult.stderr) {
170
- console.error('\nError output:');
171
- console.error(reviewResult.stderr);
170
+ console.error('\nError output:')
171
+ console.error(reviewResult.stderr)
172
172
  }
173
- throw new Error(`Review phase failed with exit code ${reviewResult.exitCode}`);
173
+ throw new Error(`Review phase failed with exit code ${reviewResult.exitCode}`)
174
174
  }
175
-
176
- reviewFeedback = reviewResult.stdout;
175
+
176
+ reviewFeedback = reviewResult.stdout
177
177
  } else {
178
- console.log('\nPhase 2: Review (skipped)');
178
+ console.log('\nPhase 2: Review (skipped)')
179
179
  }
180
-
180
+
181
181
  // Phase 3: Finalize
182
- console.log('\nPhase 3: Finalize');
183
- console.log('-'.repeat(80));
182
+ console.log('\nPhase 3: Finalize')
183
+ console.log('-'.repeat(80))
184
184
  const finalizePrompt = constructPrompt({
185
185
  phase: 'finalize',
186
186
  featureName,
187
187
  config,
188
188
  taskId: completedTaskId,
189
- reviewFeedback
190
- });
191
-
192
- const finalizeModel = resolveModelForPhase(config, 'finalize', agent);
193
-
189
+ reviewFeedback,
190
+ })
191
+
192
+ const finalizeModel = resolveModelForPhase(config, 'finalize', agent)
193
+
194
194
  const finalizeResult = await spawnAgent({
195
195
  agent,
196
196
  prompt: finalizePrompt,
197
197
  workingDir: process.cwd(),
198
- model: finalizeModel
199
- });
200
-
198
+ model: finalizeModel,
199
+ })
200
+
201
201
  if (finalizeResult.exitCode !== 0) {
202
202
  // Parse error type for better user feedback
203
- const errorInfo = parseAgentError(finalizeResult.stderr, finalizeResult.exitCode);
204
-
203
+ const errorInfo = parseAgentError(finalizeResult.stderr, finalizeResult.exitCode)
204
+
205
205
  if (errorInfo.type === 'model_unavailable') {
206
- const { message, details } = ErrorMessages.MODEL_UNAVAILABLE(finalizeModel, agent);
207
- exitWithError(message, details);
206
+ const { message, details } = ErrorMessages.MODEL_UNAVAILABLE(finalizeModel, agent)
207
+ exitWithError(message, details)
208
208
  } else if (errorInfo.type === 'rate_limit') {
209
- const { message, details } = ErrorMessages.RATE_LIMIT_ERROR(agent, errorInfo.retryAfter);
210
- exitWithError(message, details);
209
+ const { message, details } = ErrorMessages.RATE_LIMIT_ERROR(agent, errorInfo.retryAfter)
210
+ exitWithError(message, details)
211
211
  } else if (errorInfo.type === 'spawn_failed') {
212
- const { message, details } = ErrorMessages.AGENT_SPAWN_FAILED(agent, finalizeResult.stderr);
213
- exitWithError(message, details);
212
+ const { message, details } = ErrorMessages.AGENT_SPAWN_FAILED(agent, finalizeResult.stderr)
213
+ exitWithError(message, details)
214
214
  }
215
-
215
+
216
216
  // Generic error message for other failures
217
- console.error('\n✗ Finalize phase failed');
218
- console.error('\nThe agent encountered an error during task finalization.');
219
- console.error('The task may not have been properly committed or marked as completed.');
220
- console.error('Review the git status and task file manually before continuing.');
221
- console.error(`\nAgent exit code: ${finalizeResult.exitCode}`);
217
+ console.error('\n✗ Finalize phase failed')
218
+ console.error('\nThe agent encountered an error during task finalization.')
219
+ console.error('The task may not have been properly committed or marked as completed.')
220
+ console.error('Review the git status and task file manually before continuing.')
221
+ console.error(`\nAgent exit code: ${finalizeResult.exitCode}`)
222
222
  if (finalizeResult.stderr) {
223
- console.error('\nError output:');
224
- console.error(finalizeResult.stderr);
223
+ console.error('\nError output:')
224
+ console.error(finalizeResult.stderr)
225
225
  }
226
- throw new Error(`Finalize phase failed with exit code ${finalizeResult.exitCode}`);
226
+ throw new Error(`Finalize phase failed with exit code ${finalizeResult.exitCode}`)
227
227
  }
228
-
228
+
229
229
  // Verify task was finalized
230
- const finalizedTaskId = extractTaskId(finalizeResult.stdout, 'FINALIZED');
230
+ const finalizedTaskId = extractTaskId(finalizeResult.stdout, 'FINALIZED')
231
231
  if (finalizedTaskId) {
232
- console.log(`\n✓ Iteration ${i} complete - Task ${finalizedTaskId} finalized`);
232
+ console.log(`\n✓ Iteration ${i} complete - Task ${finalizedTaskId} finalized`)
233
233
  } else {
234
- console.log(`\n✓ Iteration ${i} complete`);
234
+ console.log(`\n✓ Iteration ${i} complete`)
235
235
  }
236
236
  }
237
-
238
- console.log(`\n${'='.repeat(80)}`);
239
- console.log(`Completed ${iterations} iterations`);
240
- console.log('='.repeat(80));
237
+
238
+ console.log(`\n${'='.repeat(80)}`)
239
+ console.log(`Completed ${iterations} iterations`)
240
+ console.log('='.repeat(80))
241
241
  }
242
242
 
243
243
  /**
@@ -245,8 +245,8 @@ export async function executeTasks(options: ExecuteTasksOptions): Promise<void>
245
245
  * Expected format: tasks-<feature-name>.yml or .plans/tasks-<feature-name>.yml
246
246
  */
247
247
  function extractFeatureName(tasksPath: string): string | undefined {
248
- const match = tasksPath.match(/tasks-([^/]+)\.yml$/);
249
- return match ? match[1] : undefined;
248
+ const match = tasksPath.match(/tasks-([^/]+)\.yml$/)
249
+ return match ? match[1] : undefined
250
250
  }
251
251
 
252
252
  /**
@@ -254,7 +254,7 @@ function extractFeatureName(tasksPath: string): string | undefined {
254
254
  * Looks for patterns like "TASK_COMPLETED: task-123" or "FINALIZED: task-123"
255
255
  */
256
256
  function extractTaskId(output: string, marker: 'TASK_COMPLETED' | 'FINALIZED'): string | undefined {
257
- const regex = new RegExp(`${marker}:\\s*([\\w-]+)`, 'i');
258
- const match = output.match(regex);
259
- return match ? match[1] : undefined;
257
+ const regex = new RegExp(`${marker}:\\s*([\\w-]+)`, 'i')
258
+ const match = output.match(regex)
259
+ return match ? match[1] : undefined
260
260
  }
@@ -1,6 +1,6 @@
1
- import { describe, test, expect } from 'bun:test';
2
- import { findNextTask } from './status';
3
- import type { TaskFile } from './prds';
1
+ import { describe, test, expect } from 'bun:test'
2
+ import { findNextTask } from './status'
3
+ import type { TaskFile } from './prds'
4
4
 
5
5
  describe('status', () => {
6
6
  describe('findNextTask', () => {
@@ -16,22 +16,22 @@ describe('status', () => {
16
16
  title: 'Task 1',
17
17
  description: 'First task',
18
18
  status: 'pending',
19
- dependencies: []
19
+ dependencies: [],
20
20
  },
21
21
  {
22
22
  id: 'task-2',
23
23
  title: 'Task 2',
24
24
  description: 'Second task',
25
25
  status: 'pending',
26
- dependencies: []
27
- }
28
- ]
29
- };
30
-
31
- const next = findNextTask(taskFile);
32
- expect(next?.id).toBe('task-1');
33
- });
34
-
26
+ dependencies: [],
27
+ },
28
+ ],
29
+ }
30
+
31
+ const next = findNextTask(taskFile)
32
+ expect(next?.id).toBe('task-1')
33
+ })
34
+
35
35
  test('skips pending task with incomplete dependencies', () => {
36
36
  const taskFile: TaskFile = {
37
37
  feature: 'test',
@@ -44,22 +44,22 @@ describe('status', () => {
44
44
  title: 'Task 1',
45
45
  description: 'First task',
46
46
  status: 'pending',
47
- dependencies: []
47
+ dependencies: [],
48
48
  },
49
49
  {
50
50
  id: 'task-2',
51
51
  title: 'Task 2',
52
52
  description: 'Second task',
53
53
  status: 'pending',
54
- dependencies: ['task-1']
55
- }
56
- ]
57
- };
58
-
59
- const next = findNextTask(taskFile);
60
- expect(next?.id).toBe('task-1');
61
- });
62
-
54
+ dependencies: ['task-1'],
55
+ },
56
+ ],
57
+ }
58
+
59
+ const next = findNextTask(taskFile)
60
+ expect(next?.id).toBe('task-1')
61
+ })
62
+
63
63
  test('returns pending task when dependencies completed', () => {
64
64
  const taskFile: TaskFile = {
65
65
  feature: 'test',
@@ -72,22 +72,22 @@ describe('status', () => {
72
72
  title: 'Task 1',
73
73
  description: 'First task',
74
74
  status: 'completed',
75
- dependencies: []
75
+ dependencies: [],
76
76
  },
77
77
  {
78
78
  id: 'task-2',
79
79
  title: 'Task 2',
80
80
  description: 'Second task',
81
81
  status: 'pending',
82
- dependencies: ['task-1']
83
- }
84
- ]
85
- };
86
-
87
- const next = findNextTask(taskFile);
88
- expect(next?.id).toBe('task-2');
89
- });
90
-
82
+ dependencies: ['task-1'],
83
+ },
84
+ ],
85
+ }
86
+
87
+ const next = findNextTask(taskFile)
88
+ expect(next?.id).toBe('task-2')
89
+ })
90
+
91
91
  test('returns null when no pending tasks', () => {
92
92
  const taskFile: TaskFile = {
93
93
  feature: 'test',
@@ -100,15 +100,15 @@ describe('status', () => {
100
100
  title: 'Task 1',
101
101
  description: 'First task',
102
102
  status: 'completed',
103
- dependencies: []
104
- }
105
- ]
106
- };
107
-
108
- const next = findNextTask(taskFile);
109
- expect(next).toBeNull();
110
- });
111
-
103
+ dependencies: [],
104
+ },
105
+ ],
106
+ }
107
+
108
+ const next = findNextTask(taskFile)
109
+ expect(next).toBeNull()
110
+ })
111
+
112
112
  test('returns null when all pending tasks blocked by dependencies', () => {
113
113
  const taskFile: TaskFile = {
114
114
  feature: 'test',
@@ -121,39 +121,39 @@ describe('status', () => {
121
121
  title: 'Task 1',
122
122
  description: 'First task',
123
123
  status: 'pending',
124
- dependencies: []
124
+ dependencies: [],
125
125
  },
126
126
  {
127
127
  id: 'task-2',
128
128
  title: 'Task 2',
129
129
  description: 'Second task',
130
130
  status: 'pending',
131
- dependencies: ['task-1']
132
- }
133
- ]
134
- };
135
-
131
+ dependencies: ['task-1'],
132
+ },
133
+ ],
134
+ }
135
+
136
136
  // Mark task-1 as something other than completed
137
137
  if (taskFile.tasks[0]) {
138
- taskFile.tasks[0].status = 'in_progress';
138
+ taskFile.tasks[0].status = 'in_progress'
139
139
  }
140
-
141
- const next = findNextTask(taskFile);
142
- expect(next).toBeNull();
143
- });
144
-
140
+
141
+ const next = findNextTask(taskFile)
142
+ expect(next).toBeNull()
143
+ })
144
+
145
145
  test('handles empty task list', () => {
146
146
  const taskFile: TaskFile = {
147
147
  feature: 'test',
148
148
  prd: './prd-test.md',
149
149
  created_at: '2026-01-28',
150
150
  updated_at: '2026-01-28',
151
- tasks: []
152
- };
153
-
154
- const next = findNextTask(taskFile);
155
- expect(next).toBeNull();
156
- });
151
+ tasks: [],
152
+ }
153
+
154
+ const next = findNextTask(taskFile)
155
+ expect(next).toBeNull()
156
+ })
157
157
 
158
158
  test('treats cancelled tasks as satisfied dependencies', () => {
159
159
  const taskFile: TaskFile = {
@@ -167,21 +167,21 @@ describe('status', () => {
167
167
  title: 'Task 1',
168
168
  description: 'First task',
169
169
  status: 'cancelled',
170
- dependencies: []
170
+ dependencies: [],
171
171
  },
172
172
  {
173
173
  id: 'task-2',
174
174
  title: 'Task 2',
175
175
  description: 'Second task',
176
176
  status: 'pending',
177
- dependencies: ['task-1']
178
- }
179
- ]
180
- };
181
-
182
- const next = findNextTask(taskFile);
183
- expect(next?.id).toBe('task-2');
184
- });
177
+ dependencies: ['task-1'],
178
+ },
179
+ ],
180
+ }
181
+
182
+ const next = findNextTask(taskFile)
183
+ expect(next?.id).toBe('task-2')
184
+ })
185
185
 
186
186
  test('skips cancelled tasks when searching for next', () => {
187
187
  const taskFile: TaskFile = {
@@ -195,20 +195,20 @@ describe('status', () => {
195
195
  title: 'Task 1',
196
196
  description: 'First task',
197
197
  status: 'cancelled',
198
- dependencies: []
198
+ dependencies: [],
199
199
  },
200
200
  {
201
201
  id: 'task-2',
202
202
  title: 'Task 2',
203
203
  description: 'Second task',
204
204
  status: 'pending',
205
- dependencies: []
206
- }
207
- ]
208
- };
209
-
210
- const next = findNextTask(taskFile);
211
- expect(next?.id).toBe('task-2');
212
- });
213
- });
214
- });
205
+ dependencies: [],
206
+ },
207
+ ],
208
+ }
209
+
210
+ const next = findNextTask(taskFile)
211
+ expect(next?.id).toBe('task-2')
212
+ })
213
+ })
214
+ })