berget 2.2.10 → 2.2.12

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.
@@ -3,7 +3,7 @@ import { describe, expect, it } from 'vitest';
3
3
  import type { ApiKeyServicePort, AuthServicePort } from '../ports/auth-services';
4
4
 
5
5
  import { CancelledError, CommandFailedError, PrerequisiteError } from '../errors';
6
- import { runSetup } from '../setup';
6
+ import { runInit } from '../init';
7
7
  import { FakeApiKeyService } from './fake-api-key-service';
8
8
  import { FakeAuthService } from './fake-auth-service';
9
9
  import { FakeCommandRunner } from './fake-command-runner';
@@ -11,8 +11,8 @@ import { FakeFileStore } from './fake-file-store';
11
11
  import { CANCEL, confirm, FakePrompter, multiselect, select } from './fake-prompter';
12
12
 
13
13
  const makeDeps = (
14
- overrides: Partial<Parameters<typeof runSetup>[0]> = {},
15
- ): Parameters<typeof runSetup>[0] => {
14
+ overrides: Partial<Parameters<typeof runInit>[0]> = {},
15
+ ): Parameters<typeof runInit>[0] => {
16
16
  return {
17
17
  apiKeyService:
18
18
  (overrides.apiKeyService as ApiKeyServicePort) ?? new FakeApiKeyService('sk_ber_test'),
@@ -49,7 +49,7 @@ function makeJwt(payload: Record<string, unknown>): string {
49
49
  return `${header}.${body}.signature`;
50
50
  }
51
51
 
52
- describe('runSetup', () => {
52
+ describe('runInit', () => {
53
53
  describe('happy path', () => {
54
54
  it('sets up opencode project without existing config', async () => {
55
55
  const deps = makeDeps({
@@ -61,13 +61,13 @@ describe('runSetup', () => {
61
61
  ]),
62
62
  });
63
63
 
64
- await runSetup(deps);
64
+ await runInit(deps);
65
65
 
66
66
  const files = deps.files as FakeFileStore;
67
67
  const written = files.getWrittenFiles();
68
68
  expect(written.has('/home/user/project/opencode.json')).toBe(true);
69
69
  const config = JSON.parse(written.get('/home/user/project/opencode.json')!);
70
- expect(config.plugin).toContain('@bergetai/opencode-auth');
70
+ expect(config.plugin).toContain('@bergetai/opencode-auth@1.0.21');
71
71
  });
72
72
 
73
73
  it('sets up opencode globally without existing config', async () => {
@@ -80,7 +80,7 @@ describe('runSetup', () => {
80
80
  ]),
81
81
  });
82
82
 
83
- await runSetup(deps);
83
+ await runInit(deps);
84
84
 
85
85
  const files = deps.files as FakeFileStore;
86
86
  const written = files.getWrittenFiles();
@@ -95,12 +95,13 @@ describe('runSetup', () => {
95
95
  prompter: new FakePrompter([
96
96
  select('pi'),
97
97
  select('project'),
98
+ confirm(true, 'Set up an agent for Pi?'),
98
99
  select('fullstack'), // Agent selection
99
100
  confirm(true, 'Create'),
100
101
  ]),
101
102
  });
102
103
 
103
- await runSetup(deps);
104
+ await runInit(deps);
104
105
 
105
106
  const commands = deps.commands as FakeCommandRunner;
106
107
  expect(commands.calls.length).toBeGreaterThan(0);
@@ -116,11 +117,11 @@ describe('runSetup', () => {
116
117
  prompter: new FakePrompter([
117
118
  select('pi'),
118
119
  select('project'),
119
- select('__skip__'), // Skip agent selection
120
+ confirm(false, 'Set up an agent for Pi?'),
120
121
  ]),
121
122
  });
122
123
 
123
- await runSetup(deps);
124
+ await runInit(deps);
124
125
 
125
126
  const files = deps.files as FakeFileStore;
126
127
  const written = files.getWrittenFiles();
@@ -139,7 +140,7 @@ describe('runSetup', () => {
139
140
  });
140
141
 
141
142
  // Simulate opencode not being installed
142
- await expect(runSetup(deps)).rejects.toBeInstanceOf(PrerequisiteError);
143
+ await expect(runInit(deps)).rejects.toBeInstanceOf(PrerequisiteError);
143
144
  });
144
145
  });
145
146
 
@@ -149,7 +150,7 @@ describe('runSetup', () => {
149
150
  prompter: new FakePrompter([select(CANCEL)]),
150
151
  });
151
152
 
152
- await expect(runSetup(deps)).rejects.toBeInstanceOf(CancelledError);
153
+ await expect(runInit(deps)).rejects.toBeInstanceOf(CancelledError);
153
154
  });
154
155
 
155
156
  it('throws CancelledError when user cancels at write confirmation', async () => {
@@ -161,7 +162,7 @@ describe('runSetup', () => {
161
162
  ]),
162
163
  });
163
164
 
164
- await expect(runSetup(deps)).rejects.toBeInstanceOf(CancelledError);
165
+ await expect(runInit(deps)).rejects.toBeInstanceOf(CancelledError);
165
166
  });
