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.
- package/dist/package.json +1 -1
- package/dist/src/commands/code/__tests__/setup-flow.test.js +47 -37
- package/dist/src/commands/code/auth-sync.js +3 -3
- package/dist/src/commands/code/{setup.js → init.js} +73 -87
- package/dist/src/commands/code.js +5 -553
- package/dist/src/constants/command-structure.js +1 -9
- package/dist/src/services/auth-service.js +1 -1
- package/dist/tests/commands/code.test.js +4 -415
- package/package.json +1 -1
- package/src/commands/code/__tests__/setup-flow.test.ts +49 -39
- package/src/commands/code/auth-sync.ts +3 -3
- package/src/commands/code/{setup.ts → init.ts} +82 -97
- package/src/commands/code/ports/prompter.ts +1 -0
- package/src/commands/code.ts +5 -608
- package/src/constants/command-structure.ts +1 -11
- package/src/services/auth-service.ts +1 -1
- package/tests/commands/code.test.ts +5 -483
- package/templates/agents/app.md +0 -23
- package/templates/agents/backend.md +0 -23
- package/templates/agents/devops.md +0 -30
- package/templates/agents/frontend.md +0 -25
- package/templates/agents/fullstack.md +0 -23
- package/templates/agents/quality.md +0 -69
- package/templates/agents/security.md +0 -21
package/dist/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
3
|
const vitest_1 = require("vitest");
|
|
4
4
|
const errors_1 = require("../errors");
|
|
5
|
-
const
|
|
5
|
+
const init_1 = require("../init");
|
|
6
6
|
const fake_api_key_service_1 = require("./fake-api-key-service");
|
|
7
7
|
const fake_auth_service_1 = require("./fake-auth-service");
|
|
8
8
|
const fake_command_runner_1 = require("./fake-command-runner");
|
|
@@ -35,7 +35,7 @@ function makeJwt(payload) {
|
|
|
35
35
|
const body = base64urlEncode(JSON.stringify(payload));
|
|
36
36
|
return `${header}.${body}.signature`;
|
|
37
37
|
}
|
|
38
|
-
(0, vitest_1.describe)('
|
|
38
|
+
(0, vitest_1.describe)('runInit', () => {
|
|
39
39
|
(0, vitest_1.describe)('happy path', () => {
|
|
40
40
|
(0, vitest_1.it)('sets up opencode project without existing config', async () => {
|
|
41
41
|
const deps = makeDeps({
|
|
@@ -46,12 +46,12 @@ function makeJwt(payload) {
|
|
|
46
46
|
(0, fake_prompter_1.multiselect)([]), // No agents selected
|
|
47
47
|
]),
|
|
48
48
|
});
|
|
49
|
-
await (0,
|
|
49
|
+
await (0, init_1.runInit)(deps);
|
|
50
50
|
const files = deps.files;
|
|
51
51
|
const written = files.getWrittenFiles();
|
|
52
52
|
(0, vitest_1.expect)(written.has('/home/user/project/opencode.json')).toBe(true);
|
|
53
53
|
const config = JSON.parse(written.get('/home/user/project/opencode.json'));
|
|
54
|
-
(0, vitest_1.expect)(config.plugin).toContain('@bergetai/opencode-auth');
|
|
54
|
+
(0, vitest_1.expect)(config.plugin).toContain('@bergetai/opencode-auth@1.0.21');
|
|
55
55
|
});
|
|
56
56
|
(0, vitest_1.it)('sets up opencode globally without existing config', async () => {
|
|
57
57
|
const deps = makeDeps({
|
|
@@ -62,7 +62,7 @@ function makeJwt(payload) {
|
|
|
62
62
|
(0, fake_prompter_1.multiselect)([]), // No agents selected
|
|
63
63
|
]),
|
|
64
64
|
});
|
|
65
|
-
await (0,
|
|
65
|
+
await (0, init_1.runInit)(deps);
|
|
66
66
|
const files = deps.files;
|
|
67
67
|
const written = files.getWrittenFiles();
|
|
68
68
|
(0, vitest_1.expect)(written.has('/home/user/.config/opencode/opencode.json')).toBe(true);
|
|
@@ -75,11 +75,12 @@ function makeJwt(payload) {
|
|
|
75
75
|
prompter: new fake_prompter_1.FakePrompter([
|
|
76
76
|
(0, fake_prompter_1.select)('pi'),
|
|
77
77
|
(0, fake_prompter_1.select)('project'),
|
|
78
|
+
(0, fake_prompter_1.confirm)(true, 'Set up an agent for Pi?'),
|
|
78
79
|
(0, fake_prompter_1.select)('fullstack'), // Agent selection
|
|
79
80
|
(0, fake_prompter_1.confirm)(true, 'Create'),
|
|
80
81
|
]),
|
|
81
82
|
});
|
|
82
|
-
await (0,
|
|
83
|
+
await (0, init_1.runInit)(deps);
|
|
83
84
|
const commands = deps.commands;
|
|
84
85
|
(0, vitest_1.expect)(commands.calls.length).toBeGreaterThan(0);
|
|
85
86
|
const installCall = commands.calls.find((c) => c.command === 'pi');
|
|
@@ -93,10 +94,10 @@ function makeJwt(payload) {
|
|
|
93
94
|
prompter: new fake_prompter_1.FakePrompter([
|
|
94
95
|
(0, fake_prompter_1.select)('pi'),
|
|
95
96
|
(0, fake_prompter_1.select)('project'),
|
|
96
|
-
(0, fake_prompter_1.
|
|
97
|
+
(0, fake_prompter_1.confirm)(false, 'Set up an agent for Pi?'),
|
|
97
98
|
]),
|
|
98
99
|
});
|
|
99
|
-
await (0,
|
|
100
|
+
await (0, init_1.runInit)(deps);
|
|
100
101
|
const files = deps.files;
|
|
101
102
|
const written = files.getWrittenFiles();
|
|
102
103
|
// Should not create any agent files
|
|
@@ -112,7 +113,7 @@ function makeJwt(payload) {
|
|
|
112
113
|
prompter: new fake_prompter_1.FakePrompter([(0, fake_prompter_1.select)('opencode'), (0, fake_prompter_1.select)('project')]),
|
|
113
114
|
});
|
|
114
115
|
// Simulate opencode not being installed
|
|
115
|
-
await (0, vitest_1.expect)((0,
|
|
116
|
+
await (0, vitest_1.expect)((0, init_1.runInit)(deps)).rejects.toBeInstanceOf(errors_1.PrerequisiteError);
|
|
116
117
|
});
|
|
117
118
|
});
|
|
118
119
|
(0, vitest_1.describe)('cancellation', () => {
|
|
@@ -120,7 +121,7 @@ function makeJwt(payload) {
|
|
|
120
121
|
const deps = makeDeps({
|
|
121
122
|
prompter: new fake_prompter_1.FakePrompter([(0, fake_prompter_1.select)(fake_prompter_1.CANCEL)]),
|
|
122
123
|
});
|
|
123
|
-
await (0, vitest_1.expect)((0,
|
|
124
|
+
await (0, vitest_1.expect)((0, init_1.runInit)(deps)).rejects.toBeInstanceOf(errors_1.CancelledError);
|
|
124
125
|
});
|
|
125
126
|
(0, vitest_1.it)('throws CancelledError when user cancels at write confirmation', async () => {
|
|
126
127
|
const deps = makeDeps({
|
|
@@ -130,7 +131,7 @@ function makeJwt(payload) {
|
|
|
130
131
|
(0, fake_prompter_1.confirm)(false, 'Create'),
|
|
131
132
|
]),
|
|
132
133
|
});
|
|
133
|
-
await (0, vitest_1.expect)((0,
|
|
134
|
+
await (0, vitest_1.expect)((0, init_1.runInit)(deps)).rejects.toBeInstanceOf(errors_1.CancelledError);
|
|
134
135
|
});
|
|
135
136
|
(0, vitest_1.it)('throws CancelledError when user cancels at agent write confirmation (opencode)', async () => {
|
|
136
137
|
const deps = makeDeps({
|
|
@@ -142,7 +143,7 @@ function makeJwt(payload) {
|
|
|
142
143
|
(0, fake_prompter_1.confirm)(false, 'agent'),
|
|
143
144
|
]),
|
|
144
145
|
});
|
|
145
|
-
await (0, vitest_1.expect)((0,
|
|
146
|
+
await (0, vitest_1.expect)((0, init_1.runInit)(deps)).rejects.toBeInstanceOf(errors_1.CancelledError);
|
|
146
147
|
});
|
|
147
148
|
(0, vitest_1.it)('throws CancelledError when user cancels at agent write confirmation (pi)', async () => {
|
|
148
149
|
const deps = makeDeps({
|
|
@@ -150,11 +151,12 @@ function makeJwt(payload) {
|
|
|
150
151
|
prompter: new fake_prompter_1.FakePrompter([
|
|
151
152
|
(0, fake_prompter_1.select)('pi'),
|
|
152
153
|
(0, fake_prompter_1.select)('project'),
|
|
154
|
+
(0, fake_prompter_1.confirm)(true, 'Set up an agent for Pi?'),
|
|
153
155
|
(0, fake_prompter_1.select)('fullstack'),
|
|
154
156
|
(0, fake_prompter_1.confirm)(false, /Create|Overwrite/),
|
|
155
157
|
]),
|
|
156
158
|
});
|
|
157
|
-
await (0, vitest_1.expect)((0,
|
|
159
|
+
await (0, vitest_1.expect)((0, init_1.runInit)(deps)).rejects.toBeInstanceOf(errors_1.CancelledError);
|
|
158
160
|
});
|
|
159
161
|
});
|
|
160
162
|
(0, vitest_1.describe)('file operations', () => {
|
|
@@ -172,12 +174,12 @@ function makeJwt(payload) {
|
|
|
172
174
|
customField: 'should-preserve',
|
|
173
175
|
plugin: ['other-plugin'],
|
|
174
176
|
}));
|
|
175
|
-
await (0,
|
|
177
|
+
await (0, init_1.runInit)(deps);
|
|
176
178
|
const written = files.getWrittenFiles();
|
|
177
179
|
const config = JSON.parse(written.get('/home/user/project/opencode.json'));
|
|
178
180
|
(0, vitest_1.expect)(config.customField).toBe('should-preserve');
|
|
179
181
|
(0, vitest_1.expect)(config.plugin).toContain('other-plugin');
|
|
180
|
-
(0, vitest_1.expect)(config.plugin).toContain('@bergetai/opencode-auth');
|
|
182
|
+
(0, vitest_1.expect)(config.plugin).toContain('@bergetai/opencode-auth@1.0.21');
|
|
181
183
|
});
|
|
182
184
|
(0, vitest_1.it)('preserves jsonc comments when updating', async () => {
|
|
183
185
|
const deps = makeDeps({
|
|
@@ -195,7 +197,7 @@ function makeJwt(payload) {
|
|
|
195
197
|
/* block comment explaining plugin */
|
|
196
198
|
"plugin": ["other-plugin"]
|
|
197
199
|
}`);
|
|
198
|
-
await (0,
|
|
200
|
+
await (0, init_1.runInit)(deps);
|
|
199
201
|
const written = files.getWrittenFiles();
|
|
200
202
|
const content = written.get('/home/user/project/opencode.jsonc');
|
|
201
203
|
(0, vitest_1.expect)(content).toContain('// This is my custom config');
|
|
@@ -211,14 +213,14 @@ function makeJwt(payload) {
|
|
|
211
213
|
// Already has the exact same plugin version
|
|
212
214
|
files.seed('/home/user/project/opencode.json', JSON.stringify({
|
|
213
215
|
$schema: 'https://opencode.ai/config.json',
|
|
214
|
-
plugin: ['@bergetai/opencode-auth'],
|
|
216
|
+
plugin: ['@bergetai/opencode-auth@1.0.21'],
|
|
215
217
|
}, null, 2) + '\n');
|
|
216
|
-
await (0,
|
|
218
|
+
await (0, init_1.runInit)(deps);
|
|
217
219
|
// Check that no write happened — content should be unchanged
|
|
218
220
|
const written = files.getWrittenFiles();
|
|
219
221
|
const content = written.get('/home/user/project/opencode.json');
|
|
220
222
|
const config = JSON.parse(content);
|
|
221
|
-
(0, vitest_1.expect)(config.plugin).toEqual(['@bergetai/opencode-auth']);
|
|
223
|
+
(0, vitest_1.expect)(config.plugin).toEqual(['@bergetai/opencode-auth@1.0.21']);
|
|
222
224
|
(0, vitest_1.expect)(content).toContain('$schema');
|
|
223
225
|
});
|
|
224
226
|
(0, vitest_1.it)('preserves existing Pi settings when setting defaultProvider', async () => {
|
|
@@ -227,6 +229,7 @@ function makeJwt(payload) {
|
|
|
227
229
|
prompter: new fake_prompter_1.FakePrompter([
|
|
228
230
|
(0, fake_prompter_1.select)('pi'),
|
|
229
231
|
(0, fake_prompter_1.select)('project'),
|
|
232
|
+
(0, fake_prompter_1.confirm)(true, 'Set up an agent for Pi?'),
|
|
230
233
|
(0, fake_prompter_1.select)('fullstack'),
|
|
231
234
|
(0, fake_prompter_1.confirm)(true, 'Create'),
|
|
232
235
|
]),
|
|
@@ -236,7 +239,7 @@ function makeJwt(payload) {
|
|
|
236
239
|
anotherSetting: true,
|
|
237
240
|
existingKey: 'should-preserve',
|
|
238
241
|
}));
|
|
239
|
-
await (0,
|
|
242
|
+
await (0, init_1.runInit)(deps);
|
|
240
243
|
const written = files.getWrittenFiles();
|
|
241
244
|
const settings = JSON.parse(written.get('/home/user/project/.pi/settings.json'));
|
|
242
245
|
(0, vitest_1.expect)(settings.existingKey).toBe('should-preserve');
|
|
@@ -252,7 +255,7 @@ function makeJwt(payload) {
|
|
|
252
255
|
(0, fake_prompter_1.multiselect)([]),
|
|
253
256
|
]),
|
|
254
257
|
});
|
|
255
|
-
await (0,
|
|
258
|
+
await (0, init_1.runInit)(deps);
|
|
256
259
|
const files = deps.files;
|
|
257
260
|
const written = files.getWrittenFiles();
|
|
258
261
|
(0, vitest_1.expect)(written.has('/home/user/.config/opencode/opencode.json')).toBe(true);
|
|
@@ -265,11 +268,12 @@ function makeJwt(payload) {
|
|
|
265
268
|
prompter: new fake_prompter_1.FakePrompter([
|
|
266
269
|
(0, fake_prompter_1.select)('pi'),
|
|
267
270
|
(0, fake_prompter_1.select)('project'),
|
|
271
|
+
(0, fake_prompter_1.confirm)(true, 'Set up an agent for Pi?'),
|
|
268
272
|
(0, fake_prompter_1.select)('fullstack'),
|
|
269
273
|
(0, fake_prompter_1.confirm)(true, 'Create'),
|
|
270
274
|
]),
|
|
271
275
|
});
|
|
272
|
-
await (0,
|
|
276
|
+
await (0, init_1.runInit)(deps);
|
|
273
277
|
const commands = deps.commands;
|
|
274
278
|
const installCall = commands.calls.find((c) => c.command === 'pi');
|
|
275
279
|
(0, vitest_1.expect)(installCall?.args).toContain('npm:@bergetai/pi-provider');
|
|
@@ -284,7 +288,7 @@ function makeJwt(payload) {
|
|
|
284
288
|
.handle('pi install', new Error('npm error')),
|
|
285
289
|
prompter: new fake_prompter_1.FakePrompter([(0, fake_prompter_1.select)('pi'), (0, fake_prompter_1.select)('project')]),
|
|
286
290
|
});
|
|
287
|
-
await (0, vitest_1.expect)((0,
|
|
291
|
+
await (0, vitest_1.expect)((0, init_1.runInit)(deps)).rejects.toBeInstanceOf(errors_1.CommandFailedError);
|
|
288
292
|
});
|
|
289
293
|
});
|
|
290
294
|
(0, vitest_1.describe)('auth integration', () => {
|
|
@@ -301,7 +305,7 @@ function makeJwt(payload) {
|
|
|
301
305
|
(0, fake_prompter_1.multiselect)([]),
|
|
302
306
|
]),
|
|
303
307
|
});
|
|
304
|
-
await (0,
|
|
308
|
+
await (0, init_1.runInit)(deps);
|
|
305
309
|
const prompter = deps.prompter;
|
|
306
310
|
const notes = prompter.calls.filter((c) => c.method === 'note');
|
|
307
311
|
const lastNote = notes.at(-1);
|
|
@@ -316,11 +320,12 @@ function makeJwt(payload) {
|
|
|
316
320
|
prompter: new fake_prompter_1.FakePrompter([
|
|
317
321
|
(0, fake_prompter_1.select)('pi'),
|
|
318
322
|
(0, fake_prompter_1.select)('project'),
|
|
323
|
+
(0, fake_prompter_1.confirm)(true, 'Set up an agent for Pi?'),
|
|
319
324
|
(0, fake_prompter_1.select)('fullstack'),
|
|
320
325
|
(0, fake_prompter_1.confirm)(true, 'Create'),
|
|
321
326
|
]),
|
|
322
327
|
});
|
|
323
|
-
await (0,
|
|
328
|
+
await (0, init_1.runInit)(deps);
|
|
324
329
|
const prompter = deps.prompter;
|
|
325
330
|
const notes = prompter.calls.filter((c) => c.method === 'note');
|
|
326
331
|
const lastNote = notes.at(-1);
|
|
@@ -336,11 +341,12 @@ function makeJwt(payload) {
|
|
|
336
341
|
(0, fake_prompter_1.select)('pi'),
|
|
337
342
|
(0, fake_prompter_1.select)('project'),
|
|
338
343
|
(0, fake_prompter_1.confirm)(true), // API key creation prompt
|
|
344
|
+
(0, fake_prompter_1.confirm)(true, 'Set up an agent for Pi?'),
|
|
339
345
|
(0, fake_prompter_1.select)('fullstack'),
|
|
340
346
|
(0, fake_prompter_1.confirm)(true, 'Create'),
|
|
341
347
|
]),
|
|
342
348
|
});
|
|
343
|
-
await (0,
|
|
349
|
+
await (0, init_1.runInit)(deps);
|
|
344
350
|
const written = files.getWrittenFiles();
|
|
345
351
|
(0, vitest_1.expect)(written.has('/home/user/.pi/agent/auth.json')).toBe(true);
|
|
346
352
|
const parsed = JSON.parse(written.get('/home/user/.pi/agent/auth.json'));
|
|
@@ -364,7 +370,7 @@ function makeJwt(payload) {
|
|
|
364
370
|
(0, fake_prompter_1.multiselect)([]),
|
|
365
371
|
]),
|
|
366
372
|
});
|
|
367
|
-
await (0,
|
|
373
|
+
await (0, init_1.runInit)(deps);
|
|
368
374
|
const written = files.getWrittenFiles();
|
|
369
375
|
const parsed = JSON.parse(written.get('/home/user/.local/share/opencode/auth.json'));
|
|
370
376
|
(0, vitest_1.expect)(parsed.berget.type).toBe('oauth');
|
|
@@ -381,7 +387,7 @@ function makeJwt(payload) {
|
|
|
381
387
|
(0, fake_prompter_1.confirm)(true, 'agent'),
|
|
382
388
|
]),
|
|
383
389
|
});
|
|
384
|
-
await (0,
|
|
390
|
+
await (0, init_1.runInit)(deps);
|
|
385
391
|
const files = deps.files;
|
|
386
392
|
const written = files.getWrittenFiles();
|
|
387
393
|
(0, vitest_1.expect)(written.has('/home/user/project/.opencode/agents/backend.md')).toBe(true);
|
|
@@ -396,7 +402,7 @@ function makeJwt(payload) {
|
|
|
396
402
|
(0, fake_prompter_1.multiselect)([]),
|
|
397
403
|
]),
|
|
398
404
|
});
|
|
399
|
-
await (0,
|
|
405
|
+
await (0, init_1.runInit)(deps);
|
|
400
406
|
const files = deps.files;
|
|
401
407
|
const written = files.getWrittenFiles();
|
|
402
408
|
for (const path of written.keys()) {
|
|
@@ -413,7 +419,7 @@ function makeJwt(payload) {
|
|
|
413
419
|
(0, fake_prompter_1.confirm)(true, 'agent'),
|
|
414
420
|
]),
|
|
415
421
|
});
|
|
416
|
-
await (0,
|
|
422
|
+
await (0, init_1.runInit)(deps);
|
|
417
423
|
const files = deps.files;
|
|
418
424
|
const written = files.getWrittenFiles();
|
|
419
425
|
(0, vitest_1.expect)(written.has('/home/user/.config/opencode/agents/fullstack.md')).toBe(true);
|
|
@@ -424,11 +430,12 @@ function makeJwt(payload) {
|
|
|
424
430
|
prompter: new fake_prompter_1.FakePrompter([
|
|
425
431
|
(0, fake_prompter_1.select)('pi'),
|
|
426
432
|
(0, fake_prompter_1.select)('project'),
|
|
433
|
+
(0, fake_prompter_1.confirm)(true, 'Set up an agent for Pi?'),
|
|
427
434
|
(0, fake_prompter_1.select)('fullstack'),
|
|
428
435
|
(0, fake_prompter_1.confirm)(true, 'Create'),
|
|
429
436
|
]),
|
|
430
437
|
});
|
|
431
|
-
await (0,
|
|
438
|
+
await (0, init_1.runInit)(deps);
|
|
432
439
|
const files = deps.files;
|
|
433
440
|
const written = files.getWrittenFiles();
|
|
434
441
|
(0, vitest_1.expect)(written.has('/home/user/project/.pi/SYSTEM.md')).toBe(true);
|
|
@@ -439,11 +446,12 @@ function makeJwt(payload) {
|
|
|
439
446
|
prompter: new fake_prompter_1.FakePrompter([
|
|
440
447
|
(0, fake_prompter_1.select)('pi'),
|
|
441
448
|
(0, fake_prompter_1.select)('global'),
|
|
449
|
+
(0, fake_prompter_1.confirm)(true, 'Set up an agent for Pi?'),
|
|
442
450
|
(0, fake_prompter_1.select)('backend'),
|
|
443
451
|
(0, fake_prompter_1.confirm)(true, 'Create'),
|
|
444
452
|
]),
|
|
445
453
|
});
|
|
446
|
-
await (0,
|
|
454
|
+
await (0, init_1.runInit)(deps);
|
|
447
455
|
const files = deps.files;
|
|
448
456
|
const written = files.getWrittenFiles();
|
|
449
457
|
(0, vitest_1.expect)(written.has('/home/user/.pi/agent/SYSTEM.md')).toBe(true);
|
|
@@ -459,7 +467,7 @@ function makeJwt(payload) {
|
|
|
459
467
|
]),
|
|
460
468
|
});
|
|
461
469
|
// First run writes the files
|
|
462
|
-
await (0,
|
|
470
|
+
await (0, init_1.runInit)(deps);
|
|
463
471
|
const files = deps.files;
|
|
464
472
|
const firstBackend = files
|
|
465
473
|
.getWrittenFiles()
|
|
@@ -467,16 +475,17 @@ function makeJwt(payload) {
|
|
|
467
475
|
const firstFrontend = files
|
|
468
476
|
.getWrittenFiles()
|
|
469
477
|
.get('/home/user/project/.opencode/agents/frontend.md');
|
|
470
|
-
// Second run with exact same content should
|
|
478
|
+
// Second run with exact same content should prompt for confirmation
|
|
471
479
|
const deps2 = makeDeps({
|
|
472
480
|
files,
|
|
473
481
|
prompter: new fake_prompter_1.FakePrompter([
|
|
474
482
|
(0, fake_prompter_1.select)('opencode'),
|
|
475
483
|
(0, fake_prompter_1.select)('project'),
|
|
476
484
|
(0, fake_prompter_1.multiselect)(['backend', 'frontend']),
|
|
485
|
+
(0, fake_prompter_1.confirm)(true, 'Write'),
|
|
477
486
|
]),
|
|
478
487
|
});
|
|
479
|
-
await (0,
|
|
488
|
+
await (0, init_1.runInit)(deps2);
|
|
480
489
|
// Content should be unchanged
|
|
481
490
|
(0, vitest_1.expect)(files.getWrittenFiles().get('/home/user/project/.opencode/agents/backend.md')).toBe(firstBackend);
|
|
482
491
|
(0, vitest_1.expect)(files.getWrittenFiles().get('/home/user/project/.opencode/agents/frontend.md')).toBe(firstFrontend);
|
|
@@ -490,11 +499,12 @@ function makeJwt(payload) {
|
|
|
490
499
|
prompter: new fake_prompter_1.FakePrompter([
|
|
491
500
|
(0, fake_prompter_1.select)('pi'),
|
|
492
501
|
(0, fake_prompter_1.select)('project'),
|
|
502
|
+
(0, fake_prompter_1.confirm)(true, 'Set up an agent for Pi?'),
|
|
493
503
|
(0, fake_prompter_1.select)('fullstack'),
|
|
494
|
-
(0, fake_prompter_1.confirm)(true, '
|
|
504
|
+
(0, fake_prompter_1.confirm)(true, 'SYSTEM.md already exists'),
|
|
495
505
|
]),
|
|
496
506
|
});
|
|
497
|
-
await (0,
|
|
507
|
+
await (0, init_1.runInit)(deps);
|
|
498
508
|
const written = files.getWrittenFiles();
|
|
499
509
|
const content = written.get('/home/user/project/.pi/SYSTEM.md');
|
|
500
510
|
(0, vitest_1.expect)(content).not.toBe('old agent content');
|
|
@@ -38,7 +38,7 @@ async function configureAuth(deps, tool) {
|
|
|
38
38
|
const loginResult = await authService.loginInteractive();
|
|
39
39
|
if (!loginResult.success) {
|
|
40
40
|
s.stop('Login failed.');
|
|
41
|
-
prompter.note(`${loginResult.error || 'Login timed out or was cancelled.'}\n\nPlease run \`berget auth login\` manually, then run \`berget code
|
|
41
|
+
prompter.note(`${loginResult.error || 'Login timed out or was cancelled.'}\n\nPlease run \`berget auth login\` manually, then run \`berget code init\` again.`, 'Authentication Failed');
|
|
42
42
|
return { authenticated: false };
|
|
43
43
|
}
|
|
44
44
|
s.stop('Successfully logged in to Berget.');
|
|
@@ -88,7 +88,7 @@ async function configureAuth(deps, tool) {
|
|
|
88
88
|
s.start('Creating API key...');
|
|
89
89
|
try {
|
|
90
90
|
const { key } = await apiKeyService.create({
|
|
91
|
-
description: 'Created by berget code
|
|
91
|
+
description: 'Created by berget code init',
|
|
92
92
|
name: `${tool === 'opencode' ? 'OpenCode' : 'Pi'} (created by berget CLI)`,
|
|
93
93
|
});
|
|
94
94
|
await syncApiKeyToTool(files, homeDir, tool, key);
|
|
@@ -111,7 +111,7 @@ async function configureAuth(deps, tool) {
|
|
|
111
111
|
s.start('Creating API key...');
|
|
112
112
|
try {
|
|
113
113
|
const { key } = await apiKeyService.create({
|
|
114
|
-
description: 'Created by berget code
|
|
114
|
+
description: 'Created by berget code init',
|
|
115
115
|
name: `${tool === 'opencode' ? 'OpenCode' : 'Pi'} (created by berget CLI)`,
|
|
116
116
|
});
|
|
117
117
|
await syncApiKeyToTool(files, homeDir, tool, key);
|
|
@@ -22,8 +22,12 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
26
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
27
|
+
};
|
|
25
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
-
exports.
|
|
29
|
+
exports.runInitCommand = exports.runInit = void 0;
|
|
30
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
27
31
|
const jsonc_parser_1 = require("jsonc-parser");
|
|
28
32
|
const os = __importStar(require("node:os"));
|
|
29
33
|
const index_js_1 = require("../../agents/index.js");
|
|
@@ -34,13 +38,14 @@ const fs_file_store_js_1 = require("./adapters/fs-file-store.js");
|
|
|
34
38
|
const spawn_command_runner_js_1 = require("./adapters/spawn-command-runner.js");
|
|
35
39
|
const auth_sync_js_1 = require("./auth-sync.js");
|
|
36
40
|
const errors_1 = require("./errors");
|
|
37
|
-
const OPENCODE_PLUGIN = '@bergetai/opencode-auth';
|
|
41
|
+
const OPENCODE_PLUGIN = '@bergetai/opencode-auth@1.0.21';
|
|
38
42
|
const PI_PROVIDER = 'npm:@bergetai/pi-provider';
|
|
39
43
|
const OPENCODE_PLUGIN_NAME = '@bergetai/opencode-auth';
|
|
40
44
|
const PI_PROVIDER_NAME = '@bergetai/pi-provider';
|
|
41
|
-
async function
|
|
45
|
+
async function runInit(deps) {
|
|
42
46
|
const { apiKeyService, authService, commands, cwd, files, homeDir, prompter } = deps;
|
|
43
|
-
prompter.intro('
|
|
47
|
+
prompter.intro(`${chalk_1.default.bgGreen.black(' berget code ')}`);
|
|
48
|
+
prompter.note(`Ask questions and report bugs on our GitHub repository:\n\n${chalk_1.default.cyan.underline('https://github.com/berget-ai/cli')}`, 'Need help?');
|
|
44
49
|
const ocState = await getOpencodeState(files, homeDir, cwd);
|
|
45
50
|
const piState = await getPiState(files, homeDir, cwd);
|
|
46
51
|
const tool = await prompter.select({
|
|
@@ -106,13 +111,13 @@ async function runSetup(deps) {
|
|
|
106
111
|
prompter.note(`Next steps:\n\n1. Restart Pi or run /reload\n2. Type: /login\n3. Choose your auth method:\n • "Use a subscription" → Berget AI\n • (or set BERGET_API_KEY env var)\n4. Select model: /model\n\nFor more information, see official docs:\n\nhttps://github.com/berget-ai/pi-provider`, 'Successfully configured Berget AI for Pi');
|
|
107
112
|
}
|
|
108
113
|
}
|
|
109
|
-
prompter.outro('
|
|
114
|
+
prompter.outro('Initialization complete!');
|
|
110
115
|
}
|
|
111
|
-
exports.
|
|
116
|
+
exports.runInit = runInit;
|
|
112
117
|
// ─── OpenCode ────────────────────────────────────────────────────────────────
|
|
113
|
-
async function
|
|
118
|
+
async function runInitCommand() {
|
|
114
119
|
try {
|
|
115
|
-
await
|
|
120
|
+
await runInit({
|
|
116
121
|
apiKeyService: api_key_service_js_1.ApiKeyService.getInstance(),
|
|
117
122
|
authService: auth_service_js_1.AuthService.getInstance(),
|
|
118
123
|
commands: new spawn_command_runner_js_1.SpawnCommandRunner(),
|
|
@@ -138,25 +143,8 @@ async function runSetupCommand() {
|
|
|
138
143
|
throw error;
|
|
139
144
|
}
|
|
140
145
|
}
|
|
141
|
-
exports.
|
|
142
|
-
// ───
|
|
143
|
-
function generateDiff(oldText, newText, filePath) {
|
|
144
|
-
const oldLines = oldText.split('\n');
|
|
145
|
-
const newLines = newText.split('\n');
|
|
146
|
-
let result = `--- ${filePath}\n+++ ${filePath}\n`;
|
|
147
|
-
const maxLength = Math.max(oldLines.length, newLines.length);
|
|
148
|
-
for (let index = 0; index < maxLength; index++) {
|
|
149
|
-
const oldLine = oldLines[index];
|
|
150
|
-
const newLine = newLines[index];
|
|
151
|
-
if (oldLine !== newLine) {
|
|
152
|
-
if (oldLine !== undefined)
|
|
153
|
-
result += `- ${oldLine}\n`;
|
|
154
|
-
if (newLine !== undefined)
|
|
155
|
-
result += `+ ${newLine}\n`;
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
return result.trimEnd();
|
|
159
|
-
}
|
|
146
|
+
exports.runInitCommand = runInitCommand;
|
|
147
|
+
// ─── OpenCode Config Helpers ──────────────────────────────────────────────────
|
|
160
148
|
function generateModifiedContent(existingContent, configPath) {
|
|
161
149
|
if (configPath.endsWith('.jsonc')) {
|
|
162
150
|
const content = existingContent || '{}';
|
|
@@ -318,10 +306,10 @@ async function setupOpenCode(deps) {
|
|
|
318
306
|
return;
|
|
319
307
|
}
|
|
320
308
|
if (existingContent) {
|
|
321
|
-
prompter.note(
|
|
309
|
+
prompter.note(`OpenCode config will be updated at:\n ${configPath}`, 'Config update');
|
|
322
310
|
}
|
|
323
311
|
else {
|
|
324
|
-
prompter.note(`
|
|
312
|
+
prompter.note(`OpenCode config will be created at:\n ${configPath}`, 'Config update');
|
|
325
313
|
}
|
|
326
314
|
const shouldWrite = await prompter.confirm({
|
|
327
315
|
initialValue: true,
|
|
@@ -338,41 +326,43 @@ async function setupOpenCodeAgents(deps) {
|
|
|
338
326
|
const { cwd, files, homeDir, prompter, scope } = deps;
|
|
339
327
|
const agents = (0, index_js_1.getAllAgents)().filter((a) => a.config.mode === 'primary');
|
|
340
328
|
if (agents.length === 0) {
|
|
341
|
-
return;
|
|
329
|
+
return false;
|
|
342
330
|
}
|
|
343
|
-
const
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
331
|
+
const agentsDir = scope === 'project'
|
|
332
|
+
? pathJoin(cwd, '.opencode', 'agents')
|
|
333
|
+
: pathJoin(homeDir, '.config', 'opencode', 'agents');
|
|
334
|
+
prompter.note('Space to toggle, Enter to confirm.', 'Agent Setup');
|
|
335
|
+
const agentOptions = await Promise.all(agents.map(async (agent) => {
|
|
336
|
+
const agentPath = pathJoin(agentsDir, `${agent.config.name}.md`);
|
|
337
|
+
const exists = await files.exists(agentPath);
|
|
338
|
+
return {
|
|
339
|
+
hint: exists ? 'already configured' : agent.config.description,
|
|
347
340
|
label: agent.config.name,
|
|
348
341
|
value: agent.config.name,
|
|
349
|
-
}
|
|
342
|
+
};
|
|
343
|
+
}));
|
|
344
|
+
const selectedAgents = await prompter.multiselect({
|
|
345
|
+
message: 'Select agents to set up:',
|
|
346
|
+
options: agentOptions,
|
|
347
|
+
required: false,
|
|
350
348
|
});
|
|
351
349
|
if (selectedAgents.length === 0) {
|
|
352
|
-
return;
|
|
350
|
+
return false;
|
|
353
351
|
}
|
|
354
|
-
const
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
await files.mkdir(agentsDir);
|
|
358
|
-
const hasChanges = await Promise.all(selectedAgents.map(async (agentName) => {
|
|
359
|
-
const agent = agents.find((a) => a.config.name === agentName);
|
|
360
|
-
if (!agent)
|
|
361
|
-
return false;
|
|
352
|
+
const newAgents = [];
|
|
353
|
+
const existingAgents = [];
|
|
354
|
+
await Promise.all(selectedAgents.map(async (agentName) => {
|
|
362
355
|
const agentPath = pathJoin(agentsDir, `${agentName}.md`);
|
|
363
|
-
const
|
|
364
|
-
|
|
365
|
-
if (existing === newContent) {
|
|
366
|
-
return false;
|
|
367
|
-
}
|
|
368
|
-
if (existing) {
|
|
369
|
-
prompter.note(generateDiff(existing, newContent, agentPath), `Changes to ${agentName} agent`);
|
|
370
|
-
}
|
|
371
|
-
return true;
|
|
356
|
+
const exists = await files.exists(agentPath);
|
|
357
|
+
(exists ? existingAgents : newAgents).push(agentName);
|
|
372
358
|
}));
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
359
|
+
const summaryParts = [];
|
|
360
|
+
if (newAgents.length > 0)
|
|
361
|
+
summaryParts.push(`New: ${newAgents.join(', ')}`);
|
|
362
|
+
if (existingAgents.length > 0)
|
|
363
|
+
summaryParts.push(`Replaced: ${existingAgents.join(', ')}`);
|
|
364
|
+
if (summaryParts.length > 0) {
|
|
365
|
+
prompter.note(` Agent Setup Summary:\n${summaryParts.map((part) => ` ${part}`).join('\n')}`, 'Agent Setup');
|
|
376
366
|
}
|
|
377
367
|
const shouldWrite = await prompter.confirm({
|
|
378
368
|
initialValue: true,
|
|
@@ -381,6 +371,7 @@ async function setupOpenCodeAgents(deps) {
|
|
|
381
371
|
if (!shouldWrite) {
|
|
382
372
|
throw new errors_1.CancelledError();
|
|
383
373
|
}
|
|
374
|
+
await files.mkdir(agentsDir);
|
|
384
375
|
const s = prompter.spinner();
|
|
385
376
|
s.start('Writing agent configurations...');
|
|
386
377
|
for (const agentName of selectedAgents) {
|
|
@@ -392,6 +383,7 @@ async function setupOpenCodeAgents(deps) {
|
|
|
392
383
|
await files.writeFile(agentPath, content);
|
|
393
384
|
}
|
|
394
385
|
s.stop(`Wrote ${selectedAgents.length} agent(s) to ${agentsDir}`);
|
|
386
|
+
return true;
|
|
395
387
|
}
|
|
396
388
|
async function setupPi(deps) {
|
|
397
389
|
const { commands, cwd, files, homeDir, prompter, scope } = deps;
|
|
@@ -440,43 +432,36 @@ async function setupPiAgent(deps) {
|
|
|
440
432
|
const { cwd, files, homeDir, prompter, scope } = deps;
|
|
441
433
|
const agents = (0, index_js_1.getAllAgents)().filter((a) => a.config.mode === 'primary');
|
|
442
434
|
if (agents.length === 0) {
|
|
443
|
-
return;
|
|
435
|
+
return false;
|
|
444
436
|
}
|
|
437
|
+
const systemPath = scope === 'project'
|
|
438
|
+
? pathJoin(cwd, '.pi', 'SYSTEM.md')
|
|
439
|
+
: pathJoin(homeDir, '.pi', 'agent', 'SYSTEM.md');
|
|
440
|
+
prompter.note('Pi uses a single system prompt.', 'Agent Setup');
|
|
441
|
+
const setupAgent = await prompter.confirm({
|
|
442
|
+
initialValue: false,
|
|
443
|
+
message: 'Set up an agent for Pi?',
|
|
444
|
+
});
|
|
445
|
+
if (!setupAgent)
|
|
446
|
+
return false;
|
|
445
447
|
const selectedAgentName = await prompter.select({
|
|
446
|
-
message: '
|
|
447
|
-
options:
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
value: agent.config.name,
|
|
453
|
-
})),
|
|
454
|
-
],
|
|
448
|
+
message: 'Choose an agent:',
|
|
449
|
+
options: agents.map((agent) => ({
|
|
450
|
+
hint: agent.config.description,
|
|
451
|
+
label: agent.config.name,
|
|
452
|
+
value: agent.config.name,
|
|
453
|
+
})),
|
|
455
454
|
});
|
|
456
|
-
if (selectedAgentName === '__skip__') {
|
|
457
|
-
return;
|
|
458
|
-
}
|
|
459
455
|
const agent = agents.find((a) => a.config.name === selectedAgentName);
|
|
460
456
|
if (!agent)
|
|
461
|
-
return;
|
|
462
|
-
const
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
const newContent = (0, index_js_1.toPiPrompt)(agent);
|
|
467
|
-
if (existing === newContent) {
|
|
468
|
-
prompter.note('Agent configuration is already up to date.', 'No changes needed');
|
|
469
|
-
return;
|
|
470
|
-
}
|
|
471
|
-
if (existing) {
|
|
472
|
-
prompter.note(generateDiff(existing, newContent, systemPath), 'Changes to agent configuration');
|
|
473
|
-
}
|
|
474
|
-
else {
|
|
475
|
-
prompter.note(newContent, 'New agent configuration');
|
|
476
|
-
}
|
|
457
|
+
return false;
|
|
458
|
+
const systemExists = await files.exists(systemPath);
|
|
459
|
+
const confirmMsg = systemExists
|
|
460
|
+
? `SYSTEM.md already exists. Replace with ${agent.config.name}?`
|
|
461
|
+
: 'Create agent configuration?';
|
|
477
462
|
const shouldWrite = await prompter.confirm({
|
|
478
463
|
initialValue: true,
|
|
479
|
-
message:
|
|
464
|
+
message: confirmMsg,
|
|
480
465
|
});
|
|
481
466
|
if (!shouldWrite) {
|
|
482
467
|
throw new errors_1.CancelledError();
|
|
@@ -485,8 +470,9 @@ async function setupPiAgent(deps) {
|
|
|
485
470
|
s.start('Writing agent configuration...');
|
|
486
471
|
const systemDir = scope === 'project' ? pathJoin(cwd, '.pi') : pathJoin(homeDir, '.pi', 'agent');
|
|
487
472
|
await files.mkdir(systemDir);
|
|
488
|
-
await files.writeFile(systemPath,
|
|
473
|
+
await files.writeFile(systemPath, (0, index_js_1.toPiPrompt)(agent));
|
|
489
474
|
s.stop(`Wrote agent configuration to ${systemPath}`);
|
|
475
|
+
return true;
|
|
490
476
|
}
|
|
491
477
|
function stripJsoncComments(content) {
|
|
492
478
|
content = content.replaceAll(/\/\/.*$/gm, '');
|