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.
@@ -1,21 +1,22 @@
1
- import { describe, test, expect, beforeEach, afterEach, beforeAll, afterAll } from 'bun:test';
2
- import { existsSync, rmSync, mkdirSync, writeFileSync } from 'fs';
3
- import { join } from 'path';
4
- import { spawnSync } from 'child_process';
1
+ import { describe, test, expect, beforeEach, afterEach, beforeAll, afterAll } from 'bun:test'
2
+ import { existsSync, rmSync, mkdirSync, writeFileSync } from 'fs'
3
+ import { join } from 'path'
4
+ import { spawnSync } from 'child_process'
5
+ import packageJson from '../package.json'
5
6
 
6
7
  // Set test environment
7
- const originalEnv = process.env.BUN_ENV;
8
+ const originalEnv = process.env.BUN_ENV
8
9
  beforeAll(() => {
9
- process.env.BUN_ENV = 'test';
10
- process.env.ANTHROPIC_API_KEY = 'test-api-key';
11
- });
10
+ process.env.BUN_ENV = 'test'
11
+ process.env.ANTHROPIC_API_KEY = 'test-api-key'
12
+ })
12
13
  afterAll(() => {
13
- process.env.BUN_ENV = originalEnv;
14
- delete process.env.ANTHROPIC_API_KEY;
15
- });
14
+ process.env.BUN_ENV = originalEnv
15
+ delete process.env.ANTHROPIC_API_KEY
16
+ })
16
17
 
17
- const TEST_CWD = join(process.cwd(), 'test-cli-integration');
18
- const CLI_PATH = join(process.cwd(), 'src', 'index.ts');
18
+ const TEST_CWD = join(process.cwd(), 'test-cli-integration')
19
+ const CLI_PATH = join(process.cwd(), 'src', 'index.ts')
19
20
 
20
21
  /**
21
22
  * Helper to run CLI command
@@ -24,322 +25,425 @@ function runCli(args: string[]): { stdout: string; stderr: string; exitCode: num
24
25
  const result = spawnSync('bun', [CLI_PATH, ...args], {
25
26
  cwd: TEST_CWD,
26
27
  encoding: 'utf-8',
27
- env: { ...process.env, BUN_ENV: 'test', ANTHROPIC_API_KEY: 'test-api-key' }
28
- });
29
-
28
+ env: { ...process.env, BUN_ENV: 'test', ANTHROPIC_API_KEY: 'test-api-key' },
29
+ })
30
+
30
31
  return {
31
32
  stdout: result.stdout || '',
32
33
  stderr: result.stderr || '',
33
- exitCode: result.status || 0
34
- };
34
+ exitCode: result.status || 0,
35
+ }
35
36
  }
36
37
 
37
38
  /**
38
39
  * Helper to create mock PRD file
39
40
  */
40
41
  function createMockPrd(feature: string, content: string = '# Feature\n\nTest PRD content'): void {
41
- const plansDir = join(TEST_CWD, '.plans');
42
+ const plansDir = join(TEST_CWD, '.plans')
42
43
  if (!existsSync(plansDir)) {
43
- mkdirSync(plansDir, { recursive: true });
44
+ mkdirSync(plansDir, { recursive: true })
44
45
  }
45
- writeFileSync(join(plansDir, `prd-${feature}.md`), content);
46
+ writeFileSync(join(plansDir, `prd-${feature}.md`), content)
46
47
  }
47
48
 
48
49
  /**
49
50
  * Helper to create mock task file
50
51
  */
