berget 2.2.6 → 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 (144) hide show
  1. package/.github/workflows/publish.yml +6 -6
  2. package/.github/workflows/test.yml +11 -5
  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 +28 -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 +5 -7
  30. package/dist/src/commands/code/__tests__/fake-file-store.js +9 -0
  31. package/dist/src/commands/code/__tests__/fake-prompter.js +60 -18
  32. package/dist/src/commands/code/__tests__/setup-flow.test.js +374 -107
  33. package/dist/src/commands/code/adapters/clack-prompter.js +10 -0
  34. package/dist/src/commands/code/adapters/fs-file-store.js +8 -3
  35. package/dist/src/commands/code/adapters/spawn-command-runner.js +15 -11
  36. package/dist/src/commands/code/auth-sync.js +283 -0
  37. package/dist/src/commands/code/errors.js +4 -4
  38. package/dist/src/commands/code/ports/auth-services.js +2 -0
  39. package/dist/src/commands/code/setup.js +234 -93
  40. package/dist/src/commands/code.js +139 -251
  41. package/dist/src/commands/models.js +13 -15
  42. package/dist/src/commands/users.js +6 -8
  43. package/dist/src/constants/command-structure.js +116 -116
  44. package/dist/src/services/api-key-service.js +43 -48
  45. package/dist/src/services/auth-service.js +60 -299
  46. package/dist/src/services/browser-auth.js +278 -0
  47. package/dist/src/services/chat-service.js +78 -91
  48. package/dist/src/services/cluster-service.js +6 -6
  49. package/dist/src/services/collaborator-service.js +5 -8
  50. package/dist/src/services/flux-service.js +5 -8
  51. package/dist/src/services/helm-service.js +5 -8
  52. package/dist/src/services/kubectl-service.js +7 -10
  53. package/dist/src/utils/config-checker.js +5 -5
  54. package/dist/src/utils/config-loader.js +25 -25
  55. package/dist/src/utils/default-api-key.js +23 -23
  56. package/dist/src/utils/env-manager.js +7 -7
  57. package/dist/src/utils/error-handler.js +60 -61
  58. package/dist/src/utils/logger.js +7 -7
  59. package/dist/src/utils/markdown-renderer.js +2 -2
  60. package/dist/src/utils/opencode-validator.js +17 -20
  61. package/dist/src/utils/token-manager.js +38 -11
  62. package/dist/tests/commands/chat.test.js +24 -24
  63. package/dist/tests/commands/code.test.js +147 -147
  64. package/dist/tests/utils/config-loader.test.js +114 -114
  65. package/dist/tests/utils/env-manager.test.js +57 -57
  66. package/dist/tests/utils/opencode-validator.test.js +33 -33
  67. package/dist/vitest.config.js +1 -1
  68. package/eslint.config.mjs +47 -0
  69. package/index.ts +42 -48
  70. package/package.json +28 -2
  71. package/src/agents/app.ts +27 -0
  72. package/src/agents/backend.ts +24 -0
  73. package/src/agents/devops.ts +33 -0
  74. package/src/agents/frontend.ts +24 -0
  75. package/src/agents/fullstack.ts +24 -0
  76. package/src/agents/index.ts +71 -0
  77. package/src/agents/quality.ts +69 -0
  78. package/src/agents/security.ts +26 -0
  79. package/src/agents/types.ts +17 -0
  80. package/src/client.ts +125 -167
  81. package/src/commands/api-keys.ts +261 -358
  82. package/src/commands/auth.ts +24 -30
  83. package/src/commands/autocomplete.ts +12 -12
  84. package/src/commands/billing.ts +22 -27
  85. package/src/commands/chat.ts +230 -323
  86. package/src/commands/clusters.ts +33 -33
  87. package/src/commands/code/__tests__/auth-sync.test.ts +481 -0
  88. package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
  89. package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
  90. package/src/commands/code/__tests__/fake-command-runner.ts +39 -42
  91. package/src/commands/code/__tests__/fake-file-store.ts +32 -23
  92. package/src/commands/code/__tests__/fake-prompter.ts +107 -69
  93. package/src/commands/code/__tests__/setup-flow.test.ts +624 -270
  94. package/src/commands/code/adapters/clack-prompter.ts +50 -38
  95. package/src/commands/code/adapters/fs-file-store.ts +31 -27
  96. package/src/commands/code/adapters/spawn-command-runner.ts +33 -29
  97. package/src/commands/code/auth-sync.ts +329 -0
  98. package/src/commands/code/errors.ts +15 -15
  99. package/src/commands/code/ports/auth-services.ts +14 -0
  100. package/src/commands/code/ports/command-runner.ts +8 -4
  101. package/src/commands/code/ports/file-store.ts +5 -4
  102. package/src/commands/code/ports/prompter.ts +24 -18
  103. package/src/commands/code/setup.ts +545 -317
  104. package/src/commands/code.ts +271 -473
  105. package/src/commands/index.ts +19 -19
  106. package/src/commands/models.ts +32 -37
  107. package/src/commands/users.ts +15 -22
  108. package/src/constants/command-structure.ts +119 -142
  109. package/src/services/api-key-service.ts +96 -113
  110. package/src/services/auth-service.ts +92 -339
  111. package/src/services/browser-auth.ts +296 -0
  112. package/src/services/chat-service.ts +246 -279
  113. package/src/services/cluster-service.ts +29 -32
  114. package/src/services/collaborator-service.ts +13 -18
  115. package/src/services/flux-service.ts +16 -18
  116. package/src/services/helm-service.ts +16 -18
  117. package/src/services/kubectl-service.ts +12 -14
  118. package/src/types/api.d.ts +924 -926
  119. package/src/types/json.d.ts +3 -3
  120. package/src/utils/config-checker.ts +10 -10
  121. package/src/utils/config-loader.ts +110 -127
  122. package/src/utils/default-api-key.ts +81 -93
  123. package/src/utils/env-manager.ts +36 -40
  124. package/src/utils/error-handler.ts +83 -78
  125. package/src/utils/logger.ts +41 -41
  126. package/src/utils/markdown-renderer.ts +11 -11
  127. package/src/utils/opencode-validator.ts +51 -56
  128. package/src/utils/token-manager.ts +84 -64
  129. package/templates/agents/app.md +1 -0
  130. package/templates/agents/backend.md +1 -0
  131. package/templates/agents/devops.md +2 -0
  132. package/templates/agents/frontend.md +1 -0
  133. package/templates/agents/fullstack.md +1 -0
  134. package/templates/agents/quality.md +45 -40
  135. package/templates/agents/security.md +1 -0
  136. package/tests/commands/chat.test.ts +60 -70
  137. package/tests/commands/code.test.ts +330 -376
  138. package/tests/utils/config-loader.test.ts +260 -260
  139. package/tests/utils/env-manager.test.ts +127 -134
  140. package/tests/utils/opencode-validator.test.ts +58 -63
  141. package/tsconfig.json +2 -2
  142. package/vitest.config.ts +3 -3
  143. package/AGENTS.md +0 -374
  144. package/TODO.md +0 -19