166
167
 
167
168
  it('throws CancelledError when user cancels at agent write confirmation (opencode)', async () => {
@@ -175,7 +176,7 @@ describe('runSetup', () => {
175
176
  ]),
176
177
  });
177
178
 
178
- await expect(runSetup(deps)).rejects.toBeInstanceOf(CancelledError);
179
+ await expect(runInit(deps)).rejects.toBeInstanceOf(CancelledError);
179
180
  });
180
181
 
181
182
  it('throws CancelledError when user cancels at agent write confirmation (pi)', async () => {
@@ -184,12 +185,13 @@ describe('runSetup', () => {
184
185
  prompter: new FakePrompter([
185
186
  select('pi'),
186
187
  select('project'),
188
+ confirm(true, 'Set up an agent for Pi?'),
187
189
  select('fullstack'),
188
190
  confirm(false, /Create|Overwrite/),
189
191
  ]),
190
192
  });
191
193
 
192
- await expect(runSetup(deps)).rejects.toBeInstanceOf(CancelledError);
194
+ await expect(runInit(deps)).rejects.toBeInstanceOf(CancelledError);
193
195
  });
194
196
  });
195
197
 
@@ -213,13 +215,13 @@ describe('runSetup', () => {
213
215
  }),
214
216
  );
215
217
 
216
- await runSetup(deps);
218
+ await runInit(deps);
217
219
 
218
220
  const written = files.getWrittenFiles();
219
221
  const config = JSON.parse(written.get('/home/user/project/opencode.json')!);
220
222
  expect(config.customField).toBe('should-preserve');
221
223
  expect(config.plugin).toContain('other-plugin');
222
- expect(config.plugin).toContain('@bergetai/opencode-auth');
224
+ expect(config.plugin).toContain('@bergetai/opencode-auth@1.0.21');
223
225
  });
224
226
 
