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