@@ -1,10 +1,10 @@
1
- import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest'
2
- import {
3
- ConfigLoader,
4
- getModelConfig,
5
- getProviderModels,
6
- getAllAgentConfigs
7
- } from '../../src/utils/config-loader'
1
+ import { describe, it, expect, beforeEach, afterEach, vi } from "vitest";
2
+ import {
3
+ ConfigLoader,
4
+ getModelConfig,
5
+ getProviderModels,
6
+ getAllAgentConfigs,
7
+ } from "../../src/utils/config-loader";
8
8
 
9
9
  // Mock fs module
10
10
  const mockFs = vi.hoisted(() => ({
@@ -12,309 +12,309 @@ const mockFs = vi.hoisted(() => ({
12
12
  readFileSync: vi.fn(),
13
13
  writeFileSync: vi.fn(),
14
14
  mkdirSync: vi.fn(),
15
- }))
15
+ }));
16
16
 
17
- vi.mock('fs', () => mockFs)
17
+ vi.mock("fs", () => mockFs);
18
18
 
19
- describe('ConfigLoader', () => {
20
- const testConfigPath = '/tmp/test-opencode.json'
21
- let configLoader: ConfigLoader
19
+ describe("ConfigLoader", () => {
20
+ const testConfigPath = "/tmp/test-opencode.json";
21
+ let configLoader: ConfigLoader;
22
22
 
23
23
  beforeEach(() => {
24
24
  // Reset mocks and clear singleton
25
- vi.clearAllMocks()
26
- ConfigLoader.clearInstance()
27
-
25
+ vi.clearAllMocks();
26
+ ConfigLoader.clearInstance();
27
+
28
28
  // Create new instance for each test using getInstance
29
- configLoader = ConfigLoader.getInstance(testConfigPath)
30
- })
29
+ configLoader = ConfigLoader.getInstance(testConfigPath);
30
+ });
31
31
 
32
32
  afterEach(() => {
33
- vi.clearAllMocks()
34
- ConfigLoader.clearInstance()
35
- })
33
+ vi.clearAllMocks();
34
+ ConfigLoader.clearInstance();
35
+ });
36
36
 