225
227
  it('preserves jsonc comments when updating', async () => {
@@ -243,7 +245,7 @@ describe('runSetup', () => {
243
245
  }`,
244
246
  );
245
247
 
246
- await runSetup(deps);
248
+ await runInit(deps);
247
249
 
248
250
  const written = files.getWrittenFiles();
249
251
  const content = written.get('/home/user/project/opencode.jsonc')!;
@@ -265,20 +267,20 @@ describe('runSetup', () => {
265
267
  JSON.stringify(
266
268
  {
267
269
  $schema: 'https://opencode.ai/config.json',
268
- plugin: ['@bergetai/opencode-auth'],
270
+ plugin: ['@bergetai/opencode-auth@1.0.21'],
269
271
  },
270
272
  null,
271
273
  2,
272
274
  ) + '\n',
273
275
  );
274
276
 
275
- await runSetup(deps);
277
+ await runInit(deps);
276
278
 
277
279
  // Check that no write happened — content should be unchanged
278
280
  const written = files.getWrittenFiles();
279
281
  const content = written.get('/home/user/project/opencode.json')!;
280
282
  const config = JSON.parse(content);
281
- expect(config.plugin).toEqual(['@bergetai/opencode-auth']);
283
+ expect(config.plugin).toEqual(['@bergetai/opencode-auth@1.0.21']);
282
284
  expect(content).toContain('$schema');
283
285
  });
284
286
 
@@ -288,6 +290,7 @@ describe('runSetup', () => {
288
290
  prompter: new FakePrompter([
289
291
  select('pi'),
290
292
  select('project'),
293
+ confirm(true, 'Set up an agent for Pi?'),
291
294
  select('fullstack'),
292
295
  confirm(true, 'Create'),
293
296
  ]),
@@ -302,7 +305,7 @@ describe('runSetup', () => {
302
305
  }),
303
306
  );
304
307
 
305
- await runSetup(deps);
308
+ await runInit(deps);
306
309
 
307
310
  const written = files.getWrittenFiles();
308
311
  const settings = JSON.parse(written.get('/home/user/project/.pi/settings.json')!);
@@ -321,7 +324,7 @@ describe('runSetup', () => {
321
324
  ]),
322
325
  });
323
326
 
324
- await runSetup(deps);
327
+ await runInit(deps);
325
328
 
326
329
  const files = deps.files as FakeFileStore;
327
330
  const written = files.getWrittenFiles();
@@ -336,12 +339,13 @@ describe('runSetup', () => {
336
339
  prompter: new FakePrompter([
337
340
  select('pi'),
338
341
  select('project'),
342
+ confirm(true, 'Set up an agent for Pi?'),
339
343
  select('fullstack'),
340
344
  confirm(true, 'Create'),
341
345
  ]),
342
346
  });
343
347
 
344
- await runSetup(deps);
348
+ await runInit(deps);
345
349
 
346
350
  const commands = deps.commands as FakeCommandRunner;
347
351
  const installCall = commands.calls.find((c) => c.command === 'pi');
@@ -359,7 +363,7 @@ describe('runSetup', () => {
359
363
  prompter: new FakePrompter([select('pi'), select('project')]),
360
364
  });
361
365
 
362
- await expect(runSetup(deps)).rejects.toBeInstanceOf(CommandFailedError);
366
+ await expect(runInit(deps)).rejects.toBeInstanceOf(CommandFailedError);
363
367
  });
364
368
  });
365
369
 
@@ -382,7 +386,7 @@ describe('runSetup', () => {
382
386
  ]),
383
387
  });
384
388
 
385
- await runSetup(deps);
389
+ await runInit(deps);
386
390
 
387
391
  const prompter = deps.prompter as FakePrompter;
388
392
  const notes = prompter.calls.filter((c) => c.method === 'note');
@@ -399,12 +403,13 @@ describe('runSetup', () => {
399
403
  prompter: new FakePrompter([
400
404
  select('pi'),
401
405
  select('project'),
406
+ confirm(true, 'Set up an agent for Pi?'),
402
407
  select('fullstack'),
403
408
  confirm(true, 'Create'),
404
409
  ]),
405
410
  });
406
411
 
407
- await runSetup(deps);
412
+ await runInit(deps);
408
413
 
409
414
  const prompter = deps.prompter as FakePrompter;
410
415
  const notes = prompter.calls.filter((c) => c.method === 'note');
@@ -423,12 +428,13 @@ describe('runSetup', () => {
423
428
  select('pi'),
424
429
  select('project'),
425
430
  confirm(true), // API key creation prompt
431
+ confirm(true, 'Set up an agent for Pi?'),
426
432
  select('fullstack'),
427
433
  confirm(true, 'Create'),
428
434
  ]),
429
435
  });
430
436
 
431
- await runSetup(deps);
437
+ await runInit(deps);
432
438
 
433
439
  const written = files.getWrittenFiles();
434
440
  expect(written.has('/home/user/.pi/agent/auth.json')).toBe(true);
@@ -459,7 +465,7 @@ describe('runSetup', () => {
459
465
  ]),
460
466
  });
461
467
 
462
- await runSetup(deps);
468
+ await runInit(deps);
463
469
 
464
470
  const written = files.getWrittenFiles();
465
471
  const parsed = JSON.parse(written.get('/home/user/.local/share/opencode/auth.json')!);
@@ -479,7 +485,7 @@ describe('runSetup', () => {
479
485
  ]),
480
486
  });
481
487
 
482
- await runSetup(deps);
488
+ await runInit(deps);
483
489
 
484
490
  const files = deps.files as FakeFileStore;
485
491
  const written = files.getWrittenFiles();
@@ -497,7 +503,7 @@ describe('runSetup', () => {
497
503
  ]),
498
504
  });
499
505
 
500
- await runSetup(deps);
506
+ await runInit(deps);
501
507
 
502
508
  const files = deps.files as FakeFileStore;
503
509
  const written = files.getWrittenFiles();
@@ -517,7 +523,7 @@ describe('runSetup', () => {
517
523
  ]),
518
524
  });
519
525
 
520
- await runSetup(deps);
526
+ await runInit(deps);
521
527
 
522
528
  const files = deps.files as FakeFileStore;
523
529
  const written = files.getWrittenFiles();
@@ -530,12 +536,13 @@ describe('runSetup', () => {
530
536
  prompter: new FakePrompter([
531
537
  select('pi'),
532
538
  select('project'),
539
+ confirm(true, 'Set up an agent for Pi?'),
533
540
  select('fullstack'),
534
541
  confirm(true, 'Create'),
535
542
  ]),
536
543
  });
537
544
 
538
- await runSetup(deps);
545
+ await runInit(deps);
539
546
 
540
547
  const files = deps.files as FakeFileStore;
541
548
  const written = files.getWrittenFiles();
@@ -548,12 +555,13 @@ describe('runSetup', () => {
548
555
  prompter: new FakePrompter([
549
556
  select('pi'),
550
557
  select('global'),
558
+ confirm(true, 'Set up an agent for Pi?'),
551
559
  select('backend'),
552
560
  confirm(true, 'Create'),
553
561
  ]),
554
562
  });
555
563
 
556
- await runSetup(deps);
564
+ await runInit(deps);
557
565
 
558
566
  const files = deps.files as FakeFileStore;
559
567
  const written = files.getWrittenFiles();
@@ -572,7 +580,7 @@ describe('runSetup', () => {
572
580
  });
573
581
 
574
582
  // First run writes the files
575
- await runSetup(deps);
583
+ await runInit(deps);
576
584
 
577
585
  const files = deps.files as FakeFileStore;
578
586
  const firstBackend = files
@@ -582,17 +590,18 @@ describe('runSetup', () => {
582
590
  .getWrittenFiles()
583
591
  .get('/home/user/project/.opencode/agents/frontend.md');
584
592
 
585
- // Second run with exact same content should not prompt for overwrite
593
+ // Second run with exact same content should prompt for confirmation
586
594
  const deps2 = makeDeps({
587
595
  files,
588
596
  prompter: new FakePrompter([
589
597
  select('opencode'),
590
598
  select('project'),
591
599
  multiselect(['backend', 'frontend']),
600
+ confirm(true, 'Write'),
592
601
  ]),
593
602
  });
594
603
 
595
- await runSetup(deps2);
604
+ await runInit(deps2);
596
605
 
597
606
  // Content should be unchanged
598
607
  expect(files.getWrittenFiles().get('/home/user/project/.opencode/agents/backend.md')).toBe(
@@ -613,12 +622,13 @@ describe('runSetup', () => {
613
622
  prompter: new FakePrompter([
614
623
  select('pi'),
615
624
  select('project'),
625
+ confirm(true, 'Set up an agent for Pi?'),
616
626
  select('fullstack'),
617
- confirm(true, 'Overwrite'),
627
+ confirm(true, 'SYSTEM.md already exists'),
618
628
  ]),
619
629
  });
620
630
 
621
- await runSetup(deps);
631
+ await runInit(deps);
622
632
 
623
633
  const written = files.getWrittenFiles();
624
634
  const content = written.get('/home/user/project/.pi/SYSTEM.md');
@@ -66,7 +66,7 @@ export async function configureAuth(deps: AuthDeps, tool: 'opencode' | 'pi'): Pr
66
66
  if (!loginResult.success) {
67
67
  s.stop('Login failed.');
68
68
  prompter.note(
69
- `${loginResult.error || 'Login timed out or was cancelled.'}\n\nPlease run \`berget auth login\` manually, then run \`berget code setup\` again.`,
69
+ `${loginResult.error || 'Login timed out or was cancelled.'}\n\nPlease run \`berget auth login\` manually, then run \`berget code init\` again.`,
70
70
  'Authentication Failed',
71
71
  );
72
72
  return { authenticated: false };
@@ -129,7 +129,7 @@ export async function configureAuth(deps: AuthDeps, tool: 'opencode' | 'pi'): Pr
129
129
  s.start('Creating API key...');
130
130
  try {
131
131
  const { key } = await apiKeyService.create({
132
- description: 'Created by berget code setup',
132
+ description: 'Created by berget code init',
133
133
  name: `${tool === 'opencode' ? 'OpenCode' : 'Pi'} (created by berget CLI)`,
134
134
  });
135
135
  await syncApiKeyToTool(files, homeDir, tool, key);
@@ -156,7 +156,7 @@ export async function configureAuth(deps: AuthDeps, tool: 'opencode' | 'pi'): Pr
156
156
  s.start('Creating API key...');
157
157
  try {
158
158
  const { key } = await apiKeyService.create({
159
- description: 'Created by berget code setup',
159
+ description: 'Created by berget code init',
160
160
  name: `${tool === 'opencode' ? 'OpenCode' : 'Pi'} (created by berget CLI)`,
161
161
  });
162
162
  await syncApiKeyToTool(files, homeDir, tool, key);