berget 2.2.7 → 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.
Files changed (130) hide show
  1. package/.github/workflows/publish.yml +6 -6
  2. package/.github/workflows/test.yml +1 -1
  3. package/.prettierrc +5 -3
  4. package/dist/index.js +24 -25
  5. package/dist/package.json +5 -3
  6. package/dist/src/agents/app.js +8 -8
  7. package/dist/src/agents/backend.js +3 -3
  8. package/dist/src/agents/devops.js +8 -8
  9. package/dist/src/agents/frontend.js +3 -3
  10. package/dist/src/agents/fullstack.js +3 -3
  11. package/dist/src/agents/index.js +18 -18
  12. package/dist/src/agents/quality.js +8 -8
  13. package/dist/src/agents/security.js +8 -8
  14. package/dist/src/client.js +115 -127
  15. package/dist/src/commands/api-keys.js +195 -202
  16. package/dist/src/commands/auth.js +16 -25
  17. package/dist/src/commands/autocomplete.js +8 -8
  18. package/dist/src/commands/billing.js +10 -19
  19. package/dist/src/commands/chat.js +139 -170
  20. package/dist/src/commands/clusters.js +21 -30
  21. package/dist/src/commands/code/__tests__/auth-sync.test.js +189 -186
  22. package/dist/src/commands/code/__tests__/fake-api-key-service.js +3 -13
  23. package/dist/src/commands/code/__tests__/fake-auth-service.js +21 -29
  24. package/dist/src/commands/code/__tests__/fake-command-runner.js +22 -33
  25. package/dist/src/commands/code/__tests__/fake-file-store.js +19 -41
  26. package/dist/src/commands/code/__tests__/fake-prompter.js +81 -97
  27. package/dist/src/commands/code/__tests__/setup-flow.test.js +295 -295
  28. package/dist/src/commands/code/adapters/clack-prompter.js +15 -32
  29. package/dist/src/commands/code/adapters/fs-file-store.js +25 -44
  30. package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -41
  31. package/dist/src/commands/code/auth-sync.js +215 -228
  32. package/dist/src/commands/code/errors.js +15 -12
  33. package/dist/src/commands/code/setup.js +390 -425
  34. package/dist/src/commands/code.js +279 -294
  35. package/dist/src/commands/index.js +5 -5
  36. package/dist/src/commands/models.js +16 -25
  37. package/dist/src/commands/users.js +9 -18
  38. package/dist/src/constants/command-structure.js +138 -138
  39. package/dist/src/services/api-key-service.js +132 -152
  40. package/dist/src/services/auth-service.js +81 -95
  41. package/dist/src/services/browser-auth.js +121 -131
  42. package/dist/src/services/chat-service.js +369 -386
  43. package/dist/src/services/cluster-service.js +47 -62
  44. package/dist/src/services/collaborator-service.js +9 -21
  45. package/dist/src/services/flux-service.js +13 -25
  46. package/dist/src/services/helm-service.js +9 -21
  47. package/dist/src/services/kubectl-service.js +15 -29
  48. package/dist/src/utils/config-checker.js +7 -7
  49. package/dist/src/utils/config-loader.js +109 -109
  50. package/dist/src/utils/default-api-key.js +129 -139
  51. package/dist/src/utils/env-manager.js +55 -66
  52. package/dist/src/utils/error-handler.js +62 -62
  53. package/dist/src/utils/logger.js +74 -67
  54. package/dist/src/utils/markdown-renderer.js +28 -28
  55. package/dist/src/utils/opencode-validator.js +67 -69
  56. package/dist/src/utils/token-manager.js +67 -65
  57. package/dist/tests/commands/chat.test.js +30 -39
  58. package/dist/tests/commands/code.test.js +186 -195
  59. package/dist/tests/utils/config-loader.test.js +107 -107
  60. package/dist/tests/utils/env-manager.test.js +81 -90
  61. package/dist/tests/utils/opencode-validator.test.js +42 -41
  62. package/dist/vitest.config.js +1 -1
  63. package/eslint.config.mjs +50 -30
  64. package/index.ts +30 -31
  65. package/package.json +5 -3
  66. package/src/agents/app.ts +9 -9
  67. package/src/agents/backend.ts +4 -4
  68. package/src/agents/devops.ts +9 -9
  69. package/src/agents/frontend.ts +4 -4
  70. package/src/agents/fullstack.ts +4 -4
  71. package/src/agents/index.ts +27 -25
  72. package/src/agents/quality.ts +9 -9
  73. package/src/agents/security.ts +9 -9
  74. package/src/agents/types.ts +10 -10
  75. package/src/client.ts +85 -77
  76. package/src/commands/api-keys.ts +190 -185
  77. package/src/commands/auth.ts +15 -14
  78. package/src/commands/autocomplete.ts +10 -10
  79. package/src/commands/billing.ts +13 -12
  80. package/src/commands/chat.ts +145 -142
  81. package/src/commands/clusters.ts +20 -19
  82. package/src/commands/code/__tests__/auth-sync.test.ts +176 -175
  83. package/src/commands/code/__tests__/fake-api-key-service.ts +2 -2
  84. package/src/commands/code/__tests__/fake-auth-service.ts +18 -18
  85. package/src/commands/code/__tests__/fake-command-runner.ts +28 -22
  86. package/src/commands/code/__tests__/fake-file-store.ts +15 -15
  87. package/src/commands/code/__tests__/fake-prompter.ts +86 -85
  88. package/src/commands/code/__tests__/setup-flow.test.ts +253 -251
  89. package/src/commands/code/adapters/clack-prompter.ts +32 -30
  90. package/src/commands/code/adapters/fs-file-store.ts +18 -17
  91. package/src/commands/code/adapters/spawn-command-runner.ts +20 -15
  92. package/src/commands/code/auth-sync.ts +210 -210
  93. package/src/commands/code/errors.ts +11 -11
  94. package/src/commands/code/ports/auth-services.ts +7 -7
  95. package/src/commands/code/ports/command-runner.ts +2 -2
  96. package/src/commands/code/ports/file-store.ts +3 -3
  97. package/src/commands/code/ports/prompter.ts +13 -13
  98. package/src/commands/code/setup.ts +408 -406
  99. package/src/commands/code.ts +288 -287
  100. package/src/commands/index.ts +11 -10
  101. package/src/commands/models.ts +19 -18
  102. package/src/commands/users.ts +11 -10
  103. package/src/constants/command-structure.ts +159 -159
  104. package/src/services/api-key-service.ts +85 -85
  105. package/src/services/auth-service.ts +55 -54
  106. package/src/services/browser-auth.ts +62 -62
  107. package/src/services/chat-service.ts +169 -170
  108. package/src/services/cluster-service.ts +28 -28
  109. package/src/services/collaborator-service.ts +6 -6
  110. package/src/services/flux-service.ts +17 -17
  111. package/src/services/helm-service.ts +11 -11
  112. package/src/services/kubectl-service.ts +12 -12
  113. package/src/types/api.d.ts +1933 -1933
  114. package/src/types/json.d.ts +1 -1
  115. package/src/utils/config-checker.ts +6 -6
  116. package/src/utils/config-loader.ts +130 -129
  117. package/src/utils/default-api-key.ts +81 -80
  118. package/src/utils/env-manager.ts +37 -37
  119. package/src/utils/error-handler.ts +64 -64
  120. package/src/utils/logger.ts +72 -66
  121. package/src/utils/markdown-renderer.ts +28 -28
  122. package/src/utils/opencode-validator.ts +72 -71
  123. package/src/utils/token-manager.ts +69 -68
  124. package/tests/commands/chat.test.ts +32 -31
  125. package/tests/commands/code.test.ts +182 -181
  126. package/tests/utils/config-loader.test.ts +111 -110
  127. package/tests/utils/env-manager.test.ts +83 -79
  128. package/tests/utils/opencode-validator.test.ts +43 -42
  129. package/tsconfig.json +2 -1
  130. package/vitest.config.ts +2 -2
