berget 2.2.10 → 2.2.12
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/dist/package.json +1 -1
- package/dist/src/commands/code/__tests__/setup-flow.test.js +47 -37
- package/dist/src/commands/code/auth-sync.js +3 -3
- package/dist/src/commands/code/{setup.js → init.js} +73 -87
- package/dist/src/commands/code.js +5 -553
- package/dist/src/constants/command-structure.js +1 -9
- package/dist/src/services/auth-service.js +1 -1
- package/dist/tests/commands/code.test.js +4 -415
- package/package.json +1 -1
- package/src/commands/code/__tests__/setup-flow.test.ts +49 -39
- package/src/commands/code/auth-sync.ts +3 -3
- package/src/commands/code/{setup.ts → init.ts} +82 -97
- package/src/commands/code/ports/prompter.ts +1 -0
- package/src/commands/code.ts +5 -608
- package/src/constants/command-structure.ts +1 -11
- package/src/services/auth-service.ts +1 -1
- package/tests/commands/code.test.ts +5 -483
- package/templates/agents/app.md +0 -23
- package/templates/agents/backend.md +0 -23
- package/templates/agents/devops.md +0 -30
- package/templates/agents/frontend.md +0 -25
- package/templates/agents/fullstack.md +0 -23
- package/templates/agents/quality.md +0 -69
- package/templates/agents/security.md +0 -21
package/src/commands/code.ts
CHANGED
|
@@ -1,14 +1,8 @@
|
|
|
1
|
-
import chalk from 'chalk';
|
|
2
1
|
import { Command } from 'commander';
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
4
|
-
import * as fs from 'node:fs';
|
|
5
|
-
import { readFile, writeFile } from 'node:fs/promises';
|
|
6
|
-
import path from 'node:path';
|
|
7
|
-
import readline from 'node:readline';
|
|
8
2
|
|
|
9
3
|
import { COMMAND_GROUPS, SUBCOMMANDS } from '../constants/command-structure';
|
|
10
4
|
import { handleError } from '../utils/error-handler';
|
|
11
|
-
import {
|
|
5
|
+
import { runInitCommand } from './code/init';
|
|
12
6
|
|
|
13
7
|
/**
|
|
14
8
|
* Register code commands
|
|
@@ -18,611 +12,14 @@ export function registerCodeCommands(program: Command): void {
|
|
|
18
12
|
.command(COMMAND_GROUPS.CODE)
|
|
19
13
|
.description('AI-powered coding assistant with OpenCode');
|
|
20
14
|
|
|
21
|
-
if (process.env.BERGET_EXPERIMENTAL) {
|
|
22
|
-
code
|
|
23
|
-
.command('setup')
|
|
24
|
-
.description('Interactive setup for Berget AI coding tools')
|
|
25
|
-
.action(async () => {
|
|
26
|
-
try {
|
|
27
|
-
await runSetupCommand();
|
|
28
|
-
} catch (error) {
|
|
29
|
-
handleError('Setup failed', error);
|
|
30
|
-
}
|
|
31
|
-
});
|
|
32
|
-
}
|
|
33
|
-
|
|
34
15
|
code
|
|
35
16
|
.command(SUBCOMMANDS.CODE.INIT)
|
|
36
|
-
.description('
|
|
37
|
-
.
|
|
38
|
-
.option('-f, --force', 'Overwrite existing configuration')
|
|
39
|
-
.option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
|
|
40
|
-
.action(async (options) => {
|
|
41
|
-
try {
|
|
42
|
-
const projectName = options.name || getProjectName();
|
|
43
|
-
const configPath = path.join(process.cwd(), 'opencode.json');
|
|
44
|
-
|
|
45
|
-
// Check if already initialized
|
|
46
|
-
if (fs.existsSync(configPath) && !options.force) {
|
|
47
|
-
if (!options.yes) {
|
|
48
|
-
console.log(chalk.yellow('Project already initialized for OpenCode.'));
|
|
49
|
-
console.log(chalk.dim(`Config file: ${configPath}`));
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (await confirm('Do you want to reinitialize? (Y/n): ', options.yes)) {
|
|
53
|
-
// Continue with reinitialization
|
|
54
|
-
} else {
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Ensure opencode is installed
|
|
60
|
-
if (!(await ensureOpencodeInstalled(options.yes))) {
|
|
61
|
-
return;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
console.log(chalk.cyan(`Initializing OpenCode for project: ${projectName}`));
|
|
65
|
-
|
|
66
|
-
const config = {
|
|
67
|
-
$schema: 'https://opencode.ai/config.json',
|
|
68
|
-
plugin: ['@bergetai/opencode-auth@1.0.16'],
|
|
69
|
-
};
|
|
70
|
-
|
|
71
|
-
const agentsDir = path.join(process.cwd(), '.opencode', 'agents');
|
|
72
|
-
const templatesDir = getAgentTemplatesDir();
|
|
73
|
-
|
|
74
|
-
if (!options.yes) {
|
|
75
|
-
console.log(chalk.blue('\nAbout to create configuration files:'));
|
|
76
|
-
console.log(chalk.dim(`Config: ${configPath}`));
|
|
77
|
-
console.log(chalk.dim(`Agents: ${agentsDir}/`));
|
|
78
|
-
console.log(chalk.dim('This will configure OpenCode with the Berget auth plugin.'));
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
if (await confirm('\nCreate configuration files? (Y/n): ', options.yes)) {
|
|
82
|
-
try {
|
|
83
|
-
await writeFile(configPath, JSON.stringify(config, null, 2));
|
|
84
|
-
console.log(chalk.green('✓ Created opencode.json'));
|
|
85
|
-
console.log(chalk.dim(' Plugin: @bergetai/opencode-auth'));
|
|
86
|
-
|
|
87
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
88
|
-
const templateFiles = fs.readdirSync(templatesDir).filter((f) => f.endsWith('.md'));
|
|
89
|
-
for (const file of templateFiles) {
|
|
90
|
-
const source = path.join(templatesDir, file);
|
|
91
|
-
const destination = path.join(agentsDir, file);
|
|
92
|
-
fs.copyFileSync(source, destination);
|
|
93
|
-
}
|
|
94
|
-
console.log(
|
|
95
|
-
chalk.green(
|
|
96
|
-
`✓ Created ${templateFiles.length} agent definitions in .opencode/agents/`,
|
|
97
|
-
),
|
|
98
|
-
);
|
|
99
|
-
} catch (error) {
|
|
100
|
-
console.error(chalk.red('Failed to create config files:'));
|
|
101
|
-
handleError('Config file creation failed', error);
|
|
102
|
-
return;
|
|
103
|
-
}
|
|
104
|
-
} else {
|
|
105
|
-
console.log(chalk.yellow('Configuration file creation cancelled.'));
|
|
106
|
-
return;
|
|
107
|
-
}
|
|
108
|
-
|
|
109
|
-
console.log(chalk.green('\n✅ Project initialized successfully!'));
|
|
110
|
-
console.log(chalk.blue('\nNext steps:'));
|
|
111
|
-
console.log(chalk.cyan(' 1. Run: opencode'));
|
|
112
|
-
console.log(chalk.cyan(' 2. Type: /connect'));
|
|
113
|
-
console.log(chalk.cyan(' 3. Choose your auth method:'));
|
|
114
|
-
console.log(chalk.dim(' • "Login with Berget" — Berget Code team members (SSO)'));
|
|
115
|
-
console.log(chalk.dim(' • "Enter API Key" — API key users (console.berget.ai)'));
|
|
116
|
-
} catch (error) {
|
|
117
|
-
handleError('Failed to initialize project', error);
|
|
118
|
-
}
|
|
119
|
-
});
|
|
120
|
-
|
|
121
|
-
code
|
|
122
|
-
.command(SUBCOMMANDS.CODE.RUN)
|
|
123
|
-
.description('Run AI coding assistant')
|
|
124
|
-
.argument('[prompt]', 'Prompt to send directly to OpenCode')
|
|
125
|
-
.option('-m, --model <model>', 'Model to use (overrides config)')
|
|
126
|
-
.option('-a, --analysis', 'Use fast analysis model for context building')
|
|
127
|
-
.option('--no-config', 'Run without loading project config')
|
|
128
|
-
.option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
|
|
129
|
-
.action(async (prompt: string, options: any) => {
|
|
130
|
-
try {
|
|
131
|
-
const configPath = path.join(process.cwd(), 'opencode.json');
|
|
132
|
-
|
|
133
|
-
// Ensure opencode is installed
|
|
134
|
-
if (!(await ensureOpencodeInstalled(options.yes))) {
|
|
135
|
-
return;
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
let config: any = null;
|
|
139
|
-
if (!options.noConfig && fs.existsSync(configPath)) {
|
|
140
|
-
try {
|
|
141
|
-
const configContent = await readFile(configPath, 'utf8');
|
|
142
|
-
config = JSON.parse(configContent);
|
|
143
|
-
console.log(chalk.dim(`Loaded config for project: ${config.projectName}`));
|
|
144
|
-
console.log(
|
|
145
|
-
chalk.dim(`Models: Analysis=${config.analysisModel}, Build=${config.buildModel}`),
|
|
146
|
-
);
|
|
147
|
-
} catch {
|
|
148
|
-
console.log(chalk.yellow('Warning: Failed to load opencode.json'));
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
if (!config) {
|
|
153
|
-
console.log(chalk.yellow('No project configuration found.'));
|
|
154
|
-
console.log(
|
|
155
|
-
chalk.blue(
|
|
156
|
-
`Run ${chalk.bold(`berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`)} first.`,
|
|
157
|
-
),
|
|
158
|
-
);
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
// Prepare opencode command
|
|
163
|
-
const environment = { ...process.env };
|
|
164
|
-
const opencodeArguments: string[] = [];
|
|
165
|
-
|
|
166
|
-
// Read --stage and --local from root program options
|
|
167
|
-
// (these flags are registered at program level, not subcommand level)
|
|
168
|
-
const isStage = process.argv.includes('--stage');
|
|
169
|
-
const isLocal = process.argv.includes('--local');
|
|
170
|
-
|
|
171
|
-
if (isStage) {
|
|
172
|
-
console.log(chalk.cyan('Using Berget stage environment'));
|
|
173
|
-
environment.BERGET_API_URL = 'https://api.stage.berget.ai';
|
|
174
|
-
environment.BERGET_INFERENCE_URL = 'https://api.stage.berget.ai/v1';
|
|
175
|
-
} else if (isLocal) {
|
|
176
|
-
console.log(chalk.cyan('Using local development environment'));
|
|
177
|
-
environment.BERGET_API_URL = 'http://localhost:3000';
|
|
178
|
-
environment.BERGET_INFERENCE_URL = 'http://localhost:3000/v1';
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
if (prompt) {
|
|
182
|
-
opencodeArguments.push('run', prompt);
|
|
183
|
-
}
|
|
184
|
-
|
|
185
|
-
// Choose model based on analysis flag or override
|
|
186
|
-
let selectedModel = options.model || config.buildModel;
|
|
187
|
-
if (options.analysis && !options.model) {
|
|
188
|
-
selectedModel = config.analysisModel;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
if (selectedModel) {
|
|
192
|
-
opencodeArguments.push('--model', selectedModel);
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
console.log(chalk.cyan('Starting OpenCode...'));
|
|
196
|
-
|
|
197
|
-
// Spawn opencode process
|
|
198
|
-
const opencode = spawn('opencode', opencodeArguments, {
|
|
199
|
-
env: environment,
|
|
200
|
-
stdio: 'inherit',
|
|
201
|
-
});
|
|
202
|
-
|
|
203
|
-
opencode.on('close', (code) => {
|
|
204
|
-
if (code !== 0) {
|
|
205
|
-
console.log(chalk.red(`OpenCode exited with code ${code}`));
|
|
206
|
-
}
|
|
207
|
-
});
|
|
208
|
-
|
|
209
|
-
opencode.on('error', (error) => {
|
|
210
|
-
console.error(chalk.red('Failed to start OpenCode:'));
|
|
211
|
-
console.error(error.message);
|
|
212
|
-
});
|
|
213
|
-
} catch (error) {
|
|
214
|
-
handleError('Failed to run OpenCode', error);
|
|
215
|
-
}
|
|
216
|
-
});
|
|
217
|
-
|
|
218
|
-
code
|
|
219
|
-
.command(SUBCOMMANDS.CODE.SERVE)
|
|
220
|
-
.description('Start OpenCode web server')
|
|
221
|
-
.option('-p, --port <port>', 'Port to run the server on (default: 3000)')
|
|
222
|
-
.option('-h, --host <host>', 'Host to bind the server to (default: localhost)')
|
|
223
|
-
.option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
|
|
224
|
-
.action(async (options) => {
|
|
17
|
+
.description('Interactive setup for Berget AI coding tools')
|
|
18
|
+
.action(async () => {
|
|
225
19
|
try {
|
|
226
|
-
|
|
227
|
-
if (!(await ensureOpencodeInstalled(options.yes))) {
|
|
228
|
-
return;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
console.log(chalk.cyan('🚀 Starting OpenCode web server...'));
|
|
232
|
-
|
|
233
|
-
// Prepare opencode serve command
|
|
234
|
-
const serveArguments: string[] = ['serve'];
|
|
235
|
-
|
|
236
|
-
if (options.port) {
|
|
237
|
-
serveArguments.push('--port', options.port);
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
if (options.host) {
|
|
241
|
-
serveArguments.push('--host', options.host);
|
|
242
|
-
}
|
|
243
|
-
|
|
244
|
-
// Spawn opencode serve process
|
|
245
|
-
const opencode = spawn('opencode', serveArguments, {
|
|
246
|
-
stdio: 'inherit',
|
|
247
|
-
});
|
|
248
|
-
|
|
249
|
-
opencode.on('close', (code) => {
|
|
250
|
-
if (code !== 0) {
|
|
251
|
-
console.log(chalk.red(`OpenCode server exited with code ${code}`));
|
|
252
|
-
}
|
|
253
|
-
});
|
|
254
|
-
|
|
255
|
-
opencode.on('error', (error) => {
|
|
256
|
-
console.error(chalk.red('Failed to start OpenCode server:'));
|
|
257
|
-
console.error(error.message);
|
|
258
|
-
});
|
|
20
|
+
await runInitCommand();
|
|
259
21
|
} catch (error) {
|
|
260
|
-
handleError('
|
|
261
|
-
}
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
code
|
|
265
|
-
.command(SUBCOMMANDS.CODE.UPDATE)
|
|
266
|
-
.description('Update OpenCode and agents to latest versions')
|
|
267
|
-
.option('-f, --force', 'Force update even if already latest')
|
|
268
|
-
.option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
|
|
269
|
-
.action(async (options) => {
|
|
270
|
-
try {
|
|
271
|
-
console.log(chalk.cyan('🔄 Updating OpenCode configuration...'));
|
|
272
|
-
|
|
273
|
-
// Ensure opencode is installed first
|
|
274
|
-
if (!(await ensureOpencodeInstalled(options.yes))) {
|
|
275
|
-
return;
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const configPath = path.join(process.cwd(), 'opencode.json');
|
|
279
|
-
|
|
280
|
-
// Check if project is initialized
|
|
281
|
-
if (!fs.existsSync(configPath)) {
|
|
282
|
-
console.log(chalk.red('❌ No OpenCode configuration found.'));
|
|
283
|
-
console.log(
|
|
284
|
-
chalk.blue(
|
|
285
|
-
`Run ${chalk.bold(`berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`)} first.`,
|
|
286
|
-
),
|
|
287
|
-
);
|
|
288
|
-
return;
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
// Read current configuration
|
|
292
|
-
let currentConfig: any;
|
|
293
|
-
try {
|
|
294
|
-
const configContent = await readFile(configPath, 'utf8');
|
|
295
|
-
currentConfig = JSON.parse(configContent);
|
|
296
|
-
} catch (error) {
|
|
297
|
-
console.error(chalk.red('Failed to read current opencode.json:'));
|
|
298
|
-
handleError('Config read failed', error);
|
|
299
|
-
return;
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
console.log(chalk.blue('📋 Current configuration:'));
|
|
303
|
-
if (currentConfig.model) {
|
|
304
|
-
console.log(chalk.dim(` Model: ${currentConfig.model}`));
|
|
305
|
-
}
|
|
306
|
-
|
|
307
|
-
const agentsDir = path.join(process.cwd(), '.opencode', 'agents');
|
|
308
|
-
const templatesDir = getAgentTemplatesDir();
|
|
309
|
-
const templateFiles = fs.readdirSync(templatesDir).filter((f) => f.endsWith('.md'));
|
|
310
|
-
|
|
311
|
-
// Check if agent definitions need updating
|
|
312
|
-
let agentsNeedUpdate = false;
|
|
313
|
-
|
|
314
|
-
for (const file of templateFiles) {
|
|
315
|
-
const source = path.join(templatesDir, file);
|
|
316
|
-
const destination = path.join(agentsDir, file);
|
|
317
|
-
if (!fs.existsSync(destination)) {
|
|
318
|
-
agentsNeedUpdate = true;
|
|
319
|
-
break;
|
|
320
|
-
}
|
|
321
|
-
const sourceContent = fs.readFileSync(source, 'utf8');
|
|
322
|
-
const destinationContent = fs.readFileSync(destination, 'utf8');
|
|
323
|
-
if (sourceContent !== destinationContent) {
|
|
324
|
-
agentsNeedUpdate = true;
|
|
325
|
-
break;
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Check if opencode.json still has inline agent config (needs migration)
|
|
330
|
-
const needsMigration = !!currentConfig.agent;
|
|
331
|
-
|
|
332
|
-
if (!agentsNeedUpdate && !needsMigration && !options.force) {
|
|
333
|
-
console.log(chalk.green('✅ Already using the latest configuration!'));
|
|
334
|
-
return;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
if (agentsNeedUpdate || needsMigration) {
|
|
338
|
-
console.log(chalk.blue('\n🔄 Updates available:'));
|
|
339
|
-
|
|
340
|
-
if (needsMigration) {
|
|
341
|
-
console.log(chalk.cyan(' • Migrate agents from opencode.json to .opencode/agents/'));
|
|
342
|
-
}
|
|
343
|
-
|
|
344
|
-
if (agentsNeedUpdate) {
|
|
345
|
-
console.log(chalk.cyan(' • Latest agent prompts and improvements'));
|
|
346
|
-
}
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
if (options.force) {
|
|
350
|
-
console.log(chalk.yellow('🔧 Force update requested'));
|
|
351
|
-
}
|
|
352
|
-
|
|
353
|
-
if (!options.yes) {
|
|
354
|
-
console.log(
|
|
355
|
-
chalk.blue('\nThis will update your agent definitions and OpenCode configuration.'),
|
|
356
|
-
);
|
|
357
|
-
|
|
358
|
-
const hasGitRepo = hasGit();
|
|
359
|
-
if (hasGitRepo) {
|
|
360
|
-
console.log(chalk.green('✓ Git repository detected - changes are tracked'));
|
|
361
|
-
} else {
|
|
362
|
-
console.log(chalk.yellow('⚠️ No .git repository detected - backup will be created'));
|
|
363
|
-
}
|
|
364
|
-
}
|
|
365
|
-
|
|
366
|
-
if (await confirm('\nProceed with update? (Y/n): ', options.yes)) {
|
|
367
|
-
try {
|
|
368
|
-
let backupPath: null | string = null;
|
|
369
|
-
|
|
370
|
-
if (!hasGit()) {
|
|
371
|
-
backupPath = `${configPath}.backup.${Date.now()}`;
|
|
372
|
-
await writeFile(backupPath, JSON.stringify(currentConfig, null, 2));
|
|
373
|
-
console.log(
|
|
374
|
-
chalk.green(`✓ Backed up current config to ${path.basename(backupPath)}`),
|
|
375
|
-
);
|
|
376
|
-
}
|
|
377
|
-
|
|
378
|
-
// Remove inline agent section from opencode.json if present
|
|
379
|
-
if (currentConfig.agent) {
|
|
380
|
-
delete currentConfig.agent;
|
|
381
|
-
await writeFile(configPath, JSON.stringify(currentConfig, null, 2));
|
|
382
|
-
console.log(chalk.green('✓ Removed inline agent config from opencode.json'));
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Sync agent markdown files from templates
|
|
386
|
-
fs.mkdirSync(agentsDir, { recursive: true });
|
|
387
|
-
let updatedCount = 0;
|
|
388
|
-
for (const file of templateFiles) {
|
|
389
|
-
const source = path.join(templatesDir, file);
|
|
390
|
-
const destination = path.join(agentsDir, file);
|
|
391
|
-
const agentName = path.basename(file, '.md');
|
|
392
|
-
|
|
393
|
-
if (
|
|
394
|
-
!fs.existsSync(destination) ||
|
|
395
|
-
fs.readFileSync(source, 'utf8') !== fs.readFileSync(destination, 'utf8')
|
|
396
|
-
) {
|
|
397
|
-
fs.copyFileSync(source, destination);
|
|
398
|
-
updatedCount++;
|
|
399
|
-
console.log(chalk.cyan(` • Updated agent: ${agentName}`));
|
|
400
|
-
}
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
if (updatedCount > 0) {
|
|
404
|
-
console.log(chalk.green(`✓ Updated ${updatedCount} agent definition(s)`));
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Update AGENTS.md if it doesn't exist
|
|
408
|
-
const agentsMdPath = path.join(process.cwd(), 'AGENTS.md');
|
|
409
|
-
if (!fs.existsSync(agentsMdPath)) {
|
|
410
|
-
const agentsMdContent = `# Berget Code Agents
|
|
411
|
-
|
|
412
|
-
This document describes the specialized agents available in this project for use with OpenCode.
|
|
413
|
-
|
|
414
|
-
Agents are defined as markdown files in \`.opencode/agents/\` with YAML frontmatter.
|
|
415
|
-
|
|
416
|
-
## Available Agents
|
|
417
|
-
|
|
418
|
-
### Primary Agents
|
|
419
|
-
|
|
420
|
-
| Agent | Description | Temperature |
|
|
421
|
-
|-------|-------------|-------------|
|
|
422
|
-
| fullstack | Router/coordinator for full-stack development | 0.3 |
|
|
423
|
-
| frontend | Scandinavian, type-safe UIs with React, Tailwind, Shadcn | 0.4 |
|
|
424
|
-
| backend | Functional, modular Koa + TypeScript services | 0.3 |
|
|
425
|
-
| devops | Declarative GitOps infra with FluxCD, Kustomize, Helm | 0.3 |
|
|
426
|
-
| app | Expo + React Native apps; props-first, offline-aware | 0.4 |
|
|
427
|
-
|
|
428
|
-
### Subagents
|
|
429
|
-
|
|
430
|
-
| Agent | Description | Temperature |
|
|
431
|
-
|-------|-------------|-------------|
|
|
432
|
-
| security | Security specialist for pentesting and OWASP compliance | 0.2 |
|
|
433
|
-
| quality | QA specialist for testing, building, and PR management | 0.1 |
|
|
434
|
-
|
|
435
|
-
## Usage
|
|
436
|
-
|
|
437
|
-
- **Tab** key to cycle between primary agents
|
|
438
|
-
- **@mention** to invoke subagents (e.g. \`@security review this code\`)
|
|
439
|
-
- \`/fullstack\`, \`/frontend\`, \`/backend\`, \`/devops\`, \`/app\` to switch agents
|
|
440
|
-
|
|
441
|
-
## Routing Rules
|
|
442
|
-
|
|
443
|
-
The fullstack agent automatically routes tasks based on file patterns:
|
|
444
|
-
|
|
445
|
-
- \`/apps/frontend\` or \`.tsx\` files → frontend
|
|
446
|
-
- \`/apps/app\` or Expo/React Native → app
|
|
447
|
-
- \`/infra\`, \`/k8s\`, FluxCD, Helm → devops
|
|
448
|
-
- \`/services\`, Koa routers → backend
|
|
449
|
-
|
|
450
|
-
## Customization
|
|
451
|
-
|
|
452
|
-
Edit the markdown files in \`.opencode/agents/\` to customize agent behavior.
|
|
453
|
-
See https://opencode.ai/docs/agents/ for available options.
|
|
454
|
-
|
|
455
|
-
---
|
|
456
|
-
|
|
457
|
-
*Updated by berget code update*
|
|
458
|
-
`;
|
|
459
|
-
|
|
460
|
-
await writeFile(agentsMdPath, agentsMdContent);
|
|
461
|
-
console.log(chalk.green('✓ Created AGENTS.md documentation'));
|
|
462
|
-
}
|
|
463
|
-
|
|
464
|
-
console.log(chalk.green('\n✅ Update completed successfully!'));
|
|
465
|
-
} catch (error) {
|
|
466
|
-
console.error(chalk.red('Failed to update configuration:'));
|
|
467
|
-
handleError('Update failed', error);
|
|
468
|
-
|
|
469
|
-
try {
|
|
470
|
-
await writeFile(configPath, JSON.stringify(currentConfig, null, 2));
|
|
471
|
-
console.log(chalk.yellow('📁 Restored original configuration from backup'));
|
|
472
|
-
} catch {
|
|
473
|
-
console.error(chalk.red('Failed to restore backup:'));
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
} else {
|
|
477
|
-
console.log(chalk.yellow('Update cancelled.'));
|
|
478
|
-
}
|
|
479
|
-
} catch {
|
|
480
|
-
console.error(chalk.red('Failed to update OpenCode configuration'));
|
|
481
|
-
}
|
|
482
|
-
});
|
|
483
|
-
}
|
|
484
|
-
|
|
485
|
-
/**
|
|
486
|
-
* Check if opencode is installed
|
|
487
|
-
*/
|
|
488
|
-
function checkOpencodeInstalled(): Promise<boolean> {
|
|
489
|
-
return new Promise((resolve) => {
|
|
490
|
-
const child = spawn('which', ['opencode'], {
|
|
491
|
-
stdio: 'pipe',
|
|
492
|
-
});
|
|
493
|
-
|
|
494
|
-
child.on('close', (code) => {
|
|
495
|
-
resolve(code === 0);
|
|
496
|
-
});
|
|
497
|
-
|
|
498
|
-
child.on('error', () => {
|
|
499
|
-
resolve(false);
|
|
500
|
-
});
|
|
501
|
-
});
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
/**
|
|
505
|
-
* Helper function to get user confirmation
|
|
506
|
-
*/
|
|
507
|
-
async function confirm(question: string, autoYes = false): Promise<boolean> {
|
|
508
|
-
if (autoYes) {
|
|
509
|
-
return true;
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
return new Promise((resolve) => {
|
|
513
|
-
const rl = readline.createInterface({
|
|
514
|
-
input: process.stdin,
|
|
515
|
-
output: process.stdout,
|
|
516
|
-
});
|
|
517
|
-
|
|
518
|
-
rl.question(question, (answer) => {
|
|
519
|
-
rl.close();
|
|
520
|
-
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes' || answer === '');
|
|
521
|
-
});
|
|
522
|
-
});
|
|
523
|
-
}
|
|
524
|
-
|
|
525
|
-
/**
|
|
526
|
-
* Ensure opencode is installed, offering to install if not
|
|
527
|
-
*/
|
|
528
|
-
async function ensureOpencodeInstalled(autoYes = false): Promise<boolean> {
|
|
529
|
-
let opencodeInstalled = await checkOpencodeInstalled();
|
|
530
|
-
if (!opencodeInstalled) {
|
|
531
|
-
if (!autoYes) {
|
|
532
|
-
console.log(chalk.red('OpenCode is not installed.'));
|
|
533
|
-
console.log(chalk.blue('OpenCode is required for the AI coding assistant.'));
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
if (await confirm('Would you like to install OpenCode automatically? (Y/n): ', autoYes)) {
|
|
537
|
-
opencodeInstalled = await installOpencode();
|
|
538
|
-
} else {
|
|
539
|
-
if (!autoYes) {
|
|
540
|
-
console.log(chalk.blue('\nInstallation cancelled.'));
|
|
541
|
-
console.log(
|
|
542
|
-
chalk.blue('To install manually: curl -fsSL https://opencode.ai/install | bash'),
|
|
543
|
-
);
|
|
544
|
-
console.log(chalk.blue('Or visit: https://opencode.ai/docs'));
|
|
22
|
+
handleError('Setup failed', error);
|
|
545
23
|
}
|
|
546
|
-
}
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
return opencodeInstalled;
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
/**
|
|
553
|
-
* Get the path to the bundled agent templates directory
|
|
554
|
-
*/
|
|
555
|
-
function getAgentTemplatesDir(): string {
|
|
556
|
-
return path.resolve(__dirname, '../../templates/agents');
|
|
557
|
-
}
|
|
558
|
-
|
|
559
|
-
/**
|
|
560
|
-
* Get project name from current directory or package.json
|
|
561
|
-
*/
|
|
562
|
-
function getProjectName(): string {
|
|
563
|
-
try {
|
|
564
|
-
const packageJsonPath = path.join(process.cwd(), 'package.json');
|
|
565
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
566
|
-
const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
|
|
567
|
-
const packageJson = JSON.parse(packageJsonContent);
|
|
568
|
-
return packageJson.name || path.basename(process.cwd());
|
|
569
|
-
}
|
|
570
|
-
} catch {
|
|
571
|
-
// Ignore error and fallback to directory name
|
|
572
|
-
}
|
|
573
|
-
return path.basename(process.cwd());
|
|
574
|
-
}
|
|
575
|
-
|
|
576
|
-
/**
|
|
577
|
-
* Check if current directory has git
|
|
578
|
-
*/
|
|
579
|
-
function hasGit(): boolean {
|
|
580
|
-
try {
|
|
581
|
-
return fs.existsSync(path.join(process.cwd(), '.git'));
|
|
582
|
-
} catch {
|
|
583
|
-
return false;
|
|
584
|
-
}
|
|
585
|
-
}
|
|
586
|
-
|
|
587
|
-
/**
|
|
588
|
-
* Install opencode via npm
|
|
589
|
-
*/
|
|
590
|
-
async function installOpencode(): Promise<boolean> {
|
|
591
|
-
console.log(chalk.cyan('Installing OpenCode via npm...'));
|
|
592
|
-
|
|
593
|
-
try {
|
|
594
|
-
await new Promise<void>((resolve, reject) => {
|
|
595
|
-
const install = spawn('npm', ['install', '-g', 'opencode-ai@1.3'], {
|
|
596
|
-
stdio: 'inherit',
|
|
597
|
-
});
|
|
598
|
-
|
|
599
|
-
install.on('close', (code) => {
|
|
600
|
-
if (code === 0) {
|
|
601
|
-
console.log(chalk.green('✓ OpenCode installed successfully!'));
|
|
602
|
-
resolve();
|
|
603
|
-
} else {
|
|
604
|
-
reject(new Error(`Installation failed with code ${code}`));
|
|
605
|
-
}
|
|
606
|
-
});
|
|
607
|
-
|
|
608
|
-
install.on('error', reject);
|
|
609
24
|
});
|
|
610
|
-
|
|
611
|
-
// Verify installation
|
|
612
|
-
const opencodeInstalled = await checkOpencodeInstalled();
|
|
613
|
-
if (!opencodeInstalled) {
|
|
614
|
-
console.log(chalk.yellow('Installation completed but opencode command not found.'));
|
|
615
|
-
console.log(chalk.yellow('You may need to restart your terminal or check your PATH.'));
|
|
616
|
-
return false;
|
|
617
|
-
}
|
|
618
|
-
|
|
619
|
-
return true;
|
|
620
|
-
} catch (error) {
|
|
621
|
-
console.error(chalk.red('Failed to install OpenCode:'));
|
|
622
|
-
console.error(error instanceof Error ? error.message : String(error));
|
|
623
|
-
console.log(chalk.blue('\nAlternative installation methods:'));
|
|
624
|
-
console.log(chalk.blue(' curl -fsSL https://opencode.ai/install | sh'));
|
|
625
|
-
console.log(chalk.blue(' Or visit: https://opencode.ai/docs'));
|
|
626
|
-
return false;
|
|
627
|
-
}
|
|
628
25
|
}
|
|
@@ -76,10 +76,6 @@ export const SUBCOMMANDS = {
|
|
|
76
76
|
// Code commands
|
|
77
77
|
CODE: {
|
|
78
78
|
INIT: 'init',
|
|
79
|
-
RUN: 'run',
|
|
80
|
-
SERVE: 'serve',
|
|
81
|
-
SETUP: 'setup',
|
|
82
|
-
UPDATE: 'update',
|
|
83
79
|
},
|
|
84
80
|
|
|
85
81
|
// Flux commands
|
|
@@ -164,14 +160,8 @@ export const COMMAND_DESCRIPTIONS = {
|
|
|
164
160
|
[`${COMMAND_GROUPS.CLUSTERS} ${SUBCOMMANDS.CLUSTERS.GET_USAGE}`]:
|
|
165
161
|
'Get resource usage for a cluster',
|
|
166
162
|
[`${COMMAND_GROUPS.CLUSTERS} ${SUBCOMMANDS.CLUSTERS.LIST}`]: 'List all clusters',
|
|
167
|
-
[`${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`]:
|
|
168
|
-
|
|
169
|
-
[`${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.RUN}`]: 'Run AI coding assistant',
|
|
170
|
-
[`${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.SERVE}`]: 'Start OpenCode web server',
|
|
171
|
-
[`${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.SETUP}`]:
|
|
163
|
+
[`${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`]:
|
|
172
164
|
'Interactive setup for Berget AI coding tools',
|
|
173
|
-
[`${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.UPDATE}`]:
|
|
174
|
-
'Update OpenCode and agents to latest versions',
|
|
175
165
|
|
|
176
166
|
[`${COMMAND_GROUPS.FLUX} ${SUBCOMMANDS.FLUX.BOOTSTRAP}`]: 'Bootstrap Flux CD',
|
|
177
167
|
[`${COMMAND_GROUPS.FLUX} ${SUBCOMMANDS.FLUX.INSTALL}`]: 'Install Flux CD',
|
|
@@ -85,7 +85,7 @@ export class AuthService {
|
|
|
85
85
|
|
|
86
86
|
console.log(chalk.cyan('\nNext steps:'));
|
|
87
87
|
console.log(chalk.cyan(' • Create an API key: berget api-keys create'));
|
|
88
|
-
console.log(chalk.cyan(' •
|
|
88
|
+
console.log(chalk.cyan(' • Initialize OpenCode: berget code init'));
|
|
89
89
|
|
|
90
90
|
return true;
|
|
91
91
|
} catch (error) {
|