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.
- package/dist/package.json +1 -1
- package/dist/src/commands/code/__tests__/setup-flow.test.js +34 -34
- package/dist/src/commands/code/auth-sync.js +3 -3
- package/dist/src/commands/code/{setup.js → init.js} +17 -29
- 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 +36 -36
- package/src/commands/code/auth-sync.ts +3 -3
- package/src/commands/code/{setup.ts → init.ts} +14 -26
- 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
|
@@ -1,507 +1,29 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import
|
|
3
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
4
|
-
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { beforeEach, describe, expect, it } from 'vitest';
|
|
5
3
|
|
|
6
4
|
import { registerCodeCommands } from '../../src/commands/code';
|
|
7
|
-
import { ApiKeyService } from '../../src/services/api-key-service';
|
|
8
|
-
import { updateEnvFile as updateEnvironmentFile } from '../../src/utils/env-manager';
|
|
9
|
-
|
|
10
|
-
// Mock dependencies
|
|
11
|
-
vi.mock('../../src/services/api-key-service');
|
|
12
|
-
vi.mock('fs', () => ({
|
|
13
|
-
default: {
|
|
14
|
-
existsSync: vi.fn(),
|
|
15
|
-
readFileSync: vi.fn(),
|
|
16
|
-
},
|
|
17
|
-
}));
|
|
18
|
-
vi.mock('fs/promises', () => ({
|
|
19
|
-
readFile: vi.fn(),
|
|
20
|
-
writeFile: vi.fn(),
|
|
21
|
-
}));
|
|
22
|
-
vi.mock('../../src/utils/env-manager');
|
|
23
|
-
vi.mock('child_process', () => ({
|
|
24
|
-
spawn: vi.fn(),
|
|
25
|
-
}));
|
|
26
|
-
vi.mock('readline', () => ({
|
|
27
|
-
createInterface: vi.fn(() => ({
|
|
28
|
-
close: vi.fn(),
|
|
29
|
-
question: vi.fn(),
|
|
30
|
-
})),
|
|
31
|
-
}));
|
|
32
5
|
|
|
33
6
|
describe('Code Commands', () => {
|
|
34
7
|
let program: Command;
|
|
35
|
-
let mockApiKeyService: any;
|
|
36
|
-
let mockFs: any;
|
|
37
|
-
let mockFsPromises: any;
|
|
38
|
-
let mockSpawn: any;
|
|
39
8
|
|
|
40
9
|
beforeEach(() => {
|
|
41
10
|
program = new Command();
|
|
42
|
-
|
|
43
|
-
// Mock ApiKeyService
|
|
44
|
-
mockApiKeyService = {
|
|
45
|
-
create: vi.fn(),
|
|
46
|
-
list: vi.fn(),
|
|
47
|
-
rotate: vi.fn(),
|
|
48
|
-
};
|
|
49
|
-
vi.mocked(ApiKeyService.getInstance).mockReturnValue(mockApiKeyService);
|
|
50
|
-
|
|
51
|
-
// Mock fs
|
|
52
|
-
mockFs = vi.mocked(fs);
|
|
53
|
-
mockFs.existsSync = vi.fn();
|
|
54
|
-
mockFs.readFileSync = vi.fn();
|
|
55
|
-
|
|
56
|
-
// Mock fs/promises
|
|
57
|
-
mockFsPromises = vi.mocked({ readFile, writeFile });
|
|
58
|
-
mockFsPromises.readFile = vi.fn();
|
|
59
|
-
mockFsPromises.writeFile = vi.fn();
|
|
60
|
-
|
|
61
|
-
// Mock spawn
|
|
62
|
-
mockSpawn = vi.fn();
|
|
63
|
-
vi.doMock('child_process', () => ({ spawn: mockSpawn }));
|
|
64
|
-
|
|
65
11
|
registerCodeCommands(program);
|
|
66
12
|
});
|
|
67
13
|
|
|
68
|
-
afterEach(() => {
|
|
69
|
-
vi.clearAllMocks();
|
|
70
|
-
});
|
|
71
|
-
|
|
72
14
|
describe('code init command', () => {
|
|
73
15
|
it('should register init command with correct description', () => {
|
|
74
16
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
75
17
|
const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
|
|
76
18
|
|
|
77
19
|
expect(initCommand).toBeDefined();
|
|
78
|
-
expect(initCommand?.description()).toBe('
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should have name, force, and yes options', () => {
|
|
82
|
-
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
83
|
-
const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
|
|
84
|
-
|
|
85
|
-
expect(initCommand).toBeDefined();
|
|
86
|
-
|
|
87
|
-
const nameOption = initCommand?.options.find((opt) => opt.long === '--name');
|
|
88
|
-
const forceOption = initCommand?.options.find((opt) => opt.long === '--force');
|
|
89
|
-
const yesOption = initCommand?.options.find((opt) => opt.long === '--yes');
|
|
90
|
-
|
|
91
|
-
expect(nameOption).toBeDefined();
|
|
92
|
-
expect(nameOption?.description).toContain('Project name');
|
|
93
|
-
expect(forceOption).toBeDefined();
|
|
94
|
-
expect(forceOption?.description).toContain('Overwrite existing configuration');
|
|
95
|
-
expect(yesOption).toBeDefined();
|
|
96
|
-
expect(yesOption?.description).toContain('Automatically answer yes');
|
|
97
|
-
});
|
|
98
|
-
|
|
99
|
-
it('should check if opencode is installed', () => {
|
|
100
|
-
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
101
|
-
const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
|
|
102
|
-
|
|
103
|
-
expect(initCommand).toBeDefined();
|
|
104
|
-
|
|
105
|
-
// The command should attempt to spawn opencode --version
|
|
106
|
-
// This is tested implicitly through the spawn mock
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
it('should list existing API keys and allow selection', async () => {
|
|
110
|
-
// Mock successful opencode installation check
|
|
111
|
-
mockSpawn.mockImplementation((command: string, arguments_: string[]) => {
|
|
112
|
-
if (command === 'opencode' && arguments_[0] === '--version') {
|
|
113
|
-
return {
|
|
114
|
-
on: vi.fn().mockImplementation((event, callback) => {
|
|
115
|
-
if (event === 'close') callback(0);
|
|
116
|
-
}),
|
|
117
|
-
};
|
|
118
|
-
}
|
|
119
|
-
return { on: vi.fn() };
|
|
120
|
-
});
|
|
121
|
-
|
|
122
|
-
// Mock existing API keys
|
|
123
|
-
const mockExistingKeys = [
|
|
124
|
-
{
|
|
125
|
-
created: '2023-01-01T00:00:00.000Z',
|
|
126
|
-
id: 1,
|
|
127
|
-
lastUsed: null,
|
|
128
|
-
name: 'existing-key-1',
|
|
129
|
-
prefix: 'sk_ber',
|
|
130
|
-
},
|
|
131
|
-
{
|
|
132
|
-
created: '2023-01-02T00:00:00.000Z',
|
|
133
|
-
id: 2,
|
|
134
|
-
lastUsed: '2023-01-03T00:00:00.000Z',
|
|
135
|
-
name: 'existing-key-2',
|
|
136
|
-
prefix: 'sk_ber',
|
|
137
|
-
},
|
|
138
|
-
];
|
|
139
|
-
mockApiKeyService.list.mockResolvedValue(mockExistingKeys);
|
|
140
|
-
|
|
141
|
-
// Mock file operations
|
|
142
|
-
mockFs.existsSync.mockReturnValue(false);
|
|
143
|
-
mockFsPromises.writeFile.mockResolvedValue();
|
|
144
|
-
|
|
145
|
-
// Verify that the list method is called
|
|
146
|
-
expect(mockApiKeyService.list).toBeDefined();
|
|
147
|
-
});
|
|
148
|
-
|
|
149
|
-
it('should create new API key with project-based naming', async () => {
|
|
150
|
-
// Mock successful opencode installation check
|
|
151
|
-
mockSpawn.mockImplementation((command: string, arguments_: string[]) => {
|
|
152
|
-
if (command === 'opencode' && arguments_[0] === '--version') {
|
|
153
|
-
return {
|
|
154
|
-
on: vi.fn().mockImplementation((event, callback) => {
|
|
155
|
-
if (event === 'close') callback(0);
|
|
156
|
-
}),
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
return { on: vi.fn() };
|
|
160
|
-
});
|
|
161
|
-
|
|
162
|
-
// Mock no existing keys
|
|
163
|
-
mockApiKeyService.list.mockResolvedValue([]);
|
|
164
|
-
|
|
165
|
-
// Mock successful API key creation
|
|
166
|
-
const mockApiKeyData = {
|
|
167
|
-
id: 123,
|
|
168
|
-
key: 'test-api-key-12345',
|
|
169
|
-
name: 'opencode-testproject-1234567890',
|
|
170
|
-
};
|
|
171
|
-
mockApiKeyService.create.mockResolvedValue(mockApiKeyData);
|
|
172
|
-
|
|
173
|
-
// Mock file operations
|
|
174
|
-
mockFs.existsSync.mockReturnValue(false);
|
|
175
|
-
mockFsPromises.writeFile.mockResolvedValue();
|
|
176
|
-
|
|
177
|
-
// Verify that the create method is available
|
|
178
|
-
expect(mockApiKeyService.create).toBeDefined();
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
it('should create opencode.json with correct structure', async () => {
|
|
182
|
-
// This tests the expected config structure
|
|
183
|
-
const expectedConfig = {
|
|
184
|
-
apiKey: 'test-api-key',
|
|
185
|
-
created: expect.any(String),
|
|
186
|
-
model: 'berget/glm-4-6',
|
|
187
|
-
projectName: 'testproject',
|
|
188
|
-
provider: 'berget',
|
|
189
|
-
version: '1.0.0',
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
expect(expectedConfig.model).toBe('berget/glm-4-6');
|
|
193
|
-
expect(expectedConfig.provider).toBe('berget');
|
|
194
|
-
expect(expectedConfig.version).toBe('1.0.0');
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it('should handle existing config file', () => {
|
|
198
|
-
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
199
|
-
const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
|
|
200
|
-
|
|
201
|
-
expect(initCommand).toBeDefined();
|
|
202
|
-
|
|
203
|
-
// Should check if opencode.json exists before proceeding
|
|
204
|
-
expect(mockFs.existsSync).toBeDefined();
|
|
205
|
-
});
|
|
206
|
-
});
|
|
207
|
-
|
|
208
|
-
describe('code run command', () => {
|
|
209
|
-
it('should register run command with correct description', () => {
|
|
210
|
-
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
211
|
-
const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
|
|
212
|
-
|
|
213
|
-
expect(runCommand).toBeDefined();
|
|
214
|
-
expect(runCommand?.description()).toBe('Run AI coding assistant');
|
|
215
|
-
});
|
|
216
|
-
|
|
217
|
-
it('should accept prompt argument and model, no-config, and yes options', () => {
|
|
218
|
-
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
219
|
-
const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
|
|
220
|
-
|
|
221
|
-
expect(runCommand).toBeDefined();
|
|
222
|
-
|
|
223
|
-
const modelOption = runCommand?.options.find((opt) => opt.long === '--model');
|
|
224
|
-
const noConfigOption = runCommand?.options.find((opt) => opt.long === '--no-config');
|
|
225
|
-
const yesOption = runCommand?.options.find((opt) => opt.long === '--yes');
|
|
226
|
-
|
|
227
|
-
expect(modelOption).toBeDefined();
|
|
228
|
-
expect(modelOption?.description).toContain('Model to use');
|
|
229
|
-
expect(noConfigOption).toBeDefined();
|
|
230
|
-
expect(noConfigOption?.description).toContain('Run without loading project config');
|
|
231
|
-
expect(yesOption).toBeDefined();
|
|
232
|
-
expect(yesOption?.description).toContain('Automatically answer yes');
|
|
233
|
-
});
|
|
234
|
-
|
|
235
|
-
it('should load configuration from opencode.json', async () => {
|
|
236
|
-
const mockConfig = {
|
|
237
|
-
apiKey: 'test-api-key',
|
|
238
|
-
created: '2023-01-01T00:00:00.000Z',
|
|
239
|
-
model: 'berget/glm-4-6',
|
|
240
|
-
projectName: 'testproject',
|
|
241
|
-
provider: 'berget',
|
|
242
|
-
version: '1.0.0',
|
|
243
|
-
};
|
|
244
|
-
|
|
245
|
-
// Mock file exists and contains config
|
|
246
|
-
mockFs.existsSync.mockReturnValue(true);
|
|
247
|
-
mockFsPromises.readFile.mockResolvedValue(JSON.stringify(mockConfig));
|
|
248
|
-
|
|
249
|
-
// Mock successful opencode check
|
|
250
|
-
mockSpawn.mockImplementation((command: string, arguments_: string[]) => {
|
|
251
|
-
if (command === 'opencode' && arguments_[0] === '--version') {
|
|
252
|
-
return {
|
|
253
|
-
on: vi.fn().mockImplementation((event, callback) => {
|
|
254
|
-
if (event === 'close') callback(0);
|
|
255
|
-
}),
|
|
256
|
-
};
|
|
257
|
-
}
|
|
258
|
-
return { on: vi.fn() };
|
|
259
|
-
});
|
|
260
|
-
|
|
261
|
-
// Verify config structure expectations
|
|
262
|
-
expect(mockConfig.model).toBe('berget/glm-4-6');
|
|
263
|
-
expect(mockConfig.apiKey).toBe('test-api-key');
|
|
264
|
-
expect(mockConfig.projectName).toBe('testproject');
|
|
265
|
-
});
|
|
266
|
-
|
|
267
|
-
it('should spawn opencode with correct arguments', () => {
|
|
268
|
-
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
269
|
-
const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
|
|
270
|
-
|
|
271
|
-
expect(runCommand).toBeDefined();
|
|
272
|
-
|
|
273
|
-
// Should spawn opencode with appropriate arguments
|
|
274
|
-
expect(mockSpawn).toBeDefined();
|
|
20
|
+
expect(initCommand?.description()).toBe('Interactive setup for Berget AI coding tools');
|
|
275
21
|
});
|
|
276
22
|
|
|
277
|
-
it('should
|
|
23
|
+
it('should not have any other subcommands', () => {
|
|
278
24
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
expect(runCommand).toBeDefined();
|
|
282
|
-
|
|
283
|
-
// Should check if opencode.json exists
|
|
284
|
-
expect(mockFs.existsSync).toBeDefined();
|
|
285
|
-
});
|
|
286
|
-
});
|
|
287
|
-
|
|
288
|
-
describe('opencode installation', () => {
|
|
289
|
-
it('should check if opencode is installed', () => {
|
|
290
|
-
// The spawn function should be called with opencode --version
|
|
291
|
-
expect(mockSpawn).toBeDefined();
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
it('should offer to install opencode if not found', () => {
|
|
295
|
-
// Mock opencode not installed
|
|
296
|
-
mockSpawn.mockImplementation((command: string, arguments_: string[]) => {
|
|
297
|
-
if (command === 'opencode' && arguments_[0] === '--version') {
|
|
298
|
-
return {
|
|
299
|
-
on: vi.fn().mockImplementation((event, callback) => {
|
|
300
|
-
if (event === 'close') callback(1); // Non-zero exit code
|
|
301
|
-
}),
|
|
302
|
-
};
|
|
303
|
-
}
|
|
304
|
-
return { on: vi.fn() };
|
|
305
|
-
});
|
|
306
|
-
|
|
307
|
-
// Should handle the case where opencode is not installed
|
|
308
|
-
expect(mockSpawn).toBeDefined();
|
|
309
|
-
});
|
|
310
|
-
|
|
311
|
-
it('should install opencode via npm if user agrees', () => {
|
|
312
|
-
// Should spawn npm install -g opencode-ai
|
|
313
|
-
expect(mockSpawn).toBeDefined();
|
|
314
|
-
});
|
|
315
|
-
});
|
|
316
|
-
|
|
317
|
-
describe('automation support', () => {
|
|
318
|
-
it('should support -y flag for automated initialization', () => {
|
|
319
|
-
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
320
|
-
const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
|
|
321
|
-
|
|
322
|
-
expect(initCommand).toBeDefined();
|
|
323
|
-
|
|
324
|
-
const yesOption = initCommand?.options.find((opt) => opt.long === '--yes');
|
|
325
|
-
expect(yesOption).toBeDefined();
|
|
326
|
-
expect(yesOption?.description).toContain('automation');
|
|
327
|
-
});
|
|
328
|
-
|
|
329
|
-
it('should support -y flag for automated run', () => {
|
|
330
|
-
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
331
|
-
const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
|
|
332
|
-
|
|
333
|
-
expect(runCommand).toBeDefined();
|
|
334
|
-
|
|
335
|
-
const yesOption = runCommand?.options.find((opt) => opt.long === '--yes');
|
|
336
|
-
expect(yesOption).toBeDefined();
|
|
337
|
-
expect(yesOption?.description).toContain('automation');
|
|
338
|
-
});
|
|
339
|
-
|
|
340
|
-
it('should use BERGET_API_KEY environment variable in automation mode', () => {
|
|
341
|
-
// Test that environment variable is used when -y flag is set
|
|
342
|
-
process.env.BERGET_API_KEY = 'test-env-key';
|
|
343
|
-
|
|
344
|
-
expect(process.env.BERGET_API_KEY).toBe('test-env-key');
|
|
345
|
-
|
|
346
|
-
// Clean up
|
|
347
|
-
delete process.env.BERGET_API_KEY;
|
|
348
|
-
});
|
|
349
|
-
});
|
|
350
|
-
|
|
351
|
-
describe('.env file handling', () => {
|
|
352
|
-
let mockUpdateEnvironmentFile: any;
|
|
353
|
-
|
|
354
|
-
beforeEach(() => {
|
|
355
|
-
mockUpdateEnvironmentFile = vi.mocked(updateEnvironmentFile);
|
|
356
|
-
});
|
|
357
|
-
|
|
358
|
-
it('should call updateEnvFile when creating new project', async () => {
|
|
359
|
-
mockUpdateEnvironmentFile.mockResolvedValue(true);
|
|
360
|
-
mockFs.existsSync.mockReturnValue(false); // .env doesn't exist
|
|
361
|
-
mockFsPromises.writeFile.mockResolvedValue();
|
|
362
|
-
|
|
363
|
-
// This would be tested by actually calling the init command
|
|
364
|
-
// For now we verify the mock is properly set up
|
|
365
|
-
expect(mockUpdateEnvironmentFile).toBeDefined();
|
|
366
|
-
});
|
|
367
|
-
|
|
368
|
-
it('should not overwrite existing BERGET_API_KEY in .env', async () => {
|
|
369
|
-
const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
370
|
-
|
|
371
|
-
// Mock existing .env with BERGET_API_KEY
|
|
372
|
-
mockFs.existsSync.mockReturnValue(true);
|
|
373
|
-
mockFs.readFileSync.mockReturnValue('BERGET_API_KEY=existing_key\nOTHER_KEY=value\n');
|
|
374
|
-
|
|
375
|
-
// Mock updateEnvFile to simulate the check
|
|
376
|
-
mockUpdateEnvironmentFile.mockImplementation(async (options: any) => {
|
|
377
|
-
if (options.key === 'BERGET_API_KEY' && !options.force) {
|
|
378
|
-
console.log(`⚠ ${options.key} already exists in .env - leaving unchanged`);
|
|
379
|
-
return false;
|
|
380
|
-
}
|
|
381
|
-
return true;
|
|
382
|
-
});
|
|
383
|
-
|
|
384
|
-
await updateEnvironmentFile({
|
|
385
|
-
key: 'BERGET_API_KEY',
|
|
386
|
-
value: 'new_key',
|
|
387
|
-
});
|
|
388
|
-
|
|
389
|
-
expect(consoleSpy).toHaveBeenCalledWith(
|
|
390
|
-
expect.stringContaining('BERGET_API_KEY already exists in .env - leaving unchanged'),
|
|
391
|
-
);
|
|
392
|
-
|
|
393
|
-
consoleSpy.mockRestore();
|
|
394
|
-
});
|
|
395
|
-
|
|
396
|
-
it('should add new key to existing .env file', async () => {
|
|
397
|
-
mockFs.existsSync.mockReturnValue(true);
|
|
398
|
-
mockFs.readFileSync.mockReturnValue('EXISTING_KEY=value\n');
|
|
399
|
-
mockUpdateEnvironmentFile.mockResolvedValue(true);
|
|
400
|
-
|
|
401
|
-
await updateEnvironmentFile({
|
|
402
|
-
comment: 'Berget AI Configuration',
|
|
403
|
-
key: 'BERGET_API_KEY',
|
|
404
|
-
value: 'new_api_key',
|
|
405
|
-
});
|
|
406
|
-
|
|
407
|
-
expect(mockUpdateEnvironmentFile).toHaveBeenCalledWith({
|
|
408
|
-
comment: 'Berget AI Configuration',
|
|
409
|
-
key: 'BERGET_API_KEY',
|
|
410
|
-
value: 'new_api_key',
|
|
411
|
-
});
|
|
412
|
-
});
|
|
413
|
-
|
|
414
|
-
it('should create new .env file when none exists', async () => {
|
|
415
|
-
mockFs.existsSync.mockReturnValue(false);
|
|
416
|
-
mockUpdateEnvironmentFile.mockResolvedValue(true);
|
|
417
|
-
|
|
418
|
-
await updateEnvironmentFile({
|
|
419
|
-
key: 'BERGET_API_KEY',
|
|
420
|
-
value: 'new_api_key',
|
|
421
|
-
});
|
|
422
|
-
|
|
423
|
-
expect(mockUpdateEnvironmentFile).toHaveBeenCalledWith({
|
|
424
|
-
key: 'BERGET_API_KEY',
|
|
425
|
-
value: 'new_api_key',
|
|
426
|
-
});
|
|
427
|
-
});
|
|
428
|
-
});
|
|
429
|
-
|
|
430
|
-
describe('error handling', () => {
|
|
431
|
-
it('should handle API key creation failures', () => {
|
|
432
|
-
// Mock API key service to throw error
|
|
433
|
-
mockApiKeyService.create.mockRejectedValue(new Error('API Error'));
|
|
434
|
-
|
|
435
|
-
expect(mockApiKeyService.create).toBeDefined();
|
|
436
|
-
});
|
|
437
|
-
|
|
438
|
-
it('should handle file system errors', () => {
|
|
439
|
-
// Mock file operations to throw errors
|
|
440
|
-
mockFsPromises.writeFile.mockRejectedValue(new Error('File write error'));
|
|
441
|
-
|
|
442
|
-
expect(mockFsPromises.writeFile).toBeDefined();
|
|
443
|
-
});
|
|
444
|
-
|
|
445
|
-
it('should handle spawn errors', () => {
|
|
446
|
-
// Mock spawn to throw error
|
|
447
|
-
mockSpawn.mockImplementation(() => {
|
|
448
|
-
throw new Error('Command not found');
|
|
449
|
-
});
|
|
450
|
-
|
|
451
|
-
expect(mockSpawn).toBeDefined();
|
|
452
|
-
});
|
|
453
|
-
|
|
454
|
-
it('should handle .env update failures', async () => {
|
|
455
|
-
const mockUpdateEnvironmentFile = vi.mocked(updateEnvironmentFile);
|
|
456
|
-
mockUpdateEnvironmentFile.mockRejectedValue(new Error('Env update failed'));
|
|
457
|
-
|
|
458
|
-
await expect(
|
|
459
|
-
updateEnvironmentFile({
|
|
460
|
-
key: 'TEST_KEY',
|
|
461
|
-
value: 'test_value',
|
|
462
|
-
}),
|
|
463
|
-
).rejects.toThrow('Env update failed');
|
|
464
|
-
});
|
|
465
|
-
});
|
|
466
|
-
|
|
467
|
-
describe('experimental features', () => {
|
|
468
|
-
let originalEnvironment: string | undefined;
|
|
469
|
-
|
|
470
|
-
beforeEach(() => {
|
|
471
|
-
originalEnvironment = process.env.BERGET_EXPERIMENTAL;
|
|
472
|
-
});
|
|
473
|
-
|
|
474
|
-
afterEach(() => {
|
|
475
|
-
if (originalEnvironment === undefined) {
|
|
476
|
-
delete process.env.BERGET_EXPERIMENTAL;
|
|
477
|
-
} else {
|
|
478
|
-
process.env.BERGET_EXPERIMENTAL = originalEnvironment;
|
|
479
|
-
}
|
|
480
|
-
});
|
|
481
|
-
|
|
482
|
-
it('should NOT show setup command when BERGET_EXPERIMENTAL is not set', () => {
|
|
483
|
-
delete process.env.BERGET_EXPERIMENTAL;
|
|
484
|
-
|
|
485
|
-
const freshProgram = new Command();
|
|
486
|
-
registerCodeCommands(freshProgram);
|
|
487
|
-
|
|
488
|
-
const codeCommand = freshProgram.commands.find((cmd) => cmd.name() === 'code');
|
|
489
|
-
const setupCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'setup');
|
|
490
|
-
|
|
491
|
-
expect(setupCommand).toBeUndefined();
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
it('should show setup command when BERGET_EXPERIMENTAL is set', () => {
|
|
495
|
-
process.env.BERGET_EXPERIMENTAL = '1';
|
|
496
|
-
|
|
497
|
-
const freshProgram = new Command();
|
|
498
|
-
registerCodeCommands(freshProgram);
|
|
499
|
-
|
|
500
|
-
const codeCommand = freshProgram.commands.find((cmd) => cmd.name() === 'code');
|
|
501
|
-
const setupCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'setup');
|
|
502
|
-
|
|
503
|
-
expect(setupCommand).toBeDefined();
|
|
504
|
-
expect(setupCommand?.description()).toBe('Interactive setup for Berget AI coding tools');
|
|
25
|
+
expect(codeCommand?.commands).toHaveLength(1);
|
|
26
|
+
expect(codeCommand?.commands[0]?.name()).toBe('init');
|
|
505
27
|
});
|
|
506
28
|
});
|
|
507
29
|
});
|
package/templates/agents/app.md
DELETED
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Expo + React Native apps; props-first, offline-aware, shared tokens.
|
|
3
|
-
mode: primary
|
|
4
|
-
temperature: 0.4
|
|
5
|
-
top_p: 0.9
|
|
6
|
-
permission:
|
|
7
|
-
edit: allow
|
|
8
|
-
bash: deny
|
|
9
|
-
webfetch: allow
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
You are Berget Code App agent. Voice: Scandinavian calm—precise, concise, confident. Expo + React Native + TypeScript. Structure by components/hooks/services/navigation. Components are pure; data via props; refactor shared logic into hooks/stores. Share tokens with frontend. Mock data in /data via typed hooks; later replace with live APIs. Offline via SQLite/MMKV; notifications via Expo. Request permissions only when needed. Subtle, meaningful motion; light/dark parity.
|
|
13
|
-
|
|
14
|
-
GIT WORKFLOW RULES (CRITICAL):
|
|
15
|
-
|
|
16
|
-
- NEVER push directly to main branch - ALWAYS use pull requests
|
|
17
|
-
- NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
|
|
18
|
-
- ALWAYS clean up test files, documentation files, and temporary artifacts before committing
|
|
19
|
-
- ALWAYS ensure git history maintains production quality - no test commits, no debugging code
|
|
20
|
-
- ALWAYS create descriptive commit messages following project conventions
|
|
21
|
-
- ALWAYS run tests and build before creating PR
|
|
22
|
-
|
|
23
|
-
CRITICAL: When all app implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Functional, modular Koa + TypeScript services; schema-first with code quality focus.
|
|
3
|
-
mode: primary
|
|
4
|
-
temperature: 0.3
|
|
5
|
-
top_p: 0.9
|
|
6
|
-
permission:
|
|
7
|
-
edit: allow
|
|
8
|
-
bash: allow
|
|
9
|
-
webfetch: allow
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
You are Berget Code Backend agent. Voice: Scandinavian calm—precise, concise, confident. TypeScript + Koa. Prefer many small pure functions; avoid big try/catch blocks. Routes thin; logic in services/adapters/domain. Validate with Zod; auto-generate OpenAPI. Adapters isolate external systems; domain never depends on framework. Test with supertest; idempotent and stateless by default. Each microservice emits an OpenAPI contract; changes propagate upward to types. Code Quality & Refactoring Principles: Apply Single Responsibility Principle, fail fast with explicit errors, eliminate code duplication, remove nested complexity, use descriptive error codes, keep functions under 30 lines. Always leave code cleaner and more readable than you found it.
|
|
13
|
-
|
|
14
|
-
GIT WORKFLOW RULES (CRITICAL):
|
|
15
|
-
|
|
16
|
-
- NEVER push directly to main branch - ALWAYS use pull requests
|
|
17
|
-
- NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
|
|
18
|
-
- ALWAYS clean up test files, documentation files, and temporary artifacts before committing
|
|
19
|
-
- ALWAYS ensure git history maintains production quality - no test commits, no debugging code
|
|
20
|
-
- ALWAYS create descriptive commit messages following project conventions
|
|
21
|
-
- ALWAYS run tests and build before creating PR
|
|
22
|
-
|
|
23
|
-
CRITICAL: When all backend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Declarative GitOps infra with FluxCD, Kustomize, Helm, operators.
|
|
3
|
-
mode: primary
|
|
4
|
-
temperature: 0.3
|
|
5
|
-
top_p: 0.8
|
|
6
|
-
permission:
|
|
7
|
-
edit: allow
|
|
8
|
-
bash: allow
|
|
9
|
-
webfetch: allow
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
You are Berget Code DevOps agent. Voice: Scandinavian calm—precise, concise, confident. Start simple: k8s/{deployment,service,ingress}. Add FluxCD sync to repo and image automation. Use Kustomize bases/overlays (staging, production). Add dependencies via Helm from upstream sources; prefer native operators when available (CloudNativePG, cert-manager, external-dns). SemVer with -rc tags keeps CI environments current. Observability with Prometheus/Grafana. No manual kubectl in production—Git is the source of truth.
|
|
13
|
-
|
|
14
|
-
GIT WORKFLOW RULES (CRITICAL):
|
|
15
|
-
|
|
16
|
-
- NEVER push directly to main branch - ALWAYS use pull requests
|
|
17
|
-
- NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
|
|
18
|
-
- ALWAYS clean up test files, documentation files, and temporary artifacts before committing
|
|
19
|
-
- ALWAYS ensure git history maintains production quality - no test commits, no debugging code
|
|
20
|
-
- ALWAYS create descriptive commit messages following project conventions
|
|
21
|
-
- ALWAYS run tests and build before creating PR
|
|
22
|
-
|
|
23
|
-
Helm Values Configuration Process:
|
|
24
|
-
|
|
25
|
-
1. Documentation First Approach: Always fetch official documentation from Artifact Hub/GitHub for the specific chart version before writing values. Search Artifact Hub for exact chart version documentation, check the chart's GitHub repository for official docs and examples, verify the exact version being used in the deployment.
|
|
26
|
-
2. Validation Requirements: Check for available validation schemas before committing YAML files. Use Helm's built-in validation tools (helm lint, helm template). Validate against JSON schema if available for the chart. Ensure YAML syntax correctness with linters.
|
|
27
|
-
3. Standard Workflow: Identify chart name and exact version. Fetch official documentation from Artifact Hub/GitHub. Check for available schemas and validation tools. Write values according to official documentation. Validate against schema (if available). Test with helm template or helm lint. Commit validated YAML files.
|
|
28
|
-
4. Quality Assurance: Never commit unvalidated Helm values. Use helm dependency update when adding new charts. Test rendering with helm template --dry-run before deployment. Document any custom values with comments referencing official docs.
|
|
29
|
-
|
|
30
|
-
CRITICAL: When all devops implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.
|
|
@@ -1,25 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Builds Scandinavian, type-safe UIs with React, Tailwind, Shadcn.
|
|
3
|
-
mode: primary
|
|
4
|
-
temperature: 0.4
|
|
5
|
-
top_p: 0.9
|
|
6
|
-
permission:
|
|
7
|
-
edit: allow
|
|
8
|
-
bash: deny
|
|
9
|
-
webfetch: allow
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
You are Berget Code Frontend agent. Voice: Scandinavian calm—precise, concise, confident. React 18 + TypeScript. Tailwind + Shadcn UI only via the design system (index.css, tailwind.config.ts). Use semantic tokens for color/spacing/typography/motion; never ad-hoc classes or inline colors. Components are pure and responsive; props-first data; minimal global state (Zustand/Jotai). Accessibility and keyboard navigation mandatory. Mock data only at init under /data via typed hooks (e.g., useProducts() reading /data/products.json). Design: minimal, balanced, quiet motion.
|
|
13
|
-
|
|
14
|
-
IMPORTANT: You have NO bash access and cannot run git commands. When your frontend implementation tasks are complete, inform the user that changes are ready and suggest using /pr command to create a pull request with proper testing and quality checks.
|
|
15
|
-
|
|
16
|
-
CODE QUALITY RULES:
|
|
17
|
-
|
|
18
|
-
- Write clean, production-ready code
|
|
19
|
-
- Follow React and TypeScript best practices
|
|
20
|
-
- Ensure accessibility and responsive design
|
|
21
|
-
- Use semantic tokens from design system
|
|
22
|
-
- Test your components manually when possible
|
|
23
|
-
- Document any complex logic with comments
|
|
24
|
-
|
|
25
|
-
CRITICAL: When frontend implementation is complete, ALWAYS inform the user to use "/pr" command to handle testing, building, and pull request creation.
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Router/coordinator agent for full-stack development with schema-driven architecture
|
|
3
|
-
mode: primary
|
|
4
|
-
temperature: 0.3
|
|
5
|
-
top_p: 0.9
|
|
6
|
-
permission:
|
|
7
|
-
edit: allow
|
|
8
|
-
bash: allow
|
|
9
|
-
webfetch: allow
|
|
10
|
-
---
|
|
11
|
-
|
|
12
|
-
Voice: Scandinavian calm—precise, concise, confident; no fluff. You are Berget Code Fullstack agent. Act as a router and coordinator in a monorepo. Bottom-up schema: database → OpenAPI → generated types. Top-down types: API → UI → components. Use openapi-fetch and Zod at every boundary; compile-time errors are desired when contracts change. Routing rules: if task/paths match /apps/frontend or React (.tsx) → use frontend; if /apps/app or Expo/React Native → app; if /infra, /k8s, flux-system, kustomization.yaml, Helm values → devops; if /services, Koa routers, services/adapters/domain → backend. If ambiguous, remain fullstack and outline the end-to-end plan, then delegate subtasks to the right persona. Security: validate inputs; secrets via FluxCD SOPS/Sealed Secrets. Documentation is generated from code—never duplicated.
|
|
13
|
-
|
|
14
|
-
GIT WORKFLOW RULES (CRITICAL):
|
|
15
|
-
|
|
16
|
-
- NEVER push directly to main branch - ALWAYS use pull requests
|
|
17
|
-
- NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'
|
|
18
|
-
- ALWAYS clean up test files, documentation files, and temporary artifacts before committing
|
|
19
|
-
- ALWAYS ensure git history maintains production quality - no test commits, no debugging code
|
|
20
|
-
- ALWAYS create descriptive commit messages following project conventions
|
|
21
|
-
- ALWAYS run tests and build before creating PR
|
|
22
|
-
|
|
23
|
-
CRITICAL: When all implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.
|