berget 1.3.1 → 2.0.0
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/.env.example +5 -0
- package/.github/workflows/publish.yml +56 -0
- package/.github/workflows/test.yml +38 -0
- package/AGENTS.md +184 -0
- package/README.md +177 -38
- package/TODO.md +2 -0
- package/blog-post.md +176 -0
- package/dist/index.js +11 -8
- package/dist/package.json +14 -3
- package/dist/src/commands/api-keys.js +4 -2
- package/dist/src/commands/chat.js +182 -23
- package/dist/src/commands/code.js +1424 -0
- package/dist/src/commands/index.js +2 -0
- package/dist/src/constants/command-structure.js +12 -0
- package/dist/src/schemas/opencode-schema.json +1121 -0
- package/dist/src/services/chat-service.js +10 -10
- package/dist/src/services/cluster-service.js +1 -1
- package/dist/src/utils/default-api-key.js +2 -2
- package/dist/src/utils/env-manager.js +86 -0
- package/dist/src/utils/error-handler.js +10 -3
- package/dist/src/utils/markdown-renderer.js +4 -4
- package/dist/src/utils/opencode-validator.js +122 -0
- package/dist/src/utils/token-manager.js +2 -2
- package/dist/tests/commands/chat.test.js +109 -0
- package/dist/tests/commands/code.test.js +414 -0
- package/dist/tests/utils/env-manager.test.js +148 -0
- package/dist/tests/utils/opencode-validator.test.js +103 -0
- package/dist/vitest.config.js +9 -0
- package/index.ts +67 -32
- package/opencode.json +182 -0
- package/package.json +14 -3
- package/src/client.ts +20 -20
- package/src/commands/api-keys.ts +93 -60
- package/src/commands/auth.ts +4 -2
- package/src/commands/billing.ts +6 -3
- package/src/commands/chat.ts +291 -97
- package/src/commands/clusters.ts +2 -2
- package/src/commands/code.ts +1696 -0
- package/src/commands/index.ts +2 -0
- package/src/commands/models.ts +3 -3
- package/src/commands/users.ts +2 -2
- package/src/constants/command-structure.ts +112 -58
- package/src/schemas/opencode-schema.json +991 -0
- package/src/services/api-key-service.ts +1 -1
- package/src/services/auth-service.ts +27 -25
- package/src/services/chat-service.ts +37 -44
- package/src/services/cluster-service.ts +5 -5
- package/src/services/collaborator-service.ts +3 -3
- package/src/services/flux-service.ts +2 -2
- package/src/services/helm-service.ts +2 -2
- package/src/services/kubectl-service.ts +3 -6
- package/src/types/api.d.ts +1032 -1010
- package/src/types/json.d.ts +3 -3
- package/src/utils/default-api-key.ts +54 -42
- package/src/utils/env-manager.ts +98 -0
- package/src/utils/error-handler.ts +24 -15
- package/src/utils/logger.ts +12 -12
- package/src/utils/markdown-renderer.ts +18 -18
- package/src/utils/opencode-validator.ts +134 -0
- package/src/utils/token-manager.ts +35 -23
- package/tests/commands/chat.test.ts +129 -0
- package/tests/commands/code.test.ts +505 -0
- package/tests/utils/env-manager.test.ts +199 -0
- package/tests/utils/opencode-validator.test.ts +118 -0
- package/tsconfig.json +8 -8
- package/vitest.config.ts +8 -0
- package/-27b-it +0 -0
|
@@ -0,0 +1,1424 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || function (mod) {
|
|
19
|
+
if (mod && mod.__esModule) return mod;
|
|
20
|
+
var result = {};
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
23
|
+
return result;
|
|
24
|
+
};
|
|
25
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
26
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
27
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
28
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
29
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
30
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
31
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
32
|
+
});
|
|
33
|
+
};
|
|
34
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
35
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
36
|
+
};
|
|
37
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
+
exports.registerCodeCommands = void 0;
|
|
39
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
40
|
+
const readline_1 = __importDefault(require("readline"));
|
|
41
|
+
const command_structure_1 = require("../constants/command-structure");
|
|
42
|
+
const api_key_service_1 = require("../services/api-key-service");
|
|
43
|
+
const auth_service_1 = require("../services/auth-service");
|
|
44
|
+
const error_handler_1 = require("../utils/error-handler");
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const promises_1 = require("fs/promises");
|
|
47
|
+
const path_1 = __importDefault(require("path"));
|
|
48
|
+
const child_process_1 = require("child_process");
|
|
49
|
+
const env_manager_1 = require("../utils/env-manager");
|
|
50
|
+
const client_1 = require("../client");
|
|
51
|
+
// Centralized model configuration
|
|
52
|
+
const MODEL_CONFIG = {
|
|
53
|
+
// Model names used in agent configurations (with provider prefix)
|
|
54
|
+
AGENT_MODELS: {
|
|
55
|
+
primary: 'berget/deepseek-r1',
|
|
56
|
+
small: 'berget/gpt-oss',
|
|
57
|
+
},
|
|
58
|
+
// Model definitions in provider configuration (without prefix)
|
|
59
|
+
PROVIDER_MODELS: {
|
|
60
|
+
'deepseek-r1': {
|
|
61
|
+
name: 'GLM-4.6',
|
|
62
|
+
limit: {
|
|
63
|
+
output: 4000,
|
|
64
|
+
context: 90000,
|
|
65
|
+
},
|
|
66
|
+
},
|
|
67
|
+
'gpt-oss': {
|
|
68
|
+
name: 'GPT-OSS',
|
|
69
|
+
limit: {
|
|
70
|
+
output: 4000,
|
|
71
|
+
context: 128000,
|
|
72
|
+
},
|
|
73
|
+
},
|
|
74
|
+
'llama-8b': {
|
|
75
|
+
name: 'llama-3.1-8b',
|
|
76
|
+
limit: {
|
|
77
|
+
output: 4000,
|
|
78
|
+
context: 128000,
|
|
79
|
+
},
|
|
80
|
+
},
|
|
81
|
+
},
|
|
82
|
+
};
|
|
83
|
+
/**
|
|
84
|
+
* Check if current directory has git
|
|
85
|
+
*/
|
|
86
|
+
function hasGit() {
|
|
87
|
+
try {
|
|
88
|
+
return fs.existsSync(path_1.default.join(process.cwd(), '.git'));
|
|
89
|
+
}
|
|
90
|
+
catch (_a) {
|
|
91
|
+
return false;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* Merge opencode configurations using chat completions API
|
|
96
|
+
*/
|
|
97
|
+
function mergeConfigurations(currentConfig, latestConfig) {
|
|
98
|
+
var _a, _b, _c, _d;
|
|
99
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
100
|
+
try {
|
|
101
|
+
const client = (0, client_1.createAuthenticatedClient)();
|
|
102
|
+
console.log(chalk_1.default.blue('🤖 Using AI to merge configurations...'));
|
|
103
|
+
const mergePrompt = `You are a configuration merge specialist. Merge these two OpenCode configurations:
|
|
104
|
+
|
|
105
|
+
CURRENT CONFIG (user's customizations):
|
|
106
|
+
${JSON.stringify(currentConfig, null, 2)}
|
|
107
|
+
|
|
108
|
+
LATEST CONFIG (new updates):
|
|
109
|
+
${JSON.stringify(latestConfig, null, 2)}
|
|
110
|
+
|
|
111
|
+
Merge rules:
|
|
112
|
+
1. Preserve ALL user customizations from current config
|
|
113
|
+
2. Add ALL new features and improvements from latest config
|
|
114
|
+
3. For conflicts, prefer user's customizations but add new latest features
|
|
115
|
+
4. Maintain valid JSON structure
|
|
116
|
+
5. Keep the merged configuration complete and functional
|
|
117
|
+
|
|
118
|
+
Return ONLY the merged JSON configuration, no explanations.`;
|
|
119
|
+
const response = yield client.POST('/v1/chat/completions', {
|
|
120
|
+
body: {
|
|
121
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
122
|
+
messages: [
|
|
123
|
+
{
|
|
124
|
+
role: 'user',
|
|
125
|
+
content: mergePrompt,
|
|
126
|
+
},
|
|
127
|
+
],
|
|
128
|
+
temperature: 0.1,
|
|
129
|
+
max_tokens: 8000,
|
|
130
|
+
},
|
|
131
|
+
});
|
|
132
|
+
if (response.error) {
|
|
133
|
+
console.warn(chalk_1.default.yellow('⚠️ AI merge failed, using fallback merge'));
|
|
134
|
+
return fallbackMerge(currentConfig, latestConfig);
|
|
135
|
+
}
|
|
136
|
+
const content = (_d = (_c = (_b = (_a = response.data) === null || _a === void 0 ? void 0 : _a.choices) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.content;
|
|
137
|
+
if (!content) {
|
|
138
|
+
console.warn(chalk_1.default.yellow('⚠️ No AI response, using fallback merge'));
|
|
139
|
+
return fallbackMerge(currentConfig, latestConfig);
|
|
140
|
+
}
|
|
141
|
+
try {
|
|
142
|
+
const mergedConfig = JSON.parse(content.trim());
|
|
143
|
+
console.log(chalk_1.default.green('✓ AI merge completed successfully'));
|
|
144
|
+
return mergedConfig;
|
|
145
|
+
}
|
|
146
|
+
catch (parseError) {
|
|
147
|
+
console.warn(chalk_1.default.yellow('⚠️ AI response invalid, using fallback merge'));
|
|
148
|
+
return fallbackMerge(currentConfig, latestConfig);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
catch (error) {
|
|
152
|
+
console.warn(chalk_1.default.yellow('⚠️ AI merge unavailable, using fallback merge'));
|
|
153
|
+
return fallbackMerge(currentConfig, latestConfig);
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* Fallback merge logic when AI merge is unavailable
|
|
159
|
+
*/
|
|
160
|
+
function fallbackMerge(currentConfig, latestConfig) {
|
|
161
|
+
console.log(chalk_1.default.blue('🔀 Using fallback merge logic...'));
|
|
162
|
+
const merged = Object.assign({}, latestConfig);
|
|
163
|
+
// Preserve user customizations
|
|
164
|
+
if (currentConfig.theme && currentConfig.theme !== latestConfig.theme) {
|
|
165
|
+
merged.theme = currentConfig.theme;
|
|
166
|
+
}
|
|
167
|
+
if (currentConfig.share && currentConfig.share !== latestConfig.share) {
|
|
168
|
+
merged.share = currentConfig.share;
|
|
169
|
+
}
|
|
170
|
+
// Merge custom agents while preserving new ones
|
|
171
|
+
if (currentConfig.agent) {
|
|
172
|
+
merged.agent = Object.assign({}, latestConfig.agent);
|
|
173
|
+
// Add any custom agents from current config
|
|
174
|
+
Object.keys(currentConfig.agent).forEach((agentName) => {
|
|
175
|
+
if (!latestConfig.agent[agentName]) {
|
|
176
|
+
merged.agent[agentName] = currentConfig.agent[agentName];
|
|
177
|
+
console.log(chalk_1.default.cyan(` • Preserved custom agent: ${agentName}`));
|
|
178
|
+
}
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
// Merge custom commands while preserving new ones
|
|
182
|
+
if (currentConfig.commands) {
|
|
183
|
+
merged.commands = Object.assign({}, latestConfig.commands);
|
|
184
|
+
Object.keys(currentConfig.commands).forEach((commandName) => {
|
|
185
|
+
if (!latestConfig.commands[commandName]) {
|
|
186
|
+
merged.commands[commandName] = currentConfig.commands[commandName];
|
|
187
|
+
console.log(chalk_1.default.cyan(` • Preserved custom command: ${commandName}`));
|
|
188
|
+
}
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// Preserve custom provider settings if user has modified them
|
|
192
|
+
if (currentConfig.provider) {
|
|
193
|
+
merged.provider = Object.assign({}, latestConfig.provider);
|
|
194
|
+
// Deep merge provider settings
|
|
195
|
+
Object.keys(currentConfig.provider).forEach((providerName) => {
|
|
196
|
+
if (merged.provider[providerName]) {
|
|
197
|
+
merged.provider[providerName] = Object.assign(Object.assign({}, merged.provider[providerName]), currentConfig.provider[providerName]);
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
merged.provider[providerName] = currentConfig.provider[providerName];
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
return merged;
|
|
205
|
+
}
|
|
206
|
+
/**
|
|
207
|
+
* Helper function to get user confirmation
|
|
208
|
+
*/
|
|
209
|
+
function confirm(question, autoYes = false) {
|
|
210
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
211
|
+
if (autoYes) {
|
|
212
|
+
return true;
|
|
213
|
+
}
|
|
214
|
+
return new Promise((resolve) => {
|
|
215
|
+
const rl = readline_1.default.createInterface({
|
|
216
|
+
input: process.stdin,
|
|
217
|
+
output: process.stdout,
|
|
218
|
+
});
|
|
219
|
+
rl.question(question, (answer) => {
|
|
220
|
+
rl.close();
|
|
221
|
+
resolve(answer.toLowerCase() === 'y' ||
|
|
222
|
+
answer.toLowerCase() === 'yes' ||
|
|
223
|
+
answer === '');
|
|
224
|
+
});
|
|
225
|
+
});
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Helper function to get user choice from options
|
|
230
|
+
*/
|
|
231
|
+
function askChoice(question, options, defaultChoice) {
|
|
232
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
233
|
+
return new Promise((resolve) => {
|
|
234
|
+
const rl = readline_1.default.createInterface({
|
|
235
|
+
input: process.stdin,
|
|
236
|
+
output: process.stdout,
|
|
237
|
+
});
|
|
238
|
+
rl.question(question, (answer) => {
|
|
239
|
+
rl.close();
|
|
240
|
+
const trimmed = answer.trim().toLowerCase();
|
|
241
|
+
// Handle numeric input (1, 2, etc.)
|
|
242
|
+
const numericIndex = parseInt(trimmed) - 1;
|
|
243
|
+
if (numericIndex >= 0 && numericIndex < options.length) {
|
|
244
|
+
resolve(options[numericIndex]);
|
|
245
|
+
return;
|
|
246
|
+
}
|
|
247
|
+
// Handle text input
|
|
248
|
+
const matchingOption = options.find((option) => option.toLowerCase().startsWith(trimmed));
|
|
249
|
+
if (matchingOption) {
|
|
250
|
+
resolve(matchingOption);
|
|
251
|
+
}
|
|
252
|
+
else if (defaultChoice) {
|
|
253
|
+
resolve(defaultChoice);
|
|
254
|
+
}
|
|
255
|
+
else {
|
|
256
|
+
resolve(options[0]); // Default to first option
|
|
257
|
+
}
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Helper function to get user input
|
|
264
|
+
*/
|
|
265
|
+
function getInput(question, defaultValue, autoYes = false) {
|
|
266
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
267
|
+
if (autoYes) {
|
|
268
|
+
return defaultValue;
|
|
269
|
+
}
|
|
270
|
+
const rl = readline_1.default.createInterface({
|
|
271
|
+
input: process.stdin,
|
|
272
|
+
output: process.stdout,
|
|
273
|
+
});
|
|
274
|
+
return new Promise((resolve) => {
|
|
275
|
+
rl.question(question, (answer) => {
|
|
276
|
+
rl.close();
|
|
277
|
+
resolve(answer.trim() || defaultValue);
|
|
278
|
+
});
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Get project name from current directory or package.json
|
|
284
|
+
*/
|
|
285
|
+
function getProjectName() {
|
|
286
|
+
try {
|
|
287
|
+
const packageJsonPath = path_1.default.join(process.cwd(), 'package.json');
|
|
288
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
289
|
+
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
|
|
290
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
291
|
+
return packageJson.name || path_1.default.basename(process.cwd());
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
catch (error) {
|
|
295
|
+
// Ignore error and fallback to directory name
|
|
296
|
+
}
|
|
297
|
+
return path_1.default.basename(process.cwd());
|
|
298
|
+
}
|
|
299
|
+
/**
|
|
300
|
+
* Check if opencode is installed
|
|
301
|
+
*/
|
|
302
|
+
function checkOpencodeInstalled() {
|
|
303
|
+
return new Promise((resolve) => {
|
|
304
|
+
const child = (0, child_process_1.spawn)('opencode', ['--version'], {
|
|
305
|
+
stdio: 'pipe',
|
|
306
|
+
shell: true,
|
|
307
|
+
});
|
|
308
|
+
child.on('close', (code) => {
|
|
309
|
+
resolve(code === 0);
|
|
310
|
+
});
|
|
311
|
+
child.on('error', () => {
|
|
312
|
+
resolve(false);
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
}
|
|
316
|
+
/**
|
|
317
|
+
* Install opencode via npm
|
|
318
|
+
*/
|
|
319
|
+
function installOpencode() {
|
|
320
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
321
|
+
console.log(chalk_1.default.cyan('Installing OpenCode via npm...'));
|
|
322
|
+
try {
|
|
323
|
+
yield new Promise((resolve, reject) => {
|
|
324
|
+
const install = (0, child_process_1.spawn)('npm', ['install', '-g', 'opencode-ai'], {
|
|
325
|
+
stdio: 'inherit',
|
|
326
|
+
shell: true,
|
|
327
|
+
});
|
|
328
|
+
install.on('close', (code) => {
|
|
329
|
+
if (code === 0) {
|
|
330
|
+
console.log(chalk_1.default.green('✓ OpenCode installed successfully!'));
|
|
331
|
+
resolve();
|
|
332
|
+
}
|
|
333
|
+
else {
|
|
334
|
+
reject(new Error(`Installation failed with code ${code}`));
|
|
335
|
+
}
|
|
336
|
+
});
|
|
337
|
+
install.on('error', reject);
|
|
338
|
+
});
|
|
339
|
+
// Verify installation
|
|
340
|
+
const opencodeInstalled = yield checkOpencodeInstalled();
|
|
341
|
+
if (!opencodeInstalled) {
|
|
342
|
+
console.log(chalk_1.default.yellow('Installation completed but opencode command not found.'));
|
|
343
|
+
console.log(chalk_1.default.yellow('You may need to restart your terminal or check your PATH.'));
|
|
344
|
+
return false;
|
|
345
|
+
}
|
|
346
|
+
return true;
|
|
347
|
+
}
|
|
348
|
+
catch (error) {
|
|
349
|
+
console.error(chalk_1.default.red('Failed to install OpenCode:'));
|
|
350
|
+
console.error(error instanceof Error ? error.message : String(error));
|
|
351
|
+
console.log(chalk_1.default.blue('\nAlternative installation methods:'));
|
|
352
|
+
console.log(chalk_1.default.blue(' curl -fsSL https://opencode.ai/install | bash'));
|
|
353
|
+
console.log(chalk_1.default.blue(' Or visit: https://opencode.ai/docs'));
|
|
354
|
+
return false;
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
}
|
|
358
|
+
/**
|
|
359
|
+
* Ensure opencode is installed, offering to install if not
|
|
360
|
+
*/
|
|
361
|
+
function ensureOpencodeInstalled(autoYes = false) {
|
|
362
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
363
|
+
let opencodeInstalled = yield checkOpencodeInstalled();
|
|
364
|
+
if (!opencodeInstalled) {
|
|
365
|
+
if (!autoYes) {
|
|
366
|
+
console.log(chalk_1.default.red('OpenCode is not installed.'));
|
|
367
|
+
console.log(chalk_1.default.blue('OpenCode is required for the AI coding assistant.'));
|
|
368
|
+
}
|
|
369
|
+
if (yield confirm('Would you like to install OpenCode automatically? (Y/n): ', autoYes)) {
|
|
370
|
+
opencodeInstalled = yield installOpencode();
|
|
371
|
+
}
|
|
372
|
+
else {
|
|
373
|
+
if (!autoYes) {
|
|
374
|
+
console.log(chalk_1.default.blue('\nInstallation cancelled.'));
|
|
375
|
+
console.log(chalk_1.default.blue('To install manually: curl -fsSL https://opencode.ai/install | bash'));
|
|
376
|
+
console.log(chalk_1.default.blue('Or visit: https://opencode.ai/docs'));
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return opencodeInstalled;
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
/**
|
|
384
|
+
* Register code commands
|
|
385
|
+
*/
|
|
386
|
+
function registerCodeCommands(program) {
|
|
387
|
+
const code = program
|
|
388
|
+
.command(command_structure_1.COMMAND_GROUPS.CODE)
|
|
389
|
+
.description('AI-powered coding assistant with OpenCode');
|
|
390
|
+
code
|
|
391
|
+
.command(command_structure_1.SUBCOMMANDS.CODE.INIT)
|
|
392
|
+
.description('Initialize project for AI coding assistant')
|
|
393
|
+
.option('-n, --name <name>', 'Project name (defaults to directory name)')
|
|
394
|
+
.option('-f, --force', 'Overwrite existing configuration')
|
|
395
|
+
.option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
|
|
396
|
+
.action((options) => __awaiter(this, void 0, void 0, function* () {
|
|
397
|
+
try {
|
|
398
|
+
const projectName = options.name || getProjectName();
|
|
399
|
+
const configPath = path_1.default.join(process.cwd(), 'opencode.json');
|
|
400
|
+
// Check if already initialized
|
|
401
|
+
if (fs.existsSync(configPath) && !options.force) {
|
|
402
|
+
if (!options.yes) {
|
|
403
|
+
console.log(chalk_1.default.yellow('Project already initialized for OpenCode.'));
|
|
404
|
+
console.log(chalk_1.default.dim(`Config file: ${configPath}`));
|
|
405
|
+
}
|
|
406
|
+
if (yield confirm('Do you want to reinitialize? (Y/n): ', options.yes)) {
|
|
407
|
+
// Continue with reinitialization
|
|
408
|
+
}
|
|
409
|
+
else {
|
|
410
|
+
return;
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
// Ensure opencode is installed
|
|
414
|
+
if (!(yield ensureOpencodeInstalled(options.yes))) {
|
|
415
|
+
return;
|
|
416
|
+
}
|
|
417
|
+
// Check if we have an API key in environment first
|
|
418
|
+
if (process.env.BERGET_API_KEY) {
|
|
419
|
+
console.log(chalk_1.default.blue('🔑 Using BERGET_API_KEY from environment - no authentication required'));
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
// Only require authentication if we don't have an API key
|
|
423
|
+
try {
|
|
424
|
+
const authService = auth_service_1.AuthService.getInstance();
|
|
425
|
+
// This will throw if not authenticated
|
|
426
|
+
yield authService.whoami();
|
|
427
|
+
}
|
|
428
|
+
catch (error) {
|
|
429
|
+
console.log(chalk_1.default.red('❌ Not authenticated with Berget AI.'));
|
|
430
|
+
console.log(chalk_1.default.blue('To get started, you have two options:'));
|
|
431
|
+
console.log('');
|
|
432
|
+
console.log(chalk_1.default.yellow('Option 1: Use an existing API key (recommended)'));
|
|
433
|
+
console.log(chalk_1.default.cyan(' Set BERGET_API_KEY environment variable:'));
|
|
434
|
+
console.log(chalk_1.default.dim(' export BERGET_API_KEY=your_api_key_here'));
|
|
435
|
+
console.log(chalk_1.default.cyan(' Or create a .env file in your project:'));
|
|
436
|
+
console.log(chalk_1.default.dim(' echo "BERGET_API_KEY=your_api_key_here" > .env'));
|
|
437
|
+
console.log('');
|
|
438
|
+
console.log(chalk_1.default.yellow('Option 2: Login and create a new API key'));
|
|
439
|
+
console.log(chalk_1.default.cyan(' berget auth login'));
|
|
440
|
+
console.log(chalk_1.default.cyan(` berget ${command_structure_1.COMMAND_GROUPS.CODE} ${command_structure_1.SUBCOMMANDS.CODE.INIT}`));
|
|
441
|
+
console.log('');
|
|
442
|
+
console.log(chalk_1.default.blue('Then try again.'));
|
|
443
|
+
return;
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
console.log(chalk_1.default.cyan(`Initializing OpenCode for project: ${projectName}`));
|
|
447
|
+
// Handle API key selection or creation
|
|
448
|
+
let apiKey;
|
|
449
|
+
let keyName;
|
|
450
|
+
try {
|
|
451
|
+
const apiKeyService = api_key_service_1.ApiKeyService.getInstance();
|
|
452
|
+
// Check for environment variable first (regardless of automation mode)
|
|
453
|
+
if (process.env.BERGET_API_KEY) {
|
|
454
|
+
console.log(chalk_1.default.blue('🔑 Using BERGET_API_KEY from environment'));
|
|
455
|
+
apiKey = process.env.BERGET_API_KEY;
|
|
456
|
+
keyName = `env-key-${projectName}`;
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
// List existing API keys
|
|
460
|
+
if (!options.yes) {
|
|
461
|
+
console.log(chalk_1.default.blue('\n📋 Checking existing API keys...'));
|
|
462
|
+
}
|
|
463
|
+
const existingKeys = yield apiKeyService.list();
|
|
464
|
+
if (existingKeys.length > 0 && !options.yes) {
|
|
465
|
+
console.log(chalk_1.default.blue('Found existing API keys:'));
|
|
466
|
+
console.log(chalk_1.default.dim('─'.repeat(60)));
|
|
467
|
+
existingKeys.forEach((key, index) => {
|
|
468
|
+
console.log(`${chalk_1.default.cyan((index + 1).toString())}. ${chalk_1.default.bold(key.name)} (${key.prefix}...)`);
|
|
469
|
+
console.log(chalk_1.default.dim(` Created: ${new Date(key.created).toLocaleDateString('sv-SE')}`));
|
|
470
|
+
console.log(chalk_1.default.dim(` Last used: ${key.lastUsed ? new Date(key.lastUsed).toLocaleDateString('sv-SE') : 'Never'}`));
|
|
471
|
+
if (index < existingKeys.length - 1)
|
|
472
|
+
console.log();
|
|
473
|
+
});
|
|
474
|
+
console.log(chalk_1.default.dim('─'.repeat(60)));
|
|
475
|
+
console.log(chalk_1.default.cyan(`${existingKeys.length + 1}. Create a new API key`));
|
|
476
|
+
// Get user choice
|
|
477
|
+
const choice = yield new Promise((resolve) => {
|
|
478
|
+
const rl = readline_1.default.createInterface({
|
|
479
|
+
input: process.stdin,
|
|
480
|
+
output: process.stdout,
|
|
481
|
+
});
|
|
482
|
+
rl.question(chalk_1.default.blue('\nSelect an option (1-' +
|
|
483
|
+
(existingKeys.length + 1) +
|
|
484
|
+
'): '), (answer) => {
|
|
485
|
+
rl.close();
|
|
486
|
+
resolve(answer.trim());
|
|
487
|
+
});
|
|
488
|
+
});
|
|
489
|
+
const choiceIndex = parseInt(choice) - 1;
|
|
490
|
+
if (choiceIndex >= 0 && choiceIndex < existingKeys.length) {
|
|
491
|
+
// Use existing key
|
|
492
|
+
const selectedKey = existingKeys[choiceIndex];
|
|
493
|
+
keyName = selectedKey.name;
|
|
494
|
+
// We need to rotate the key to get the actual key value
|
|
495
|
+
console.log(chalk_1.default.yellow(`\n🔄 Rotating API key "${selectedKey.name}" to get the key value...`));
|
|
496
|
+
if (yield confirm(chalk_1.default.yellow('This will invalidate the current key. Continue? (Y/n): '), options.yes)) {
|
|
497
|
+
const rotatedKey = yield apiKeyService.rotate(selectedKey.id.toString());
|
|
498
|
+
apiKey = rotatedKey.key;
|
|
499
|
+
console.log(chalk_1.default.green(`✓ API key rotated successfully`));
|
|
500
|
+
}
|
|
501
|
+
else {
|
|
502
|
+
console.log(chalk_1.default.yellow('Cancelled. Please select a different option or create a new key.'));
|
|
503
|
+
return;
|
|
504
|
+
}
|
|
505
|
+
}
|
|
506
|
+
else if (choiceIndex === existingKeys.length) {
|
|
507
|
+
// Create new key
|
|
508
|
+
console.log(chalk_1.default.blue('\n🔑 Creating new API key...'));
|
|
509
|
+
const defaultKeyName = `opencode-${projectName}-${Date.now()}`;
|
|
510
|
+
const customName = yield getInput(chalk_1.default.blue(`Enter key name (default: ${defaultKeyName}): `), defaultKeyName, options.yes);
|
|
511
|
+
keyName = customName;
|
|
512
|
+
const createOptions = { name: keyName };
|
|
513
|
+
const keyData = yield apiKeyService.create(createOptions);
|
|
514
|
+
apiKey = keyData.key;
|
|
515
|
+
console.log(chalk_1.default.green(`✓ Created new API key: ${keyName}`));
|
|
516
|
+
}
|
|
517
|
+
else {
|
|
518
|
+
console.log(chalk_1.default.red('Invalid selection.'));
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
}
|
|
522
|
+
else {
|
|
523
|
+
// No existing keys or automation mode - create new one
|
|
524
|
+
if (!options.yes) {
|
|
525
|
+
console.log(chalk_1.default.yellow('No existing API keys found.'));
|
|
526
|
+
console.log(chalk_1.default.blue('Creating a new API key...'));
|
|
527
|
+
}
|
|
528
|
+
const defaultKeyName = `opencode-${projectName}-${Date.now()}`;
|
|
529
|
+
const customName = yield getInput(chalk_1.default.blue(`Enter key name (default: ${defaultKeyName}): `), defaultKeyName, options.yes);
|
|
530
|
+
keyName = customName;
|
|
531
|
+
const createOptions = { name: keyName };
|
|
532
|
+
const keyData = yield apiKeyService.create(createOptions);
|
|
533
|
+
apiKey = keyData.key;
|
|
534
|
+
console.log(chalk_1.default.green(`✓ Created new API key: ${keyName}`));
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
catch (error) {
|
|
539
|
+
if (process.env.BERGET_API_KEY) {
|
|
540
|
+
console.log(chalk_1.default.yellow('⚠️ Could not verify API key with Berget API, but continuing with environment key'));
|
|
541
|
+
console.log(chalk_1.default.dim('This might be due to network issues or an invalid key'));
|
|
542
|
+
}
|
|
543
|
+
else {
|
|
544
|
+
console.error(chalk_1.default.red('❌ Failed to handle API keys:'));
|
|
545
|
+
console.log(chalk_1.default.blue('This could be due to:'));
|
|
546
|
+
console.log(chalk_1.default.dim(' • Network connectivity issues'));
|
|
547
|
+
console.log(chalk_1.default.dim(' • Invalid authentication credentials'));
|
|
548
|
+
console.log(chalk_1.default.dim(' • API service temporarily unavailable'));
|
|
549
|
+
console.log('');
|
|
550
|
+
console.log(chalk_1.default.blue('Try using an API key directly:'));
|
|
551
|
+
console.log(chalk_1.default.cyan(' export BERGET_API_KEY=your_api_key_here'));
|
|
552
|
+
console.log(chalk_1.default.cyan(` berget ${command_structure_1.COMMAND_GROUPS.CODE} ${command_structure_1.SUBCOMMANDS.CODE.INIT} --yes`));
|
|
553
|
+
(0, error_handler_1.handleError)('API key operation failed', error);
|
|
554
|
+
}
|
|
555
|
+
return;
|
|
556
|
+
}
|
|
557
|
+
// Prepare .env file path for safe update
|
|
558
|
+
const envPath = path_1.default.join(process.cwd(), '.env');
|
|
559
|
+
// Create opencode.json config with optimized agent-based format
|
|
560
|
+
const config = {
|
|
561
|
+
$schema: 'https://opencode.ai/config.json',
|
|
562
|
+
username: 'berget-code',
|
|
563
|
+
theme: 'berget-dark',
|
|
564
|
+
share: 'manual',
|
|
565
|
+
autoupdate: true,
|
|
566
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
567
|
+
small_model: MODEL_CONFIG.AGENT_MODELS.small,
|
|
568
|
+
agent: {
|
|
569
|
+
fullstack: {
|
|
570
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
571
|
+
temperature: 0.3,
|
|
572
|
+
top_p: 0.9,
|
|
573
|
+
mode: 'primary',
|
|
574
|
+
permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
|
|
575
|
+
description: 'Router/coordinator agent for full-stack development with schema-driven architecture',
|
|
576
|
+
prompt: 'Voice: Scandinavian calm—precise, concise, confident; no fluff. You are Berget Code Fullstack agent. Act as a router and coordinator in a monorepo. Bottom-up schema: database → OpenAPI → generated types. Top-down types: API → UI → components. Use openapi-fetch and Zod at every boundary; compile-time errors are desired when contracts change. Routing rules: if task/paths match /apps/frontend or React (.tsx) → use frontend; if /apps/app or Expo/React Native → app; if /infra, /k8s, flux-system, kustomization.yaml, Helm values → devops; if /services, Koa routers, services/adapters/domain → backend. If ambiguous, remain fullstack and outline the end-to-end plan, then delegate subtasks to the right persona. Security: validate inputs; secrets via FluxCD SOPS/Sealed Secrets. Documentation is generated from code—never duplicated. CRITICAL: When all implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
|
|
577
|
+
},
|
|
578
|
+
frontend: {
|
|
579
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
580
|
+
temperature: 0.4,
|
|
581
|
+
top_p: 0.9,
|
|
582
|
+
mode: 'primary',
|
|
583
|
+
permission: { edit: 'allow', bash: 'deny', webfetch: 'allow' },
|
|
584
|
+
note: 'Bash access is denied for frontend persona to prevent shell command execution in UI environments. This restriction enforces security and architectural boundaries.',
|
|
585
|
+
description: 'Builds Scandinavian, type-safe UIs with React, Tailwind, Shadcn.',
|
|
586
|
+
prompt: 'You are Berget Code Frontend agent. Voice: Scandinavian calm—precise, concise, confident. React 18 + TypeScript. Tailwind + Shadcn UI only via the design system (index.css, tailwind.config.ts). Use semantic tokens for color/spacing/typography/motion; never ad-hoc classes or inline colors. Components are pure and responsive; props-first data; minimal global state (Zustand/Jotai). Accessibility and keyboard navigation mandatory. Mock data only at init under /data via typed hooks (e.g., useProducts() reading /data/products.json). Design: minimal, balanced, quiet motion. CRITICAL: When all frontend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
|
|
587
|
+
},
|
|
588
|
+
backend: {
|
|
589
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
590
|
+
temperature: 0.3,
|
|
591
|
+
top_p: 0.9,
|
|
592
|
+
mode: 'primary',
|
|
593
|
+
permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
|
|
594
|
+
description: 'Functional, modular Koa + TypeScript services; schema-first with code quality focus.',
|
|
595
|
+
prompt: 'You are Berget Code Backend agent. Voice: Scandinavian calm—precise, concise, confident. TypeScript + Koa. Prefer many small pure functions; avoid big try/catch blocks. Routes thin; logic in services/adapters/domain. Validate with Zod; auto-generate OpenAPI. Adapters isolate external systems; domain never depends on framework. Test with supertest; idempotent and stateless by default. Each microservice emits an OpenAPI contract; changes propagate upward to types. Code Quality & Refactoring Principles: Apply Single Responsibility Principle, fail fast with explicit errors, eliminate code duplication, remove nested complexity, use descriptive error codes, keep functions under 30 lines. Always leave code cleaner and more readable than you found it. CRITICAL: When all backend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
|
|
596
|
+
},
|
|
597
|
+
devops: {
|
|
598
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
599
|
+
temperature: 0.3,
|
|
600
|
+
top_p: 0.8,
|
|
601
|
+
mode: 'primary',
|
|
602
|
+
permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
|
|
603
|
+
description: 'Declarative GitOps infra with FluxCD, Kustomize, Helm, operators.',
|
|
604
|
+
prompt: 'You are Berget Code DevOps agent. Voice: Scandinavian calm—precise, concise, confident. Start simple: k8s/{deployment,service,ingress}. Add FluxCD sync to repo and image automation. Use Kustomize bases/overlays (staging, production). Add dependencies via Helm from upstream sources; prefer native operators when available (CloudNativePG, cert-manager, external-dns). SemVer with -rc tags keeps CI environments current. Observability with Prometheus/Grafana. No manual kubectl in production—Git is the source of truth.',
|
|
605
|
+
},
|
|
606
|
+
app: {
|
|
607
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
608
|
+
temperature: 0.4,
|
|
609
|
+
top_p: 0.9,
|
|
610
|
+
mode: 'primary',
|
|
611
|
+
permission: { edit: 'allow', bash: 'deny', webfetch: 'allow' },
|
|
612
|
+
note: 'Bash access is denied for app persona to prevent shell command execution in mobile/Expo environments. This restriction enforces security and architectural boundaries.',
|
|
613
|
+
description: 'Expo + React Native apps; props-first, offline-aware, shared tokens.',
|
|
614
|
+
prompt: 'You are Berget Code App agent. Voice: Scandinavian calm—precise, concise, confident. Expo + React Native + TypeScript. Structure by components/hooks/services/navigation. Components are pure; data via props; refactor shared logic into hooks/stores. Share tokens with frontend. Mock data in /data via typed hooks; later replace with live APIs. Offline via SQLite/MMKV; notifications via Expo. Request permissions only when needed. Subtle, meaningful motion; light/dark parity.',
|
|
615
|
+
},
|
|
616
|
+
security: {
|
|
617
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
618
|
+
temperature: 0.2,
|
|
619
|
+
top_p: 0.8,
|
|
620
|
+
mode: 'subagent',
|
|
621
|
+
permission: { edit: 'deny', bash: 'allow', webfetch: 'allow' },
|
|
622
|
+
description: 'Security specialist for pentesting, OWASP compliance, and vulnerability assessments.',
|
|
623
|
+
prompt: 'Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Security agent. Expert in application security, penetration testing, and OWASP standards. Core responsibilities: Conduct security assessments and penetration tests, Validate OWASP Top 10 compliance, Review code for security vulnerabilities, Implement security headers and Content Security Policy (CSP), Audit API security, Check for sensitive data exposure, Validate input sanitization and output encoding, Assess dependency security and supply chain risks. Tools and techniques: OWASP ZAP, Burp Suite, security linters, dependency scanners, manual code review. Always provide specific, actionable security recommendations with priority levels.',
|
|
624
|
+
},
|
|
625
|
+
quality: {
|
|
626
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
627
|
+
temperature: 0.1,
|
|
628
|
+
top_p: 0.9,
|
|
629
|
+
mode: 'subagent',
|
|
630
|
+
permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
|
|
631
|
+
description: 'Quality assurance specialist for testing, building, and PR management.',
|
|
632
|
+
prompt: 'Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Quality agent. Specialist in code quality assurance, testing, building, and pull request management.\n\nCore responsibilities:\n - Run comprehensive test suites (npm test, npm run test, jest, vitest)\n - Execute build processes (npm run build, webpack, vite, tsc)\n - Create and manage pull requests with proper descriptions\n - Monitor GitHub for Copilot/reviewer comments\n - Ensure code quality standards are met\n - Validate linting and formatting (npm run lint, prettier)\n - Check test coverage and performance benchmarks\n - Handle CI/CD pipeline validation\n\nCommon CLI commands:\n - npm test or npm run test (run test suite)\n - npm run build (build project)\n - npm run lint (run linting)\n - npm run format (format code)\n - npm run test:coverage (check coverage)\n - gh pr create (create pull request)\n - gh pr view --comments (check PR comments)\n - git add . && git commit -m "message" && git push (commit and push)\n\nPR Workflow:\n 1. Ensure all tests pass: npm test\n 2. Build successfully: npm run build\n 3. Create/update PR with clear description\n 4. Monitor for reviewer comments\n 5. Address feedback promptly\n 6. Update PR with fixes\n 7. Ensure CI checks pass\n\nAlways provide specific command examples and wait for processes to complete before proceeding.',
|
|
633
|
+
},
|
|
634
|
+
},
|
|
635
|
+
command: {
|
|
636
|
+
fullstack: {
|
|
637
|
+
description: 'Switch to Fullstack (router)',
|
|
638
|
+
template: '{{input}}',
|
|
639
|
+
agent: 'fullstack',
|
|
640
|
+
},
|
|
641
|
+
route: {
|
|
642
|
+
description: 'Let Fullstack auto-route to the right persona based on files/intent',
|
|
643
|
+
template: 'ROUTE {{input}}',
|
|
644
|
+
agent: 'fullstack',
|
|
645
|
+
subtask: true,
|
|
646
|
+
},
|
|
647
|
+
frontend: {
|
|
648
|
+
description: 'Switch to Frontend persona',
|
|
649
|
+
template: '{{input}}',
|
|
650
|
+
agent: 'frontend',
|
|
651
|
+
},
|
|
652
|
+
backend: {
|
|
653
|
+
description: 'Switch to Backend persona',
|
|
654
|
+
template: '{{input}}',
|
|
655
|
+
agent: 'backend',
|
|
656
|
+
},
|
|
657
|
+
devops: {
|
|
658
|
+
description: 'Switch to DevOps persona',
|
|
659
|
+
template: '{{input}}',
|
|
660
|
+
agent: 'devops',
|
|
661
|
+
},
|
|
662
|
+
app: {
|
|
663
|
+
description: 'Switch to App persona',
|
|
664
|
+
template: '{{input}}',
|
|
665
|
+
agent: 'app',
|
|
666
|
+
},
|
|
667
|
+
quality: {
|
|
668
|
+
description: 'Switch to Quality agent for testing, building, and PR management',
|
|
669
|
+
template: '{{input}}',
|
|
670
|
+
agent: 'quality',
|
|
671
|
+
},
|
|
672
|
+
},
|
|
673
|
+
watcher: {
|
|
674
|
+
ignore: ['node_modules', 'dist', '.git', 'coverage'],
|
|
675
|
+
},
|
|
676
|
+
provider: {
|
|
677
|
+
berget: {
|
|
678
|
+
npm: '@ai-sdk/openai-compatible',
|
|
679
|
+
name: 'Berget AI',
|
|
680
|
+
options: { baseURL: 'https://api.berget.ai/v1' },
|
|
681
|
+
models: MODEL_CONFIG.PROVIDER_MODELS,
|
|
682
|
+
},
|
|
683
|
+
},
|
|
684
|
+
};
|
|
685
|
+
// Ask for permission to create config files
|
|
686
|
+
if (!options.yes) {
|
|
687
|
+
console.log(chalk_1.default.blue('\nAbout to create configuration files:'));
|
|
688
|
+
console.log(chalk_1.default.dim(`Config: ${configPath}`));
|
|
689
|
+
console.log(chalk_1.default.dim(`Environment: ${envPath}`));
|
|
690
|
+
console.log(chalk_1.default.dim(`Documentation: ${path_1.default.join(process.cwd(), 'AGENTS.md')} (if not exists)`));
|
|
691
|
+
console.log(chalk_1.default.dim(`Environment: ${path_1.default.join(process.cwd(), '.env')} will be updated`));
|
|
692
|
+
console.log(chalk_1.default.dim('This will configure OpenCode to use Berget AI models.'));
|
|
693
|
+
console.log(chalk_1.default.cyan('\n💡 Benefits:'));
|
|
694
|
+
console.log(chalk_1.default.cyan(' • API key stored separately in .env file (not committed to Git)'));
|
|
695
|
+
console.log(chalk_1.default.cyan(' • Easy cost separation per project/customer'));
|
|
696
|
+
console.log(chalk_1.default.cyan(' • Secure key management with environment variables'));
|
|
697
|
+
console.log(chalk_1.default.cyan(" • Project-specific agent documentation (won't overwrite existing)"));
|
|
698
|
+
}
|
|
699
|
+
if (yield confirm('\nCreate configuration files? (Y/n): ', options.yes)) {
|
|
700
|
+
try {
|
|
701
|
+
// Safely update .env file using dotenv
|
|
702
|
+
yield (0, env_manager_1.updateEnvFile)({
|
|
703
|
+
envPath,
|
|
704
|
+
key: 'BERGET_API_KEY',
|
|
705
|
+
value: apiKey,
|
|
706
|
+
comment: `Berget AI Configuration for ${projectName} - Generated by berget code init - Do not commit to version control`,
|
|
707
|
+
});
|
|
708
|
+
// Create opencode.json
|
|
709
|
+
yield (0, promises_1.writeFile)(configPath, JSON.stringify(config, null, 2));
|
|
710
|
+
console.log(chalk_1.default.green(`✓ Created opencode.json`));
|
|
711
|
+
console.log(chalk_1.default.dim(` Model: ${config.model}`));
|
|
712
|
+
console.log(chalk_1.default.dim(` Small Model: ${config.small_model}`));
|
|
713
|
+
console.log(chalk_1.default.dim(` Theme: ${config.theme}`));
|
|
714
|
+
console.log(chalk_1.default.dim(` API Key: Stored in .env as BERGET_API_KEY`));
|
|
715
|
+
// Create AGENTS.md documentation only if it doesn't exist
|
|
716
|
+
const agentsMdPath = path_1.default.join(process.cwd(), 'AGENTS.md');
|
|
717
|
+
if (!fs.existsSync(agentsMdPath)) {
|
|
718
|
+
const agentsMdContent = `# Berget Code Agents
|
|
719
|
+
|
|
720
|
+
This document describes the specialized agents available in this project for use with OpenCode.
|
|
721
|
+
|
|
722
|
+
## Available Agents
|
|
723
|
+
|
|
724
|
+
### Primary Agents
|
|
725
|
+
|
|
726
|
+
#### fullstack
|
|
727
|
+
Router/coordinator agent for full-stack development with schema-driven architecture. Handles routing between different personas based on file paths and task requirements.
|
|
728
|
+
|
|
729
|
+
**Use when:**
|
|
730
|
+
- Working across multiple parts of a monorepo
|
|
731
|
+
- Need to coordinate between frontend, backend, devops, and app
|
|
732
|
+
- Starting new projects and need to determine tech stack
|
|
733
|
+
|
|
734
|
+
**Key features:**
|
|
735
|
+
- Schema-driven development (database → OpenAPI → types)
|
|
736
|
+
- Automatic routing to appropriate persona
|
|
737
|
+
- Tech stack discovery and recommendations
|
|
738
|
+
|
|
739
|
+
#### frontend
|
|
740
|
+
Builds Scandinavian, type-safe UIs with React, Tailwind, and Shadcn.
|
|
741
|
+
|
|
742
|
+
**Use when:**
|
|
743
|
+
- Working with React components (.tsx files)
|
|
744
|
+
- Frontend development in /apps/frontend
|
|
745
|
+
- UI/UX implementation
|
|
746
|
+
|
|
747
|
+
**Key features:**
|
|
748
|
+
- Design system integration
|
|
749
|
+
- Semantic tokens and accessibility
|
|
750
|
+
- Props-first component architecture
|
|
751
|
+
|
|
752
|
+
#### backend
|
|
753
|
+
Functional, modular Koa + TypeScript services with schema-first approach and code quality focus.
|
|
754
|
+
|
|
755
|
+
**Use when:**
|
|
756
|
+
- Working with Koa routers and services
|
|
757
|
+
- Backend development in /services
|
|
758
|
+
- API development and database work
|
|
759
|
+
|
|
760
|
+
**Key features:**
|
|
761
|
+
- Zod validation and OpenAPI generation
|
|
762
|
+
- Code quality and refactoring principles
|
|
763
|
+
- PR workflow integration
|
|
764
|
+
|
|
765
|
+
#### devops
|
|
766
|
+
Declarative GitOps infrastructure with FluxCD, Kustomize, Helm, and operators.
|
|
767
|
+
|
|
768
|
+
**Use when:**
|
|
769
|
+
- Working with Kubernetes manifests
|
|
770
|
+
- Infrastructure in /infra or /k8s
|
|
771
|
+
- CI/CD and deployment configurations
|
|
772
|
+
|
|
773
|
+
**Key features:**
|
|
774
|
+
- GitOps workflows
|
|
775
|
+
- Operator-first approach
|
|
776
|
+
- SemVer with release candidates
|
|
777
|
+
|
|
778
|
+
#### app
|
|
779
|
+
Expo + React Native applications with props-first architecture and offline awareness.
|
|
780
|
+
|
|
781
|
+
**Use when:**
|
|
782
|
+
- Mobile app development with Expo
|
|
783
|
+
- React Native projects in /apps/app
|
|
784
|
+
- Cross-platform mobile development
|
|
785
|
+
|
|
786
|
+
**Key features:**
|
|
787
|
+
- Shared design tokens with frontend
|
|
788
|
+
- Offline-first architecture
|
|
789
|
+
- Expo integration
|
|
790
|
+
|
|
791
|
+
### Subagents
|
|
792
|
+
|
|
793
|
+
#### security
|
|
794
|
+
Security specialist for penetration testing, OWASP compliance, and vulnerability assessments.
|
|
795
|
+
|
|
796
|
+
**Use when:**
|
|
797
|
+
- Need security review of code changes
|
|
798
|
+
- OWASP Top 10 compliance checks
|
|
799
|
+
- Vulnerability assessments
|
|
800
|
+
|
|
801
|
+
**Key features:**
|
|
802
|
+
- OWASP standards compliance
|
|
803
|
+
- Security best practices
|
|
804
|
+
- Actionable remediation strategies
|
|
805
|
+
|
|
806
|
+
#### quality
|
|
807
|
+
Quality assurance specialist for testing, building, and PR management.
|
|
808
|
+
|
|
809
|
+
**Use when:**
|
|
810
|
+
- Need to run test suites and build processes
|
|
811
|
+
- Creating or updating pull requests
|
|
812
|
+
- Monitoring GitHub for reviewer comments
|
|
813
|
+
- Ensuring code quality standards
|
|
814
|
+
|
|
815
|
+
**Key features:**
|
|
816
|
+
- Comprehensive testing and building workflows
|
|
817
|
+
- PR creation and management
|
|
818
|
+
- GitHub integration for reviewer feedback
|
|
819
|
+
- CLI command expertise for quality assurance
|
|
820
|
+
|
|
821
|
+
## Usage
|
|
822
|
+
|
|
823
|
+
### Switching Agents
|
|
824
|
+
Use the \`<tab>\` key to cycle through primary agents during a session.
|
|
825
|
+
|
|
826
|
+
### Manual Agent Selection
|
|
827
|
+
Use commands to switch to specific agents:
|
|
828
|
+
- \`/fullstack\` - Switch to Fullstack agent
|
|
829
|
+
- \`/frontend\` - Switch to Frontend agent
|
|
830
|
+
- \`/backend\` - Switch to Backend agent
|
|
831
|
+
- \`/devops\` - Switch to DevOps agent
|
|
832
|
+
- \`/app\` - Switch to App agent
|
|
833
|
+
- \`/quality\` - Switch to Quality agent for testing and PR management
|
|
834
|
+
|
|
835
|
+
### Using Subagents
|
|
836
|
+
Mention subagents with \`@\` symbol:
|
|
837
|
+
- \`@security review this authentication implementation\`
|
|
838
|
+
- \`@quality run tests and create PR for these changes\`
|
|
839
|
+
|
|
840
|
+
## Routing Rules
|
|
841
|
+
|
|
842
|
+
The fullstack agent automatically routes tasks based on file patterns:
|
|
843
|
+
|
|
844
|
+
- \`/apps/frontend\` or \`.tsx\` files → frontend
|
|
845
|
+
- \`/apps/app\` or Expo/React Native → app
|
|
846
|
+
- \`/infra\`, \`/k8s\`, FluxCD, Helm → devops
|
|
847
|
+
- \`/services\`, Koa routers → backend
|
|
848
|
+
|
|
849
|
+
## Configuration
|
|
850
|
+
|
|
851
|
+
All agents are configured in \`opencode.json\` with:
|
|
852
|
+
- Specialized prompts and temperature settings
|
|
853
|
+
- Appropriate tool permissions
|
|
854
|
+
- Model optimizations for their specific tasks
|
|
855
|
+
|
|
856
|
+
## Environment Setup
|
|
857
|
+
|
|
858
|
+
Configure \`.env\` with your API key:
|
|
859
|
+
\`\`\`
|
|
860
|
+
BERGET_API_KEY=your_api_key_here
|
|
861
|
+
\`\`\`
|
|
862
|
+
|
|
863
|
+
## Workflow
|
|
864
|
+
|
|
865
|
+
All agents follow these principles:
|
|
866
|
+
- Never work directly in main branch
|
|
867
|
+
- Follow branch strategy and commit conventions
|
|
868
|
+
- Create PRs for new functionality
|
|
869
|
+
- Run tests before committing
|
|
870
|
+
- Address reviewer feedback promptly
|
|
871
|
+
|
|
872
|
+
---
|
|
873
|
+
|
|
874
|
+
*Generated by berget code init for ${projectName}*
|
|
875
|
+
`;
|
|
876
|
+
yield (0, promises_1.writeFile)(agentsMdPath, agentsMdContent);
|
|
877
|
+
console.log(chalk_1.default.green(`✓ Created AGENTS.md`));
|
|
878
|
+
console.log(chalk_1.default.dim(` Documentation for available agents and usage`));
|
|
879
|
+
}
|
|
880
|
+
else {
|
|
881
|
+
console.log(chalk_1.default.yellow(`⚠ AGENTS.md already exists, skipping creation`));
|
|
882
|
+
}
|
|
883
|
+
// Check if .gitignore exists and add .env if not already there
|
|
884
|
+
const gitignorePath = path_1.default.join(process.cwd(), '.gitignore');
|
|
885
|
+
let gitignoreContent = '';
|
|
886
|
+
if (fs.existsSync(gitignorePath)) {
|
|
887
|
+
gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
|
|
888
|
+
}
|
|
889
|
+
if (!gitignoreContent.includes('.env')) {
|
|
890
|
+
gitignoreContent +=
|
|
891
|
+
(gitignoreContent.endsWith('\n') ? '' : '\n') + '.env\n';
|
|
892
|
+
yield (0, promises_1.writeFile)(gitignorePath, gitignoreContent);
|
|
893
|
+
console.log(chalk_1.default.green(`✓ Added .env to .gitignore`));
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
catch (error) {
|
|
897
|
+
console.error(chalk_1.default.red('Failed to create config files:'));
|
|
898
|
+
(0, error_handler_1.handleError)('Config file creation failed', error);
|
|
899
|
+
return;
|
|
900
|
+
}
|
|
901
|
+
}
|
|
902
|
+
else {
|
|
903
|
+
console.log(chalk_1.default.yellow('Configuration file creation cancelled.'));
|
|
904
|
+
return;
|
|
905
|
+
}
|
|
906
|
+
console.log(chalk_1.default.green('\n✅ Project initialized successfully!'));
|
|
907
|
+
console.log(chalk_1.default.blue('Next steps:'));
|
|
908
|
+
console.log(chalk_1.default.blue(` berget ${command_structure_1.COMMAND_GROUPS.CODE} ${command_structure_1.SUBCOMMANDS.CODE.RUN}`));
|
|
909
|
+
console.log(chalk_1.default.blue(' Or run: opencode'));
|
|
910
|
+
}
|
|
911
|
+
catch (error) {
|
|
912
|
+
(0, error_handler_1.handleError)('Failed to initialize project', error);
|
|
913
|
+
}
|
|
914
|
+
}));
|
|
915
|
+
code
|
|
916
|
+
.command(command_structure_1.SUBCOMMANDS.CODE.RUN)
|
|
917
|
+
.description('Run AI coding assistant')
|
|
918
|
+
.argument('[prompt]', 'Prompt to send directly to OpenCode')
|
|
919
|
+
.option('-m, --model <model>', 'Model to use (overrides config)')
|
|
920
|
+
.option('-a, --analysis', 'Use fast analysis model for context building')
|
|
921
|
+
.option('--no-config', 'Run without loading project config')
|
|
922
|
+
.option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
|
|
923
|
+
.action((prompt, options) => __awaiter(this, void 0, void 0, function* () {
|
|
924
|
+
try {
|
|
925
|
+
const configPath = path_1.default.join(process.cwd(), 'opencode.json');
|
|
926
|
+
// Ensure opencode is installed
|
|
927
|
+
if (!(yield ensureOpencodeInstalled(options.yes))) {
|
|
928
|
+
return;
|
|
929
|
+
}
|
|
930
|
+
let config = null;
|
|
931
|
+
if (!options.noConfig && fs.existsSync(configPath)) {
|
|
932
|
+
try {
|
|
933
|
+
const configContent = yield (0, promises_1.readFile)(configPath, 'utf8');
|
|
934
|
+
config = JSON.parse(configContent);
|
|
935
|
+
console.log(chalk_1.default.dim(`Loaded config for project: ${config.projectName}`));
|
|
936
|
+
console.log(chalk_1.default.dim(`Models: Analysis=${config.analysisModel}, Build=${config.buildModel}`));
|
|
937
|
+
}
|
|
938
|
+
catch (error) {
|
|
939
|
+
console.log(chalk_1.default.yellow('Warning: Failed to load opencode.json'));
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
if (!config) {
|
|
943
|
+
console.log(chalk_1.default.yellow('No project configuration found.'));
|
|
944
|
+
console.log(chalk_1.default.blue(`Run ${chalk_1.default.bold(`berget ${command_structure_1.COMMAND_GROUPS.CODE} ${command_structure_1.SUBCOMMANDS.CODE.INIT}`)} first.`));
|
|
945
|
+
return;
|
|
946
|
+
}
|
|
947
|
+
// Set environment variables for opencode
|
|
948
|
+
const env = Object.assign({}, process.env);
|
|
949
|
+
env.OPENCODE_API_KEY = config.apiKey;
|
|
950
|
+
// Prepare opencode command
|
|
951
|
+
const opencodeArgs = [];
|
|
952
|
+
if (prompt) {
|
|
953
|
+
opencodeArgs.push('run', prompt);
|
|
954
|
+
}
|
|
955
|
+
// Choose model based on analysis flag or override
|
|
956
|
+
let selectedModel = options.model || config.buildModel;
|
|
957
|
+
if (options.analysis && !options.model) {
|
|
958
|
+
selectedModel = config.analysisModel;
|
|
959
|
+
}
|
|
960
|
+
if (selectedModel) {
|
|
961
|
+
opencodeArgs.push('--model', selectedModel);
|
|
962
|
+
}
|
|
963
|
+
console.log(chalk_1.default.cyan('Starting OpenCode...'));
|
|
964
|
+
// Spawn opencode process
|
|
965
|
+
const opencode = (0, child_process_1.spawn)('opencode', opencodeArgs, {
|
|
966
|
+
stdio: 'inherit',
|
|
967
|
+
env: env,
|
|
968
|
+
shell: true,
|
|
969
|
+
});
|
|
970
|
+
opencode.on('close', (code) => {
|
|
971
|
+
if (code !== 0) {
|
|
972
|
+
console.log(chalk_1.default.red(`OpenCode exited with code ${code}`));
|
|
973
|
+
}
|
|
974
|
+
});
|
|
975
|
+
opencode.on('error', (error) => {
|
|
976
|
+
console.error(chalk_1.default.red('Failed to start OpenCode:'));
|
|
977
|
+
console.error(error.message);
|
|
978
|
+
});
|
|
979
|
+
}
|
|
980
|
+
catch (error) {
|
|
981
|
+
(0, error_handler_1.handleError)('Failed to run OpenCode', error);
|
|
982
|
+
}
|
|
983
|
+
}));
|
|
984
|
+
code
|
|
985
|
+
.command(command_structure_1.SUBCOMMANDS.CODE.UPDATE)
|
|
986
|
+
.description('Update OpenCode and agents to latest versions')
|
|
987
|
+
.option('-f, --force', 'Force update even if already latest')
|
|
988
|
+
.option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
|
|
989
|
+
.action((options) => __awaiter(this, void 0, void 0, function* () {
|
|
990
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
991
|
+
try {
|
|
992
|
+
console.log(chalk_1.default.cyan('🔄 Updating OpenCode configuration...'));
|
|
993
|
+
// Ensure opencode is installed first
|
|
994
|
+
if (!(yield ensureOpencodeInstalled(options.yes))) {
|
|
995
|
+
return;
|
|
996
|
+
}
|
|
997
|
+
const configPath = path_1.default.join(process.cwd(), 'opencode.json');
|
|
998
|
+
// Check if project is initialized
|
|
999
|
+
if (!fs.existsSync(configPath)) {
|
|
1000
|
+
console.log(chalk_1.default.red('❌ No OpenCode configuration found.'));
|
|
1001
|
+
console.log(chalk_1.default.blue(`Run ${chalk_1.default.bold(`berget ${command_structure_1.COMMAND_GROUPS.CODE} ${command_structure_1.SUBCOMMANDS.CODE.INIT}`)} first.`));
|
|
1002
|
+
return;
|
|
1003
|
+
}
|
|
1004
|
+
// Read current configuration
|
|
1005
|
+
let currentConfig;
|
|
1006
|
+
try {
|
|
1007
|
+
const configContent = yield (0, promises_1.readFile)(configPath, 'utf8');
|
|
1008
|
+
currentConfig = JSON.parse(configContent);
|
|
1009
|
+
}
|
|
1010
|
+
catch (error) {
|
|
1011
|
+
console.error(chalk_1.default.red('Failed to read current opencode.json:'));
|
|
1012
|
+
(0, error_handler_1.handleError)('Config read failed', error);
|
|
1013
|
+
return;
|
|
1014
|
+
}
|
|
1015
|
+
console.log(chalk_1.default.blue('📋 Current configuration:'));
|
|
1016
|
+
console.log(chalk_1.default.dim(` Model: ${currentConfig.model}`));
|
|
1017
|
+
console.log(chalk_1.default.dim(` Theme: ${currentConfig.theme}`));
|
|
1018
|
+
console.log(chalk_1.default.dim(` Agents: ${Object.keys(currentConfig.agent || {}).length} configured`));
|
|
1019
|
+
// Create latest configuration with all improvements
|
|
1020
|
+
const latestConfig = {
|
|
1021
|
+
$schema: 'https://opencode.ai/config.json',
|
|
1022
|
+
username: 'berget-code',
|
|
1023
|
+
theme: 'berget-dark',
|
|
1024
|
+
share: 'manual',
|
|
1025
|
+
autoupdate: true,
|
|
1026
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
1027
|
+
small_model: MODEL_CONFIG.AGENT_MODELS.small,
|
|
1028
|
+
agent: {
|
|
1029
|
+
fullstack: {
|
|
1030
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
1031
|
+
temperature: 0.3,
|
|
1032
|
+
top_p: 0.9,
|
|
1033
|
+
mode: 'primary',
|
|
1034
|
+
permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
|
|
1035
|
+
description: 'Router/coordinator agent for full-stack development with schema-driven architecture',
|
|
1036
|
+
prompt: 'Voice: Scandinavian calm—precise, concise, confident; no fluff. You are Berget Code Fullstack agent. Act as a router and coordinator in a monorepo. Bottom-up schema: database → OpenAPI → generated types. Top-down types: API → UI → components. Use openapi-fetch and Zod at every boundary; compile-time errors are desired when contracts change. Routing rules: if task/paths match /apps/frontend or React (.tsx) → use frontend; if /apps/app or Expo/React Native → app; if /infra, /k8s, flux-system, kustomization.yaml, Helm values → devops; if /services, Koa routers, services/adapters/domain → backend. If ambiguous, remain fullstack and outline the end-to-end plan, then delegate subtasks to the right persona. Security: validate inputs; secrets via FluxCD SOPS/Sealed Secrets. Documentation is generated from code—never duplicated. CRITICAL: When all implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
|
|
1037
|
+
},
|
|
1038
|
+
frontend: {
|
|
1039
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
1040
|
+
temperature: 0.4,
|
|
1041
|
+
top_p: 0.9,
|
|
1042
|
+
mode: 'primary',
|
|
1043
|
+
permission: { edit: 'allow', bash: 'deny', webfetch: 'allow' },
|
|
1044
|
+
note: 'Bash access is denied for frontend persona to prevent shell command execution in UI environments. This restriction enforces security and architectural boundaries.',
|
|
1045
|
+
description: 'Builds Scandinavian, type-safe UIs with React, Tailwind, Shadcn.',
|
|
1046
|
+
prompt: 'You are Berget Code Frontend agent. Voice: Scandinavian calm—precise, concise, confident. React 18 + TypeScript. Tailwind + Shadcn UI only via the design system (index.css, tailwind.config.ts). Use semantic tokens for color/spacing/typography/motion; never ad-hoc classes or inline colors. Components are pure and responsive; props-first data; minimal global state (Zustand/Jotai). Accessibility and keyboard navigation mandatory. Mock data only at init under /data via typed hooks (e.g., useProducts() reading /data/products.json). Design: minimal, balanced, quiet motion. CRITICAL: When all frontend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
|
|
1047
|
+
},
|
|
1048
|
+
backend: {
|
|
1049
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
1050
|
+
temperature: 0.3,
|
|
1051
|
+
top_p: 0.9,
|
|
1052
|
+
mode: 'primary',
|
|
1053
|
+
permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
|
|
1054
|
+
description: 'Functional, modular Koa + TypeScript services; schema-first with code quality focus.',
|
|
1055
|
+
prompt: 'You are Berget Code Backend agent. Voice: Scandinavian calm—precise, concise, confident. TypeScript + Koa. Prefer many small pure functions; avoid big try/catch blocks. Routes thin; logic in services/adapters/domain. Validate with Zod; auto-generate OpenAPI. Adapters isolate external systems; domain never depends on framework. Test with supertest; idempotent and stateless by default. Each microservice emits an OpenAPI contract; changes propagate upward to types. Code Quality & Refactoring Principles: Apply Single Responsibility Principle, fail fast with explicit errors, eliminate code duplication, remove nested complexity, use descriptive error codes, keep functions under 30 lines. Always leave code cleaner and more readable than you found it. CRITICAL: When all backend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
|
|
1056
|
+
},
|
|
1057
|
+
devops: {
|
|
1058
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
1059
|
+
temperature: 0.3,
|
|
1060
|
+
top_p: 0.8,
|
|
1061
|
+
mode: 'primary',
|
|
1062
|
+
permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
|
|
1063
|
+
description: 'Declarative GitOps infra with FluxCD, Kustomize, Helm, operators.',
|
|
1064
|
+
prompt: 'You are Berget Code DevOps agent. Voice: Scandinavian calm—precise, concise, confident. Start simple: k8s/{deployment,service,ingress}. Add FluxCD sync to repo and image automation. Use Kustomize bases/overlays (staging, production). Add dependencies via Helm from upstream sources; prefer native operators when available (CloudNativePG, cert-manager, external-dns). SemVer with -rc tags keeps CI environments current. Observability with Prometheus/Grafana. No manual kubectl in production—Git is the source of truth. For testing, building, and PR management, use @quality subagent.',
|
|
1065
|
+
},
|
|
1066
|
+
app: {
|
|
1067
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
1068
|
+
temperature: 0.4,
|
|
1069
|
+
top_p: 0.9,
|
|
1070
|
+
mode: 'primary',
|
|
1071
|
+
permission: { edit: 'allow', bash: 'deny', webfetch: 'allow' },
|
|
1072
|
+
note: 'Bash access is denied for app persona to prevent shell command execution in mobile/Expo environments. This restriction enforces security and architectural boundaries.',
|
|
1073
|
+
description: 'Expo + React Native apps; props-first, offline-aware, shared tokens.',
|
|
1074
|
+
prompt: 'You are Berget Code App agent. Voice: Scandinavian calm—precise, concise, confident. Expo + React Native + TypeScript. Structure by components/hooks/services/navigation. Components are pure; data via props; refactor shared logic into hooks/stores. Share tokens with frontend. Mock data in /data via typed hooks; later replace with live APIs. Offline via SQLite/MMKV; notifications via Expo. Request permissions only when needed. Subtle, meaningful motion; light/dark parity. For testing, building, and PR management, use @quality subagent.',
|
|
1075
|
+
},
|
|
1076
|
+
security: {
|
|
1077
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
1078
|
+
temperature: 0.2,
|
|
1079
|
+
top_p: 0.8,
|
|
1080
|
+
mode: 'subagent',
|
|
1081
|
+
permission: { edit: 'deny', bash: 'allow', webfetch: 'allow' },
|
|
1082
|
+
description: 'Security specialist for pentesting, OWASP compliance, and vulnerability assessments.',
|
|
1083
|
+
prompt: 'Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Security agent. Expert in application security, penetration testing, and OWASP standards. Core responsibilities: Conduct security assessments and penetration tests, Validate OWASP Top 10 compliance, Review code for security vulnerabilities, Implement security headers and Content Security Policy (CSP), Audit API security, Check for sensitive data exposure, Validate input sanitization and output encoding, Assess dependency security and supply chain risks. Tools and techniques: OWASP ZAP, Burp Suite, security linters, dependency scanners, manual code review. Always provide specific, actionable security recommendations with priority levels. Workflow: Always follow branch_strategy and commit_convention from workflow section. Never work directly in main. Agent awareness: Review code from all personas (frontend, backend, app, devops). If implementation changes are needed, suggest <tab> to switch to appropriate persona after security assessment.',
|
|
1084
|
+
},
|
|
1085
|
+
quality: {
|
|
1086
|
+
model: MODEL_CONFIG.AGENT_MODELS.primary,
|
|
1087
|
+
temperature: 0.1,
|
|
1088
|
+
top_p: 0.9,
|
|
1089
|
+
mode: 'subagent',
|
|
1090
|
+
permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
|
|
1091
|
+
description: 'Quality assurance specialist for testing, building, and complete PR management.',
|
|
1092
|
+
prompt: 'Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Quality agent. Specialist in code quality assurance, testing, building, and complete pull request lifecycle management.\n\nCore responsibilities:\n - Run comprehensive test suites (npm test, npm run test, jest, vitest)\n - Execute build processes (npm run build, webpack, vite, tsc)\n - Create and manage pull requests with proper descriptions\n - Handle merge conflicts and keep main updated\n - Monitor GitHub for reviewer comments and address them\n - Ensure code quality standards are met\n - Validate linting and formatting (npm run lint, prettier)\n - Check test coverage and performance benchmarks\n - Handle CI/CD pipeline validation\n\nComplete PR Workflow:\n 1. Ensure all tests pass: npm test\n 2. Build successfully: npm run build\n 3. Commit all changes with proper message\n 4. Push to feature branch\n 5. Update main branch and handle merge conflicts\n 6. Create or update PR with comprehensive description\n 7. Monitor for reviewer comments\n 8. Address feedback and push updates\n 9. Always provide PR URL for user review\n\nEssential CLI commands:\n - npm test or npm run test (run test suite)\n - npm run build (build project)\n - npm run lint (run linting)\n - npm run format (format code)\n - npm run test:coverage (check coverage)\n - git add . && git commit -m "message" && git push (commit and push)\n - git checkout main && git pull origin main (update main)\n - git checkout feature-branch && git merge main (handle conflicts)\n - gh pr create --title "title" --body "body" (create PR)\n - gh pr view --comments (check PR comments)\n - gh pr edit --title "title" --body "body" (update PR)\n\nPR Creation Process:\n - Always include clear summary of changes\n - List technical details and improvements\n - Include testing and validation results\n - Add any breaking changes or migration notes\n - Provide PR URL immediately after creation\n\nMerge Conflict Resolution:\n - Always update main before creating/updating PR\n - Handle conflicts automatically when possible\n - If conflicts require human input, clearly explain what\'s needed\n - Re-run tests after conflict resolution\n - Ensure CI checks pass before finalizing\n\nReviewer Comment Handling:\n - Monitor PR for new comments regularly\n - Address each comment specifically\n - Push fixes and update PR accordingly\n - Always provide updated PR URL after changes\n - Continue monitoring until all feedback is addressed\n\nCRITICAL: When invoked by other agents (@quality), you MUST:\n - Complete all testing and building tasks\n - Handle entire PR creation/update process\n - Provide PR URL at the end\n - Ensure main branch is properly merged\n - Handle any merge conflicts automatically\n\nAlways provide specific command examples and wait for processes to complete before proceeding.\nWorkflow: Always follow branch_strategy and commit_convention from workflow section. Never work directly in main.\nAgent awareness: Can be invoked by any primary agent (@quality) for complete testing, building, and PR management. You are the final step before user review - ensure everything is perfect.',
|
|
1093
|
+
},
|
|
1094
|
+
},
|
|
1095
|
+
command: {
|
|
1096
|
+
fullstack: {
|
|
1097
|
+
description: 'Switch to Fullstack (router)',
|
|
1098
|
+
template: '{{input}}',
|
|
1099
|
+
agent: 'fullstack',
|
|
1100
|
+
},
|
|
1101
|
+
route: {
|
|
1102
|
+
description: 'Let Fullstack auto-route to the right persona based on files/intent',
|
|
1103
|
+
template: 'ROUTE {{input}}',
|
|
1104
|
+
agent: 'fullstack',
|
|
1105
|
+
subtask: true,
|
|
1106
|
+
},
|
|
1107
|
+
frontend: {
|
|
1108
|
+
description: 'Switch to Frontend persona',
|
|
1109
|
+
template: '{{input}}',
|
|
1110
|
+
agent: 'frontend',
|
|
1111
|
+
},
|
|
1112
|
+
backend: {
|
|
1113
|
+
description: 'Switch to Backend persona',
|
|
1114
|
+
template: '{{input}}',
|
|
1115
|
+
agent: 'backend',
|
|
1116
|
+
},
|
|
1117
|
+
devops: {
|
|
1118
|
+
description: 'Switch to DevOps persona',
|
|
1119
|
+
template: '{{input}}',
|
|
1120
|
+
agent: 'devops',
|
|
1121
|
+
},
|
|
1122
|
+
app: {
|
|
1123
|
+
description: 'Switch to App persona',
|
|
1124
|
+
template: '{{input}}',
|
|
1125
|
+
agent: 'app',
|
|
1126
|
+
},
|
|
1127
|
+
security: {
|
|
1128
|
+
description: 'Switch to Security persona for pentesting and OWASP compliance',
|
|
1129
|
+
template: '{{input}}',
|
|
1130
|
+
agent: 'security',
|
|
1131
|
+
},
|
|
1132
|
+
quality: {
|
|
1133
|
+
description: 'Switch to Quality agent for testing, building, and PR management',
|
|
1134
|
+
template: '{{input}}',
|
|
1135
|
+
agent: 'quality',
|
|
1136
|
+
},
|
|
1137
|
+
},
|
|
1138
|
+
watcher: {
|
|
1139
|
+
ignore: ['node_modules', 'dist', '.git', 'coverage'],
|
|
1140
|
+
},
|
|
1141
|
+
provider: {
|
|
1142
|
+
berget: {
|
|
1143
|
+
npm: '@ai-sdk/openai-compatible',
|
|
1144
|
+
name: 'Berget AI',
|
|
1145
|
+
options: {
|
|
1146
|
+
baseURL: 'https://api.berget.ai/v1',
|
|
1147
|
+
apiKey: '{env:BERGET_API_KEY}',
|
|
1148
|
+
},
|
|
1149
|
+
models: MODEL_CONFIG.PROVIDER_MODELS,
|
|
1150
|
+
},
|
|
1151
|
+
},
|
|
1152
|
+
};
|
|
1153
|
+
// Check if update is needed
|
|
1154
|
+
const needsUpdate = JSON.stringify(currentConfig) !== JSON.stringify(latestConfig);
|
|
1155
|
+
if (!needsUpdate && !options.force) {
|
|
1156
|
+
console.log(chalk_1.default.green('✅ Already using the latest configuration!'));
|
|
1157
|
+
return;
|
|
1158
|
+
}
|
|
1159
|
+
if (needsUpdate) {
|
|
1160
|
+
console.log(chalk_1.default.blue('\n🔄 Updates available:'));
|
|
1161
|
+
// Compare agents
|
|
1162
|
+
const currentAgents = Object.keys(currentConfig.agent || {});
|
|
1163
|
+
const latestAgents = Object.keys(latestConfig.agent);
|
|
1164
|
+
const newAgents = latestAgents.filter((agent) => !currentAgents.includes(agent));
|
|
1165
|
+
if (newAgents.length > 0) {
|
|
1166
|
+
console.log(chalk_1.default.cyan(` • New agents: ${newAgents.join(', ')}`));
|
|
1167
|
+
}
|
|
1168
|
+
// Check for quality agent specifically
|
|
1169
|
+
if (!((_a = currentConfig.agent) === null || _a === void 0 ? void 0 : _a.quality) && latestConfig.agent.quality) {
|
|
1170
|
+
console.log(chalk_1.default.cyan(' • Quality subagent for testing and PR management'));
|
|
1171
|
+
}
|
|
1172
|
+
// Check for security subagent mode
|
|
1173
|
+
if (((_c = (_b = currentConfig.agent) === null || _b === void 0 ? void 0 : _b.security) === null || _c === void 0 ? void 0 : _c.mode) !== 'subagent') {
|
|
1174
|
+
console.log(chalk_1.default.cyan(' • Security agent converted to subagent (read-only)'));
|
|
1175
|
+
}
|
|
1176
|
+
// Check for GLM-4.6 optimizations
|
|
1177
|
+
if (!((_h = (_g = (_f = (_e = (_d = currentConfig.provider) === null || _d === void 0 ? void 0 : _d.berget) === null || _e === void 0 ? void 0 : _e.models) === null || _f === void 0 ? void 0 : _f[MODEL_CONFIG.AGENT_MODELS.primary.replace('berget/', '')]) === null || _g === void 0 ? void 0 : _g.limit) === null || _h === void 0 ? void 0 : _h.context)) {
|
|
1178
|
+
console.log(chalk_1.default.cyan(' • GLM-4.6 token limits and auto-compaction'));
|
|
1179
|
+
}
|
|
1180
|
+
console.log(chalk_1.default.cyan(' • Latest agent prompts and improvements'));
|
|
1181
|
+
}
|
|
1182
|
+
if (options.force) {
|
|
1183
|
+
console.log(chalk_1.default.yellow('🔧 Force update requested'));
|
|
1184
|
+
}
|
|
1185
|
+
if (!options.yes) {
|
|
1186
|
+
console.log(chalk_1.default.blue('\nThis will update your OpenCode configuration with the latest improvements.'));
|
|
1187
|
+
// Check if user has git for backup
|
|
1188
|
+
const hasGitRepo = hasGit();
|
|
1189
|
+
if (!hasGitRepo) {
|
|
1190
|
+
console.log(chalk_1.default.yellow('⚠️ No .git repository detected - backup will be created'));
|
|
1191
|
+
}
|
|
1192
|
+
else {
|
|
1193
|
+
console.log(chalk_1.default.green('✓ Git repository detected - changes are tracked'));
|
|
1194
|
+
}
|
|
1195
|
+
}
|
|
1196
|
+
// Ask user what they want to do
|
|
1197
|
+
console.log(chalk_1.default.blue('\nChoose update strategy:'));
|
|
1198
|
+
console.log(chalk_1.default.cyan('1) Replace - Use latest configuration (your customizations will be lost)'));
|
|
1199
|
+
console.log(chalk_1.default.cyan('2) Merge - Combine latest updates with your customizations (recommended)'));
|
|
1200
|
+
let mergeChoice = 'merge';
|
|
1201
|
+
if (!options.yes) {
|
|
1202
|
+
const choice = yield askChoice('\nYour choice (1-2, default: 2): ', ['replace', 'merge'], 'merge');
|
|
1203
|
+
mergeChoice = choice;
|
|
1204
|
+
}
|
|
1205
|
+
if (yield confirm(`\nProceed with ${mergeChoice}? (Y/n): `, options.yes)) {
|
|
1206
|
+
try {
|
|
1207
|
+
let finalConfig;
|
|
1208
|
+
let backupPath = null;
|
|
1209
|
+
// Create backup if no git
|
|
1210
|
+
if (!hasGit()) {
|
|
1211
|
+
backupPath = `${configPath}.backup.${Date.now()}`;
|
|
1212
|
+
yield (0, promises_1.writeFile)(backupPath, JSON.stringify(currentConfig, null, 2));
|
|
1213
|
+
console.log(chalk_1.default.green(`✓ Backed up current config to ${path_1.default.basename(backupPath)}`));
|
|
1214
|
+
}
|
|
1215
|
+
if (mergeChoice === 'merge') {
|
|
1216
|
+
// Merge configurations
|
|
1217
|
+
finalConfig = yield mergeConfigurations(currentConfig, latestConfig);
|
|
1218
|
+
console.log(chalk_1.default.green('✓ Merged configurations with latest updates'));
|
|
1219
|
+
}
|
|
1220
|
+
else {
|
|
1221
|
+
// Replace with latest
|
|
1222
|
+
finalConfig = latestConfig;
|
|
1223
|
+
console.log(chalk_1.default.green('✓ Replaced with latest configuration'));
|
|
1224
|
+
}
|
|
1225
|
+
// Write final configuration
|
|
1226
|
+
yield (0, promises_1.writeFile)(configPath, JSON.stringify(finalConfig, null, 2));
|
|
1227
|
+
console.log(chalk_1.default.green(`✓ Updated opencode.json with ${mergeChoice} strategy`));
|
|
1228
|
+
// Update AGENTS.md if it doesn't exist
|
|
1229
|
+
const agentsMdPath = path_1.default.join(process.cwd(), 'AGENTS.md');
|
|
1230
|
+
if (!fs.existsSync(agentsMdPath)) {
|
|
1231
|
+
const agentsMdContent = `# Berget Code Agents
|
|
1232
|
+
|
|
1233
|
+
This document describes the specialized agents available in this project for use with OpenCode.
|
|
1234
|
+
|
|
1235
|
+
## Available Agents
|
|
1236
|
+
|
|
1237
|
+
### Primary Agents
|
|
1238
|
+
|
|
1239
|
+
#### fullstack
|
|
1240
|
+
Router/coordinator agent for full-stack development with schema-driven architecture. Handles routing between different personas based on file paths and task requirements.
|
|
1241
|
+
|
|
1242
|
+
**Use when:**
|
|
1243
|
+
- Working across multiple parts of a monorepo
|
|
1244
|
+
- Need to coordinate between frontend, backend, devops, and app
|
|
1245
|
+
- Starting new projects and need to determine tech stack
|
|
1246
|
+
|
|
1247
|
+
**Key features:**
|
|
1248
|
+
- Schema-driven development (database → OpenAPI → types)
|
|
1249
|
+
- Automatic routing to appropriate persona
|
|
1250
|
+
- Tech stack discovery and recommendations
|
|
1251
|
+
|
|
1252
|
+
#### frontend
|
|
1253
|
+
Builds Scandinavian, type-safe UIs with React, Tailwind, and Shadcn.
|
|
1254
|
+
|
|
1255
|
+
**Use when:**
|
|
1256
|
+
- Working with React components (.tsx files)
|
|
1257
|
+
- Frontend development in /apps/frontend
|
|
1258
|
+
- UI/UX implementation
|
|
1259
|
+
|
|
1260
|
+
**Key features:**
|
|
1261
|
+
- Design system integration
|
|
1262
|
+
- Semantic tokens and accessibility
|
|
1263
|
+
- Props-first component architecture
|
|
1264
|
+
|
|
1265
|
+
#### backend
|
|
1266
|
+
Functional, modular Koa + TypeScript services with schema-first approach and code quality focus.
|
|
1267
|
+
|
|
1268
|
+
**Use when:**
|
|
1269
|
+
- Working with Koa routers and services
|
|
1270
|
+
- Backend development in /services
|
|
1271
|
+
- API development and database work
|
|
1272
|
+
|
|
1273
|
+
**Key features:**
|
|
1274
|
+
- Zod validation and OpenAPI generation
|
|
1275
|
+
- Code quality and refactoring principles
|
|
1276
|
+
- PR workflow integration
|
|
1277
|
+
|
|
1278
|
+
#### devops
|
|
1279
|
+
Declarative GitOps infrastructure with FluxCD, Kustomize, Helm, and operators.
|
|
1280
|
+
|
|
1281
|
+
**Use when:**
|
|
1282
|
+
- Working with Kubernetes manifests
|
|
1283
|
+
- Infrastructure in /infra or /k8s
|
|
1284
|
+
- CI/CD and deployment configurations
|
|
1285
|
+
|
|
1286
|
+
**Key features:**
|
|
1287
|
+
- GitOps workflows
|
|
1288
|
+
- Operator-first approach
|
|
1289
|
+
- SemVer with release candidates
|
|
1290
|
+
|
|
1291
|
+
#### app
|
|
1292
|
+
Expo + React Native applications with props-first architecture and offline awareness.
|
|
1293
|
+
|
|
1294
|
+
**Use when:**
|
|
1295
|
+
- Mobile app development with Expo
|
|
1296
|
+
- React Native projects in /apps/app
|
|
1297
|
+
- Cross-platform mobile development
|
|
1298
|
+
|
|
1299
|
+
**Key features:**
|
|
1300
|
+
- Shared design tokens with frontend
|
|
1301
|
+
- Offline-first architecture
|
|
1302
|
+
- Expo integration
|
|
1303
|
+
|
|
1304
|
+
### Subagents
|
|
1305
|
+
|
|
1306
|
+
#### security
|
|
1307
|
+
Security specialist for penetration testing, OWASP compliance, and vulnerability assessments.
|
|
1308
|
+
|
|
1309
|
+
**Use when:**
|
|
1310
|
+
- Need security review of code changes
|
|
1311
|
+
- OWASP Top 10 compliance checks
|
|
1312
|
+
- Vulnerability assessments
|
|
1313
|
+
|
|
1314
|
+
**Key features:**
|
|
1315
|
+
- OWASP standards compliance
|
|
1316
|
+
- Security best practices
|
|
1317
|
+
- Actionable remediation strategies
|
|
1318
|
+
|
|
1319
|
+
#### quality
|
|
1320
|
+
Quality assurance specialist for testing, building, and PR management.
|
|
1321
|
+
|
|
1322
|
+
**Use when:**
|
|
1323
|
+
- Need to run test suites and build processes
|
|
1324
|
+
- Creating or updating pull requests
|
|
1325
|
+
- Monitoring GitHub for reviewer comments
|
|
1326
|
+
- Ensuring code quality standards
|
|
1327
|
+
|
|
1328
|
+
**Key features:**
|
|
1329
|
+
- Comprehensive testing and building workflows
|
|
1330
|
+
- PR creation and management
|
|
1331
|
+
- GitHub integration for reviewer feedback
|
|
1332
|
+
- CLI command expertise for quality assurance
|
|
1333
|
+
|
|
1334
|
+
## Usage
|
|
1335
|
+
|
|
1336
|
+
### Switching Agents
|
|
1337
|
+
Use the \`<tab>\` key to cycle through primary agents during a session.
|
|
1338
|
+
|
|
1339
|
+
### Manual Agent Selection
|
|
1340
|
+
Use commands to switch to specific agents:
|
|
1341
|
+
- \`/fullstack\` - Switch to Fullstack agent
|
|
1342
|
+
- \`/frontend\` - Switch to Frontend agent
|
|
1343
|
+
- \`/backend\` - Switch to Backend agent
|
|
1344
|
+
- \`/devops\` - Switch to DevOps agent
|
|
1345
|
+
- \`/app\` - Switch to App agent
|
|
1346
|
+
- \`/quality\` - Switch to Quality agent for testing and PR management
|
|
1347
|
+
|
|
1348
|
+
### Using Subagents
|
|
1349
|
+
Mention subagents with \`@\` symbol:
|
|
1350
|
+
- \`@security review this authentication implementation\`
|
|
1351
|
+
- \`@quality run tests and create PR for these changes\`
|
|
1352
|
+
|
|
1353
|
+
## Routing Rules
|
|
1354
|
+
|
|
1355
|
+
The fullstack agent automatically routes tasks based on file patterns:
|
|
1356
|
+
|
|
1357
|
+
- \`/apps/frontend\` or \`.tsx\` files → frontend
|
|
1358
|
+
- \`/apps/app\` or Expo/React Native → app
|
|
1359
|
+
- \`/infra\`, \`/k8s\`, FluxCD, Helm → devops
|
|
1360
|
+
- \`/services\`, Koa routers → backend
|
|
1361
|
+
|
|
1362
|
+
## Configuration
|
|
1363
|
+
|
|
1364
|
+
All agents are configured in \`opencode.json\` with:
|
|
1365
|
+
- Specialized prompts and temperature settings
|
|
1366
|
+
- Appropriate tool permissions
|
|
1367
|
+
- Model optimizations for their specific tasks
|
|
1368
|
+
|
|
1369
|
+
## Environment Setup
|
|
1370
|
+
|
|
1371
|
+
Configure \`.env\` with your API key:
|
|
1372
|
+
\`\`\`
|
|
1373
|
+
BERGET_API_KEY=your_api_key_here
|
|
1374
|
+
\`\`\`
|
|
1375
|
+
|
|
1376
|
+
## Workflow
|
|
1377
|
+
|
|
1378
|
+
All agents follow these principles:
|
|
1379
|
+
- Never work directly in main branch
|
|
1380
|
+
- Follow branch strategy and commit conventions
|
|
1381
|
+
- Create PRs for new functionality
|
|
1382
|
+
- Run tests before committing
|
|
1383
|
+
- Address reviewer feedback promptly
|
|
1384
|
+
|
|
1385
|
+
---
|
|
1386
|
+
|
|
1387
|
+
*Updated by berget code update*
|
|
1388
|
+
`;
|
|
1389
|
+
yield (0, promises_1.writeFile)(agentsMdPath, agentsMdContent);
|
|
1390
|
+
console.log(chalk_1.default.green('✓ Updated AGENTS.md documentation'));
|
|
1391
|
+
}
|
|
1392
|
+
console.log(chalk_1.default.green('\n✅ Update completed successfully!'));
|
|
1393
|
+
console.log(chalk_1.default.blue('New features available:'));
|
|
1394
|
+
console.log(chalk_1.default.cyan(' • @quality subagent for testing and PR management'));
|
|
1395
|
+
console.log(chalk_1.default.cyan(' • @security subagent for security reviews'));
|
|
1396
|
+
console.log(chalk_1.default.cyan(' • Improved agent prompts and routing'));
|
|
1397
|
+
console.log(chalk_1.default.cyan(' • GLM-4.6 token optimizations'));
|
|
1398
|
+
console.log(chalk_1.default.blue('\nTry these new commands:'));
|
|
1399
|
+
console.log(chalk_1.default.cyan(' @quality run tests and create PR'));
|
|
1400
|
+
console.log(chalk_1.default.cyan(' @security review this code'));
|
|
1401
|
+
}
|
|
1402
|
+
catch (error) {
|
|
1403
|
+
console.error(chalk_1.default.red('Failed to update configuration:'));
|
|
1404
|
+
(0, error_handler_1.handleError)('Update failed', error);
|
|
1405
|
+
// Restore from backup if update failed
|
|
1406
|
+
try {
|
|
1407
|
+
yield (0, promises_1.writeFile)(configPath, JSON.stringify(currentConfig, null, 2));
|
|
1408
|
+
console.log(chalk_1.default.yellow('📁 Restored original configuration from backup'));
|
|
1409
|
+
}
|
|
1410
|
+
catch (restoreError) {
|
|
1411
|
+
console.error(chalk_1.default.red('Failed to restore backup:'));
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
}
|
|
1415
|
+
else {
|
|
1416
|
+
console.log(chalk_1.default.yellow('Update cancelled.'));
|
|
1417
|
+
}
|
|
1418
|
+
}
|
|
1419
|
+
catch (error) {
|
|
1420
|
+
(0, error_handler_1.handleError)('Failed to update OpenCode configuration', error);
|
|
1421
|
+
}
|
|
1422
|
+
}));
|
|
1423
|
+
}
|
|
1424
|
+
exports.registerCodeCommands = registerCodeCommands;
|