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