37
- describe('when config file does not exist', () => {
37
+ describe("when config file does not exist", () => {
38
38
  beforeEach(() => {
39
- mockFs.existsSync.mockReturnValue(false)
40
- })
39
+ mockFs.existsSync.mockReturnValue(false);
40
+ });
41
+
42
+ describe("getModelConfig", () => {
43
+ it("should return default values when config file does not exist", () => {
44
+ const modelConfig = configLoader.getModelConfig();
41
45
 
42
- describe('getModelConfig', () => {
43
- it('should return default values when config file does not exist', () => {
44
- const modelConfig = configLoader.getModelConfig()
45
-
46
46
  expect(modelConfig).toEqual({
47
- primary: 'berget/glm-4.7',
48
- small: 'berget/gpt-oss'
49
- })
50
- })
51
-
52
- it('should return default values when using convenience function', () => {
53
- const modelConfig = getModelConfig(testConfigPath)
54
-
47
+ primary: "berget/glm-4.7",
48
+ small: "berget/gpt-oss",
49
+ });
50
+ });
51
+
52
+ it("should return default values when using convenience function", () => {
53
+ const modelConfig = getModelConfig(testConfigPath);
54
+
55
55
  expect(modelConfig).toEqual({
56
- primary: 'berget/glm-4.7',
57
- small: 'berget/gpt-oss'
58
- })
59
- })
60
- })
61
-
62
- describe('getProviderModels', () => {
63
- it('should return default provider models when config file does not exist', () => {
64
- const models = configLoader.getProviderModels()
65
-
56
+ primary: "berget/glm-4.7",
57
+ small: "berget/gpt-oss",
58
+ });
59
+ });
60
+ });
61
+
62
+ describe("getProviderModels", () => {
63
+ it("should return default provider models when config file does not exist", () => {
64
+ const models = configLoader.getProviderModels();
65
+
66
66
  expect(models).toEqual({
67
- 'glm-4.7': {
68
- name: 'GLM-4.7',
69
- limit: { output: 4000, context: 90000 }
67
+ "glm-4.7": {
68
+ name: "GLM-4.7",
69
+ limit: { output: 4000, context: 90000 },
70
70
  },
71
- 'gpt-oss': {
72
- name: 'GPT-OSS',
71
+ "gpt-oss": {
72
+ name: "GPT-OSS",
73
73
  limit: { output: 4000, context: 128000 },
74
74
  modalities: {
75
- input: ['text', 'image'],
76
- output: ['text']
77
- }
75
+ input: ["text", "image"],
76
+ output: ["text"],
77
+ },
78
+ },
79
+ "llama-8b": {
80
+ name: "llama-3.1-8b",
81
+ limit: { output: 4000, context: 128000 },
78
82
  },
79
- 'llama-8b': {
80
- name: 'llama-3.1-8b',
81
- limit: { output: 4000, context: 128000 }
82
- }
83
- })
84
- })
85
-
86
- it('should return default provider models when using convenience function', () => {
87
- const models = getProviderModels(testConfigPath)
88
-
83
+ });
84
+ });
85
+
86
+ it("should return default provider models when using convenience function", () => {
87
+ const models = getProviderModels(testConfigPath);
88
+
89
89
  expect(models).toEqual({
90
- 'glm-4.7': {
91
- name: 'GLM-4.7',
92
- limit: { output: 4000, context: 90000 }
90
+ "glm-4.7": {
91
+ name: "GLM-4.7",
92
+ limit: { output: 4000, context: 90000 },
93
93
  },
94
- 'gpt-oss': {
95
- name: 'GPT-OSS',
94
+ "gpt-oss": {
95
+ name: "GPT-OSS",
96
96
  limit: { output: 4000, context: 128000 },
97
97
  modalities: {
98
- input: ['text', 'image'],
99
- output: ['text']
100
- }
98
+ input: ["text", "image"],
99
+ output: ["text"],
100
+ },
101
+ },
102
+ "llama-8b": {
103
+ name: "llama-3.1-8b",
104
+ limit: { output: 4000, context: 128000 },
101
105
  },
102
- 'llama-8b': {
103
- name: 'llama-3.1-8b',
104
- limit: { output: 4000, context: 128000 }
105
- }
106
- })
107
- })
108
- })
109
-
110
- describe('getAllAgentConfigs', () => {
111
- it('should return empty object when config file does not exist', () => {
112
- const agents = configLoader.getAllAgentConfigs()
113
-
114
- expect(agents).toEqual({})
115
- })
116
-
117
- it('should return empty object when using convenience function', () => {
118
- const agents = getAllAgentConfigs(testConfigPath)
119
-
120
- expect(agents).toEqual({})
121
- })
122
- })
123
-
124
- describe('getAgentConfig', () => {
125
- it('should return null when config file does not exist', () => {
126
- const agent = configLoader.getAgentConfig('fullstack')
127
-
128
- expect(agent).toBeNull()
129
- })
130
- })
131
- })
132
-
133
- describe('when config file exists', () => {
106
+ });
107
+ });
108
+ });
109
+
110
+ describe("getAllAgentConfigs", () => {
111
+ it("should return empty object when config file does not exist", () => {
112
+ const agents = configLoader.getAllAgentConfigs();
113
+
114
+ expect(agents).toEqual({});
115
+ });
116
+
117
+ it("should return empty object when using convenience function", () => {
118
+ const agents = getAllAgentConfigs(testConfigPath);
119
+
120
+ expect(agents).toEqual({});
121
+ });
122
+ });
123
+
124
+ describe("getAgentConfig", () => {
125
+ it("should return null when config file does not exist", () => {
126
+ const agent = configLoader.getAgentConfig("fullstack");
127
+
128
+ expect(agent).toBeNull();
129
+ });
130
+ });
131
+ });
132
+
133
+ describe("when config file exists", () => {
134
134
  const mockConfig = {
135
- model: 'custom-model',
136
- small_model: 'custom-small-model',
135
+ model: "custom-model",
136
+ small_model: "custom-small-model",
137
137
  agent: {
138
138
  fullstack: {
139
- model: 'custom-agent-model',
139
+ model: "custom-agent-model",
140
140
  temperature: 0.5,
141
- mode: 'primary' as const,
141
+ mode: "primary" as const,
142
142
  permission: {
143
- edit: 'allow' as const,
144
- bash: 'allow' as const,
145
- webfetch: 'allow' as const
146
- }
147
- }
143
+ edit: "allow" as const,
144
+ bash: "allow" as const,
145
+ webfetch: "allow" as const,
146
+ },
147
+ },
148
148
  },
149
149
  command: {
150
150
  test: {
151
- description: 'Test command'
152
- }
151
+ description: "Test command",
152
+ },
153
153
  },
