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