@@ -1,35 +1,36 @@
1
- import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
2
- import { Command } from "commander";
3
- import { registerCodeCommands } from "../../src/commands/code";
4
- import { ApiKeyService } from "../../src/services/api-key-service";
5
- import * as fs from "fs";
6
- import { readFile, writeFile } from "fs/promises";
7
- import { updateEnvFile } from "../../src/utils/env-manager";
1
+ import { Command } from 'commander';
2
+ import * as fs from 'node:fs';
3
+ import { readFile, writeFile } from 'node:fs/promises';
4
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
5
+
6
+ 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';
8
9
 
9
10
  // Mock dependencies
10
- vi.mock("../../src/services/api-key-service");
11
- vi.mock("fs", () => ({
11
+ vi.mock('../../src/services/api-key-service');
12
+ vi.mock('fs', () => ({
12
13
  default: {
13
14
  existsSync: vi.fn(),
14
15
  readFileSync: vi.fn(),
15
16
  },
16
17
  }));
17
- vi.mock("fs/promises", () => ({
18
+ vi.mock('fs/promises', () => ({
18
19
  readFile: vi.fn(),
19
20
  writeFile: vi.fn(),
20
21
  }));
21
- vi.mock("../../src/utils/env-manager");
22
- vi.mock("child_process", () => ({
22
+ vi.mock('../../src/utils/env-manager');
23
+ vi.mock('child_process', () => ({
23
24
  spawn: vi.fn(),
24
25
  }));
25
- vi.mock("readline", () => ({
26
+ vi.mock('readline', () => ({
26
27
  createInterface: vi.fn(() => ({
27
- question: vi.fn(),
28
28
  close: vi.fn(),
29
+ question: vi.fn(),
29
30
  })),
30
31
  }));
31
32
 
32
- describe("Code Commands", () => {
33
+ describe('Code Commands', () => {
33
34
  let program: Command;
34
35
  let mockApiKeyService: any;
35
36
  let mockFs: any;
@@ -59,7 +60,7 @@ describe("Code Commands", () => {
59
60
 
60
61
  // Mock spawn
61
62
  mockSpawn = vi.fn();
62
- vi.doMock("child_process", () => ({ spawn: mockSpawn }));
63
+ vi.doMock('child_process', () => ({ spawn: mockSpawn }));
63
64
 
64
65
  registerCodeCommands(program);
65
66
  });
@@ -68,36 +69,36 @@ describe("Code Commands", () => {
68
69
  vi.clearAllMocks();
69
70
  });
70
71
 
71
- describe("code init command", () => {
72
- it("should register init command with correct description", () => {
73
- const codeCommand = program.commands.find(cmd => cmd.name() === "code");
74
- const initCommand = codeCommand?.commands.find(cmd => cmd.name() === "init");
72
+ describe('code init command', () => {
73
+ it('should register init command with correct description', () => {
74
+ const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
75
+ const initCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'init');
75
76
 
76
77
  expect(initCommand).toBeDefined();
77
- expect(initCommand?.description()).toBe("Initialize project for AI coding assistant");
78
+ expect(initCommand?.description()).toBe('Initialize project for AI coding assistant');
78
79
  });
79
80
 
80
- it("should have name, force, and yes options", () => {
81
- const codeCommand = program.commands.find(cmd => cmd.name() === "code");
82
- const initCommand = codeCommand?.commands.find(cmd => cmd.name() === "init");
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');
83
84
 
84
85
  expect(initCommand).toBeDefined();
85
86
 
86
- const nameOption = initCommand?.options.find(opt => opt.long === "--name");
87
- const forceOption = initCommand?.options.find(opt => opt.long === "--force");
88
- const yesOption = initCommand?.options.find(opt => opt.long === "--yes");
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');
89
90
 
90
91
  expect(nameOption).toBeDefined();
91
- expect(nameOption?.description).toContain("Project name");
92
+ expect(nameOption?.description).toContain('Project name');
92
93
  expect(forceOption).toBeDefined();
93
- expect(forceOption?.description).toContain("Overwrite existing configuration");
94
+ expect(forceOption?.description).toContain('Overwrite existing configuration');
94
95
  expect(yesOption).toBeDefined();
95
- expect(yesOption?.description).toContain("Automatically answer yes");
96
+ expect(yesOption?.description).toContain('Automatically answer yes');
96
97
  });
97
98
 
98
- it("should check if opencode is installed", () => {
99
- const codeCommand = program.commands.find(cmd => cmd.name() === "code");
100
- const initCommand = codeCommand?.commands.find(cmd => cmd.name() === "init");
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');
101
102
 
102
103
  expect(initCommand).toBeDefined();
103
104
 
@@ -105,13 +106,13 @@ describe("Code Commands", () => {
105
106
  // This is tested implicitly through the spawn mock
106
107
  });
107
108
 
108
- it("should list existing API keys and allow selection", async () => {
109
+ it('should list existing API keys and allow selection', async () => {
109
110
  // Mock successful opencode installation check
110
- mockSpawn.mockImplementation((command: string, args: string[]) => {
111
- if (command === "opencode" && args[0] === "--version") {
111
+ mockSpawn.mockImplementation((command: string, arguments_: string[]) => {
112
+ if (command === 'opencode' && arguments_[0] === '--version') {
112
113
  return {
113
114
  on: vi.fn().mockImplementation((event, callback) => {
114
- if (event === "close") callback(0);
115
+ if (event === 'close') callback(0);
115
116
  }),
116
117
  };
117
118
  }
@@ -121,37 +122,37 @@ describe("Code Commands", () => {
121
122
  // Mock existing API keys
122
123
  const mockExistingKeys = [
123
124
  {
125
+ created: '2023-01-01T00:00:00.000Z',
124
126
  id: 1,
125
- name: "existing-key-1",
126
- prefix: "sk_ber",
127
- created: "2023-01-01T00:00:00.000Z",
128
127
  lastUsed: null,
128
+ name: 'existing-key-1',
129
+ prefix: 'sk_ber',
129
130
  },
130
131
  {
132
+ created: '2023-01-02T00:00:00.000Z',
131
133
  id: 2,
132
- name: "existing-key-2",
133
- prefix: "sk_ber",
134
- created: "2023-01-02T00:00:00.000Z",
135
- lastUsed: "2023-01-03T00:00:00.000Z",
134
+ lastUsed: '2023-01-03T00:00:00.000Z',
135
+ name: 'existing-key-2',
136
+ prefix: 'sk_ber',
136
137
  },
137
138
  ];
138
139
  mockApiKeyService.list.mockResolvedValue(mockExistingKeys);
139
140
 
140
141
  // Mock file operations
141
142
  mockFs.existsSync.mockReturnValue(false);
142
- mockFsPromises.writeFile.mockResolvedValue(undefined);
143
+ mockFsPromises.writeFile.mockResolvedValue();
143
144
 
144
145
  // Verify that the list method is called
145
146
  expect(mockApiKeyService.list).toBeDefined();
146
147
  });
147
148
 
148
- it("should create new API key with project-based naming", async () => {
149
+ it('should create new API key with project-based naming', async () => {
149
150
  // Mock successful opencode installation check
150
- mockSpawn.mockImplementation((command: string, args: string[]) => {
151
- if (command === "opencode" && args[0] === "--version") {
151
+ mockSpawn.mockImplementation((command: string, arguments_: string[]) => {
152
+ if (command === 'opencode' && arguments_[0] === '--version') {
152
153
  return {
153
154
  on: vi.fn().mockImplementation((event, callback) => {
154
- if (event === "close") callback(0);
155
+ if (event === 'close') callback(0);
155
156
  }),
156
157
  };
157
158
  }
@@ -164,38 +165,38 @@ describe("Code Commands", () => {
164
165
  // Mock successful API key creation
165
166
  const mockApiKeyData = {
166
167
  id: 123,
167
- name: "opencode-testproject-1234567890",
168
- key: "test-api-key-12345",
168
+ key: 'test-api-key-12345',
169
+ name: 'opencode-testproject-1234567890',
169
170
  };
170
171
  mockApiKeyService.create.mockResolvedValue(mockApiKeyData);
171
172
 
172
173
  // Mock file operations
173
174
  mockFs.existsSync.mockReturnValue(false);
174
- mockFsPromises.writeFile.mockResolvedValue(undefined);
175
+ mockFsPromises.writeFile.mockResolvedValue();
175
176
 
176
177
  // Verify that the create method is available
177
178
  expect(mockApiKeyService.create).toBeDefined();
178
179
  });
179
180
 
180
- it("should create opencode.json with correct structure", async () => {
181
+ it('should create opencode.json with correct structure', async () => {
181
182
  // This tests the expected config structure
182
183
  const expectedConfig = {
183
- model: "berget/glm-4-6",
184
- apiKey: "test-api-key",
185
- projectName: "testproject",
186
- provider: "berget",
184
+ apiKey: 'test-api-key',
187
185
  created: expect.any(String),
188
- version: "1.0.0",
186
+ model: 'berget/glm-4-6',
187
+ projectName: 'testproject',
188
+ provider: 'berget',
189
+ version: '1.0.0',
189
190
  };
190
191
 
191
- expect(expectedConfig.model).toBe("berget/glm-4-6");
192
- expect(expectedConfig.provider).toBe("berget");
193
- expect(expectedConfig.version).toBe("1.0.0");
192
+ expect(expectedConfig.model).toBe('berget/glm-4-6');
193
+ expect(expectedConfig.provider).toBe('berget');
194
+ expect(expectedConfig.version).toBe('1.0.0');
194
195
  });
195
196
 
196
- it("should handle existing config file", () => {
197
- const codeCommand = program.commands.find(cmd => cmd.name() === "code");
198
- const initCommand = codeCommand?.commands.find(cmd => cmd.name() === "init");
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');
199
200
 
200
201
  expect(initCommand).toBeDefined();
201
202
 
@@ -204,41 +205,41 @@ describe("Code Commands", () => {
204
205
  });
205
206
  });
206
207
 
207
- describe("code run command", () => {
208
- it("should register run command with correct description", () => {
209
- const codeCommand = program.commands.find(cmd => cmd.name() === "code");
210
- const runCommand = codeCommand?.commands.find(cmd => cmd.name() === "run");
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');
211
212
 
212
213
  expect(runCommand).toBeDefined();
213
- expect(runCommand?.description()).toBe("Run AI coding assistant");
214
+ expect(runCommand?.description()).toBe('Run AI coding assistant');
214
215
  });
215
216
 
216
- it("should accept prompt argument and model, no-config, and yes options", () => {
217
- const codeCommand = program.commands.find(cmd => cmd.name() === "code");
218
- const runCommand = codeCommand?.commands.find(cmd => cmd.name() === "run");
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');
219
220
 
220
221
  expect(runCommand).toBeDefined();
221
222
 
222
- const modelOption = runCommand?.options.find(opt => opt.long === "--model");
223
- const noConfigOption = runCommand?.options.find(opt => opt.long === "--no-config");
224
- const yesOption = runCommand?.options.find(opt => opt.long === "--yes");
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');
225
226
 
226
227
  expect(modelOption).toBeDefined();
227
- expect(modelOption?.description).toContain("Model to use");
228
+ expect(modelOption?.description).toContain('Model to use');
228
229
  expect(noConfigOption).toBeDefined();
229
- expect(noConfigOption?.description).toContain("Run without loading project config");
230
+ expect(noConfigOption?.description).toContain('Run without loading project config');
230
231
  expect(yesOption).toBeDefined();
231
- expect(yesOption?.description).toContain("Automatically answer yes");
232
+ expect(yesOption?.description).toContain('Automatically answer yes');
232
233
  });
233
234
 
234
- it("should load configuration from opencode.json", async () => {
235
+ it('should load configuration from opencode.json', async () => {
235
236
  const mockConfig = {
236
- model: "berget/glm-4-6",
237
- apiKey: "test-api-key",
238
- projectName: "testproject",
239
- provider: "berget",
240
- created: "2023-01-01T00:00:00.000Z",
241
- version: "1.0.0",
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',
242
243
  };
243
244
 
244
245
  // Mock file exists and contains config
@@ -246,11 +247,11 @@ describe("Code Commands", () => {
246
247
  mockFsPromises.readFile.mockResolvedValue(JSON.stringify(mockConfig));
247
248
 
248
249
  // Mock successful opencode check
249
- mockSpawn.mockImplementation((command: string, args: string[]) => {
250
- if (command === "opencode" && args[0] === "--version") {
250
+ mockSpawn.mockImplementation((command: string, arguments_: string[]) => {
251
+ if (command === 'opencode' && arguments_[0] === '--version') {
251
252
  return {
252
253
  on: vi.fn().mockImplementation((event, callback) => {
253
- if (event === "close") callback(0);
254
+ if (event === 'close') callback(0);
254
255
  }),
255
256
  };
256
257
  }
@@ -258,14 +259,14 @@ describe("Code Commands", () => {
258
259
  });
259
260
 
260
261
  // Verify config structure expectations
261
- expect(mockConfig.model).toBe("berget/glm-4-6");
262
- expect(mockConfig.apiKey).toBe("test-api-key");
263
- expect(mockConfig.projectName).toBe("testproject");
262
+ expect(mockConfig.model).toBe('berget/glm-4-6');
263
+ expect(mockConfig.apiKey).toBe('test-api-key');
264
+ expect(mockConfig.projectName).toBe('testproject');
264
265
  });
265
266
 
266
- it("should spawn opencode with correct arguments", () => {
267
- const codeCommand = program.commands.find(cmd => cmd.name() === "code");
268
- const runCommand = codeCommand?.commands.find(cmd => cmd.name() === "run");
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');
269
270
 
270
271
  expect(runCommand).toBeDefined();
271
272
 
@@ -273,9 +274,9 @@ describe("Code Commands", () => {
273
274
  expect(mockSpawn).toBeDefined();
274
275
  });
275
276
 
276
- it("should handle missing configuration file", () => {
277
- const codeCommand = program.commands.find(cmd => cmd.name() === "code");
278
- const runCommand = codeCommand?.commands.find(cmd => cmd.name() === "run");
277
+ it('should handle missing configuration file', () => {
278
+ const codeCommand = program.commands.find((cmd) => cmd.name() === 'code');
279
+ const runCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'run');
279
280
 
280
281
  expect(runCommand).toBeDefined();
281
282
 
@@ -284,19 +285,19 @@ describe("Code Commands", () => {
284
285
  });
285
286
  });
286
287
 
287
- describe("opencode installation", () => {
288
- it("should check if opencode is installed", () => {
288
+ describe('opencode installation', () => {
289
+ it('should check if opencode is installed', () => {
289
290
  // The spawn function should be called with opencode --version
290
291
  expect(mockSpawn).toBeDefined();
291
292
  });
292
293
 
293
- it("should offer to install opencode if not found", () => {
294
+ it('should offer to install opencode if not found', () => {
294
295
  // Mock opencode not installed
295
- mockSpawn.mockImplementation((command: string, args: string[]) => {
296
- if (command === "opencode" && args[0] === "--version") {
296
+ mockSpawn.mockImplementation((command: string, arguments_: string[]) => {
297
+ if (command === 'opencode' && arguments_[0] === '--version') {
297
298
  return {
298
299
  on: vi.fn().mockImplementation((event, callback) => {
299
- if (event === "close") callback(1); // Non-zero exit code
300
+ if (event === 'close') callback(1); // Non-zero exit code
300
301
  }),
301
302
  };
302
303
  }
@@ -307,200 +308,200 @@ describe("Code Commands", () => {
307
308
  expect(mockSpawn).toBeDefined();
308
309
  });
309
310
 
310
- it("should install opencode via npm if user agrees", () => {
311
+ it('should install opencode via npm if user agrees', () => {
311
312
  // Should spawn npm install -g opencode-ai
312
313
  expect(mockSpawn).toBeDefined();
313
314
  });
314
315
  });
315
316
 
316
- describe("automation support", () => {
317
- it("should support -y flag for automated initialization", () => {
318
- const codeCommand = program.commands.find(cmd => cmd.name() === "code");
319
- const initCommand = codeCommand?.commands.find(cmd => cmd.name() === "init");
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');
320
321
 
321
322
  expect(initCommand).toBeDefined();
322
323
 
323
- const yesOption = initCommand?.options.find(opt => opt.long === "--yes");
324
+ const yesOption = initCommand?.options.find((opt) => opt.long === '--yes');
324
325
  expect(yesOption).toBeDefined();
325
- expect(yesOption?.description).toContain("automation");
326
+ expect(yesOption?.description).toContain('automation');
326
327
  });
327
328
 
328
- it("should support -y flag for automated run", () => {
329
- const codeCommand = program.commands.find(cmd => cmd.name() === "code");
330
- const runCommand = codeCommand?.commands.find(cmd => cmd.name() === "run");
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');
331
332
 
332
333
  expect(runCommand).toBeDefined();
333
334
 
334
- const yesOption = runCommand?.options.find(opt => opt.long === "--yes");
335
+ const yesOption = runCommand?.options.find((opt) => opt.long === '--yes');
335
336
  expect(yesOption).toBeDefined();
336
- expect(yesOption?.description).toContain("automation");
337
+ expect(yesOption?.description).toContain('automation');
337
338
  });
338
339
 
339
- it("should use BERGET_API_KEY environment variable in automation mode", () => {
340
+ it('should use BERGET_API_KEY environment variable in automation mode', () => {
340
341
  // Test that environment variable is used when -y flag is set
341
- process.env.BERGET_API_KEY = "test-env-key";
342
+ process.env.BERGET_API_KEY = 'test-env-key';
342
343
 
343
- expect(process.env.BERGET_API_KEY).toBe("test-env-key");
344
+ expect(process.env.BERGET_API_KEY).toBe('test-env-key');
344
345
 
345
346
  // Clean up
346
347
  delete process.env.BERGET_API_KEY;
347
348
  });
348
349
  });
349
350
 
350
- describe(".env file handling", () => {
351
- let mockUpdateEnvFile: any;
351
+ describe('.env file handling', () => {
352
+ let mockUpdateEnvironmentFile: any;
352
353
 
353
354
  beforeEach(() => {
354
- mockUpdateEnvFile = vi.mocked(updateEnvFile);
355
+ mockUpdateEnvironmentFile = vi.mocked(updateEnvironmentFile);
355
356
  });
356
357
 
357
- it("should call updateEnvFile when creating new project", async () => {
358
- mockUpdateEnvFile.mockResolvedValue(true);
358
+ it('should call updateEnvFile when creating new project', async () => {
359
+ mockUpdateEnvironmentFile.mockResolvedValue(true);
359
360
  mockFs.existsSync.mockReturnValue(false); // .env doesn't exist
360
- mockFsPromises.writeFile.mockResolvedValue(undefined);
361
+ mockFsPromises.writeFile.mockResolvedValue();
361
362
 
362
363
  // This would be tested by actually calling the init command
363
364
  // For now we verify the mock is properly set up
364
- expect(mockUpdateEnvFile).toBeDefined();
365
+ expect(mockUpdateEnvironmentFile).toBeDefined();
365
366
  });
366
367
 
367
- it("should not overwrite existing BERGET_API_KEY in .env", async () => {
368
- const consoleSpy = vi.spyOn(console, "log").mockImplementation(() => {});
368
+ it('should not overwrite existing BERGET_API_KEY in .env', async () => {
369
+ const consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
369
370
 
370
371
  // Mock existing .env with BERGET_API_KEY
371
372
  mockFs.existsSync.mockReturnValue(true);
372
- mockFs.readFileSync.mockReturnValue("BERGET_API_KEY=existing_key\nOTHER_KEY=value\n");
373
+ mockFs.readFileSync.mockReturnValue('BERGET_API_KEY=existing_key\nOTHER_KEY=value\n');
373
374
 
374
375
  // Mock updateEnvFile to simulate the check
375
- mockUpdateEnvFile.mockImplementation(async (options: any) => {
376
- if (options.key === "BERGET_API_KEY" && !options.force) {
376
+ mockUpdateEnvironmentFile.mockImplementation(async (options: any) => {
377
+ if (options.key === 'BERGET_API_KEY' && !options.force) {
377
378
  console.log(`⚠ ${options.key} already exists in .env - leaving unchanged`);
378
379
  return false;
379
380
  }
380
381
  return true;
381
382
  });
382
383
 
383
- await updateEnvFile({
384
- key: "BERGET_API_KEY",
385
- value: "new_key",
384
+ await updateEnvironmentFile({
385
+ key: 'BERGET_API_KEY',
386
+ value: 'new_key',
386
387
  });
387
388
 
388
389
  expect(consoleSpy).toHaveBeenCalledWith(
389
- expect.stringContaining("BERGET_API_KEY already exists in .env - leaving unchanged")
390
+ expect.stringContaining('BERGET_API_KEY already exists in .env - leaving unchanged'),
390
391
  );
391
392
 
392
393
  consoleSpy.mockRestore();
393
394
  });
394
395
 
395
- it("should add new key to existing .env file", async () => {
396
+ it('should add new key to existing .env file', async () => {
396
397
  mockFs.existsSync.mockReturnValue(true);
397
- mockFs.readFileSync.mockReturnValue("EXISTING_KEY=value\n");
398
- mockUpdateEnvFile.mockResolvedValue(true);
398
+ mockFs.readFileSync.mockReturnValue('EXISTING_KEY=value\n');
399
+ mockUpdateEnvironmentFile.mockResolvedValue(true);
399
400
 
400
- await updateEnvFile({
401
- key: "BERGET_API_KEY",
402
- value: "new_api_key",
403
- comment: "Berget AI Configuration",
401
+ await updateEnvironmentFile({
402
+ comment: 'Berget AI Configuration',
403
+ key: 'BERGET_API_KEY',
404
+ value: 'new_api_key',
404
405
  });
405
406
 
406
- expect(mockUpdateEnvFile).toHaveBeenCalledWith({
407
- key: "BERGET_API_KEY",
408
- value: "new_api_key",
409
- comment: "Berget AI Configuration",
407
+ expect(mockUpdateEnvironmentFile).toHaveBeenCalledWith({
408
+ comment: 'Berget AI Configuration',
409
+ key: 'BERGET_API_KEY',
410
+ value: 'new_api_key',
410
411
  });
411
412
  });
412
413
 
413
- it("should create new .env file when none exists", async () => {
414
+ it('should create new .env file when none exists', async () => {
414
415
  mockFs.existsSync.mockReturnValue(false);
415
- mockUpdateEnvFile.mockResolvedValue(true);
416
+ mockUpdateEnvironmentFile.mockResolvedValue(true);
416
417
 
417
- await updateEnvFile({
418
- key: "BERGET_API_KEY",
419
- value: "new_api_key",
418
+ await updateEnvironmentFile({
419
+ key: 'BERGET_API_KEY',
420
+ value: 'new_api_key',
420
421
  });
421
422
 
422
- expect(mockUpdateEnvFile).toHaveBeenCalledWith({
423
- key: "BERGET_API_KEY",
424
- value: "new_api_key",
423
+ expect(mockUpdateEnvironmentFile).toHaveBeenCalledWith({
424
+ key: 'BERGET_API_KEY',
425
+ value: 'new_api_key',
425
426
  });
426
427
  });
427
428
  });
428
429
 
429
- describe("error handling", () => {
430
- it("should handle API key creation failures", () => {
430
+ describe('error handling', () => {
431
+ it('should handle API key creation failures', () => {
431
432
  // Mock API key service to throw error
432
- mockApiKeyService.create.mockRejectedValue(new Error("API Error"));
433
+ mockApiKeyService.create.mockRejectedValue(new Error('API Error'));
433
434
 
434
435
  expect(mockApiKeyService.create).toBeDefined();
435
436
  });
436
437
 
437
- it("should handle file system errors", () => {
438
+ it('should handle file system errors', () => {
438
439
  // Mock file operations to throw errors
439
- mockFsPromises.writeFile.mockRejectedValue(new Error("File write error"));
440
+ mockFsPromises.writeFile.mockRejectedValue(new Error('File write error'));
440
441
 
441
442
  expect(mockFsPromises.writeFile).toBeDefined();
442
443
  });
443
444
 
444
- it("should handle spawn errors", () => {
445
+ it('should handle spawn errors', () => {
445
446
  // Mock spawn to throw error
446
447
  mockSpawn.mockImplementation(() => {
447
- throw new Error("Command not found");
448
+ throw new Error('Command not found');
448
449
  });
449
450
 
450
451
  expect(mockSpawn).toBeDefined();
451
452
  });
452
453
 
453
- it("should handle .env update failures", async () => {
454
- const mockUpdateEnvFile = vi.mocked(updateEnvFile);
455
- mockUpdateEnvFile.mockRejectedValue(new Error("Env update failed"));
454
+ it('should handle .env update failures', async () => {
455
+ const mockUpdateEnvironmentFile = vi.mocked(updateEnvironmentFile);
456
+ mockUpdateEnvironmentFile.mockRejectedValue(new Error('Env update failed'));
456
457
 
457
458
  await expect(
458
- updateEnvFile({
459
- key: "TEST_KEY",
460
- value: "test_value",
461
- })
462
- ).rejects.toThrow("Env update failed");
459
+ updateEnvironmentFile({
460
+ key: 'TEST_KEY',
461
+ value: 'test_value',
462
+ }),
463
+ ).rejects.toThrow('Env update failed');
463
464
  });
464
465
  });
465
466
 
466
- describe("experimental features", () => {
467
- let originalEnv: string | undefined;
467
+ describe('experimental features', () => {
468
+ let originalEnvironment: string | undefined;
468
469
 
469
470
  beforeEach(() => {
470
- originalEnv = process.env.BERGET_EXPERIMENTAL;
471
+ originalEnvironment = process.env.BERGET_EXPERIMENTAL;
471
472
  });
472
473
 
473
474
  afterEach(() => {
474
- if (originalEnv === undefined) {
475
+ if (originalEnvironment === undefined) {
475
476
  delete process.env.BERGET_EXPERIMENTAL;
476
477
  } else {
477
- process.env.BERGET_EXPERIMENTAL = originalEnv;
478
+ process.env.BERGET_EXPERIMENTAL = originalEnvironment;
478
479
  }
479
480
  });
480
481
 
481
- it("should NOT show setup command when BERGET_EXPERIMENTAL is not set", () => {
482
+ it('should NOT show setup command when BERGET_EXPERIMENTAL is not set', () => {
482
483
  delete process.env.BERGET_EXPERIMENTAL;
483
484
 
484
485
  const freshProgram = new Command();
485
486
  registerCodeCommands(freshProgram);
486
487
 
487
- const codeCommand = freshProgram.commands.find(cmd => cmd.name() === "code");
488
- const setupCommand = codeCommand?.commands.find(cmd => cmd.name() === "setup");
488
+ const codeCommand = freshProgram.commands.find((cmd) => cmd.name() === 'code');
489
+ const setupCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'setup');
489
490
 
490
491
  expect(setupCommand).toBeUndefined();
491
492
  });
492
493
 
493
- it("should show setup command when BERGET_EXPERIMENTAL is set", () => {
494
- process.env.BERGET_EXPERIMENTAL = "1";
494
+ it('should show setup command when BERGET_EXPERIMENTAL is set', () => {
495
+ process.env.BERGET_EXPERIMENTAL = '1';
495
496
 
496
497
  const freshProgram = new Command();
497
498
  registerCodeCommands(freshProgram);
498
499
 
499
- const codeCommand = freshProgram.commands.find(cmd => cmd.name() === "code");
500
- const setupCommand = codeCommand?.commands.find(cmd => cmd.name() === "setup");
500
+ const codeCommand = freshProgram.commands.find((cmd) => cmd.name() === 'code');
501
+ const setupCommand = codeCommand?.commands.find((cmd) => cmd.name() === 'setup');
501
502
 
502
503
  expect(setupCommand).toBeDefined();
503
- expect(setupCommand?.description()).toBe("Interactive setup for Berget AI coding tools");
504
+ expect(setupCommand?.description()).toBe('Interactive setup for Berget AI coding tools');
504
505
  });
505
506
  });
506
507
  });