berget 2.0.6 → 2.1.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.
@@ -11,12 +11,12 @@ import path from 'path'
11
11
  import { spawn } from 'child_process'
12
12
  import { updateEnvFile } from '../utils/env-manager'
13
13
  import { createAuthenticatedClient } from '../client'
14
- import {
15
- getConfigLoader,
16
- getModelConfig,
14
+ import {
15
+ getConfigLoader,
16
+ getModelConfig,
17
17
  getProviderModels,
18
18
  type OpenCodeConfig,
19
- type AgentConfig
19
+ type AgentConfig,
20
20
  } from '../utils/config-loader'
21
21
 
22
22
  /**
@@ -35,11 +35,22 @@ function hasGit(): boolean {
35
35
  */
36
36
  async function mergeConfigurations(
37
37
  currentConfig: any,
38
- latestConfig: any,
38
+ latestConfig: any
39
39
  ): Promise<any> {
40
40
  try {
41
41
  const client = createAuthenticatedClient()
42
- const modelConfig = getModelConfig()
42
+
43
+ // Get model config with fallback for init scenario
44
+ let modelConfig: { primary: string; small: string }
45
+ try {
46
+ modelConfig = getModelConfig()
47
+ } catch (error) {
48
+ // Fallback to defaults when no config exists (init scenario)
49
+ modelConfig = {
50
+ primary: 'berget/glm-4.7',
51
+ small: 'berget/gpt-oss',
52
+ }
53
+ }
43
54
 
44
55
  console.log(chalk.blue('🤖 Using AI to merge configurations...'))
45
56
 
@@ -91,7 +102,7 @@ Return ONLY the merged JSON configuration, no explanations.`
91
102
  return mergedConfig
92
103
  } catch (parseError) {
93
104
  console.warn(
94
- chalk.yellow('⚠️ AI response invalid, using fallback merge'),
105
+ chalk.yellow('⚠️ AI response invalid, using fallback merge')
95
106
  )
96
107
  return fallbackMerge(currentConfig, latestConfig)
97
108
  }
@@ -182,7 +193,7 @@ async function confirm(question: string, autoYes = false): Promise<boolean> {
182
193
  resolve(
183
194
  answer.toLowerCase() === 'y' ||
184
195
  answer.toLowerCase() === 'yes' ||
185
- answer === '',
196
+ answer === ''
186
197
  )
187
198
  })
188
199
  })
@@ -194,7 +205,7 @@ async function confirm(question: string, autoYes = false): Promise<boolean> {
194
205
  async function askChoice(
195
206
  question: string,
196
207
  options: string[],
197
- defaultChoice?: string,
208
+ defaultChoice?: string
198
209
  ): Promise<string> {
199
210
  return new Promise((resolve) => {
200
211
  const rl = readline.createInterface({
@@ -216,7 +227,7 @@ async function askChoice(
216
227
 
217
228
  // Handle text input
218
229
  const matchingOption = options.find((option) =>
219
- option.toLowerCase().startsWith(trimmed),
230
+ option.toLowerCase().startsWith(trimmed)
220
231
  )
221
232
 
222
233
  if (matchingOption) {
@@ -236,7 +247,7 @@ async function askChoice(
236
247
  async function getInput(
237
248
  question: string,
238
249
  defaultValue: string,
239
- autoYes = false,
250
+ autoYes = false
240
251
  ): Promise<string> {
241
252
  if (autoYes) {
242
253
  return defaultValue
@@ -273,17 +284,90 @@ function getProjectName(): string {
273
284
  }
274
285
 
275
286
  /**
276
- * Load the latest agent configuration from opencode.json
287
+ * Load the latest agent configuration from embedded config
277
288
  */
278
289
  async function loadLatestAgentConfig(): Promise<any> {
279
- try {
280
- const configPath = path.join(__dirname, '../../opencode.json')
281
- const configContent = await readFile(configPath, 'utf8')
282
- const config = JSON.parse(configContent)
283
- return config.agent || {}
284
- } catch (error) {
285
- console.warn(chalk.yellow('⚠️ Could not load latest agent config, using fallback'))
286
- return {}
290
+ // Return the latest agent configuration directly - no file reading needed
291
+ return {
292
+ fullstack: {
293
+ model: 'berget/glm-4.7',
294
+ temperature: 0.3,
295
+ top_p: 0.9,
296
+ mode: 'primary',
297
+ permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
298
+ description:
299
+ 'Router/coordinator agent for full-stack development with schema-driven architecture',
300
+ prompt:
301
+ "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.\n\nGIT WORKFLOW RULES (CRITICAL):\n- NEVER push directly to main branch - ALWAYS use pull requests\n- NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'\n- ALWAYS clean up test files, documentation files, and temporary artifacts before committing\n- ALWAYS ensure git history maintains production quality - no test commits, no debugging code\n- ALWAYS create descriptive commit messages following project conventions\n- ALWAYS run tests and build before creating PR\n\nCRITICAL: 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.",
302
+ },
303
+ frontend: {
304
+ model: 'berget/glm-4.7',
305
+ temperature: 0.4,
306
+ top_p: 0.9,
307
+ mode: 'primary',
308
+ permission: { edit: 'allow', bash: 'deny', webfetch: 'allow' },
309
+ note: 'Bash access is denied for frontend persona to prevent shell command execution in UI environments. This restriction enforces security and architectural boundaries.',
310
+ description:
311
+ 'Builds Scandinavian, type-safe UIs with React, Tailwind, Shadcn.',
312
+ prompt:
313
+ '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.\n\nIMPORTANT: You have NO bash access and cannot run git commands. When your frontend implementation tasks are complete, inform the user that changes are ready and suggest using /pr command to create a pull request with proper testing and quality checks.\n\nCODE QUALITY RULES:\n- Write clean, production-ready code\n- Follow React and TypeScript best practices\n- Ensure accessibility and responsive design\n- Use semantic tokens from design system\n- Test your components manually when possible\n- Document any complex logic with comments\n\nCRITICAL: When frontend implementation is complete, ALWAYS inform the user to use "/pr" command to handle testing, building, and pull request creation.',
314
+ },
315
+ backend: {
316
+ model: 'berget/glm-4.7',
317
+ temperature: 0.3,
318
+ top_p: 0.9,
319
+ mode: 'primary',
320
+ permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
321
+ description:
322
+ 'Functional, modular Koa + TypeScript services; schema-first with code quality focus.',
323
+ prompt:
324
+ "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.\n\nGIT WORKFLOW RULES (CRITICAL):\n- NEVER push directly to main branch - ALWAYS use pull requests\n- NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'\n- ALWAYS clean up test files, documentation files, and temporary artifacts before committing\n- ALWAYS ensure git history maintains production quality - no test commits, no debugging code\n- ALWAYS create descriptive commit messages following project conventions\n- ALWAYS run tests and build before creating PR\n\nCRITICAL: 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.",
325
+ },
326
+ devops: {
327
+ model: 'berget/glm-4.7',
328
+ temperature: 0.3,
329
+ top_p: 0.8,
330
+ mode: 'primary',
331
+ permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
332
+ description:
333
+ 'Declarative GitOps infra with FluxCD, Kustomize, Helm, operators.',
334
+ prompt:
335
+ "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.\n\nGIT WORKFLOW RULES (CRITICAL):\n- NEVER push directly to main branch - ALWAYS use pull requests\n- NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'\n- ALWAYS clean up test files, documentation files, and temporary artifacts before committing\n- ALWAYS ensure git history maintains production quality - no test commits, no debugging code\n- ALWAYS create descriptive commit messages following project conventions\n- ALWAYS run tests and build before creating PR\n\nHelm Values Configuration Process:\n1. Documentation First Approach: Always fetch official documentation from Artifact Hub/GitHub for the specific chart version before writing values. Search Artifact Hub for exact chart version documentation, check the chart's GitHub repository for official docs and examples, verify the exact version being used in the deployment.\n2. Validation Requirements: Check for available validation schemas before committing YAML files. Use Helm's built-in validation tools (helm lint, helm template). Validate against JSON schema if available for the chart. Ensure YAML syntax correctness with linters.\n3. Standard Workflow: Identify chart name and exact version. Fetch official documentation from Artifact Hub/GitHub. Check for available schemas and validation tools. Write values according to official documentation. Validate against schema (if available). Test with helm template or helm lint. Commit validated YAML files.\n4. Quality Assurance: Never commit unvalidated Helm values. Use helm dependency update when adding new charts. Test rendering with helm template --dry-run before deployment. Document any custom values with comments referencing official docs.",
336
+ },
337
+ app: {
338
+ model: 'berget/glm-4.7',
339
+ temperature: 0.4,
340
+ top_p: 0.9,
341
+ mode: 'primary',
342
+ permission: { edit: 'allow', bash: 'deny', webfetch: 'allow' },
343
+ note: 'Bash access is denied for app persona to prevent shell command execution in mobile/Expo environments. This restriction enforces security and architectural boundaries.',
344
+ description:
345
+ 'Expo + React Native apps; props-first, offline-aware, shared tokens.',
346
+ prompt:
347
+ "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.\n\nGIT WORKFLOW RULES (CRITICAL):\n- NEVER push directly to main branch - ALWAYS use pull requests\n- NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'\n- ALWAYS clean up test files, documentation files, and temporary artifacts before committing\n- ALWAYS ensure git history maintains production quality - no test commits, no debugging code\n- ALWAYS create descriptive commit messages following project conventions\n- ALWAYS run tests and build before creating PR\n\nCRITICAL: When all app implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.",
348
+ },
349
+ security: {
350
+ model: 'berget/glm-4.7',
351
+ temperature: 0.2,
352
+ top_p: 0.8,
353
+ mode: 'subagent',
354
+ permission: { edit: 'deny', bash: 'allow', webfetch: 'allow' },
355
+ description:
356
+ 'Security specialist for pentesting, OWASP compliance, and vulnerability assessments.',
357
+ prompt:
358
+ "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.\n\nGIT WORKFLOW RULES (CRITICAL):\n- NEVER push directly to main branch - ALWAYS use pull requests\n- NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'\n- ALWAYS clean up test files, documentation files, and temporary artifacts before committing\n- ALWAYS ensure git history maintains production quality - no test commits, no debugging code\n- ALWAYS create descriptive commit messages following project conventions\n- ALWAYS run tests and build before creating PR",
359
+ },
360
+ quality: {
361
+ model: 'berget/glm-4.7',
362
+ temperature: 0.1,
363
+ top_p: 0.9,
364
+ mode: 'subagent',
365
+ permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
366
+ description:
367
+ 'Quality assurance specialist for testing, building, and PR management.',
368
+ prompt:
369
+ "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\\nGIT WORKFLOW RULES (CRITICAL - ENFORCE STRICTLY):\\n - NEVER push directly to main branch - ALWAYS use pull requests\\n - NEVER use 'git add .' - ALWAYS add specific files with 'git add path/to/file'\\n - ALWAYS clean up test files, documentation files, and temporary artifacts before committing\\n - ALWAYS ensure git history maintains production quality - no test commits, no debugging code\\n - ALWAYS create descriptive commit messages following project conventions\\n - ALWAYS run tests and build before creating PR\\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 specific/files && git commit -m \\\"message\\\" && git push (NEVER use git add .)\\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.",
370
+ },
287
371
  }
288
372
  }
289
373
 
@@ -317,7 +401,6 @@ async function installOpencode(): Promise<boolean> {
317
401
  await new Promise<void>((resolve, reject) => {
318
402
  const install = spawn('npm', ['install', '-g', 'opencode-ai'], {
319
403
  stdio: 'inherit',
320
- shell: true,
321
404
  })
322
405
 
323
406
  install.on('close', (code) => {
@@ -336,12 +419,12 @@ async function installOpencode(): Promise<boolean> {
336
419
  const opencodeInstalled = await checkOpencodeInstalled()
337
420
  if (!opencodeInstalled) {
338
421
  console.log(
339
- chalk.yellow('Installation completed but opencode command not found.'),
422
+ chalk.yellow('Installation completed but opencode command not found.')
340
423
  )
341
424
  console.log(
342
425
  chalk.yellow(
343
- 'You may need to restart your terminal or check your PATH.',
344
- ),
426
+ 'You may need to restart your terminal or check your PATH.'
427
+ )
345
428
  )
346
429
  return false
347
430
  }
@@ -351,7 +434,7 @@ async function installOpencode(): Promise<boolean> {
351
434
  console.error(chalk.red('Failed to install OpenCode:'))
352
435
  console.error(error instanceof Error ? error.message : String(error))
353
436
  console.log(chalk.blue('\nAlternative installation methods:'))
354
- console.log(chalk.blue(' curl -fsSL https://opencode.ai/install | bash'))
437
+ console.log(chalk.blue(' curl -fsSL https://opencode.ai/install | sh'))
355
438
  console.log(chalk.blue(' Or visit: https://opencode.ai/docs'))
356
439
  return false
357
440
  }
@@ -366,14 +449,14 @@ async function ensureOpencodeInstalled(autoYes = false): Promise<boolean> {
366
449
  if (!autoYes) {
367
450
  console.log(chalk.red('OpenCode is not installed.'))
368
451
  console.log(
369
- chalk.blue('OpenCode is required for the AI coding assistant.'),
452
+ chalk.blue('OpenCode is required for the AI coding assistant.')
370
453
  )
371
454
  }
372
455
 
373
456
  if (
374
457
  await confirm(
375
458
  'Would you like to install OpenCode automatically? (Y/n): ',
376
- autoYes,
459
+ autoYes
377
460
  )
378
461
  ) {
379
462
  opencodeInstalled = await installOpencode()
@@ -382,8 +465,8 @@ async function ensureOpencodeInstalled(autoYes = false): Promise<boolean> {
382
465
  console.log(chalk.blue('\nInstallation cancelled.'))
383
466
  console.log(
384
467
  chalk.blue(
385
- 'To install manually: curl -fsSL https://opencode.ai/install | bash',
386
- ),
468
+ 'To install manually: curl -fsSL https://opencode.ai/install | bash'
469
+ )
387
470
  )
388
471
  console.log(chalk.blue('Or visit: https://opencode.ai/docs'))
389
472
  }
@@ -408,7 +491,7 @@ export function registerCodeCommands(program: Command): void {
408
491
  .option('-f, --force', 'Overwrite existing configuration')
409
492
  .option(
410
493
  '-y, --yes',
411
- 'Automatically answer yes to all prompts (for automation)',
494
+ 'Automatically answer yes to all prompts (for automation)'
412
495
  )
413
496
  .action(async (options) => {
414
497
  try {
@@ -419,7 +502,7 @@ export function registerCodeCommands(program: Command): void {
419
502
  if (fs.existsSync(configPath) && !options.force) {
420
503
  if (!options.yes) {
421
504
  console.log(
422
- chalk.yellow('Project already initialized for OpenCode.'),
505
+ chalk.yellow('Project already initialized for OpenCode.')
423
506
  )
424
507
  console.log(chalk.dim(`Config file: ${configPath}`))
425
508
  }
@@ -440,7 +523,11 @@ export function registerCodeCommands(program: Command): void {
440
523
 
441
524
  // Check if we have an API key in environment first
442
525
  if (process.env.BERGET_API_KEY) {
443
- console.log(chalk.blue('🔑 Using BERGET_API_KEY from environment - no authentication required'))
526
+ console.log(
527
+ chalk.blue(
528
+ '🔑 Using BERGET_API_KEY from environment - no authentication required'
529
+ )
530
+ )
444
531
  } else {
445
532
  // Only require authentication if we don't have an API key
446
533
  try {
@@ -451,15 +538,29 @@ export function registerCodeCommands(program: Command): void {
451
538
  console.log(chalk.red('❌ Not authenticated with Berget AI.'))
452
539
  console.log(chalk.blue('To get started, you have two options:'))
453
540
  console.log('')
454
- console.log(chalk.yellow('Option 1: Use an existing API key (recommended)'))
455
- console.log(chalk.cyan(' Set BERGET_API_KEY environment variable:'))
456
- console.log(chalk.dim(' export BERGET_API_KEY=your_api_key_here'))
541
+ console.log(
542
+ chalk.yellow('Option 1: Use an existing API key (recommended)')
543
+ )
544
+ console.log(
545
+ chalk.cyan(' Set BERGET_API_KEY environment variable:')
546
+ )
547
+ console.log(
548
+ chalk.dim(' export BERGET_API_KEY=your_api_key_here')
549
+ )
457
550
  console.log(chalk.cyan(' Or create a .env file in your project:'))
458
- console.log(chalk.dim(' echo "BERGET_API_KEY=your_api_key_here" > .env'))
551
+ console.log(
552
+ chalk.dim(' echo "BERGET_API_KEY=your_api_key_here" > .env')
553
+ )
459
554
  console.log('')
460
- console.log(chalk.yellow('Option 2: Login and create a new API key'))
555
+ console.log(
556
+ chalk.yellow('Option 2: Login and create a new API key')
557
+ )
461
558
  console.log(chalk.cyan(' berget auth login'))
462
- console.log(chalk.cyan(` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`))
559
+ console.log(
560
+ chalk.cyan(
561
+ ` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`
562
+ )
563
+ )
463
564
  console.log('')
464
565
  console.log(chalk.blue('Then try again.'))
465
566
  return
@@ -467,7 +568,7 @@ export function registerCodeCommands(program: Command): void {
467
568
  }
468
569
 
469
570
  console.log(
470
- chalk.cyan(`Initializing OpenCode for project: ${projectName}`),
571
+ chalk.cyan(`Initializing OpenCode for project: ${projectName}`)
471
572
  )
472
573
 
473
574
  // Handle API key selection or creation
@@ -494,23 +595,31 @@ export function registerCodeCommands(program: Command): void {
494
595
  console.log(chalk.dim('─'.repeat(60)))
495
596
  existingKeys.forEach((key, index) => {
496
597
  console.log(
497
- `${chalk.cyan((index + 1).toString())}. ${chalk.bold(key.name)} (${key.prefix}...)`,
598
+ `${chalk.cyan((index + 1).toString())}. ${chalk.bold(
599
+ key.name
600
+ )} (${key.prefix}...)`
498
601
  )
499
602
  console.log(
500
603
  chalk.dim(
501
- ` Created: ${new Date(key.created).toLocaleDateString('sv-SE')}`,
502
- ),
604
+ ` Created: ${new Date(key.created).toLocaleDateString(
605
+ 'sv-SE'
606
+ )}`
607
+ )
503
608
  )
504
609
  console.log(
505
610
  chalk.dim(
506
- ` Last used: ${key.lastUsed ? new Date(key.lastUsed).toLocaleDateString('sv-SE') : 'Never'}`,
507
- ),
611
+ ` Last used: ${
612
+ key.lastUsed
613
+ ? new Date(key.lastUsed).toLocaleDateString('sv-SE')
614
+ : 'Never'
615
+ }`
616
+ )
508
617
  )
509
618
  if (index < existingKeys.length - 1) console.log()
510
619
  })
511
620
  console.log(chalk.dim('─'.repeat(60)))
512
621
  console.log(
513
- chalk.cyan(`${existingKeys.length + 1}. Create a new API key`),
622
+ chalk.cyan(`${existingKeys.length + 1}. Create a new API key`)
514
623
  )
515
624
 
516
625
  // Get user choice
@@ -521,14 +630,12 @@ export function registerCodeCommands(program: Command): void {
521
630
  })
522
631
  rl.question(
523
632
  chalk.blue(
524
- '\nSelect an option (1-' +
525
- (existingKeys.length + 1) +
526
- '): ',
633
+ '\nSelect an option (1-' + (existingKeys.length + 1) + '): '
527
634
  ),
528
635
  (answer) => {
529
636
  rl.close()
530
637
  resolve(answer.trim())
531
- },
638
+ }
532
639
  )
533
640
  })
534
641
 
@@ -542,28 +649,28 @@ export function registerCodeCommands(program: Command): void {
542
649
  // We need to rotate the key to get the actual key value
543
650
  console.log(
544
651
  chalk.yellow(
545
- `\n🔄 Rotating API key "${selectedKey.name}" to get the key value...`,
546
- ),
652
+ `\n🔄 Rotating API key "${selectedKey.name}" to get the key value...`
653
+ )
547
654
  )
548
655
 
549
656
  if (
550
657
  await confirm(
551
658
  chalk.yellow(
552
- 'This will invalidate the current key. Continue? (Y/n): ',
659
+ 'This will invalidate the current key. Continue? (Y/n): '
553
660
  ),
554
- options.yes,
661
+ options.yes
555
662
  )
556
663
  ) {
557
664
  const rotatedKey = await apiKeyService.rotate(
558
- selectedKey.id.toString(),
665
+ selectedKey.id.toString()
559
666
  )
560
667
  apiKey = rotatedKey.key
561
668
  console.log(chalk.green(`✓ API key rotated successfully`))
562
669
  } else {
563
670
  console.log(
564
671
  chalk.yellow(
565
- 'Cancelled. Please select a different option or create a new key.',
566
- ),
672
+ 'Cancelled. Please select a different option or create a new key.'
673
+ )
567
674
  )
568
675
  return
569
676
  }
@@ -575,7 +682,7 @@ export function registerCodeCommands(program: Command): void {
575
682
  const customName = await getInput(
576
683
  chalk.blue(`Enter key name (default: ${defaultKeyName}): `),
577
684
  defaultKeyName,
578
- options.yes,
685
+ options.yes
579
686
  )
580
687
 
581
688
  keyName = customName
@@ -598,7 +705,7 @@ export function registerCodeCommands(program: Command): void {
598
705
  const customName = await getInput(
599
706
  chalk.blue(`Enter key name (default: ${defaultKeyName}): `),
600
707
  defaultKeyName,
601
- options.yes,
708
+ options.yes
602
709
  )
603
710
 
604
711
  keyName = customName
@@ -610,8 +717,14 @@ export function registerCodeCommands(program: Command): void {
610
717
  }
611
718
  } catch (error) {
612
719
  if (process.env.BERGET_API_KEY) {
613
- console.log(chalk.yellow('⚠️ Could not verify API key with Berget API, but continuing with environment key'))
614
- console.log(chalk.dim('This might be due to network issues or an invalid key'))
720
+ console.log(
721
+ chalk.yellow(
722
+ '⚠️ Could not verify API key with Berget API, but continuing with environment key'
723
+ )
724
+ )
725
+ console.log(
726
+ chalk.dim('This might be due to network issues or an invalid key')
727
+ )
615
728
  } else {
616
729
  console.error(chalk.red('❌ Failed to handle API keys:'))
617
730
  console.log(chalk.blue('This could be due to:'))
@@ -621,7 +734,11 @@ export function registerCodeCommands(program: Command): void {
621
734
  console.log('')
622
735
  console.log(chalk.blue('Try using an API key directly:'))
623
736
  console.log(chalk.cyan(' export BERGET_API_KEY=your_api_key_here'))
624
- console.log(chalk.cyan(` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT} --yes`))
737
+ console.log(
738
+ chalk.cyan(
739
+ ` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT} --yes`
740
+ )
741
+ )
625
742
  handleError('API key operation failed', error)
626
743
  }
627
744
  return
@@ -630,9 +747,14 @@ export function registerCodeCommands(program: Command): void {
630
747
  // Prepare .env file path for safe update
631
748
  const envPath = path.join(process.cwd(), '.env')
632
749
 
633
- // Load latest agent configuration to ensure consistency
750
+ // Load latest agent configuration from our own codebase
634
751
  const latestAgentConfig = await loadLatestAgentConfig()
635
- const modelConfig = getModelConfig()
752
+
753
+ // Use hardcoded defaults for init - never try to load from project
754
+ const modelConfig = {
755
+ primary: 'berget/glm-4.7',
756
+ small: 'berget/gpt-oss',
757
+ }
636
758
 
637
759
  // Create opencode.json config with optimized agent-based format
638
760
  const config = {
@@ -772,11 +894,25 @@ export function registerCodeCommands(program: Command): void {
772
894
  berget: {
773
895
  npm: '@ai-sdk/openai-compatible',
774
896
  name: 'Berget AI',
775
- options: {
897
+ options: {
776
898
  baseURL: 'https://api.berget.ai/v1',
777
899
  apiKey: '{env:BERGET_API_KEY}',
778
900
  },
779
- models: getProviderModels(),
901
+ models: {
902
+ 'glm-4.7': {
903
+ name: 'GLM-4.7',
904
+ limit: { output: 4000, context: 90000 },
905
+ },
906
+ 'gpt-oss': {
907
+ name: 'GPT-OSS',
908
+ limit: { output: 4000, context: 128000 },
909
+ modalities: ['text', 'image'],
910
+ },
911
+ 'llama-8b': {
912
+ name: 'llama-3.1-8b',
913
+ limit: { output: 4000, context: 128000 },
914
+ },
915
+ },
780
916
  },
781
917
  },
782
918
  }
@@ -788,33 +924,36 @@ export function registerCodeCommands(program: Command): void {
788
924
  console.log(chalk.dim(`Environment: ${envPath}`))
789
925
  console.log(
790
926
  chalk.dim(
791
- `Documentation: ${path.join(process.cwd(), 'AGENTS.md')} (if not exists)`,
792
- ),
927
+ `Documentation: ${path.join(
928
+ process.cwd(),
929
+ 'AGENTS.md'
930
+ )} (if not exists)`
931
+ )
793
932
  )
794
933
  console.log(
795
934
  chalk.dim(
796
- `Environment: ${path.join(process.cwd(), '.env')} will be updated`,
797
- ),
935
+ `Environment: ${path.join(process.cwd(), '.env')} will be updated`
936
+ )
798
937
  )
799
938
  console.log(
800
- chalk.dim('This will configure OpenCode to use Berget AI models.'),
939
+ chalk.dim('This will configure OpenCode to use Berget AI models.')
801
940
  )
802
941
  console.log(chalk.cyan('\n💡 Benefits:'))
803
942
  console.log(
804
943
  chalk.cyan(
805
- ' • API key stored separately in .env file (not committed to Git)',
806
- ),
944
+ ' • API key stored separately in .env file (not committed to Git)'
945
+ )
807
946
  )
808
947
  console.log(
809
- chalk.cyan(' • Easy cost separation per project/customer'),
948
+ chalk.cyan(' • Easy cost separation per project/customer')
810
949
  )
811
950
  console.log(
812
- chalk.cyan(' • Secure key management with environment variables'),
951
+ chalk.cyan(' • Secure key management with environment variables')
813
952
  )
814
953
  console.log(
815
954
  chalk.cyan(
816
- " • Project-specific agent documentation (won't overwrite existing)",
817
- ),
955
+ " • Project-specific agent documentation (won't overwrite existing)"
956
+ )
818
957
  )
819
958
  }
820
959
 
@@ -837,7 +976,7 @@ export function registerCodeCommands(program: Command): void {
837
976
  console.log(chalk.dim(` Small Model: ${config.small_model}`))
838
977
  console.log(chalk.dim(` Theme: ${config.theme}`))
839
978
  console.log(
840
- chalk.dim(` API Key: Stored in .env as BERGET_API_KEY`),
979
+ chalk.dim(` API Key: Stored in .env as BERGET_API_KEY`)
841
980
  )
842
981
 
843
982
  // Create AGENTS.md documentation only if it doesn't exist
@@ -1011,11 +1150,11 @@ All agents follow these principles:
1011
1150
  await writeFile(agentsMdPath, agentsMdContent)
1012
1151
  console.log(chalk.green(`✓ Created AGENTS.md`))
1013
1152
  console.log(
1014
- chalk.dim(` Documentation for available agents and usage`),
1153
+ chalk.dim(` Documentation for available agents and usage`)
1015
1154
  )
1016
1155
  } else {
1017
1156
  console.log(
1018
- chalk.yellow(`⚠ AGENTS.md already exists, skipping creation`),
1157
+ chalk.yellow(`⚠ AGENTS.md already exists, skipping creation`)
1019
1158
  )
1020
1159
  }
1021
1160
 
@@ -1046,7 +1185,7 @@ All agents follow these principles:
1046
1185
  console.log(chalk.green('\n✅ Project initialized successfully!'))
1047
1186
  console.log(chalk.blue('Next steps:'))
1048
1187
  console.log(
1049
- chalk.blue(` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.RUN}`),
1188
+ chalk.blue(` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.RUN}`)
1050
1189
  )
1051
1190
  console.log(chalk.blue(' Or run: opencode'))
1052
1191
  } catch (error) {
@@ -1063,7 +1202,7 @@ All agents follow these principles:
1063
1202
  .option('--no-config', 'Run without loading project config')
1064
1203
  .option(
1065
1204
  '-y, --yes',
1066
- 'Automatically answer yes to all prompts (for automation)',
1205
+ 'Automatically answer yes to all prompts (for automation)'
1067
1206
  )
1068
1207
  .action(async (prompt: string, options: any) => {
1069
1208
  try {
@@ -1080,12 +1219,12 @@ All agents follow these principles:
1080
1219
  const configContent = await readFile(configPath, 'utf8')
1081
1220
  config = JSON.parse(configContent)
1082
1221
  console.log(
1083
- chalk.dim(`Loaded config for project: ${config.projectName}`),
1222
+ chalk.dim(`Loaded config for project: ${config.projectName}`)
1084
1223
  )
1085
1224
  console.log(
1086
1225
  chalk.dim(
1087
- `Models: Analysis=${config.analysisModel}, Build=${config.buildModel}`,
1088
- ),
1226
+ `Models: Analysis=${config.analysisModel}, Build=${config.buildModel}`
1227
+ )
1089
1228
  )
1090
1229
  } catch (error) {
1091
1230
  console.log(chalk.yellow('Warning: Failed to load opencode.json'))
@@ -1096,8 +1235,10 @@ All agents follow these principles:
1096
1235
  console.log(chalk.yellow('No project configuration found.'))
1097
1236
  console.log(
1098
1237
  chalk.blue(
1099
- `Run ${chalk.bold(`berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`)} first.`,
1100
- ),
1238
+ `Run ${chalk.bold(
1239
+ `berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`
1240
+ )} first.`
1241
+ )
1101
1242
  )
1102
1243
  return
1103
1244
  }
@@ -1150,10 +1291,13 @@ All agents follow these principles:
1150
1291
  .command(SUBCOMMANDS.CODE.SERVE)
1151
1292
  .description('Start OpenCode web server')
1152
1293
  .option('-p, --port <port>', 'Port to run the server on (default: 3000)')
1153
- .option('-h, --host <host>', 'Host to bind the server to (default: localhost)')
1294
+ .option(
1295
+ '-h, --host <host>',
1296
+ 'Host to bind the server to (default: localhost)'
1297
+ )
1154
1298
  .option(
1155
1299
  '-y, --yes',
1156
- 'Automatically answer yes to all prompts (for automation)',
1300
+ 'Automatically answer yes to all prompts (for automation)'
1157
1301
  )
1158
1302
  .action(async (options) => {
1159
1303
  try {
@@ -1202,7 +1346,7 @@ All agents follow these principles:
1202
1346
  .option('-f, --force', 'Force update even if already latest')
1203
1347
  .option(
1204
1348
  '-y, --yes',
1205
- 'Automatically answer yes to all prompts (for automation)',
1349
+ 'Automatically answer yes to all prompts (for automation)'
1206
1350
  )
1207
1351
  .action(async (options) => {
1208
1352
  try {
@@ -1220,8 +1364,10 @@ All agents follow these principles:
1220
1364
  console.log(chalk.red('❌ No OpenCode configuration found.'))
1221
1365
  console.log(
1222
1366
  chalk.blue(
1223
- `Run ${chalk.bold(`berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`)} first.`,
1224
- ),
1367
+ `Run ${chalk.bold(
1368
+ `berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`
1369
+ )} first.`
1370
+ )
1225
1371
  )
1226
1372
  return
1227
1373
  }
@@ -1242,13 +1388,26 @@ All agents follow these principles:
1242
1388
  console.log(chalk.dim(` Theme: ${currentConfig.theme}`))
1243
1389
  console.log(
1244
1390
  chalk.dim(
1245
- ` Agents: ${Object.keys(currentConfig.agent || {}).length} configured`,
1246
- ),
1391
+ ` Agents: ${
1392
+ Object.keys(currentConfig.agent || {}).length
1393
+ } configured`
1394
+ )
1247
1395
  )
1248
1396
 
1249
1397
  // Load latest agent configuration to ensure consistency
1250
1398
  const latestAgentConfig = await loadLatestAgentConfig()
1251
- const modelConfig = getModelConfig()
1399
+
1400
+ // Get model config with fallback for init scenario
1401
+ let modelConfig: { primary: string; small: string }
1402
+ try {
1403
+ modelConfig = getModelConfig()
1404
+ } catch (error) {
1405
+ // Fallback to defaults when no config exists (init scenario)
1406
+ modelConfig = {
1407
+ primary: 'berget/glm-4.7',
1408
+ small: 'berget/gpt-oss',
1409
+ }
1410
+ }
1252
1411
 
1253
1412
  // Create latest configuration with all improvements
1254
1413
  const latestConfig = {
@@ -1419,7 +1578,7 @@ All agents follow these principles:
1419
1578
  const currentAgents = Object.keys(currentConfig.agent || {})
1420
1579
  const latestAgents = Object.keys(latestConfig.agent)
1421
1580
  const newAgents = latestAgents.filter(
1422
- (agent) => !currentAgents.includes(agent),
1581
+ (agent) => !currentAgents.includes(agent)
1423
1582
  )
1424
1583
 
1425
1584
  if (newAgents.length > 0) {
@@ -1429,25 +1588,25 @@ All agents follow these principles:
1429
1588
  // Check for quality agent specifically
1430
1589
  if (!currentConfig.agent?.quality && latestConfig.agent.quality) {
1431
1590
  console.log(
1432
- chalk.cyan(' • Quality subagent for testing and PR management'),
1591
+ chalk.cyan(' • Quality subagent for testing and PR management')
1433
1592
  )
1434
1593
  }
1435
1594
 
1436
1595
  // Check for security subagent mode
1437
1596
  if (currentConfig.agent?.security?.mode !== 'subagent') {
1438
1597
  console.log(
1439
- chalk.cyan(
1440
- ' • Security agent converted to subagent (read-only)',
1441
- ),
1598
+ chalk.cyan(' • Security agent converted to subagent (read-only)')
1442
1599
  )
1443
1600
  }
1444
1601
 
1445
- // Check for GLM-4.6 optimizations
1602
+ // Check for GLM-4.7 optimizations
1446
1603
  if (
1447
- !currentConfig.provider?.berget?.models?.[modelConfig.primary.replace('berget/', '')]?.limit?.context
1604
+ !currentConfig.provider?.berget?.models?.[
1605
+ modelConfig.primary.replace('berget/', '')
1606
+ ]?.limit?.context
1448
1607
  ) {
1449
1608
  console.log(
1450
- chalk.cyan(' • GLM-4.6 token limits and auto-compaction'),
1609
+ chalk.cyan(' • GLM-4.7 token limits and auto-compaction')
1451
1610
  )
1452
1611
  }
1453
1612
 
@@ -1461,8 +1620,8 @@ All agents follow these principles:
1461
1620
  if (!options.yes) {
1462
1621
  console.log(
1463
1622
  chalk.blue(
1464
- '\nThis will update your OpenCode configuration with the latest improvements.',
1465
- ),
1623
+ '\nThis will update your OpenCode configuration with the latest improvements.'
1624
+ )
1466
1625
  )
1467
1626
 
1468
1627
  // Check if user has git for backup
@@ -1470,12 +1629,12 @@ All agents follow these principles:
1470
1629
  if (!hasGitRepo) {
1471
1630
  console.log(
1472
1631
  chalk.yellow(
1473
- '⚠️ No .git repository detected - backup will be created',
1474
- ),
1632
+ '⚠️ No .git repository detected - backup will be created'
1633
+ )
1475
1634
  )
1476
1635
  } else {
1477
1636
  console.log(
1478
- chalk.green('✓ Git repository detected - changes are tracked'),
1637
+ chalk.green('✓ Git repository detected - changes are tracked')
1479
1638
  )
1480
1639
  }
1481
1640
  }
@@ -1484,13 +1643,13 @@ All agents follow these principles:
1484
1643
  console.log(chalk.blue('\nChoose update strategy:'))
1485
1644
  console.log(
1486
1645
  chalk.cyan(
1487
- '1) Replace - Use latest configuration (your customizations will be lost)',
1488
- ),
1646
+ '1) Replace - Use latest configuration (your customizations will be lost)'
1647
+ )
1489
1648
  )
1490
1649
  console.log(
1491
1650
  chalk.cyan(
1492
- '2) Merge - Combine latest updates with your customizations (recommended)',
1493
- ),
1651
+ '2) Merge - Combine latest updates with your customizations (recommended)'
1652
+ )
1494
1653
  )
1495
1654
 
1496
1655
  let mergeChoice: 'replace' | 'merge' = 'merge'
@@ -1499,7 +1658,7 @@ All agents follow these principles:
1499
1658
  const choice = await askChoice(
1500
1659
  '\nYour choice (1-2, default: 2): ',
1501
1660
  ['replace', 'merge'],
1502
- 'merge',
1661
+ 'merge'
1503
1662
  )
1504
1663
  mergeChoice = choice as 'replace' | 'merge'
1505
1664
  }
@@ -1516,12 +1675,12 @@ All agents follow these principles:
1516
1675
  backupPath = `${configPath}.backup.${Date.now()}`
1517
1676
  await writeFile(
1518
1677
  backupPath,
1519
- JSON.stringify(currentConfig, null, 2),
1678
+ JSON.stringify(currentConfig, null, 2)
1520
1679
  )
1521
1680
  console.log(
1522
1681
  chalk.green(
1523
- `✓ Backed up current config to ${path.basename(backupPath)}`,
1524
- ),
1682
+ `✓ Backed up current config to ${path.basename(backupPath)}`
1683
+ )
1525
1684
  )
1526
1685
  }
1527
1686
 
@@ -1529,10 +1688,10 @@ All agents follow these principles:
1529
1688
  // Merge configurations
1530
1689
  finalConfig = await mergeConfigurations(
1531
1690
  currentConfig,
1532
- latestConfig,
1691
+ latestConfig
1533
1692
  )
1534
1693
  console.log(
1535
- chalk.green('✓ Merged configurations with latest updates'),
1694
+ chalk.green('✓ Merged configurations with latest updates')
1536
1695
  )
1537
1696
  } else {
1538
1697
  // Replace with latest
@@ -1544,8 +1703,8 @@ All agents follow these principles:
1544
1703
  await writeFile(configPath, JSON.stringify(finalConfig, null, 2))
1545
1704
  console.log(
1546
1705
  chalk.green(
1547
- `✓ Updated opencode.json with ${mergeChoice} strategy`,
1548
- ),
1706
+ `✓ Updated opencode.json with ${mergeChoice} strategy`
1707
+ )
1549
1708
  )
1550
1709
 
1551
1710
  // Update AGENTS.md if it doesn't exist
@@ -1723,13 +1882,13 @@ All agents follow these principles:
1723
1882
  console.log(chalk.green('\n✅ Update completed successfully!'))
1724
1883
  console.log(chalk.blue('New features available:'))
1725
1884
  console.log(
1726
- chalk.cyan(' • @quality subagent for testing and PR management'),
1885
+ chalk.cyan(' • @quality subagent for testing and PR management')
1727
1886
  )
1728
1887
  console.log(
1729
- chalk.cyan(' • @security subagent for security reviews'),
1888
+ chalk.cyan(' • @security subagent for security reviews')
1730
1889
  )
1731
1890
  console.log(chalk.cyan(' • Improved agent prompts and routing'))
1732
- console.log(chalk.cyan(' • GLM-4.6 token optimizations'))
1891
+ console.log(chalk.cyan(' • GLM-4.7 token optimizations'))
1733
1892
  console.log(chalk.blue('\nTry these new commands:'))
1734
1893
  console.log(chalk.cyan(' @quality run tests and create PR'))
1735
1894
  console.log(chalk.cyan(' @security review this code'))
@@ -1741,10 +1900,10 @@ All agents follow these principles:
1741
1900
  try {
1742
1901
  await writeFile(
1743
1902
  configPath,
1744
- JSON.stringify(currentConfig, null, 2),
1903
+ JSON.stringify(currentConfig, null, 2)
1745
1904
  )
1746
1905
  console.log(
1747
- chalk.yellow('📁 Restored original configuration from backup'),
1906
+ chalk.yellow('📁 Restored original configuration from backup')
1748
1907
  )
1749
1908
  } catch (restoreError) {
1750
1909
  console.error(chalk.red('Failed to restore backup:'))