51
52
  function createMockTaskFile(feature: string, tasks: any[]): void {
52
- const plansDir = join(TEST_CWD, '.plans');
53
+ const plansDir = join(TEST_CWD, '.plans')
53
54
  if (!existsSync(plansDir)) {
54
- mkdirSync(plansDir, { recursive: true });
55
+ mkdirSync(plansDir, { recursive: true })
55
56
  }
56
-
57
+
57
58
  const yaml = `feature: ${feature}
58
59
  prd: ./prd-${feature}.md
59
60
  created_at: 2026-01-28T12:00:00Z
60
61
  updated_at: 2026-01-28T12:00:00Z
61
62
 
62
63
  tasks:
63
- ${tasks.map(t => ` - id: ${t.id}
64
+ ${tasks
65
+ .map(
66
+ t => ` - id: ${t.id}
64
67
  title: "${t.title}"
65
68
  description: "${t.description}"
66
69
  status: ${t.status}
67
70
  dependencies: ${t.dependencies ? JSON.stringify(t.dependencies) : '[]'}
68
71
  acceptance_criteria: ${t.acceptance_criteria ? JSON.stringify(t.acceptance_criteria) : '[]'}
69
- completed_at: ${t.completed_at || 'null'}`).join('\n')}
70
- `;
71
-
72
- writeFileSync(join(plansDir, `tasks-${feature}.yml`), yaml);
72
+ completed_at: ${t.completed_at || 'null'}`
73
+ )
74
+ .join('\n')}
75
+ `
76
+
77
+ writeFileSync(join(plansDir, `tasks-${feature}.yml`), yaml)
73
78
  }
74
79
 
75
80
  describe('CLI Integration Tests', () => {
76
81
  beforeEach(() => {
77
82
  // Create test workspace
78
83
  if (existsSync(TEST_CWD)) {
79
- rmSync(TEST_CWD, { recursive: true, force: true });
84
+ rmSync(TEST_CWD, { recursive: true, force: true })
80
85
  }
81
- mkdirSync(TEST_CWD, { recursive: true });
82
- });
86
+ mkdirSync(TEST_CWD, { recursive: true })
87
+ })
83
88
 
84
89
  afterEach(() => {
85
90
  // Cleanup
86
91
  if (existsSync(TEST_CWD)) {
87
- rmSync(TEST_CWD, { recursive: true, force: true });
92
+ rmSync(TEST_CWD, { recursive: true, force: true })
88
93
  }
89
- });
94
+ })
90
95
 
91
96
  describe('prds command', () => {
92
97
  test('shows message when no PRDs exist', () => {
93
- const result = runCli(['prds']);
94
-
95
- expect(result.exitCode).toBe(0);
96
- expect(result.stdout).toContain('No PRDs found');
97
- expect(result.stdout).toContain('Create a PRD with: hone prd');
98
- });
98
+ const result = runCli(['prds'])
99
+
100
+ expect(result.exitCode).toBe(0)
101
+ expect(result.stdout).toContain('No PRDs found')
102
+ expect(result.stdout).toContain('Create a PRD with: hone prd')
103
+ })
99
104
 
100
105
  test('lists PRD with no task file', () => {
101
- createMockPrd('test-feature');
102
-
103
- const result = runCli(['prds']);
104
-
105
- expect(result.exitCode).toBe(0);
106
- expect(result.stdout).toContain('.plans/prd-test-feature.md');
107
- expect(result.stdout).toContain('Tasks: none');
108
- expect(result.stdout).toContain('Status: not started');
109
- });
106
+ createMockPrd('test-feature')
107
+
108
+ const result = runCli(['prds'])
109
+
110
+ expect(result.exitCode).toBe(0)
111
+ expect(result.stdout).toContain('.plans/prd-test-feature.md')
112
+ expect(result.stdout).toContain('Tasks: none')
113
+ expect(result.stdout).toContain('Status: not started')
114
+ })
110
115
 
111
116
  test('lists PRD with task file showing in progress status', () => {
112
- createMockPrd('test-feature');
117
+ createMockPrd('test-feature')
113
118
  createMockTaskFile('test-feature', [
114
- { id: 'task-1', title: 'Task 1', description: 'Test', status: 'completed', dependencies: [], completed_at: '2026-01-28T12:00:00Z' },
115
- { id: 'task-2', title: 'Task 2', description: 'Test', status: 'pending', dependencies: [] }
116
- ]);
117
-
118
- const result = runCli(['prds']);
119
-
120
- expect(result.exitCode).toBe(0);
121
- expect(result.stdout).toContain('.plans/prd-test-feature.md');
122
- expect(result.stdout).toContain('Tasks: .plans/tasks-test-feature.yml');
123
- expect(result.stdout).toContain('Status: in progress (1/2 completed)');
124
- });
119
+ {
120
+ id: 'task-1',
121
+ title: 'Task 1',
122
+ description: 'Test',
123
+ status: 'completed',
124
+ dependencies: [],
125
+ completed_at: '2026-01-28T12:00:00Z',
126
+ },
127
+ { id: 'task-2', title: 'Task 2', description: 'Test', status: 'pending', dependencies: [] },
128
+ ])
129
+
130
+ const result = runCli(['prds'])
131
+
132
+ expect(result.exitCode).toBe(0)
133
+ expect(result.stdout).toContain('.plans/prd-test-feature.md')
134
+ expect(result.stdout).toContain('Tasks: .plans/tasks-test-feature.yml')
135
+ expect(result.stdout).toContain('Status: in progress (1/2 completed)')
136
+ })
125
137
 
126
138
  test('lists PRD with completed status', () => {
127
- createMockPrd('test-feature');
139
+ createMockPrd('test-feature')
128
140
  createMockTaskFile('test-feature', [
129
- { id: 'task-1', title: 'Task 1', description: 'Test', status: 'completed', dependencies: [], completed_at: '2026-01-28T12:00:00Z' },
130
- { id: 'task-2', title: 'Task 2', description: 'Test', status: 'completed', dependencies: [], completed_at: '2026-01-28T12:00:00Z' }
131
- ]);
132
-
133
- const result = runCli(['prds']);
134
-
135
- expect(result.exitCode).toBe(0);
136
- expect(result.stdout).toContain('.plans/prd-test-feature.md');
137
- expect(result.stdout).toContain('Status: completed');
138
- });
141
+ {
142
+ id: 'task-1',
143
+ title: 'Task 1',
144
+ description: 'Test',
145
+ status: 'completed',
146
+ dependencies: [],
147
+ completed_at: '2026-01-28T12:00:00Z',
148
+ },
149
+ {
150
+ id: 'task-2',
151
+ title: 'Task 2',
152
+ description: 'Test',
153
+ status: 'completed',
154
+ dependencies: [],
155
+ completed_at: '2026-01-28T12:00:00Z',
156
+ },
157
+ ])
158
+
159
+ const result = runCli(['prds'])
160
+
161
+ expect(result.exitCode).toBe(0)
162
+ expect(result.stdout).toContain('.plans/prd-test-feature.md')
163
+ expect(result.stdout).toContain('Status: completed')
164
+ })
139
165
 
140
166
  test('lists multiple PRDs', () => {
141
- createMockPrd('feature-one');
142
- createMockPrd('feature-two');
167
+ createMockPrd('feature-one')
168
+ createMockPrd('feature-two')
143
169
  createMockTaskFile('feature-one', [
144
- { id: 'task-1', title: 'Task 1', description: 'Test', status: 'pending', dependencies: [] }
145
- ]);
146
-
147
- const result = runCli(['prds']);
148
-
149
- expect(result.exitCode).toBe(0);
150
- expect(result.stdout).toContain('.plans/prd-feature-one.md');
151
- expect(result.stdout).toContain('.plans/prd-feature-two.md');
152
- expect(result.stdout).toContain('.plans/tasks-feature-one.yml');
153
- expect(result.stdout).toContain('Tasks: none');
154
- });
155
- });
170
+ { id: 'task-1', title: 'Task 1', description: 'Test', status: 'pending', dependencies: [] },
171
+ ])
172
+
173
+ const result = runCli(['prds'])
174
+
175
+ expect(result.exitCode).toBe(0)
176
+ expect(result.stdout).toContain('.plans/prd-feature-one.md')
177
+ expect(result.stdout).toContain('.plans/prd-feature-two.md')
178
+ expect(result.stdout).toContain('.plans/tasks-feature-one.yml')
179
+ expect(result.stdout).toContain('Tasks: none')
180
+ })
181
+ })
156
182
 
157
183
  describe('status command', () => {
158
184
  test('shows message when no incomplete tasks', () => {
159
- const result = runCli(['status']);
160
-
161
- expect(result.exitCode).toBe(0);
162
- expect(result.stdout).toContain('No incomplete task lists found');
163
- expect(result.stdout).toContain('All tasks completed!');
164
- });
185
+ const result = runCli(['status'])
186
+
187
+ expect(result.exitCode).toBe(0)
188
+ expect(result.stdout).toContain('No incomplete task lists found')
189
+ expect(result.stdout).toContain('All tasks completed!')
190
+ })
165
191
 
166
192
  test('lists incomplete task file with next task', () => {
167
193
  createMockTaskFile('test-feature', [
168
- { id: 'task-1', title: 'Task 1', description: 'Test', status: 'completed', dependencies: [], completed_at: '2026-01-28T12:00:00Z' },
169
- { id: 'task-2', title: 'Task 2', description: 'Test task 2', status: 'pending', dependencies: [] }
170
- ]);
171
-
172
- const result = runCli(['status']);
173
-
174
- expect(result.exitCode).toBe(0);
175
- expect(result.stdout).toContain('.plans/tasks-test-feature.yml');
176
- expect(result.stdout).toContain('Feature: test-feature');
177
- expect(result.stdout).toContain('Progress: 1/2 tasks completed');
178
- expect(result.stdout).toContain('Next: task-2 - Task 2');
179
- });
194
+ {
195
+ id: 'task-1',
196
+ title: 'Task 1',
197
+ description: 'Test',
198
+ status: 'completed',
199
+ dependencies: [],
200
+ completed_at: '2026-01-28T12:00:00Z',
201
+ },
202
+ {
203
+ id: 'task-2',
204
+ title: 'Task 2',
205
+ description: 'Test task 2',
206
+ status: 'pending',
207
+ dependencies: [],
208
+ },
209
+ ])
210
+
211
+ const result = runCli(['status'])
212
+
213
+ expect(result.exitCode).toBe(0)
214
+ expect(result.stdout).toContain('.plans/tasks-test-feature.yml')
215
+ expect(result.stdout).toContain('Feature: test-feature')
216
+ expect(result.stdout).toContain('Progress: 1/2 tasks completed')
217
+ expect(result.stdout).toContain('Next: task-2 - Task 2')
218
+ })
180
219
 
181
220
  test('shows waiting for dependencies when no task available', () => {
182
221
  createMockTaskFile('test-feature', [
183
222
  { id: 'task-1', title: 'Task 1', description: 'Test', status: 'pending', dependencies: [] },
184
- { id: 'task-2', title: 'Task 2', description: 'Test', status: 'pending', dependencies: ['task-1'] }
185
- ]);
186
-
223
+ {
224
+ id: 'task-2',
225
+ title: 'Task 2',
226
+ description: 'Test',
227
+ status: 'pending',
228
+ dependencies: ['task-1'],
229
+ },
230
+ ])
231
+
187
232
  // Mark task-1 as in_progress so task-2 is blocked
188
233
  createMockTaskFile('test-feature', [
189
- { id: 'task-1', title: 'Task 1', description: 'Test', status: 'in_progress', dependencies: [] },
190
- { id: 'task-2', title: 'Task 2', description: 'Test', status: 'pending', dependencies: ['task-1'] }
191
- ]);
192
-
193
- const result = runCli(['status']);
194
-
195
- expect(result.exitCode).toBe(0);
196
- expect(result.stdout).toContain('Next: (waiting for dependencies)');
197
- });
234
+ {
235
+ id: 'task-1',
236
+ title: 'Task 1',
237
+ description: 'Test',
238
+ status: 'in_progress',
239
+ dependencies: [],
240
+ },
241
+ {
242
+ id: 'task-2',
243
+ title: 'Task 2',
244
+ description: 'Test',
245
+ status: 'pending',
246
+ dependencies: ['task-1'],
247
+ },
248
+ ])
249
+
250
+ const result = runCli(['status'])
251
+
252
+ expect(result.exitCode).toBe(0)
253
+ expect(result.stdout).toContain('Next: (waiting for dependencies)')
254
+ })
198
255
 
199
256
  test('excludes fully completed task files', () => {
200
257
  createMockTaskFile('completed-feature', [
201
- { id: 'task-1', title: 'Task 1', description: 'Test', status: 'completed', dependencies: [], completed_at: '2026-01-28T12:00:00Z' }
202
- ]);
258
+ {
259
+ id: 'task-1',
260
+ title: 'Task 1',
261
+ description: 'Test',
262
+ status: 'completed',
263
+ dependencies: [],
264
+ completed_at: '2026-01-28T12:00:00Z',
265
+ },
266
+ ])
203
267
  createMockTaskFile('incomplete-feature', [
204
- { id: 'task-1', title: 'Task 1', description: 'Test', status: 'pending', dependencies: [] }
205
- ]);
206
-
207
- const result = runCli(['status']);
208
-
209
- expect(result.exitCode).toBe(0);
210
- expect(result.stdout).not.toContain('completed-feature');
211
- expect(result.stdout).toContain('incomplete-feature');
212
- });
268
+ { id: 'task-1', title: 'Task 1', description: 'Test', status: 'pending', dependencies: [] },
269
+ ])
270
+
271
+ const result = runCli(['status'])
272
+
273
+ expect(result.exitCode).toBe(0)
274
+ expect(result.stdout).not.toContain('completed-feature')
275
+ expect(result.stdout).toContain('incomplete-feature')
276
+ })
213
277
 
214
278
  test('lists multiple incomplete task files', () => {
215
279
  createMockTaskFile('feature-one', [
216
- { id: 'task-1', title: 'Task 1', description: 'Test', status: 'pending', dependencies: [] }
217
- ]);
280
+ { id: 'task-1', title: 'Task 1', description: 'Test', status: 'pending', dependencies: [] },
281
+ ])
218
282
  createMockTaskFile('feature-two', [
219
- { id: 'task-1', title: 'Task 1', description: 'Test', status: 'completed', dependencies: [], completed_at: '2026-01-28T12:00:00Z' },
220
- { id: 'task-2', title: 'Task 2', description: 'Test', status: 'pending', dependencies: [] }
221
- ]);
222
-
223
- const result = runCli(['status']);
224
-
225
- expect(result.exitCode).toBe(0);
226
- expect(result.stdout).toContain('feature-one');
227
- expect(result.stdout).toContain('feature-two');
228
- expect(result.stdout).toContain('0/1 tasks completed');
229
- expect(result.stdout).toContain('1/2 tasks completed');
230
- });
231
- });
283
+ {
284
+ id: 'task-1',
285
+ title: 'Task 1',
286
+ description: 'Test',
287
+ status: 'completed',
288
+ dependencies: [],
289
+ completed_at: '2026-01-28T12:00:00Z',
290
+ },
291
+ { id: 'task-2', title: 'Task 2', description: 'Test', status: 'pending', dependencies: [] },
292
+ ])
293
+
294
+ const result = runCli(['status'])
295
+
296
+ expect(result.exitCode).toBe(0)
297
+ expect(result.stdout).toContain('feature-one')
298
+ expect(result.stdout).toContain('feature-two')
299
+ expect(result.stdout).toContain('0/1 tasks completed')
300
+ expect(result.stdout).toContain('1/2 tasks completed')
301
+ })
302
+ })
232
303
 
233
304
  describe('CLI flags', () => {
234
305
  test('--help shows usage information', () => {
235
- const result = runCli(['--help']);
236
-
237
- expect(result.exitCode).toBe(0);
238
- expect(result.stdout).toContain('hone');
239
- expect(result.stdout).toContain('AI Coding Agent Orchestrator');
240
- });
306
+ const result = runCli(['--help'])
307
+
308
+ expect(result.exitCode).toBe(0)
309
+ expect(result.stdout).toContain('hone')
310
+ expect(result.stdout).toContain('AI Coding Agent Orchestrator')
311
+ })
241
312
 
242
313
  test('--version shows version', () => {
243
- const result = runCli(['--version']);
244
-
245
- expect(result.exitCode).toBe(0);
246
- expect(result.stdout).toContain('0.1.0');
247
- });
248
- });
314
+ const result = runCli(['--version'])
315
+
316
+ expect(result.exitCode).toBe(0)
317
+ expect(result.stdout).toContain(packageJson.version)
318
+ })
319
+
320
+ test('-v shows version', () => {
321
+ const result = runCli(['-v'])
322
+
323
+ expect(result.exitCode).toBe(0)
324
+ expect(result.stdout).toContain(packageJson.version)
325
+ })
326
+
327
+ test('unknown option shows help instead of error', () => {
328
+ const result = runCli(['--unknown'])
329
+
330
+ expect(result.exitCode).toBe(0)
331
+ expect(result.stdout).toContain('Usage: hone [options] [command]')
332
+ expect(result.stdout).toContain('AI Coding Agent Orchestrator')
333
+ expect(result.stderr).not.toContain('unknown option')
334
+ })
335
+
336
+ test('unknown command shows help instead of error', () => {
337
+ const result = runCli(['unknown-command'])
338
+
339
+ expect(result.exitCode).toBe(0)
340
+ expect(result.stdout).toContain('Usage: hone [options] [command]')
341
+ expect(result.stdout).toContain('AI Coding Agent Orchestrator')
342
+ expect(result.stderr).not.toContain('unknown command')
343
+ })
344
+
345
+ test('multiple unknown options show help instead of error', () => {
346
+ const result = runCli(['--test', '--another'])
347
+
348
+ expect(result.exitCode).toBe(0)
349
+ expect(result.stdout).toContain('Usage: hone [options] [command]')
350
+ expect(result.stdout).toContain('AI Coding Agent Orchestrator')
351
+ })
352
+ })
249
353
 
250
354
  describe('prd-to-tasks command', () => {
251
355
  test('shows error when PRD file does not exist', () => {
252
- const result = runCli(['prd-to-tasks', 'nonexistent-prd.md']);
253
-
254
- expect(result.exitCode).toBe(1);
255
- expect(result.stderr).toContain('Error generating tasks');
256
- });
356
+ const result = runCli(['prd-to-tasks', 'nonexistent-prd.md'])
357
+
358
+ expect(result.exitCode).toBe(1)
359
+ expect(result.stderr).toContain('Error generating tasks')
360
+ })
257
361
 
258
362
  test('shows error when PRD filename format is invalid', () => {
259
363
  // Create a file with invalid format
260
- const plansDir = join(TEST_CWD, '.plans');
261
- mkdirSync(plansDir, { recursive: true });
262
- writeFileSync(join(plansDir, 'invalid-format.md'), '# Test');
263
-
264
- const result = runCli(['prd-to-tasks', join(plansDir, 'invalid-format.md')]);
265
-
266
- expect(result.exitCode).toBe(1);
267
- expect(result.stderr).toContain('Error generating tasks');
268
- });
269
- });
364
+ const plansDir = join(TEST_CWD, '.plans')
365
+ mkdirSync(plansDir, { recursive: true })
366
+ writeFileSync(join(plansDir, 'invalid-format.md'), '# Test')
367
+
368
+ const result = runCli(['prd-to-tasks', join(plansDir, 'invalid-format.md')])
369
+
370
+ expect(result.exitCode).toBe(1)
371
+ expect(result.stderr).toContain('Error generating tasks')
372
+ })
373
+ })
270
374
 
271
375
  describe('run command', () => {
272
376
  test('requires iterations flag', () => {
273
377
  createMockTaskFile('test-feature', [
274
- { id: 'task-1', title: 'Task 1', description: 'Test', status: 'pending', dependencies: [] }
275
- ]);
276
-
277
- const result = runCli(['run', '.plans/tasks-test-feature.yml']);
278
-
279
- expect(result.exitCode).toBe(1);
280
- expect(result.stderr).toContain('required option');
281
- });
378
+ { id: 'task-1', title: 'Task 1', description: 'Test', status: 'pending', dependencies: [] },
379
+ ])
380
+
381
+ const result = runCli(['run', '.plans/tasks-test-feature.yml'])
382
+
383
+ expect(result.exitCode).toBe(1)
384
+ expect(result.stderr).toContain('required option')
385
+ })
282
386
 
283
387
  test('requires valid iterations number', () => {
284
388
  createMockTaskFile('test-feature', [
285
- { id: 'task-1', title: 'Task 1', description: 'Test', status: 'pending', dependencies: [] }
286
- ]);
287
-
288
- const result = runCli(['run', '.plans/tasks-test-feature.yml', '-i', 'invalid']);
289
-
389
+ { id: 'task-1', title: 'Task 1', description: 'Test', status: 'pending', dependencies: [] },
390
+ ])
391
+
392
+ const result = runCli(['run', '.plans/tasks-test-feature.yml', '-i', 'invalid'])
393
+
290
394
  // Should fail because 'invalid' parses to NaN which fails validation
291
- expect(result.exitCode).toBe(1);
292
- });
395
+ expect(result.exitCode).toBe(1)
396
+ })
293
397
 
294
398
  test('validates tasks file exists', () => {
295
- const result = runCli(['run', 'nonexistent.yml', '-i', '1']);
296
-
297
- expect(result.exitCode).toBe(1);
298
- expect(result.stderr).toContain('Error executing tasks');
299
- });
300
- });
399
+ const result = runCli(['run', 'nonexistent.yml', '-i', '1'])
400
+
401
+ expect(result.exitCode).toBe(1)
402
+ expect(result.stderr).toContain('Error executing tasks')
403
+ })
404
+ })
301
405
 
302
406
  describe('init command', () => {
303
407
  test('creates .plans directory and config file in fresh directory', () => {
304
- const result = runCli(['init']);
305
-
306
- expect(result.exitCode).toBe(0);
307
- expect(result.stdout).toContain('Initialized hone successfully!');
308
- expect(result.stdout).toContain('✓ Created .plans/ directory');
309
- expect(result.stdout).toContain('✓ Created .plans/hone.config.yml');
310
- expect(result.stdout).toContain('Next steps:');
311
-
408
+ const result = runCli(['init'])
409
+
410
+ expect(result.exitCode).toBe(0)
411
+ expect(result.stdout).toContain('Initialized hone successfully!')
412
+ expect(result.stdout).toContain('✓ Created .plans/ directory')
413
+ expect(result.stdout).toContain('✓ Created .plans/hone.config.yml')
414
+ expect(result.stdout).toContain('Next steps:')
415
+
312
416
  // Verify files were created
313
- expect(existsSync(join(TEST_CWD, '.plans'))).toBe(true);
314
- expect(existsSync(join(TEST_CWD, '.plans', 'hone.config.yml'))).toBe(true);
315
- });
417
+ expect(existsSync(join(TEST_CWD, '.plans'))).toBe(true)
418
+ expect(existsSync(join(TEST_CWD, '.plans', 'hone.config.yml'))).toBe(true)
419
+ })
316
420
 
317
421
  test('detects when already initialized', () => {
318
422
  // First init
319
- runCli(['init']);
320
-
423
+ runCli(['init'])
424
+
321
425
  // Second init
322
- const result = runCli(['init']);
323
-
324
- expect(result.exitCode).toBe(0);
325
- expect(result.stdout).toContain('hone is already initialized');
326
- expect(result.stdout).toContain('.plans/ directory: exists');
327
- expect(result.stdout).toContain('config file: exists');
328
- });
426
+ const result = runCli(['init'])
427
+
428
+ expect(result.exitCode).toBe(0)
429
+ expect(result.stdout).toContain('hone is already initialized')
430
+ expect(result.stdout).toContain('.plans/ directory: exists')
431
+ expect(result.stdout).toContain('config file: exists')
432
+ })
329
433
 
330
434
  test('creates only missing parts when partially initialized', () => {
331
435
  // Create .plans directory manually
332
- mkdirSync(join(TEST_CWD, '.plans'), { recursive: true });
333
-
334
- const result = runCli(['init']);
335
-
336
- expect(result.exitCode).toBe(0);
337
- expect(result.stdout).toContain('Initialized hone successfully!');
338
- expect(result.stdout).toContain('• .plans/ directory already exists');
339
- expect(result.stdout).toContain('✓ Created .plans/hone.config.yml');
340
-
436
+ mkdirSync(join(TEST_CWD, '.plans'), { recursive: true })
437
+
438
+ const result = runCli(['init'])
439
+
440
+ expect(result.exitCode).toBe(0)
441
+ expect(result.stdout).toContain('Initialized hone successfully!')
442
+ expect(result.stdout).toContain('• .plans/ directory already exists')
443
+ expect(result.stdout).toContain('✓ Created .plans/hone.config.yml')
444
+
341
445
  // Verify config was created
342
- expect(existsSync(join(TEST_CWD, '.plans', 'hone.config.yml'))).toBe(true);
343
- });
344
- });
345
- });
446
+ expect(existsSync(join(TEST_CWD, '.plans', 'hone.config.yml'))).toBe(true)
447
+ })
448
+ })
449
+ })