berget 2.2.11 → 2.2.13

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.22');
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();
@@ -101,7 +101,7 @@ describe('runSetup', () => {
101
101
  ]),
102
102
  });
103
103
 
104
- await runSetup(deps);
104
+ await runInit(deps);
105
105
 
106
106
  const commands = deps.commands as FakeCommandRunner;
107
107
  expect(commands.calls.length).toBeGreaterThan(0);
@@ -121,7 +121,7 @@ describe('runSetup', () => {
121
121
  ]),
122
122
  });
123
123
 
124
- await runSetup(deps);
124
+ await runInit(deps);
125
125
 
126
126
  const files = deps.files as FakeFileStore;
127
127
  const written = files.getWrittenFiles();
@@ -140,7 +140,7 @@ describe('runSetup', () => {
140
140
  });
141
141
 
142
142
  // Simulate opencode not being installed
143
- await expect(runSetup(deps)).rejects.toBeInstanceOf(PrerequisiteError);
143
+ await expect(runInit(deps)).rejects.toBeInstanceOf(PrerequisiteError);
144
144
  });
145
145
  });
146
146
 
@@ -150,7 +150,7 @@ describe('runSetup', () => {
150
150
  prompter: new FakePrompter([select(CANCEL)]),
151
151
  });
152
152
 
153
- await expect(runSetup(deps)).rejects.toBeInstanceOf(CancelledError);
153
+ await expect(runInit(deps)).rejects.toBeInstanceOf(CancelledError);
154
154
  });
155
155
 
156
156
  it('throws CancelledError when user cancels at write confirmation', async () => {
@@ -162,7 +162,7 @@ describe('runSetup', () => {
162
162
  ]),
163
163
  });
164
164
 
165
- await expect(runSetup(deps)).rejects.toBeInstanceOf(CancelledError);
165
+ await expect(runInit(deps)).rejects.toBeInstanceOf(CancelledError);
166
166
  });
167
167
 
168
168
  it('throws CancelledError when user cancels at agent write confirmation (opencode)', async () => {
@@ -176,7 +176,7 @@ describe('runSetup', () => {
176
176
  ]),
177
177
  });
178
178
 
179
- await expect(runSetup(deps)).rejects.toBeInstanceOf(CancelledError);
179
+ await expect(runInit(deps)).rejects.toBeInstanceOf(CancelledError);
180
180
  });
181
181
 
182
182
  it('throws CancelledError when user cancels at agent write confirmation (pi)', async () => {
@@ -191,7 +191,7 @@ describe('runSetup', () => {
191
191
  ]),
192
192
  });
193
193
 
194
- await expect(runSetup(deps)).rejects.toBeInstanceOf(CancelledError);
194
+ await expect(runInit(deps)).rejects.toBeInstanceOf(CancelledError);
195
195
  });
196
196
  });
197
197
 
@@ -215,13 +215,13 @@ describe('runSetup', () => {
215
215
  }),
216
216
  );
217
217
 
218
- await runSetup(deps);
218
+ await runInit(deps);
219
219
 
220
220
  const written = files.getWrittenFiles();
221
221
  const config = JSON.parse(written.get('/home/user/project/opencode.json')!);
222
222
  expect(config.customField).toBe('should-preserve');
223
223
  expect(config.plugin).toContain('other-plugin');
224
- expect(config.plugin).toContain('@bergetai/opencode-auth');
224
+ expect(config.plugin).toContain('@bergetai/opencode-auth@1.0.22');
225
225
  });
226
226
 