154
154
  watcher: {
155
- ignore: ['custom-ignore']
155
+ ignore: ["custom-ignore"],
156
156
  },
157
157
  provider: {
158
158
  berget: {
159
159
  models: {
160
- 'custom-model': {
161
- name: 'Custom Model',
162
- limit: { output: 8000, context: 160000 }
163
- }
164
- }
165
- }
166
- }
167
- }
160
+ "custom-model": {
161
+ name: "Custom Model",
162
+ limit: { output: 8000, context: 160000 },
163
+ },
164
+ },
165
+ },
166
+ },
167
+ };
168
168
 
169
169
  beforeEach(() => {
170
- mockFs.existsSync.mockReturnValue(true)
171
- mockFs.readFileSync.mockReturnValue(JSON.stringify(mockConfig))
172
- })
173
-
174
- describe('getModelConfig', () => {
175
- it('should return values from config file', () => {
176
- const modelConfig = configLoader.getModelConfig()
177
-
170
+ mockFs.existsSync.mockReturnValue(true);
171
+ mockFs.readFileSync.mockReturnValue(JSON.stringify(mockConfig));
172
+ });
173
+
174
+ describe("getModelConfig", () => {
175
+ it("should return values from config file", () => {
176
+ const modelConfig = configLoader.getModelConfig();
177
+
178
178
  expect(modelConfig).toEqual({
179
- primary: 'custom-model',
180
- small: 'custom-small-model'
181
- })
182
- })
183
- })
184
-
185
- describe('getProviderModels', () => {
186
- it('should return models from config file', () => {
187
- const models = configLoader.getProviderModels()
188
-
179
+ primary: "custom-model",
180
+ small: "custom-small-model",
181
+ });
182
+ });
183
+ });
184
+
185
+ describe("getProviderModels", () => {
186
+ it("should return models from config file", () => {
187
+ const models = configLoader.getProviderModels();
188
+
189
189
  expect(models).toEqual({
190
- 'custom-model': {
191
- name: 'Custom Model',
192
- limit: { output: 8000, context: 160000 }
193
- }
194
- })
195
- })
196
- })
197
-
198
- describe('getAllAgentConfigs', () => {
199
- it('should return agents from config file', () => {
200
- const agents = configLoader.getAllAgentConfigs()
201
-
202
- expect(agents).toEqual(mockConfig.agent)
203
- })
204
- })
205
-
206
- describe('getAgentConfig', () => {
207
- it('should return specific agent from config file', () => {
208
- const agent = configLoader.getAgentConfig('fullstack')
209
-
210
- expect(agent).toEqual(mockConfig.agent.fullstack)
211
- })
212
-
213
- it('should return null for non-existent agent', () => {
214
- const agent = configLoader.getAgentConfig('nonexistent')
215
-
216
- expect(agent).toBeNull()
217
- })
218
- })
219
- })
220
-
221
- describe('when config file is invalid JSON', () => {
222
- beforeEach(() => {
223
- mockFs.existsSync.mockReturnValue(true)
224
- mockFs.readFileSync.mockReturnValue('invalid json {')
225
- })
226
-
227
- it('should fall back to defaults for getModelConfig', () => {
228
- const modelConfig = configLoader.getModelConfig()
229
-
230
- expect(modelConfig).toEqual({
231
- primary: 'berget/glm-4.7',
232
- small: 'berget/gpt-oss'
233
- })
234
- })
235
-
236
- it('should fall back to defaults for getProviderModels', () => {
237
- const models = configLoader.getProviderModels()
238
-
239
- expect(models).toEqual({
240
- 'glm-4.7': {
241
- name: 'GLM-4.7',
242
- limit: { output: 4000, context: 90000 }
190
+ "custom-model": {
191
+ name: "Custom Model",
192
+ limit: { output: 8000, context: 160000 },
243
193
  },
244
- 'gpt-oss': {
245
- name: 'GPT-OSS',
194
+ });
195
+ });
196
+ });
197
+
198
+ describe("getAllAgentConfigs", () => {
199
+ it("should return agents from config file", () => {
200
+ const agents = configLoader.getAllAgentConfigs();
201
+
202
+ expect(agents).toEqual(mockConfig.agent);
203
+ });
204
+ });
205
+
206
+ describe("getAgentConfig", () => {
207
+ it("should return specific agent from config file", () => {
208
+ const agent = configLoader.getAgentConfig("fullstack");
209
+
210
+ expect(agent).toEqual(mockConfig.agent.fullstack);
211
+ });
212
+
213
+ it("should return null for non-existent agent", () => {
214
+ const agent = configLoader.getAgentConfig("nonexistent");
215
+
216
+ expect(agent).toBeNull();
217
+ });
218
+ });
219
+ });
220
+
221
+ describe("when config file is invalid JSON", () => {
222
+ beforeEach(() => {
223
+ mockFs.existsSync.mockReturnValue(true);
224
+ mockFs.readFileSync.mockReturnValue("invalid json {");
225
+ });
226
+
227
+ it("should fall back to defaults for getModelConfig", () => {
228
+ const modelConfig = configLoader.getModelConfig();
229
+
230
+ expect(modelConfig).toEqual({
231
+ primary: "berget/glm-4.7",
232
+ small: "berget/gpt-oss",
233
+ });
234
+ });
235
+
236
+ it("should fall back to defaults for getProviderModels", () => {
237
+ const models = configLoader.getProviderModels();
238
+
239
+ expect(models).toEqual({
240
+ "glm-4.7": {
241
+ name: "GLM-4.7",
242
+ limit: { output: 4000, context: 90000 },
243
+ },
244
+ "gpt-oss": {
245
+ name: "GPT-OSS",
246
246
  limit: { output: 4000, context: 128000 },
247
247
  modalities: {
248
- input: ['text', 'image'],
249
- output: ['text']
250
- }
248
+ input: ["text", "image"],
249
+ output: ["text"],
250
+ },
251
+ },
252
+ "llama-8b": {
253
+ name: "llama-3.1-8b",
254
+ limit: { output: 4000, context: 128000 },
251
255
  },
252
- 'llama-8b': {
253
- name: 'llama-3.1-8b',
254
- limit: { output: 4000, context: 128000 }
255
- }
256
- })
257
- })
258
-
259
- it('should fall back to defaults for getAllAgentConfigs', () => {
260
- const agents = configLoader.getAllAgentConfigs()
261
-
262
- expect(agents).toEqual({})
263
- })
264
- })
265
-
266
- describe('singleton pattern', () => {
267
- it('should return the same instance for same path', () => {
268
- ConfigLoader.clearInstance()
269
- const loader1 = ConfigLoader.getInstance(testConfigPath)
270
- const loader2 = ConfigLoader.getInstance(testConfigPath)
271
-
272
- expect(loader1).toBe(loader2)
273
- })
274
-
275
- it('should return the same instance even for different paths (true singleton)', () => {
276
- ConfigLoader.clearInstance()
277
- const loader1 = ConfigLoader.getInstance('/path1/config.json')
278
- const loader2 = ConfigLoader.getInstance('/path2/config.json')
279
-
256
+ });
257
+ });
258
+
259
+ it("should fall back to defaults for getAllAgentConfigs", () => {
260
+ const agents = configLoader.getAllAgentConfigs();
261
+
262
+ expect(agents).toEqual({});
263
+ });
264
+ });
265
+
266
+ describe("singleton pattern", () => {
267
+ it("should return the same instance for same path", () => {
268
+ ConfigLoader.clearInstance();
269
+ const loader1 = ConfigLoader.getInstance(testConfigPath);
270
+ const loader2 = ConfigLoader.getInstance(testConfigPath);
271
+
272
+ expect(loader1).toBe(loader2);
273
+ });
274
+
275
+ it("should return the same instance even for different paths (true singleton)", () => {
276
+ ConfigLoader.clearInstance();
277
+ const loader1 = ConfigLoader.getInstance("/path1/config.json");
278
+ const loader2 = ConfigLoader.getInstance("/path2/config.json");
279
+
280
280
  // ConfigLoader is a true singleton - it returns the same instance regardless of path
281
- expect(loader1).toBe(loader2)
282
- })
283
- })
281
+ expect(loader1).toBe(loader2);
282
+ });
283
+ });
284
284
 
