berget 2.2.5 → 2.2.7

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