berget 2.2.6 → 2.2.8
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/.github/workflows/publish.yml +2 -2
- package/.github/workflows/test.yml +10 -4
- package/.husky/pre-commit +1 -0
- package/.prettierignore +15 -0
- package/.prettierrc +7 -3
- package/CONTRIBUTING.md +38 -0
- package/README.md +2 -148
- package/dist/index.js +10 -11
- package/dist/package.json +30 -2
- package/dist/src/agents/app.js +28 -0
- package/dist/src/agents/backend.js +25 -0
- package/dist/src/agents/devops.js +34 -0
- package/dist/src/agents/frontend.js +25 -0
- package/dist/src/agents/fullstack.js +25 -0
- package/dist/src/agents/index.js +61 -0
- package/dist/src/agents/quality.js +70 -0
- package/dist/src/agents/security.js +26 -0
- package/dist/src/agents/types.js +2 -0
- package/dist/src/client.js +97 -117
- package/dist/src/commands/api-keys.js +75 -90
- package/dist/src/commands/auth.js +7 -16
- package/dist/src/commands/autocomplete.js +1 -1
- package/dist/src/commands/billing.js +6 -17
- package/dist/src/commands/chat.js +68 -101
- package/dist/src/commands/clusters.js +9 -18
- package/dist/src/commands/code/__tests__/auth-sync.test.js +351 -0
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +13 -0
- package/dist/src/commands/code/__tests__/fake-auth-service.js +47 -0
- package/dist/src/commands/code/__tests__/fake-command-runner.js +21 -34
- package/dist/src/commands/code/__tests__/fake-file-store.js +20 -33
- package/dist/src/commands/code/__tests__/fake-prompter.js +83 -57
- package/dist/src/commands/code/__tests__/setup-flow.test.js +359 -92
- package/dist/src/commands/code/adapters/clack-prompter.js +15 -22
- package/dist/src/commands/code/adapters/fs-file-store.js +26 -40
- package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -37
- package/dist/src/commands/code/auth-sync.js +270 -0
- package/dist/src/commands/code/errors.js +12 -9
- package/dist/src/commands/code/ports/auth-services.js +2 -0
- package/dist/src/commands/code/setup.js +387 -281
- package/dist/src/commands/code.js +205 -332
- package/dist/src/commands/index.js +5 -5
- package/dist/src/commands/models.js +6 -17
- package/dist/src/commands/users.js +5 -16
- package/dist/src/constants/command-structure.js +104 -104
- package/dist/src/services/api-key-service.js +132 -157
- package/dist/src/services/auth-service.js +89 -342
- package/dist/src/services/browser-auth.js +268 -0
- package/dist/src/services/chat-service.js +371 -401
- package/dist/src/services/cluster-service.js +47 -62
- package/dist/src/services/collaborator-service.js +10 -25
- package/dist/src/services/flux-service.js +14 -29
- package/dist/src/services/helm-service.js +10 -25
- package/dist/src/services/kubectl-service.js +16 -33
- package/dist/src/utils/config-checker.js +3 -3
- package/dist/src/utils/config-loader.js +95 -95
- package/dist/src/utils/default-api-key.js +124 -134
- package/dist/src/utils/env-manager.js +55 -66
- package/dist/src/utils/error-handler.js +20 -21
- package/dist/src/utils/logger.js +72 -65
- package/dist/src/utils/markdown-renderer.js +27 -27
- package/dist/src/utils/opencode-validator.js +63 -68
- package/dist/src/utils/token-manager.js +74 -45
- package/dist/tests/commands/chat.test.js +16 -25
- package/dist/tests/commands/code.test.js +95 -104
- package/dist/tests/utils/config-loader.test.js +48 -48
- package/dist/tests/utils/env-manager.test.js +43 -52
- package/dist/tests/utils/opencode-validator.test.js +22 -21
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +67 -0
- package/index.ts +35 -42
- package/package.json +30 -2
- package/src/agents/app.ts +27 -0
- package/src/agents/backend.ts +24 -0
- package/src/agents/devops.ts +33 -0
- package/src/agents/frontend.ts +24 -0
- package/src/agents/fullstack.ts +24 -0
- package/src/agents/index.ts +73 -0
- package/src/agents/quality.ts +69 -0
- package/src/agents/security.ts +26 -0
- package/src/agents/types.ts +17 -0
- package/src/client.ts +118 -152
- package/src/commands/api-keys.ts +241 -333
- package/src/commands/auth.ts +22 -27
- package/src/commands/autocomplete.ts +9 -9
- package/src/commands/billing.ts +20 -24
- package/src/commands/chat.ts +248 -338
- package/src/commands/clusters.ts +27 -26
- package/src/commands/code/__tests__/auth-sync.test.ts +482 -0
- package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
- package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
- package/src/commands/code/__tests__/fake-command-runner.ts +45 -42
- package/src/commands/code/__tests__/fake-file-store.ts +32 -23
- package/src/commands/code/__tests__/fake-prompter.ts +116 -77
- package/src/commands/code/__tests__/setup-flow.test.ts +624 -268
- package/src/commands/code/adapters/clack-prompter.ts +53 -39
- package/src/commands/code/adapters/fs-file-store.ts +32 -27
- package/src/commands/code/adapters/spawn-command-runner.ts +38 -29
- package/src/commands/code/auth-sync.ts +329 -0
- package/src/commands/code/errors.ts +18 -18
- package/src/commands/code/ports/auth-services.ts +14 -0
- package/src/commands/code/ports/command-runner.ts +8 -4
- package/src/commands/code/ports/file-store.ts +5 -4
- package/src/commands/code/ports/prompter.ts +24 -18
- package/src/commands/code/setup.ts +570 -340
- package/src/commands/code.ts +338 -539
- package/src/commands/index.ts +20 -19
- package/src/commands/models.ts +28 -32
- package/src/commands/users.ts +15 -21
- package/src/constants/command-structure.ts +134 -157
- package/src/services/api-key-service.ts +105 -122
- package/src/services/auth-service.ts +99 -345
- package/src/services/browser-auth.ts +296 -0
- package/src/services/chat-service.ts +265 -299
- package/src/services/cluster-service.ts +42 -45
- package/src/services/collaborator-service.ts +14 -19
- package/src/services/flux-service.ts +23 -25
- package/src/services/helm-service.ts +19 -21
- package/src/services/kubectl-service.ts +17 -19
- package/src/types/api.d.ts +1905 -1907
- package/src/types/json.d.ts +2 -2
- package/src/utils/config-checker.ts +10 -10
- package/src/utils/config-loader.ts +162 -178
- package/src/utils/default-api-key.ts +114 -125
- package/src/utils/env-manager.ts +53 -57
- package/src/utils/error-handler.ts +61 -56
- package/src/utils/logger.ts +79 -73
- package/src/utils/markdown-renderer.ts +31 -31
- package/src/utils/opencode-validator.ts +85 -89
- package/src/utils/token-manager.ts +108 -87
- package/templates/agents/app.md +1 -0
- package/templates/agents/backend.md +1 -0
- package/templates/agents/devops.md +2 -0
- package/templates/agents/frontend.md +1 -0
- package/templates/agents/fullstack.md +1 -0
- package/templates/agents/quality.md +45 -40
- package/templates/agents/security.md +1 -0
- package/tests/commands/chat.test.ts +53 -62
- package/tests/commands/code.test.ts +265 -310
- package/tests/utils/config-loader.test.ts +189 -188
- package/tests/utils/env-manager.test.ts +110 -113
- package/tests/utils/opencode-validator.test.ts +52 -56
- package/tsconfig.json +4 -3
- package/vitest.config.ts +3 -3
- package/AGENTS.md +0 -374
- package/TODO.md +0 -19
|
@@ -22,22 +22,13 @@ var __importStar = (this && this.__importStar) || function (mod) {
|
|
|
22
22
|
__setModuleDefault(result, mod);
|
|
23
23
|
return result;
|
|
24
24
|
};
|
|
25
|
-
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
-
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
-
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
-
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
-
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
-
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
-
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
-
});
|
|
33
|
-
};
|
|
34
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
35
|
-
const vitest_1 = require("vitest");
|
|
36
26
|
const commander_1 = require("commander");
|
|
27
|
+
const fs = __importStar(require("node:fs"));
|
|
28
|
+
const promises_1 = require("node:fs/promises");
|
|
29
|
+
const vitest_1 = require("vitest");
|
|
37
30
|
const code_1 = require("../../src/commands/code");
|
|
38
31
|
const api_key_service_1 = require("../../src/services/api-key-service");
|
|
39
|
-
const fs = __importStar(require("fs"));
|
|
40
|
-
const promises_1 = require("fs/promises");
|
|
41
32
|
const env_manager_1 = require("../../src/utils/env-manager");
|
|
42
33
|
// Mock dependencies
|
|
43
34
|
vitest_1.vi.mock('../../src/services/api-key-service');
|
|
@@ -57,8 +48,8 @@ vitest_1.vi.mock('child_process', () => ({
|
|
|
57
48
|
}));
|
|
58
49
|
vitest_1.vi.mock('readline', () => ({
|
|
59
50
|
createInterface: vitest_1.vi.fn(() => ({
|
|
60
|
-
question: vitest_1.vi.fn(),
|
|
61
51
|
close: vitest_1.vi.fn(),
|
|
52
|
+
question: vitest_1.vi.fn(),
|
|
62
53
|
})),
|
|
63
54
|
}));
|
|
64
55
|
(0, vitest_1.describe)('Code Commands', () => {
|
|
@@ -95,35 +86,35 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
95
86
|
(0, vitest_1.describe)('code init command', () => {
|
|
96
87
|
(0, vitest_1.it)('should register init command with correct description', () => {
|
|
97
88
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
98
|
-
const initCommand = codeCommand
|
|
89
|
+
const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
|
|
99
90
|
(0, vitest_1.expect)(initCommand).toBeDefined();
|
|
100
|
-
(0, vitest_1.expect)(initCommand
|
|
91
|
+
(0, vitest_1.expect)(initCommand?.description()).toBe('Initialize project for AI coding assistant');
|
|
101
92
|
});
|
|
102
93
|
(0, vitest_1.it)('should have name, force, and yes options', () => {
|
|
103
94
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
104
|
-
const initCommand = codeCommand
|
|
95
|
+
const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
|
|
105
96
|
(0, vitest_1.expect)(initCommand).toBeDefined();
|
|
106
|
-
const nameOption = initCommand
|
|
107
|
-
const forceOption = initCommand
|
|
108
|
-
const yesOption = initCommand
|
|
97
|
+
const nameOption = initCommand?.options.find((opt) => opt.long === '--name');
|
|
98
|
+
const forceOption = initCommand?.options.find((opt) => opt.long === '--force');
|
|
99
|
+
const yesOption = initCommand?.options.find((opt) => opt.long === '--yes');
|
|
109
100
|
(0, vitest_1.expect)(nameOption).toBeDefined();
|
|
110
|
-
(0, vitest_1.expect)(nameOption
|
|
101
|
+
(0, vitest_1.expect)(nameOption?.description).toContain('Project name');
|
|
111
102
|
(0, vitest_1.expect)(forceOption).toBeDefined();
|
|
112
|
-
(0, vitest_1.expect)(forceOption
|
|
103
|
+
(0, vitest_1.expect)(forceOption?.description).toContain('Overwrite existing configuration');
|
|
113
104
|
(0, vitest_1.expect)(yesOption).toBeDefined();
|
|
114
|
-
(0, vitest_1.expect)(yesOption
|
|
105
|
+
(0, vitest_1.expect)(yesOption?.description).toContain('Automatically answer yes');
|
|
115
106
|
});
|
|
116
107
|
(0, vitest_1.it)('should check if opencode is installed', () => {
|
|
117
108
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
118
|
-
const initCommand = codeCommand
|
|
109
|
+
const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
|
|
119
110
|
(0, vitest_1.expect)(initCommand).toBeDefined();
|
|
120
111
|
// The command should attempt to spawn opencode --version
|
|
121
112
|
// This is tested implicitly through the spawn mock
|
|
122
113
|
});
|
|
123
|
-
(0, vitest_1.it)('should list existing API keys and allow selection', () =>
|
|
114
|
+
(0, vitest_1.it)('should list existing API keys and allow selection', async () => {
|
|
124
115
|
// Mock successful opencode installation check
|
|
125
|
-
mockSpawn.mockImplementation((command,
|
|
126
|
-
if (command === 'opencode' &&
|
|
116
|
+
mockSpawn.mockImplementation((command, arguments_) => {
|
|
117
|
+
if (command === 'opencode' && arguments_[0] === '--version') {
|
|
127
118
|
return {
|
|
128
119
|
on: vitest_1.vi.fn().mockImplementation((event, callback) => {
|
|
129
120
|
if (event === 'close')
|
|
@@ -136,31 +127,31 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
136
127
|
// Mock existing API keys
|
|
137
128
|
const mockExistingKeys = [
|
|
138
129
|
{
|
|
130
|
+
created: '2023-01-01T00:00:00.000Z',
|
|
139
131
|
id: 1,
|
|
132
|
+
lastUsed: null,
|
|
140
133
|
name: 'existing-key-1',
|
|
141
134
|
prefix: 'sk_ber',
|
|
142
|
-
created: '2023-01-01T00:00:00.000Z',
|
|
143
|
-
lastUsed: null,
|
|
144
135
|
},
|
|
145
136
|
{
|
|
137
|
+
created: '2023-01-02T00:00:00.000Z',
|
|
146
138
|
id: 2,
|
|
139
|
+
lastUsed: '2023-01-03T00:00:00.000Z',
|
|
147
140
|
name: 'existing-key-2',
|
|
148
141
|
prefix: 'sk_ber',
|
|
149
|
-
created: '2023-01-02T00:00:00.000Z',
|
|
150
|
-
lastUsed: '2023-01-03T00:00:00.000Z',
|
|
151
142
|
},
|
|
152
143
|
];
|
|
153
144
|
mockApiKeyService.list.mockResolvedValue(mockExistingKeys);
|
|
154
145
|
// Mock file operations
|
|
155
146
|
mockFs.existsSync.mockReturnValue(false);
|
|
156
|
-
mockFsPromises.writeFile.mockResolvedValue(
|
|
147
|
+
mockFsPromises.writeFile.mockResolvedValue();
|
|
157
148
|
// Verify that the list method is called
|
|
158
149
|
(0, vitest_1.expect)(mockApiKeyService.list).toBeDefined();
|
|
159
|
-
})
|
|
160
|
-
(0, vitest_1.it)('should create new API key with project-based naming', () =>
|
|
150
|
+
});
|
|
151
|
+
(0, vitest_1.it)('should create new API key with project-based naming', async () => {
|
|
161
152
|
// Mock successful opencode installation check
|
|
162
|
-
mockSpawn.mockImplementation((command,
|
|
163
|
-
if (command === 'opencode' &&
|
|
153
|
+
mockSpawn.mockImplementation((command, arguments_) => {
|
|
154
|
+
if (command === 'opencode' && arguments_[0] === '--version') {
|
|
164
155
|
return {
|
|
165
156
|
on: vitest_1.vi.fn().mockImplementation((event, callback) => {
|
|
166
157
|
if (event === 'close')
|
|
@@ -175,33 +166,33 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
175
166
|
// Mock successful API key creation
|
|
176
167
|
const mockApiKeyData = {
|
|
177
168
|
id: 123,
|
|
178
|
-
name: 'opencode-testproject-1234567890',
|
|
179
169
|
key: 'test-api-key-12345',
|
|
170
|
+
name: 'opencode-testproject-1234567890',
|
|
180
171
|
};
|
|
181
172
|
mockApiKeyService.create.mockResolvedValue(mockApiKeyData);
|
|
182
173
|
// Mock file operations
|
|
183
174
|
mockFs.existsSync.mockReturnValue(false);
|
|
184
|
-
mockFsPromises.writeFile.mockResolvedValue(
|
|
175
|
+
mockFsPromises.writeFile.mockResolvedValue();
|
|
185
176
|
// Verify that the create method is available
|
|
186
177
|
(0, vitest_1.expect)(mockApiKeyService.create).toBeDefined();
|
|
187
|
-
})
|
|
188
|
-
(0, vitest_1.it)('should create opencode.json with correct structure', () =>
|
|
178
|
+
});
|
|
179
|
+
(0, vitest_1.it)('should create opencode.json with correct structure', async () => {
|
|
189
180
|
// This tests the expected config structure
|
|
190
181
|
const expectedConfig = {
|
|
191
|
-
model: 'berget/glm-4-6',
|
|
192
182
|
apiKey: 'test-api-key',
|
|
183
|
+
created: vitest_1.expect.any(String),
|
|
184
|
+
model: 'berget/glm-4-6',
|
|
193
185
|
projectName: 'testproject',
|
|
194
186
|
provider: 'berget',
|
|
195
|
-
created: vitest_1.expect.any(String),
|
|
196
187
|
version: '1.0.0',
|
|
197
188
|
};
|
|
198
189
|
(0, vitest_1.expect)(expectedConfig.model).toBe('berget/glm-4-6');
|
|
199
190
|
(0, vitest_1.expect)(expectedConfig.provider).toBe('berget');
|
|
200
191
|
(0, vitest_1.expect)(expectedConfig.version).toBe('1.0.0');
|
|
201
|
-
})
|
|
192
|
+
});
|
|
202
193
|
(0, vitest_1.it)('should handle existing config file', () => {
|
|
203
194
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
204
|
-
const initCommand = codeCommand
|
|
195
|
+
const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
|
|
205
196
|
(0, vitest_1.expect)(initCommand).toBeDefined();
|
|
206
197
|
// Should check if opencode.json exists before proceeding
|
|
207
198
|
(0, vitest_1.expect)(mockFs.existsSync).toBeDefined();
|
|
@@ -210,39 +201,39 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
210
201
|
(0, vitest_1.describe)('code run command', () => {
|
|
211
202
|
(0, vitest_1.it)('should register run command with correct description', () => {
|
|
212
203
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
213
|
-
const runCommand = codeCommand
|
|
204
|
+
const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
|
|
214
205
|
(0, vitest_1.expect)(runCommand).toBeDefined();
|
|
215
|
-
(0, vitest_1.expect)(runCommand
|
|
206
|
+
(0, vitest_1.expect)(runCommand?.description()).toBe('Run AI coding assistant');
|
|
216
207
|
});
|
|
217
208
|
(0, vitest_1.it)('should accept prompt argument and model, no-config, and yes options', () => {
|
|
218
209
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
219
|
-
const runCommand = codeCommand
|
|
210
|
+
const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
|
|
220
211
|
(0, vitest_1.expect)(runCommand).toBeDefined();
|
|
221
|
-
const modelOption = runCommand
|
|
222
|
-
const noConfigOption = runCommand
|
|
223
|
-
const yesOption = runCommand
|
|
212
|
+
const modelOption = runCommand?.options.find((opt) => opt.long === '--model');
|
|
213
|
+
const noConfigOption = runCommand?.options.find((opt) => opt.long === '--no-config');
|
|
214
|
+
const yesOption = runCommand?.options.find((opt) => opt.long === '--yes');
|
|
224
215
|
(0, vitest_1.expect)(modelOption).toBeDefined();
|
|
225
|
-
(0, vitest_1.expect)(modelOption
|
|
216
|
+
(0, vitest_1.expect)(modelOption?.description).toContain('Model to use');
|
|
226
217
|
(0, vitest_1.expect)(noConfigOption).toBeDefined();
|
|
227
|
-
(0, vitest_1.expect)(noConfigOption
|
|
218
|
+
(0, vitest_1.expect)(noConfigOption?.description).toContain('Run without loading project config');
|
|
228
219
|
(0, vitest_1.expect)(yesOption).toBeDefined();
|
|
229
|
-
(0, vitest_1.expect)(yesOption
|
|
220
|
+
(0, vitest_1.expect)(yesOption?.description).toContain('Automatically answer yes');
|
|
230
221
|
});
|
|
231
|
-
(0, vitest_1.it)('should load configuration from opencode.json', () =>
|
|
222
|
+
(0, vitest_1.it)('should load configuration from opencode.json', async () => {
|
|
232
223
|
const mockConfig = {
|
|
233
|
-
model: 'berget/glm-4-6',
|
|
234
224
|
apiKey: 'test-api-key',
|
|
225
|
+
created: '2023-01-01T00:00:00.000Z',
|
|
226
|
+
model: 'berget/glm-4-6',
|
|
235
227
|
projectName: 'testproject',
|
|
236
228
|
provider: 'berget',
|
|
237
|
-
created: '2023-01-01T00:00:00.000Z',
|
|
238
229
|
version: '1.0.0',
|
|
239
230
|
};
|
|
240
231
|
// Mock file exists and contains config
|
|
241
232
|
mockFs.existsSync.mockReturnValue(true);
|
|
242
233
|
mockFsPromises.readFile.mockResolvedValue(JSON.stringify(mockConfig));
|
|
243
234
|
// Mock successful opencode check
|
|
244
|
-
mockSpawn.mockImplementation((command,
|
|
245
|
-
if (command === 'opencode' &&
|
|
235
|
+
mockSpawn.mockImplementation((command, arguments_) => {
|
|
236
|
+
if (command === 'opencode' && arguments_[0] === '--version') {
|
|
246
237
|
return {
|
|
247
238
|
on: vitest_1.vi.fn().mockImplementation((event, callback) => {
|
|
248
239
|
if (event === 'close')
|
|
@@ -256,17 +247,17 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
256
247
|
(0, vitest_1.expect)(mockConfig.model).toBe('berget/glm-4-6');
|
|
257
248
|
(0, vitest_1.expect)(mockConfig.apiKey).toBe('test-api-key');
|
|
258
249
|
(0, vitest_1.expect)(mockConfig.projectName).toBe('testproject');
|
|
259
|
-
})
|
|
250
|
+
});
|
|
260
251
|
(0, vitest_1.it)('should spawn opencode with correct arguments', () => {
|
|
261
252
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
262
|
-
const runCommand = codeCommand
|
|
253
|
+
const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
|
|
263
254
|
(0, vitest_1.expect)(runCommand).toBeDefined();
|
|
264
255
|
// Should spawn opencode with appropriate arguments
|
|
265
256
|
(0, vitest_1.expect)(mockSpawn).toBeDefined();
|
|
266
257
|
});
|
|
267
258
|
(0, vitest_1.it)('should handle missing configuration file', () => {
|
|
268
259
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
269
|
-
const runCommand = codeCommand
|
|
260
|
+
const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
|
|
270
261
|
(0, vitest_1.expect)(runCommand).toBeDefined();
|
|
271
262
|
// Should check if opencode.json exists
|
|
272
263
|
(0, vitest_1.expect)(mockFs.existsSync).toBeDefined();
|
|
@@ -279,8 +270,8 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
279
270
|
});
|
|
280
271
|
(0, vitest_1.it)('should offer to install opencode if not found', () => {
|
|
281
272
|
// Mock opencode not installed
|
|
282
|
-
mockSpawn.mockImplementation((command,
|
|
283
|
-
if (command === 'opencode' &&
|
|
273
|
+
mockSpawn.mockImplementation((command, arguments_) => {
|
|
274
|
+
if (command === 'opencode' && arguments_[0] === '--version') {
|
|
284
275
|
return {
|
|
285
276
|
on: vitest_1.vi.fn().mockImplementation((event, callback) => {
|
|
286
277
|
if (event === 'close')
|
|
@@ -301,19 +292,19 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
301
292
|
(0, vitest_1.describe)('automation support', () => {
|
|
302
293
|
(0, vitest_1.it)('should support -y flag for automated initialization', () => {
|
|
303
294
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
304
|
-
const initCommand = codeCommand
|
|
295
|
+
const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
|
|
305
296
|
(0, vitest_1.expect)(initCommand).toBeDefined();
|
|
306
|
-
const yesOption = initCommand
|
|
297
|
+
const yesOption = initCommand?.options.find((opt) => opt.long === '--yes');
|
|
307
298
|
(0, vitest_1.expect)(yesOption).toBeDefined();
|
|
308
|
-
(0, vitest_1.expect)(yesOption
|
|
299
|
+
(0, vitest_1.expect)(yesOption?.description).toContain('automation');
|
|
309
300
|
});
|
|
310
301
|
(0, vitest_1.it)('should support -y flag for automated run', () => {
|
|
311
302
|
const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
|
|
312
|
-
const runCommand = codeCommand
|
|
303
|
+
const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
|
|
313
304
|
(0, vitest_1.expect)(runCommand).toBeDefined();
|
|
314
|
-
const yesOption = runCommand
|
|
305
|
+
const yesOption = runCommand?.options.find((opt) => opt.long === '--yes');
|
|
315
306
|
(0, vitest_1.expect)(yesOption).toBeDefined();
|
|
316
|
-
(0, vitest_1.expect)(yesOption
|
|
307
|
+
(0, vitest_1.expect)(yesOption?.description).toContain('automation');
|
|
317
308
|
});
|
|
318
309
|
(0, vitest_1.it)('should use BERGET_API_KEY environment variable in automation mode', () => {
|
|
319
310
|
// Test that environment variable is used when -y flag is set
|
|
@@ -324,65 +315,65 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
324
315
|
});
|
|
325
316
|
});
|
|
326
317
|
(0, vitest_1.describe)('.env file handling', () => {
|
|
327
|
-
let
|
|
318
|
+
let mockUpdateEnvironmentFile;
|
|
328
319
|
(0, vitest_1.beforeEach)(() => {
|
|
329
|
-
|
|
320
|
+
mockUpdateEnvironmentFile = vitest_1.vi.mocked(env_manager_1.updateEnvFile);
|
|
330
321
|
});
|
|
331
|
-
(0, vitest_1.it)('should call updateEnvFile when creating new project', () =>
|
|
332
|
-
|
|
322
|
+
(0, vitest_1.it)('should call updateEnvFile when creating new project', async () => {
|
|
323
|
+
mockUpdateEnvironmentFile.mockResolvedValue(true);
|
|
333
324
|
mockFs.existsSync.mockReturnValue(false); // .env doesn't exist
|
|
334
|
-
mockFsPromises.writeFile.mockResolvedValue(
|
|
325
|
+
mockFsPromises.writeFile.mockResolvedValue();
|
|
335
326
|
// This would be tested by actually calling the init command
|
|
336
327
|
// For now we verify the mock is properly set up
|
|
337
|
-
(0, vitest_1.expect)(
|
|
338
|
-
})
|
|
339
|
-
(0, vitest_1.it)('should not overwrite existing BERGET_API_KEY in .env', () =>
|
|
328
|
+
(0, vitest_1.expect)(mockUpdateEnvironmentFile).toBeDefined();
|
|
329
|
+
});
|
|
330
|
+
(0, vitest_1.it)('should not overwrite existing BERGET_API_KEY in .env', async () => {
|
|
340
331
|
const consoleSpy = vitest_1.vi.spyOn(console, 'log').mockImplementation(() => { });
|
|
341
332
|
// Mock existing .env with BERGET_API_KEY
|
|
342
333
|
mockFs.existsSync.mockReturnValue(true);
|
|
343
334
|
mockFs.readFileSync.mockReturnValue('BERGET_API_KEY=existing_key\nOTHER_KEY=value\n');
|
|
344
335
|
// Mock updateEnvFile to simulate the check
|
|
345
|
-
|
|
336
|
+
mockUpdateEnvironmentFile.mockImplementation(async (options) => {
|
|
346
337
|
if (options.key === 'BERGET_API_KEY' && !options.force) {
|
|
347
338
|
console.log(`⚠ ${options.key} already exists in .env - leaving unchanged`);
|
|
348
339
|
return false;
|
|
349
340
|
}
|
|
350
341
|
return true;
|
|
351
|
-
})
|
|
352
|
-
|
|
342
|
+
});
|
|
343
|
+
await (0, env_manager_1.updateEnvFile)({
|
|
353
344
|
key: 'BERGET_API_KEY',
|
|
354
345
|
value: 'new_key',
|
|
355
346
|
});
|
|
356
347
|
(0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith(vitest_1.expect.stringContaining('BERGET_API_KEY already exists in .env - leaving unchanged'));
|
|
357
348
|
consoleSpy.mockRestore();
|
|
358
|
-
})
|
|
359
|
-
(0, vitest_1.it)('should add new key to existing .env file', () =>
|
|
349
|
+
});
|
|
350
|
+
(0, vitest_1.it)('should add new key to existing .env file', async () => {
|
|
360
351
|
mockFs.existsSync.mockReturnValue(true);
|
|
361
352
|
mockFs.readFileSync.mockReturnValue('EXISTING_KEY=value\n');
|
|
362
|
-
|
|
363
|
-
|
|
353
|
+
mockUpdateEnvironmentFile.mockResolvedValue(true);
|
|
354
|
+
await (0, env_manager_1.updateEnvFile)({
|
|
355
|
+
comment: 'Berget AI Configuration',
|
|
364
356
|
key: 'BERGET_API_KEY',
|
|
365
357
|
value: 'new_api_key',
|
|
366
|
-
comment: 'Berget AI Configuration',
|
|
367
358
|
});
|
|
368
|
-
(0, vitest_1.expect)(
|
|
359
|
+
(0, vitest_1.expect)(mockUpdateEnvironmentFile).toHaveBeenCalledWith({
|
|
360
|
+
comment: 'Berget AI Configuration',
|
|
369
361
|
key: 'BERGET_API_KEY',
|
|
370
362
|
value: 'new_api_key',
|
|
371
|
-
comment: 'Berget AI Configuration',
|
|
372
363
|
});
|
|
373
|
-
})
|
|
374
|
-
(0, vitest_1.it)('should create new .env file when none exists', () =>
|
|
364
|
+
});
|
|
365
|
+
(0, vitest_1.it)('should create new .env file when none exists', async () => {
|
|
375
366
|
mockFs.existsSync.mockReturnValue(false);
|
|
376
|
-
|
|
377
|
-
|
|
367
|
+
mockUpdateEnvironmentFile.mockResolvedValue(true);
|
|
368
|
+
await (0, env_manager_1.updateEnvFile)({
|
|
378
369
|
key: 'BERGET_API_KEY',
|
|
379
370
|
value: 'new_api_key',
|
|
380
371
|
});
|
|
381
|
-
(0, vitest_1.expect)(
|
|
372
|
+
(0, vitest_1.expect)(mockUpdateEnvironmentFile).toHaveBeenCalledWith({
|
|
382
373
|
key: 'BERGET_API_KEY',
|
|
383
374
|
value: 'new_api_key',
|
|
384
375
|
});
|
|
385
|
-
})
|
|
376
|
+
});
|
|
386
377
|
});
|
|
387
378
|
(0, vitest_1.describe)('error handling', () => {
|
|
388
379
|
(0, vitest_1.it)('should handle API key creation failures', () => {
|
|
@@ -402,26 +393,26 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
402
393
|
});
|
|
403
394
|
(0, vitest_1.expect)(mockSpawn).toBeDefined();
|
|
404
395
|
});
|
|
405
|
-
(0, vitest_1.it)('should handle .env update failures', () =>
|
|
406
|
-
const
|
|
407
|
-
|
|
408
|
-
|
|
396
|
+
(0, vitest_1.it)('should handle .env update failures', async () => {
|
|
397
|
+
const mockUpdateEnvironmentFile = vitest_1.vi.mocked(env_manager_1.updateEnvFile);
|
|
398
|
+
mockUpdateEnvironmentFile.mockRejectedValue(new Error('Env update failed'));
|
|
399
|
+
await (0, vitest_1.expect)((0, env_manager_1.updateEnvFile)({
|
|
409
400
|
key: 'TEST_KEY',
|
|
410
401
|
value: 'test_value',
|
|
411
402
|
})).rejects.toThrow('Env update failed');
|
|
412
|
-
})
|
|
403
|
+
});
|
|
413
404
|
});
|
|
414
405
|
(0, vitest_1.describe)('experimental features', () => {
|
|
415
|
-
let
|
|
406
|
+
let originalEnvironment;
|
|
416
407
|
(0, vitest_1.beforeEach)(() => {
|
|
417
|
-
|
|
408
|
+
originalEnvironment = process.env.BERGET_EXPERIMENTAL;
|
|
418
409
|
});
|
|
419
410
|
(0, vitest_1.afterEach)(() => {
|
|
420
|
-
if (
|
|
411
|
+
if (originalEnvironment === undefined) {
|
|
421
412
|
delete process.env.BERGET_EXPERIMENTAL;
|
|
422
413
|
}
|
|
423
414
|
else {
|
|
424
|
-
process.env.BERGET_EXPERIMENTAL =
|
|
415
|
+
process.env.BERGET_EXPERIMENTAL = originalEnvironment;
|
|
425
416
|
}
|
|
426
417
|
});
|
|
427
418
|
(0, vitest_1.it)('should NOT show setup command when BERGET_EXPERIMENTAL is not set', () => {
|
|
@@ -429,7 +420,7 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
429
420
|
const freshProgram = new commander_1.Command();
|
|
430
421
|
(0, code_1.registerCodeCommands)(freshProgram);
|
|
431
422
|
const codeCommand = freshProgram.commands.find((cmd) => cmd.name() === 'code');
|
|
432
|
-
const setupCommand = codeCommand
|
|
423
|
+
const setupCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'setup');
|
|
433
424
|
(0, vitest_1.expect)(setupCommand).toBeUndefined();
|
|
434
425
|
});
|
|
435
426
|
(0, vitest_1.it)('should show setup command when BERGET_EXPERIMENTAL is set', () => {
|
|
@@ -437,9 +428,9 @@ vitest_1.vi.mock('readline', () => ({
|
|
|
437
428
|
const freshProgram = new commander_1.Command();
|
|
438
429
|
(0, code_1.registerCodeCommands)(freshProgram);
|
|
439
430
|
const codeCommand = freshProgram.commands.find((cmd) => cmd.name() === 'code');
|
|
440
|
-
const setupCommand = codeCommand
|
|
431
|
+
const setupCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'setup');
|
|
441
432
|
(0, vitest_1.expect)(setupCommand).toBeDefined();
|
|
442
|
-
(0, vitest_1.expect)(setupCommand
|
|
433
|
+
(0, vitest_1.expect)(setupCommand?.description()).toBe('Interactive setup for Berget AI coding tools');
|
|
443
434
|
});
|
|
444
435
|
});
|
|
445
436
|
});
|
|
@@ -5,9 +5,9 @@ const config_loader_1 = require("../../src/utils/config-loader");
|
|
|
5
5
|
// Mock fs module
|
|
6
6
|
const mockFs = vitest_1.vi.hoisted(() => ({
|
|
7
7
|
existsSync: vitest_1.vi.fn(),
|
|
8
|
+
mkdirSync: vitest_1.vi.fn(),
|
|
8
9
|
readFileSync: vitest_1.vi.fn(),
|
|
9
10
|
writeFileSync: vitest_1.vi.fn(),
|
|
10
|
-
mkdirSync: vitest_1.vi.fn(),
|
|
11
11
|
}));
|
|
12
12
|
vitest_1.vi.mock('fs', () => mockFs);
|
|
13
13
|
(0, vitest_1.describe)('ConfigLoader', () => {
|
|
@@ -33,14 +33,14 @@ vitest_1.vi.mock('fs', () => mockFs);
|
|
|
33
33
|
const modelConfig = configLoader.getModelConfig();
|
|
34
34
|
(0, vitest_1.expect)(modelConfig).toEqual({
|
|
35
35
|
primary: 'berget/glm-4.7',
|
|
36
|
-
small: 'berget/gpt-oss'
|
|
36
|
+
small: 'berget/gpt-oss',
|
|
37
37
|
});
|
|
38
38
|
});
|
|
39
39
|
(0, vitest_1.it)('should return default values when using convenience function', () => {
|
|
40
40
|
const modelConfig = (0, config_loader_1.getModelConfig)(testConfigPath);
|
|
41
41
|
(0, vitest_1.expect)(modelConfig).toEqual({
|
|
42
42
|
primary: 'berget/glm-4.7',
|
|
43
|
-
small: 'berget/gpt-oss'
|
|
43
|
+
small: 'berget/gpt-oss',
|
|
44
44
|
});
|
|
45
45
|
});
|
|
46
46
|
});
|
|
@@ -49,42 +49,42 @@ vitest_1.vi.mock('fs', () => mockFs);
|
|
|
49
49
|
const models = configLoader.getProviderModels();
|
|
50
50
|
(0, vitest_1.expect)(models).toEqual({
|
|
51
51
|
'glm-4.7': {
|
|
52
|
+
limit: { context: 90000, output: 4000 },
|
|
52
53
|
name: 'GLM-4.7',
|
|
53
|
-
limit: { output: 4000, context: 90000 }
|
|
54
54
|
},
|
|
55
55
|
'gpt-oss': {
|
|
56
|
-
|
|
57
|
-
limit: { output: 4000, context: 128000 },
|
|
56
|
+
limit: { context: 128000, output: 4000 },
|
|
58
57
|
modalities: {
|
|
59
58
|
input: ['text', 'image'],
|
|
60
|
-
output: ['text']
|
|
61
|
-
}
|
|
59
|
+
output: ['text'],
|
|
60
|
+
},
|
|
61
|
+
name: 'GPT-OSS',
|
|
62
62
|
},
|
|
63
63
|
'llama-8b': {
|
|
64
|
+
limit: { context: 128000, output: 4000 },
|
|
64
65
|
name: 'llama-3.1-8b',
|
|
65
|
-
|
|
66
|
-
}
|
|
66
|
+
},
|
|
67
67
|
});
|
|
68
68
|
});
|
|
69
69
|
(0, vitest_1.it)('should return default provider models when using convenience function', () => {
|
|
70
70
|
const models = (0, config_loader_1.getProviderModels)(testConfigPath);
|
|
71
71
|
(0, vitest_1.expect)(models).toEqual({
|
|
72
72
|
'glm-4.7': {
|
|
73
|
+
limit: { context: 90000, output: 4000 },
|
|
73
74
|
name: 'GLM-4.7',
|
|
74
|
-
limit: { output: 4000, context: 90000 }
|
|
75
75
|
},
|
|
76
76
|
'gpt-oss': {
|
|
77
|
-
|
|
78
|
-
limit: { output: 4000, context: 128000 },
|
|
77
|
+
limit: { context: 128000, output: 4000 },
|
|
79
78
|
modalities: {
|
|
80
79
|
input: ['text', 'image'],
|
|
81
|
-
output: ['text']
|
|
82
|
-
}
|
|
80
|
+
output: ['text'],
|
|
81
|
+
},
|
|
82
|
+
name: 'GPT-OSS',
|
|
83
83
|
},
|
|
84
84
|
'llama-8b': {
|
|
85
|
+
limit: { context: 128000, output: 4000 },
|
|
85
86
|
name: 'llama-3.1-8b',
|
|
86
|
-
|
|
87
|
-
}
|
|
87
|
+
},
|
|
88
88
|
});
|
|
89
89
|
});
|
|
90
90
|
});
|
|
@@ -107,38 +107,38 @@ vitest_1.vi.mock('fs', () => mockFs);
|
|
|
107
107
|
});
|
|
108
108
|
(0, vitest_1.describe)('when config file exists', () => {
|
|
109
109
|
const mockConfig = {
|
|
110
|
-
model: 'custom-model',
|
|
111
|
-
small_model: 'custom-small-model',
|
|
112
110
|
agent: {
|
|
113
111
|
fullstack: {
|
|
114
|
-
model: 'custom-agent-model',
|
|
115
|
-
temperature: 0.5,
|
|
116
112
|
mode: 'primary',
|
|
113
|
+
model: 'custom-agent-model',
|
|
117
114
|
permission: {
|
|
118
|
-
edit: 'allow',
|
|
119
115
|
bash: 'allow',
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
116
|
+
edit: 'allow',
|
|
117
|
+
webfetch: 'allow',
|
|
118
|
+
},
|
|
119
|
+
temperature: 0.5,
|
|
120
|
+
},
|
|
123
121
|
},
|
|
124
122
|
command: {
|
|
125
123
|
test: {
|
|
126
|
-
description: 'Test command'
|
|
127
|
-
}
|
|
128
|
-
},
|
|
129
|
-
watcher: {
|
|
130
|
-
ignore: ['custom-ignore']
|
|
124
|
+
description: 'Test command',
|
|
125
|
+
},
|
|
131
126
|
},
|
|
127
|
+
model: 'custom-model',
|
|
132
128
|
provider: {
|
|
133
129
|
berget: {
|
|
134
130
|
models: {
|
|
135
131
|
'custom-model': {
|
|
132
|
+
limit: { context: 160000, output: 8000 },
|
|
136
133
|
name: 'Custom Model',
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
},
|
|
137
|
+
},
|
|
138
|
+
small_model: 'custom-small-model',
|
|
139
|
+
watcher: {
|
|
140
|
+
ignore: ['custom-ignore'],
|
|
141
|
+
},
|
|
142
142
|
};
|
|
143
143
|
(0, vitest_1.beforeEach)(() => {
|
|
144
144
|
mockFs.existsSync.mockReturnValue(true);
|
|
@@ -149,7 +149,7 @@ vitest_1.vi.mock('fs', () => mockFs);
|
|
|
149
149
|
const modelConfig = configLoader.getModelConfig();
|
|
150
150
|
(0, vitest_1.expect)(modelConfig).toEqual({
|
|
151
151
|
primary: 'custom-model',
|
|
152
|
-
small: 'custom-small-model'
|
|
152
|
+
small: 'custom-small-model',
|
|
153
153
|
});
|
|
154
154
|
});
|
|
155
155
|
});
|
|
@@ -158,9 +158,9 @@ vitest_1.vi.mock('fs', () => mockFs);
|
|
|
158
158
|
const models = configLoader.getProviderModels();
|
|
159
159
|
(0, vitest_1.expect)(models).toEqual({
|
|
160
160
|
'custom-model': {
|
|
161
|
+
limit: { context: 160000, output: 8000 },
|
|
161
162
|
name: 'Custom Model',
|
|
162
|
-
|
|
163
|
-
}
|
|
163
|
+
},
|
|
164
164
|
});
|
|
165
165
|
});
|
|
166
166
|
});
|
|
@@ -190,28 +190,28 @@ vitest_1.vi.mock('fs', () => mockFs);
|
|
|
190
190
|
const modelConfig = configLoader.getModelConfig();
|
|
191
191
|
(0, vitest_1.expect)(modelConfig).toEqual({
|
|
192
192
|
primary: 'berget/glm-4.7',
|
|
193
|
-
small: 'berget/gpt-oss'
|
|
193
|
+
small: 'berget/gpt-oss',
|
|
194
194
|
});
|
|
195
195
|
});
|
|
196
196
|
(0, vitest_1.it)('should fall back to defaults for getProviderModels', () => {
|
|
197
197
|
const models = configLoader.getProviderModels();
|
|
198
198
|
(0, vitest_1.expect)(models).toEqual({
|
|
199
199
|
'glm-4.7': {
|
|
200
|
+
limit: { context: 90000, output: 4000 },
|
|
200
201
|
name: 'GLM-4.7',
|
|
201
|
-
limit: { output: 4000, context: 90000 }
|
|
202
202
|
},
|
|
203
203
|
'gpt-oss': {
|
|
204
|
-
|
|
205
|
-
limit: { output: 4000, context: 128000 },
|
|
204
|
+
limit: { context: 128000, output: 4000 },
|
|
206
205
|
modalities: {
|
|
207
206
|
input: ['text', 'image'],
|
|
208
|
-
output: ['text']
|
|
209
|
-
}
|
|
207
|
+
output: ['text'],
|
|
208
|
+
},
|
|
209
|
+
name: 'GPT-OSS',
|
|
210
210
|
},
|
|
211
211
|
'llama-8b': {
|
|
212
|
+
limit: { context: 128000, output: 4000 },
|
|
212
213
|
name: 'llama-3.1-8b',
|
|
213
|
-
|
|
214
|
-
}
|
|
214
|
+
},
|
|
215
215
|
});
|
|
216
216
|
});
|
|
217
217
|
(0, vitest_1.it)('should fall back to defaults for getAllAgentConfigs', () => {
|
|
@@ -246,7 +246,7 @@ vitest_1.vi.mock('fs', () => mockFs);
|
|
|
246
246
|
// And return sensible defaults
|
|
247
247
|
(0, vitest_1.expect)(configLoader.getModelConfig()).toEqual({
|
|
248
248
|
primary: 'berget/glm-4.7',
|
|
249
|
-
small: 'berget/gpt-oss'
|
|
249
|
+
small: 'berget/gpt-oss',
|
|
250
250
|
});
|
|
251
251
|
(0, vitest_1.expect)(configLoader.getAllAgentConfigs()).toEqual({});
|
|
252
252
|
(0, vitest_1.expect)(configLoader.getAgentConfig('fullstack')).toBeNull();
|
|
@@ -259,7 +259,7 @@ vitest_1.vi.mock('fs', () => mockFs);
|
|
|
259
259
|
(0, vitest_1.expect)(() => (0, config_loader_1.getAllAgentConfigs)(testConfigPath)).not.toThrow();
|
|
260
260
|
(0, vitest_1.expect)((0, config_loader_1.getModelConfig)(testConfigPath)).toEqual({
|
|
261
261
|
primary: 'berget/glm-4.7',
|
|
262
|
-
small: 'berget/gpt-oss'
|
|
262
|
+
small: 'berget/gpt-oss',
|
|
263
263
|
});
|
|
264
264
|
(0, vitest_1.expect)((0, config_loader_1.getAllAgentConfigs)(testConfigPath)).toEqual({});
|
|
265
265
|
});
|