285
- describe('init scenario regression tests', () => {
286
- it('should handle missing config file during init scenario', () => {
285
+ describe("init scenario regression tests", () => {
286
+ it("should handle missing config file during init scenario", () => {
287
287
  // This test specifically verifies the fix for the init issue
288
- mockFs.existsSync.mockReturnValue(false)
289
-
288
+ mockFs.existsSync.mockReturnValue(false);
289
+
290
290
  // All these methods should work without throwing errors
291
- expect(() => configLoader.getModelConfig()).not.toThrow()
292
- expect(() => configLoader.getProviderModels()).not.toThrow()
293
- expect(() => configLoader.getAllAgentConfigs()).not.toThrow()
294
- expect(() => configLoader.getAgentConfig('fullstack')).not.toThrow()
295
-
291
+ expect(() => configLoader.getModelConfig()).not.toThrow();
292
+ expect(() => configLoader.getProviderModels()).not.toThrow();
293
+ expect(() => configLoader.getAllAgentConfigs()).not.toThrow();
294
+ expect(() => configLoader.getAgentConfig("fullstack")).not.toThrow();
295
+
296
296
  // And return sensible defaults
297
297
  expect(configLoader.getModelConfig()).toEqual({
298
- primary: 'berget/glm-4.7',
299
- small: 'berget/gpt-oss'
300
- })
301
- expect(configLoader.getAllAgentConfigs()).toEqual({})
302
- expect(configLoader.getAgentConfig('fullstack')).toBeNull()
303
- })
304
-
305
- it('should work with convenience functions during init scenario', () => {
298
+ primary: "berget/glm-4.7",
299
+ small: "berget/gpt-oss",
300
+ });
301
+ expect(configLoader.getAllAgentConfigs()).toEqual({});
302
+ expect(configLoader.getAgentConfig("fullstack")).toBeNull();
303
+ });
304
+
305
+ it("should work with convenience functions during init scenario", () => {
306
306
  // This test verifies that convenience functions also work during init
307
- mockFs.existsSync.mockReturnValue(false)
308
-
309
- expect(() => getModelConfig(testConfigPath)).not.toThrow()
310
- expect(() => getProviderModels(testConfigPath)).not.toThrow()
311
- expect(() => getAllAgentConfigs(testConfigPath)).not.toThrow()
312
-
307
+ mockFs.existsSync.mockReturnValue(false);
308
+
309
+ expect(() => getModelConfig(testConfigPath)).not.toThrow();
310
+ expect(() => getProviderModels(testConfigPath)).not.toThrow();
311
+ expect(() => getAllAgentConfigs(testConfigPath)).not.toThrow();
312
+
313
313
  expect(getModelConfig(testConfigPath)).toEqual({
314
- primary: 'berget/glm-4.7',
315
- small: 'berget/gpt-oss'
316
- })
317
- expect(getAllAgentConfigs(testConfigPath)).toEqual({})
318
- })
319
- })
320
- })
314
+ primary: "berget/glm-4.7",
315
+ small: "berget/gpt-oss",
316
+ });
317
+ expect(getAllAgentConfigs(testConfigPath)).toEqual({});
318
+ });
319
+ });
320
+ });