227
227
  it('preserves jsonc comments when updating', async () => {
@@ -245,7 +245,7 @@ describe('runSetup', () => {
245
245
  }`,
246
246
  );
247
247
 
248
- await runSetup(deps);
248
+ await runInit(deps);
249
249
 
250
250
  const written = files.getWrittenFiles();
251
251
  const content = written.get('/home/user/project/opencode.jsonc')!;
@@ -267,20 +267,20 @@ describe('runSetup', () => {
267
267
  JSON.stringify(
268
268
  {
269
269
  $schema: 'https://opencode.ai/config.json',
270
- plugin: ['@bergetai/opencode-auth'],
270
+ plugin: ['@bergetai/opencode-auth@1.0.22'],
271
271
  },
272
272
  null,
273
273
  2,
274
274
  ) + '\n',
275
275
  );
276
276
 
277
- await runSetup(deps);
277
+ await runInit(deps);
278
278
 
279
279
  // Check that no write happened — content should be unchanged
280
280
  const written = files.getWrittenFiles();
281
281
  const content = written.get('/home/user/project/opencode.json')!;
282
282
  const config = JSON.parse(content);
283
- expect(config.plugin).toEqual(['@bergetai/opencode-auth']);
283
+ expect(config.plugin).toEqual(['@bergetai/opencode-auth@1.0.22']);
284
284
  expect(content).toContain('$schema');
285
285
  });
286
286
 
@@ -305,7 +305,7 @@ describe('runSetup', () => {
305
305
  }),
306
306
  );
307
307
 
308
- await runSetup(deps);
308
+ await runInit(deps);
309
309
 
310
310
  const written = files.getWrittenFiles();
311
311
  const settings = JSON.parse(written.get('/home/user/project/.pi/settings.json')!);
@@ -324,7 +324,7 @@ describe('runSetup', () => {
324
324
  ]),
325
325
  });
326
326
 
327
- await runSetup(deps);
327
+ await runInit(deps);
328
328
 
329
329
  const files = deps.files as FakeFileStore;
330
330
  const written = files.getWrittenFiles();
@@ -345,7 +345,7 @@ describe('runSetup', () => {
345
345
  ]),
346
346
  });
347
347
 
348
- await runSetup(deps);
348
+ await runInit(deps);
349
349
 
350
350
  const commands = deps.commands as FakeCommandRunner;
351
351
  const installCall = commands.calls.find((c) => c.command === 'pi');
@@ -363,7 +363,7 @@ describe('runSetup', () => {
363
363
  prompter: new FakePrompter([select('pi'), select('project')]),
364
364
  });
365
365
 
366
- await expect(runSetup(deps)).rejects.toBeInstanceOf(CommandFailedError);
366
+ await expect(runInit(deps)).rejects.toBeInstanceOf(CommandFailedError);
367
367
  });
368
368
  });
369
369
 
@@ -386,7 +386,7 @@ describe('runSetup', () => {
386
386
  ]),
387
387
  });
388
388
 
389
- await runSetup(deps);
389
+ await runInit(deps);
390
390
 
391
391
  const prompter = deps.prompter as FakePrompter;
392
392
  const notes = prompter.calls.filter((c) => c.method === 'note');
@@ -409,7 +409,7 @@ describe('runSetup', () => {
409
409
  ]),
410
410
  });
411
411
 
412
- await runSetup(deps);
412
+ await runInit(deps);
413
413
 
414
414
  const prompter = deps.prompter as FakePrompter;
415
415
  const notes = prompter.calls.filter((c) => c.method === 'note');
@@ -434,7 +434,7 @@ describe('runSetup', () => {
434
434
  ]),
435
435
  });
436
436
 
437
- await runSetup(deps);
437
+ await runInit(deps);
438
438
 
439
439
  const written = files.getWrittenFiles();
440
440
  expect(written.has('/home/user/.pi/agent/auth.json')).toBe(true);
@@ -465,7 +465,7 @@ describe('runSetup', () => {
465
465
  ]),
466
466
  });
467
467
 
468
- await runSetup(deps);
468
+ await runInit(deps);
469
469
 
470
470
  const written = files.getWrittenFiles();
471
471
  const parsed = JSON.parse(written.get('/home/user/.local/share/opencode/auth.json')!);
@@ -485,7 +485,7 @@ describe('runSetup', () => {
485
485
  ]),
486
486
  });
487
487
 
488
- await runSetup(deps);
488
+ await runInit(deps);
489
489
 
490
490
  const files = deps.files as FakeFileStore;
491
491
  const written = files.getWrittenFiles();
@@ -503,7 +503,7 @@ describe('runSetup', () => {
503
503
  ]),
504
504
  });
505
505
 
506
- await runSetup(deps);
506
+ await runInit(deps);
507
507
 
508
508
  const files = deps.files as FakeFileStore;
509
509
  const written = files.getWrittenFiles();
@@ -523,7 +523,7 @@ describe('runSetup', () => {
523
523
  ]),
524
524
  });
525
525
 
526
- await runSetup(deps);
526
+ await runInit(deps);
527
527
 
528
528
  const files = deps.files as FakeFileStore;
529
529
  const written = files.getWrittenFiles();
@@ -542,7 +542,7 @@ describe('runSetup', () => {
542
542
  ]),
543
543
  });
544
544
 
545
- await runSetup(deps);
545
+ await runInit(deps);
546
546
 
547
547
  const files = deps.files as FakeFileStore;
548
548
  const written = files.getWrittenFiles();
@@ -561,7 +561,7 @@ describe('runSetup', () => {
561
561
  ]),
562
562
  });
563
563
 
564
- await runSetup(deps);
564
+ await runInit(deps);
565
565
 
566
566
  const files = deps.files as FakeFileStore;
567
567
  const written = files.getWrittenFiles();
@@ -580,7 +580,7 @@ describe('runSetup', () => {
580
580
  });
581
581
 
582
582
  // First run writes the files
583
- await runSetup(deps);
583
+ await runInit(deps);
584
584
 
585
585
  const files = deps.files as FakeFileStore;
586
586
  const firstBackend = files
@@ -601,7 +601,7 @@ describe('runSetup', () => {
601
601
  ]),
602
602
  });
603
603
 
604
- await runSetup(deps2);
604
+ await runInit(deps2);
605
605
 
606
606
  // Content should be unchanged
607
607
  expect(files.getWrittenFiles().get('/home/user/project/.opencode/agents/backend.md')).toBe(
@@ -628,7 +628,7 @@ describe('runSetup', () => {
628
628
  ]),
629
629
  });
630
630
 
631
- await runSetup(deps);
631
+ await runInit(deps);
632
632
 
633
633
  const written = files.getWrittenFiles();
634
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);
@@ -1,3 +1,4 @@
1
+ import chalk from 'chalk';
1
2
  import { applyEdits, modify, parse } from 'jsonc-parser';
2
3
  import * as os from 'node:os';
3
4
 
@@ -15,7 +16,7 @@ import { SpawnCommandRunner } from './adapters/spawn-command-runner.js';
15
16
  import { configureAuth } from './auth-sync.js';
16
17
  import { CancelledError, CommandFailedError, PrerequisiteError } from './errors';
17
18
 
18
- const OPENCODE_PLUGIN = '@bergetai/opencode-auth';
19
+ const OPENCODE_PLUGIN = '@bergetai/opencode-auth@1.0.22';
19
20
  const PI_PROVIDER = 'npm:@bergetai/pi-provider';
20
21
  const OPENCODE_PLUGIN_NAME = '@bergetai/opencode-auth';
21
22
  const PI_PROVIDER_NAME = '@bergetai/pi-provider';
@@ -30,10 +31,14 @@ export interface WizardDeps {
30
31
  prompter: Prompter;
31
32
  }
32
33
 
33
- export async function runSetup(deps: WizardDeps): Promise<void> {
34
+ export async function runInit(deps: WizardDeps): Promise<void> {
34
35
  const { apiKeyService, authService, commands, cwd, files, homeDir, prompter } = deps;
35
36
 
36
- prompter.intro('\uD83D\uDD27 Berget Code Setup');
37
+ prompter.intro(`${chalk.bgGreen.black(' berget code ')}`);
38
+ prompter.note(
39
+ `Ask questions and report bugs on our GitHub repository:\n\n${chalk.cyan.underline('https://github.com/berget-ai/cli')}`,
40
+ 'Need help?',
41
+ );
37
42
 
38
43
  const ocState = await getOpencodeState(files, homeDir, cwd);
39
44
  const piState = await getPiState(files, homeDir, cwd);
@@ -121,14 +126,14 @@ export async function runSetup(deps: WizardDeps): Promise<void> {
121
126
  }
122
127
  }
123
128
 
124
- prompter.outro('Setup complete!');
129
+ prompter.outro('Initialization complete!');
125
130
  }
126
131
 
127
132
  // ─── OpenCode ────────────────────────────────────────────────────────────────
128
133
 
129
- export async function runSetupCommand(): Promise<void> {
134
+ export async function runInitCommand(): Promise<void> {
130
135
  try {
131
- await runSetup({
136
+ await runInit({
132
137
  apiKeyService: ApiKeyService.getInstance(),
133
138
  authService: AuthService.getInstance(),
134
139
  commands: new SpawnCommandRunner(),
@@ -154,24 +159,7 @@ export async function runSetupCommand(): Promise<void> {
154
159
  }
155
160
  }
156
161
 
157
- // ─── Pi ────────────────────────────────────────────────────────────────────────
158
-
159
- function generateDiff(oldText: string, newText: string, filePath: string): string {
160
- const oldLines = oldText.split('\n');
161
- const newLines = newText.split('\n');
162
- let result = `--- ${filePath}\n+++ ${filePath}\n`;
163
-
164
- const maxLength = Math.max(oldLines.length, newLines.length);
165
- for (let index = 0; index < maxLength; index++) {
166
- const oldLine = oldLines[index];
167
- const newLine = newLines[index];
168
- if (oldLine !== newLine) {
169
- if (oldLine !== undefined) result += `- ${oldLine}\n`;
170
- if (newLine !== undefined) result += `+ ${newLine}\n`;
171
- }
172
- }
173
- return result.trimEnd();
174
- }
162
+ // ─── OpenCode Config Helpers ──────────────────────────────────────────────────
175
163
 
176
164
  function generateModifiedContent(existingContent: null | string, configPath: string): string {
177
165
  if (configPath.endsWith('.jsonc')) {
@@ -381,9 +369,9 @@ async function setupOpenCode(deps: {
381
369
  }
382
370
 
383
371
  if (existingContent) {
384
- prompter.note(generateDiff(existingContent, newContent, configPath), 'Changes to be written');
372
+ prompter.note(`OpenCode config will be updated at:\n ${configPath}`, 'Config update');
385
373
  } else {
386
- prompter.note(`New config at ${configPath}:\n\n${newContent}`, 'Config preview');
374
+ prompter.note(`OpenCode config will be created at:\n ${configPath}`, 'Config update');
387
375
  }
388
376
 
389
377
  const shouldWrite = await prompter.confirm({