berget 2.0.6 ā 2.1.1
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/AGENTS.md +163 -2
- package/dist/package.json +1 -1
- package/dist/src/commands/chat.js +1 -2
- package/dist/src/commands/code.js +152 -87
- package/dist/src/services/auth-service.js +1 -2
- package/dist/src/services/chat-service.js +77 -5
- package/dist/src/utils/config-loader.js +83 -27
- package/dist/tests/commands/chat.test.js +2 -2
- package/dist/tests/commands/code.test.js +4 -4
- package/dist/tests/utils/config-loader.test.js +248 -163
- package/opencode.json +31 -71
- package/package.json +1 -1
- package/src/commands/chat.ts +68 -61
- package/src/commands/code.ts +304 -213
- package/src/services/auth-service.ts +1 -4
- package/src/services/chat-service.ts +86 -5
- package/src/utils/config-loader.ts +113 -38
- package/tests/commands/chat.test.ts +2 -2
- package/tests/commands/code.test.ts +4 -4
- package/tests/utils/config-loader.test.ts +320 -0
- package/blog-post.md +0 -176
package/src/commands/code.ts
CHANGED
|
@@ -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
|
-
|
|
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
|
|
287
|
+
* Load the latest agent configuration from embedded config
|
|
277
288
|
*/
|
|
278
289
|
async function loadLatestAgentConfig(): Promise<any> {
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
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
|
|
|
@@ -292,9 +376,8 @@ async function loadLatestAgentConfig(): Promise<any> {
|
|
|
292
376
|
*/
|
|
293
377
|
function checkOpencodeInstalled(): Promise<boolean> {
|
|
294
378
|
return new Promise((resolve) => {
|
|
295
|
-
const child = spawn('
|
|
379
|
+
const child = spawn('which', ['opencode'], {
|
|
296
380
|
stdio: 'pipe',
|
|
297
|
-
shell: true,
|
|
298
381
|
})
|
|
299
382
|
|
|
300
383
|
child.on('close', (code) => {
|
|
@@ -317,7 +400,6 @@ async function installOpencode(): Promise<boolean> {
|
|
|
317
400
|
await new Promise<void>((resolve, reject) => {
|
|
318
401
|
const install = spawn('npm', ['install', '-g', 'opencode-ai'], {
|
|
319
402
|
stdio: 'inherit',
|
|
320
|
-
shell: true,
|
|
321
403
|
})
|
|
322
404
|
|
|
323
405
|
install.on('close', (code) => {
|
|
@@ -336,12 +418,12 @@ async function installOpencode(): Promise<boolean> {
|
|
|
336
418
|
const opencodeInstalled = await checkOpencodeInstalled()
|
|
337
419
|
if (!opencodeInstalled) {
|
|
338
420
|
console.log(
|
|
339
|
-
chalk.yellow('Installation completed but opencode command not found.')
|
|
421
|
+
chalk.yellow('Installation completed but opencode command not found.')
|
|
340
422
|
)
|
|
341
423
|
console.log(
|
|
342
424
|
chalk.yellow(
|
|
343
|
-
'You may need to restart your terminal or check your PATH.'
|
|
344
|
-
)
|
|
425
|
+
'You may need to restart your terminal or check your PATH.'
|
|
426
|
+
)
|
|
345
427
|
)
|
|
346
428
|
return false
|
|
347
429
|
}
|
|
@@ -351,7 +433,7 @@ async function installOpencode(): Promise<boolean> {
|
|
|
351
433
|
console.error(chalk.red('Failed to install OpenCode:'))
|
|
352
434
|
console.error(error instanceof Error ? error.message : String(error))
|
|
353
435
|
console.log(chalk.blue('\nAlternative installation methods:'))
|
|
354
|
-
console.log(chalk.blue(' curl -fsSL https://opencode.ai/install |
|
|
436
|
+
console.log(chalk.blue(' curl -fsSL https://opencode.ai/install | sh'))
|
|
355
437
|
console.log(chalk.blue(' Or visit: https://opencode.ai/docs'))
|
|
356
438
|
return false
|
|
357
439
|
}
|
|
@@ -366,14 +448,14 @@ async function ensureOpencodeInstalled(autoYes = false): Promise<boolean> {
|
|
|
366
448
|
if (!autoYes) {
|
|
367
449
|
console.log(chalk.red('OpenCode is not installed.'))
|
|
368
450
|
console.log(
|
|
369
|
-
chalk.blue('OpenCode is required for the AI coding assistant.')
|
|
451
|
+
chalk.blue('OpenCode is required for the AI coding assistant.')
|
|
370
452
|
)
|
|
371
453
|
}
|
|
372
454
|
|
|
373
455
|
if (
|
|
374
456
|
await confirm(
|
|
375
457
|
'Would you like to install OpenCode automatically? (Y/n): ',
|
|
376
|
-
autoYes
|
|
458
|
+
autoYes
|
|
377
459
|
)
|
|
378
460
|
) {
|
|
379
461
|
opencodeInstalled = await installOpencode()
|
|
@@ -382,8 +464,8 @@ async function ensureOpencodeInstalled(autoYes = false): Promise<boolean> {
|
|
|
382
464
|
console.log(chalk.blue('\nInstallation cancelled.'))
|
|
383
465
|
console.log(
|
|
384
466
|
chalk.blue(
|
|
385
|
-
'To install manually: curl -fsSL https://opencode.ai/install | bash'
|
|
386
|
-
)
|
|
467
|
+
'To install manually: curl -fsSL https://opencode.ai/install | bash'
|
|
468
|
+
)
|
|
387
469
|
)
|
|
388
470
|
console.log(chalk.blue('Or visit: https://opencode.ai/docs'))
|
|
389
471
|
}
|
|
@@ -408,7 +490,7 @@ export function registerCodeCommands(program: Command): void {
|
|
|
408
490
|
.option('-f, --force', 'Overwrite existing configuration')
|
|
409
491
|
.option(
|
|
410
492
|
'-y, --yes',
|
|
411
|
-
'Automatically answer yes to all prompts (for automation)'
|
|
493
|
+
'Automatically answer yes to all prompts (for automation)'
|
|
412
494
|
)
|
|
413
495
|
.action(async (options) => {
|
|
414
496
|
try {
|
|
@@ -419,7 +501,7 @@ export function registerCodeCommands(program: Command): void {
|
|
|
419
501
|
if (fs.existsSync(configPath) && !options.force) {
|
|
420
502
|
if (!options.yes) {
|
|
421
503
|
console.log(
|
|
422
|
-
chalk.yellow('Project already initialized for OpenCode.')
|
|
504
|
+
chalk.yellow('Project already initialized for OpenCode.')
|
|
423
505
|
)
|
|
424
506
|
console.log(chalk.dim(`Config file: ${configPath}`))
|
|
425
507
|
}
|
|
@@ -440,26 +522,43 @@ export function registerCodeCommands(program: Command): void {
|
|
|
440
522
|
|
|
441
523
|
// Check if we have an API key in environment first
|
|
442
524
|
if (process.env.BERGET_API_KEY) {
|
|
443
|
-
console.log(
|
|
525
|
+
console.log(
|
|
526
|
+
chalk.blue(
|
|
527
|
+
'š Using BERGET_API_KEY from environment - no authentication required'
|
|
528
|
+
)
|
|
529
|
+
)
|
|
444
530
|
} else {
|
|
445
531
|
// Only require authentication if we don't have an API key
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
} catch (error) {
|
|
532
|
+
const authService = AuthService.getInstance()
|
|
533
|
+
const profile = await authService.whoami()
|
|
534
|
+
|
|
535
|
+
if (!profile) {
|
|
451
536
|
console.log(chalk.red('ā Not authenticated with Berget AI.'))
|
|
452
537
|
console.log(chalk.blue('To get started, you have two options:'))
|
|
453
538
|
console.log('')
|
|
454
|
-
console.log(
|
|
455
|
-
|
|
456
|
-
|
|
539
|
+
console.log(
|
|
540
|
+
chalk.yellow('Option 1: Use an existing API key (recommended)')
|
|
541
|
+
)
|
|
542
|
+
console.log(
|
|
543
|
+
chalk.cyan(' Set BERGET_API_KEY environment variable:')
|
|
544
|
+
)
|
|
545
|
+
console.log(
|
|
546
|
+
chalk.dim(' export BERGET_API_KEY=your_api_key_here')
|
|
547
|
+
)
|
|
457
548
|
console.log(chalk.cyan(' Or create a .env file in your project:'))
|
|
458
|
-
console.log(
|
|
549
|
+
console.log(
|
|
550
|
+
chalk.dim(' echo "BERGET_API_KEY=your_api_key_here" > .env')
|
|
551
|
+
)
|
|
459
552
|
console.log('')
|
|
460
|
-
console.log(
|
|
553
|
+
console.log(
|
|
554
|
+
chalk.yellow('Option 2: Login and create a new API key')
|
|
555
|
+
)
|
|
461
556
|
console.log(chalk.cyan(' berget auth login'))
|
|
462
|
-
console.log(
|
|
557
|
+
console.log(
|
|
558
|
+
chalk.cyan(
|
|
559
|
+
` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`
|
|
560
|
+
)
|
|
561
|
+
)
|
|
463
562
|
console.log('')
|
|
464
563
|
console.log(chalk.blue('Then try again.'))
|
|
465
564
|
return
|
|
@@ -467,7 +566,7 @@ export function registerCodeCommands(program: Command): void {
|
|
|
467
566
|
}
|
|
468
567
|
|
|
469
568
|
console.log(
|
|
470
|
-
chalk.cyan(`Initializing OpenCode for project: ${projectName}`)
|
|
569
|
+
chalk.cyan(`Initializing OpenCode for project: ${projectName}`)
|
|
471
570
|
)
|
|
472
571
|
|
|
473
572
|
// Handle API key selection or creation
|
|
@@ -477,105 +576,46 @@ export function registerCodeCommands(program: Command): void {
|
|
|
477
576
|
try {
|
|
478
577
|
const apiKeyService = ApiKeyService.getInstance()
|
|
479
578
|
|
|
480
|
-
// Check for environment variable first (regardless of automation mode)
|
|
481
579
|
if (process.env.BERGET_API_KEY) {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
console.log(chalk.blue('\nš Checking existing API keys...'))
|
|
489
|
-
}
|
|
490
|
-
const existingKeys = await apiKeyService.list()
|
|
491
|
-
|
|
492
|
-
if (existingKeys.length > 0 && !options.yes) {
|
|
493
|
-
console.log(chalk.blue('Found existing API keys:'))
|
|
494
|
-
console.log(chalk.dim('ā'.repeat(60)))
|
|
495
|
-
existingKeys.forEach((key, index) => {
|
|
496
|
-
console.log(
|
|
497
|
-
`${chalk.cyan((index + 1).toString())}. ${chalk.bold(key.name)} (${key.prefix}...)`,
|
|
498
|
-
)
|
|
499
|
-
console.log(
|
|
500
|
-
chalk.dim(
|
|
501
|
-
` Created: ${new Date(key.created).toLocaleDateString('sv-SE')}`,
|
|
502
|
-
),
|
|
503
|
-
)
|
|
504
|
-
console.log(
|
|
505
|
-
chalk.dim(
|
|
506
|
-
` Last used: ${key.lastUsed ? new Date(key.lastUsed).toLocaleDateString('sv-SE') : 'Never'}`,
|
|
507
|
-
),
|
|
508
|
-
)
|
|
509
|
-
if (index < existingKeys.length - 1) console.log()
|
|
510
|
-
})
|
|
580
|
+
if (options.yes) {
|
|
581
|
+
console.log(chalk.blue('š Using BERGET_API_KEY from environment'))
|
|
582
|
+
apiKey = process.env.BERGET_API_KEY
|
|
583
|
+
keyName = `env-key-${projectName}`
|
|
584
|
+
} else {
|
|
585
|
+
console.log(chalk.blue('\nš API key setup:'))
|
|
511
586
|
console.log(chalk.dim('ā'.repeat(60)))
|
|
512
587
|
console.log(
|
|
513
|
-
chalk.cyan(
|
|
588
|
+
`${chalk.cyan('1')} ${chalk.bold('Use existing API key')}`
|
|
514
589
|
)
|
|
590
|
+
console.log(chalk.dim(' Uses BERGET_API_KEY from environment'))
|
|
591
|
+
console.log(
|
|
592
|
+
`${chalk.cyan('2')} ${chalk.bold('Create a new API key')}`
|
|
593
|
+
)
|
|
594
|
+
console.log(chalk.dim('ā'.repeat(60)))
|
|
515
595
|
|
|
516
|
-
// Get user choice
|
|
517
596
|
const choice = await new Promise<string>((resolve) => {
|
|
518
597
|
const rl = readline.createInterface({
|
|
519
598
|
input: process.stdin,
|
|
520
599
|
output: process.stdout,
|
|
521
600
|
})
|
|
522
|
-
rl.question(
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
'): ',
|
|
527
|
-
),
|
|
528
|
-
(answer) => {
|
|
529
|
-
rl.close()
|
|
530
|
-
resolve(answer.trim())
|
|
531
|
-
},
|
|
532
|
-
)
|
|
601
|
+
rl.question(chalk.blue('\nSelect an option (1-2, default: 1): '), (answer) => {
|
|
602
|
+
rl.close()
|
|
603
|
+
resolve(answer.trim() || '1')
|
|
604
|
+
})
|
|
533
605
|
})
|
|
534
606
|
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
keyName = selectedKey.name
|
|
541
|
-
|
|
542
|
-
// We need to rotate the key to get the actual key value
|
|
543
|
-
console.log(
|
|
544
|
-
chalk.yellow(
|
|
545
|
-
`\nš Rotating API key "${selectedKey.name}" to get the key value...`,
|
|
546
|
-
),
|
|
547
|
-
)
|
|
548
|
-
|
|
549
|
-
if (
|
|
550
|
-
await confirm(
|
|
551
|
-
chalk.yellow(
|
|
552
|
-
'This will invalidate the current key. Continue? (Y/n): ',
|
|
553
|
-
),
|
|
554
|
-
options.yes,
|
|
555
|
-
)
|
|
556
|
-
) {
|
|
557
|
-
const rotatedKey = await apiKeyService.rotate(
|
|
558
|
-
selectedKey.id.toString(),
|
|
559
|
-
)
|
|
560
|
-
apiKey = rotatedKey.key
|
|
561
|
-
console.log(chalk.green(`ā API key rotated successfully`))
|
|
562
|
-
} else {
|
|
563
|
-
console.log(
|
|
564
|
-
chalk.yellow(
|
|
565
|
-
'Cancelled. Please select a different option or create a new key.',
|
|
566
|
-
),
|
|
567
|
-
)
|
|
568
|
-
return
|
|
569
|
-
}
|
|
570
|
-
} else if (choiceIndex === existingKeys.length) {
|
|
571
|
-
// Create new key
|
|
607
|
+
if (choice === '1') {
|
|
608
|
+
console.log(chalk.blue('š Using BERGET_API_KEY from environment'))
|
|
609
|
+
apiKey = process.env.BERGET_API_KEY
|
|
610
|
+
keyName = `env-key-${projectName}`
|
|
611
|
+
} else if (choice === '2') {
|
|
572
612
|
console.log(chalk.blue('\nš Creating new API key...'))
|
|
573
613
|
|
|
574
614
|
const defaultKeyName = `opencode-${projectName}-${Date.now()}`
|
|
575
615
|
const customName = await getInput(
|
|
576
616
|
chalk.blue(`Enter key name (default: ${defaultKeyName}): `),
|
|
577
617
|
defaultKeyName,
|
|
578
|
-
options.yes
|
|
618
|
+
options.yes
|
|
579
619
|
)
|
|
580
620
|
|
|
581
621
|
keyName = customName
|
|
@@ -587,31 +627,38 @@ export function registerCodeCommands(program: Command): void {
|
|
|
587
627
|
console.log(chalk.red('Invalid selection.'))
|
|
588
628
|
return
|
|
589
629
|
}
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
630
|
+
}
|
|
631
|
+
} else {
|
|
632
|
+
if (!options.yes) {
|
|
633
|
+
console.log(chalk.yellow('No BERGET_API_KEY environment variable found.'))
|
|
634
|
+
console.log(chalk.blue('Creating a new API key...'))
|
|
635
|
+
console.log(chalk.dim('\nš” Tip: Set BERGET_API_KEY environment variable to reuse an existing key:'))
|
|
636
|
+
console.log(chalk.dim(' export BERGET_API_KEY=your_api_key_here'))
|
|
637
|
+
}
|
|
596
638
|
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
639
|
+
const defaultKeyName = `opencode-${projectName}-${Date.now()}`
|
|
640
|
+
const customName = await getInput(
|
|
641
|
+
chalk.blue(`Enter key name (default: ${defaultKeyName}): `),
|
|
642
|
+
defaultKeyName,
|
|
643
|
+
options.yes
|
|
644
|
+
)
|
|
603
645
|
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
}
|
|
646
|
+
keyName = customName
|
|
647
|
+
const createOptions: CreateApiKeyOptions = { name: keyName }
|
|
648
|
+
const keyData = await apiKeyService.create(createOptions)
|
|
649
|
+
apiKey = keyData.key
|
|
650
|
+
console.log(chalk.green(`ā Created new API key: ${keyName}`))
|
|
610
651
|
}
|
|
611
652
|
} catch (error) {
|
|
612
653
|
if (process.env.BERGET_API_KEY) {
|
|
613
|
-
console.log(
|
|
614
|
-
|
|
654
|
+
console.log(
|
|
655
|
+
chalk.yellow(
|
|
656
|
+
'ā ļø Could not verify API key with Berget API, but continuing with environment key'
|
|
657
|
+
)
|
|
658
|
+
)
|
|
659
|
+
console.log(
|
|
660
|
+
chalk.dim('This might be due to network issues or an invalid key')
|
|
661
|
+
)
|
|
615
662
|
} else {
|
|
616
663
|
console.error(chalk.red('ā Failed to handle API keys:'))
|
|
617
664
|
console.log(chalk.blue('This could be due to:'))
|
|
@@ -621,7 +668,11 @@ export function registerCodeCommands(program: Command): void {
|
|
|
621
668
|
console.log('')
|
|
622
669
|
console.log(chalk.blue('Try using an API key directly:'))
|
|
623
670
|
console.log(chalk.cyan(' export BERGET_API_KEY=your_api_key_here'))
|
|
624
|
-
console.log(
|
|
671
|
+
console.log(
|
|
672
|
+
chalk.cyan(
|
|
673
|
+
` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT} --yes`
|
|
674
|
+
)
|
|
675
|
+
)
|
|
625
676
|
handleError('API key operation failed', error)
|
|
626
677
|
}
|
|
627
678
|
return
|
|
@@ -630,9 +681,14 @@ export function registerCodeCommands(program: Command): void {
|
|
|
630
681
|
// Prepare .env file path for safe update
|
|
631
682
|
const envPath = path.join(process.cwd(), '.env')
|
|
632
683
|
|
|
633
|
-
// Load latest agent configuration
|
|
684
|
+
// Load latest agent configuration from our own codebase
|
|
634
685
|
const latestAgentConfig = await loadLatestAgentConfig()
|
|
635
|
-
|
|
686
|
+
|
|
687
|
+
// Use hardcoded defaults for init - never try to load from project
|
|
688
|
+
const modelConfig = {
|
|
689
|
+
primary: 'berget/glm-4.7',
|
|
690
|
+
small: 'berget/gpt-oss',
|
|
691
|
+
}
|
|
636
692
|
|
|
637
693
|
// Create opencode.json config with optimized agent-based format
|
|
638
694
|
const config = {
|
|
@@ -772,11 +828,25 @@ export function registerCodeCommands(program: Command): void {
|
|
|
772
828
|
berget: {
|
|
773
829
|
npm: '@ai-sdk/openai-compatible',
|
|
774
830
|
name: 'Berget AI',
|
|
775
|
-
options: {
|
|
831
|
+
options: {
|
|
776
832
|
baseURL: 'https://api.berget.ai/v1',
|
|
777
833
|
apiKey: '{env:BERGET_API_KEY}',
|
|
778
834
|
},
|
|
779
|
-
models:
|
|
835
|
+
models: {
|
|
836
|
+
'glm-4.7': {
|
|
837
|
+
name: 'GLM-4.7',
|
|
838
|
+
limit: { output: 4000, context: 90000 },
|
|
839
|
+
},
|
|
840
|
+
'gpt-oss': {
|
|
841
|
+
name: 'GPT-OSS',
|
|
842
|
+
limit: { output: 4000, context: 128000 },
|
|
843
|
+
modalities: ['text', 'image'],
|
|
844
|
+
},
|
|
845
|
+
'llama-8b': {
|
|
846
|
+
name: 'llama-3.1-8b',
|
|
847
|
+
limit: { output: 4000, context: 128000 },
|
|
848
|
+
},
|
|
849
|
+
},
|
|
780
850
|
},
|
|
781
851
|
},
|
|
782
852
|
}
|
|
@@ -788,33 +858,36 @@ export function registerCodeCommands(program: Command): void {
|
|
|
788
858
|
console.log(chalk.dim(`Environment: ${envPath}`))
|
|
789
859
|
console.log(
|
|
790
860
|
chalk.dim(
|
|
791
|
-
`Documentation: ${path.join(
|
|
792
|
-
|
|
861
|
+
`Documentation: ${path.join(
|
|
862
|
+
process.cwd(),
|
|
863
|
+
'AGENTS.md'
|
|
864
|
+
)} (if not exists)`
|
|
865
|
+
)
|
|
793
866
|
)
|
|
794
867
|
console.log(
|
|
795
868
|
chalk.dim(
|
|
796
|
-
`Environment: ${path.join(process.cwd(), '.env')} will be updated
|
|
797
|
-
)
|
|
869
|
+
`Environment: ${path.join(process.cwd(), '.env')} will be updated`
|
|
870
|
+
)
|
|
798
871
|
)
|
|
799
872
|
console.log(
|
|
800
|
-
chalk.dim('This will configure OpenCode to use Berget AI models.')
|
|
873
|
+
chalk.dim('This will configure OpenCode to use Berget AI models.')
|
|
801
874
|
)
|
|
802
875
|
console.log(chalk.cyan('\nš” Benefits:'))
|
|
803
876
|
console.log(
|
|
804
877
|
chalk.cyan(
|
|
805
|
-
' ⢠API key stored separately in .env file (not committed to Git)'
|
|
806
|
-
)
|
|
878
|
+
' ⢠API key stored separately in .env file (not committed to Git)'
|
|
879
|
+
)
|
|
807
880
|
)
|
|
808
881
|
console.log(
|
|
809
|
-
chalk.cyan(' ⢠Easy cost separation per project/customer')
|
|
882
|
+
chalk.cyan(' ⢠Easy cost separation per project/customer')
|
|
810
883
|
)
|
|
811
884
|
console.log(
|
|
812
|
-
chalk.cyan(' ⢠Secure key management with environment variables')
|
|
885
|
+
chalk.cyan(' ⢠Secure key management with environment variables')
|
|
813
886
|
)
|
|
814
887
|
console.log(
|
|
815
888
|
chalk.cyan(
|
|
816
|
-
" ⢠Project-specific agent documentation (won't overwrite existing)"
|
|
817
|
-
)
|
|
889
|
+
" ⢠Project-specific agent documentation (won't overwrite existing)"
|
|
890
|
+
)
|
|
818
891
|
)
|
|
819
892
|
}
|
|
820
893
|
|
|
@@ -837,7 +910,7 @@ export function registerCodeCommands(program: Command): void {
|
|
|
837
910
|
console.log(chalk.dim(` Small Model: ${config.small_model}`))
|
|
838
911
|
console.log(chalk.dim(` Theme: ${config.theme}`))
|
|
839
912
|
console.log(
|
|
840
|
-
chalk.dim(` API Key: Stored in .env as BERGET_API_KEY`)
|
|
913
|
+
chalk.dim(` API Key: Stored in .env as BERGET_API_KEY`)
|
|
841
914
|
)
|
|
842
915
|
|
|
843
916
|
// Create AGENTS.md documentation only if it doesn't exist
|
|
@@ -1011,11 +1084,11 @@ All agents follow these principles:
|
|
|
1011
1084
|
await writeFile(agentsMdPath, agentsMdContent)
|
|
1012
1085
|
console.log(chalk.green(`ā Created AGENTS.md`))
|
|
1013
1086
|
console.log(
|
|
1014
|
-
chalk.dim(` Documentation for available agents and usage`)
|
|
1087
|
+
chalk.dim(` Documentation for available agents and usage`)
|
|
1015
1088
|
)
|
|
1016
1089
|
} else {
|
|
1017
1090
|
console.log(
|
|
1018
|
-
chalk.yellow(`ā AGENTS.md already exists, skipping creation`)
|
|
1091
|
+
chalk.yellow(`ā AGENTS.md already exists, skipping creation`)
|
|
1019
1092
|
)
|
|
1020
1093
|
}
|
|
1021
1094
|
|
|
@@ -1046,7 +1119,7 @@ All agents follow these principles:
|
|
|
1046
1119
|
console.log(chalk.green('\nā
Project initialized successfully!'))
|
|
1047
1120
|
console.log(chalk.blue('Next steps:'))
|
|
1048
1121
|
console.log(
|
|
1049
|
-
chalk.blue(` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.RUN}`)
|
|
1122
|
+
chalk.blue(` berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.RUN}`)
|
|
1050
1123
|
)
|
|
1051
1124
|
console.log(chalk.blue(' Or run: opencode'))
|
|
1052
1125
|
} catch (error) {
|
|
@@ -1063,7 +1136,7 @@ All agents follow these principles:
|
|
|
1063
1136
|
.option('--no-config', 'Run without loading project config')
|
|
1064
1137
|
.option(
|
|
1065
1138
|
'-y, --yes',
|
|
1066
|
-
'Automatically answer yes to all prompts (for automation)'
|
|
1139
|
+
'Automatically answer yes to all prompts (for automation)'
|
|
1067
1140
|
)
|
|
1068
1141
|
.action(async (prompt: string, options: any) => {
|
|
1069
1142
|
try {
|
|
@@ -1080,12 +1153,12 @@ All agents follow these principles:
|
|
|
1080
1153
|
const configContent = await readFile(configPath, 'utf8')
|
|
1081
1154
|
config = JSON.parse(configContent)
|
|
1082
1155
|
console.log(
|
|
1083
|
-
chalk.dim(`Loaded config for project: ${config.projectName}`)
|
|
1156
|
+
chalk.dim(`Loaded config for project: ${config.projectName}`)
|
|
1084
1157
|
)
|
|
1085
1158
|
console.log(
|
|
1086
1159
|
chalk.dim(
|
|
1087
|
-
`Models: Analysis=${config.analysisModel}, Build=${config.buildModel}
|
|
1088
|
-
)
|
|
1160
|
+
`Models: Analysis=${config.analysisModel}, Build=${config.buildModel}`
|
|
1161
|
+
)
|
|
1089
1162
|
)
|
|
1090
1163
|
} catch (error) {
|
|
1091
1164
|
console.log(chalk.yellow('Warning: Failed to load opencode.json'))
|
|
@@ -1096,8 +1169,10 @@ All agents follow these principles:
|
|
|
1096
1169
|
console.log(chalk.yellow('No project configuration found.'))
|
|
1097
1170
|
console.log(
|
|
1098
1171
|
chalk.blue(
|
|
1099
|
-
`Run ${chalk.bold(
|
|
1100
|
-
|
|
1172
|
+
`Run ${chalk.bold(
|
|
1173
|
+
`berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`
|
|
1174
|
+
)} first.`
|
|
1175
|
+
)
|
|
1101
1176
|
)
|
|
1102
1177
|
return
|
|
1103
1178
|
}
|
|
@@ -1128,7 +1203,6 @@ All agents follow these principles:
|
|
|
1128
1203
|
const opencode = spawn('opencode', opencodeArgs, {
|
|
1129
1204
|
stdio: 'inherit',
|
|
1130
1205
|
env: env,
|
|
1131
|
-
shell: true,
|
|
1132
1206
|
})
|
|
1133
1207
|
|
|
1134
1208
|
opencode.on('close', (code) => {
|
|
@@ -1150,10 +1224,13 @@ All agents follow these principles:
|
|
|
1150
1224
|
.command(SUBCOMMANDS.CODE.SERVE)
|
|
1151
1225
|
.description('Start OpenCode web server')
|
|
1152
1226
|
.option('-p, --port <port>', 'Port to run the server on (default: 3000)')
|
|
1153
|
-
.option(
|
|
1227
|
+
.option(
|
|
1228
|
+
'-h, --host <host>',
|
|
1229
|
+
'Host to bind the server to (default: localhost)'
|
|
1230
|
+
)
|
|
1154
1231
|
.option(
|
|
1155
1232
|
'-y, --yes',
|
|
1156
|
-
'Automatically answer yes to all prompts (for automation)'
|
|
1233
|
+
'Automatically answer yes to all prompts (for automation)'
|
|
1157
1234
|
)
|
|
1158
1235
|
.action(async (options) => {
|
|
1159
1236
|
try {
|
|
@@ -1178,7 +1255,6 @@ All agents follow these principles:
|
|
|
1178
1255
|
// Spawn opencode serve process
|
|
1179
1256
|
const opencode = spawn('opencode', serveArgs, {
|
|
1180
1257
|
stdio: 'inherit',
|
|
1181
|
-
shell: true,
|
|
1182
1258
|
})
|
|
1183
1259
|
|
|
1184
1260
|
opencode.on('close', (code) => {
|
|
@@ -1202,7 +1278,7 @@ All agents follow these principles:
|
|
|
1202
1278
|
.option('-f, --force', 'Force update even if already latest')
|
|
1203
1279
|
.option(
|
|
1204
1280
|
'-y, --yes',
|
|
1205
|
-
'Automatically answer yes to all prompts (for automation)'
|
|
1281
|
+
'Automatically answer yes to all prompts (for automation)'
|
|
1206
1282
|
)
|
|
1207
1283
|
.action(async (options) => {
|
|
1208
1284
|
try {
|
|
@@ -1220,8 +1296,10 @@ All agents follow these principles:
|
|
|
1220
1296
|
console.log(chalk.red('ā No OpenCode configuration found.'))
|
|
1221
1297
|
console.log(
|
|
1222
1298
|
chalk.blue(
|
|
1223
|
-
`Run ${chalk.bold(
|
|
1224
|
-
|
|
1299
|
+
`Run ${chalk.bold(
|
|
1300
|
+
`berget ${COMMAND_GROUPS.CODE} ${SUBCOMMANDS.CODE.INIT}`
|
|
1301
|
+
)} first.`
|
|
1302
|
+
)
|
|
1225
1303
|
)
|
|
1226
1304
|
return
|
|
1227
1305
|
}
|
|
@@ -1242,13 +1320,26 @@ All agents follow these principles:
|
|
|
1242
1320
|
console.log(chalk.dim(` Theme: ${currentConfig.theme}`))
|
|
1243
1321
|
console.log(
|
|
1244
1322
|
chalk.dim(
|
|
1245
|
-
` Agents: ${
|
|
1246
|
-
|
|
1323
|
+
` Agents: ${
|
|
1324
|
+
Object.keys(currentConfig.agent || {}).length
|
|
1325
|
+
} configured`
|
|
1326
|
+
)
|
|
1247
1327
|
)
|
|
1248
1328
|
|
|
1249
1329
|
// Load latest agent configuration to ensure consistency
|
|
1250
1330
|
const latestAgentConfig = await loadLatestAgentConfig()
|
|
1251
|
-
|
|
1331
|
+
|
|
1332
|
+
// Get model config with fallback for init scenario
|
|
1333
|
+
let modelConfig: { primary: string; small: string }
|
|
1334
|
+
try {
|
|
1335
|
+
modelConfig = getModelConfig()
|
|
1336
|
+
} catch (error) {
|
|
1337
|
+
// Fallback to defaults when no config exists (init scenario)
|
|
1338
|
+
modelConfig = {
|
|
1339
|
+
primary: 'berget/glm-4.7',
|
|
1340
|
+
small: 'berget/gpt-oss',
|
|
1341
|
+
}
|
|
1342
|
+
}
|
|
1252
1343
|
|
|
1253
1344
|
// Create latest configuration with all improvements
|
|
1254
1345
|
const latestConfig = {
|
|
@@ -1419,7 +1510,7 @@ All agents follow these principles:
|
|
|
1419
1510
|
const currentAgents = Object.keys(currentConfig.agent || {})
|
|
1420
1511
|
const latestAgents = Object.keys(latestConfig.agent)
|
|
1421
1512
|
const newAgents = latestAgents.filter(
|
|
1422
|
-
(agent) => !currentAgents.includes(agent)
|
|
1513
|
+
(agent) => !currentAgents.includes(agent)
|
|
1423
1514
|
)
|
|
1424
1515
|
|
|
1425
1516
|
if (newAgents.length > 0) {
|
|
@@ -1429,25 +1520,25 @@ All agents follow these principles:
|
|
|
1429
1520
|
// Check for quality agent specifically
|
|
1430
1521
|
if (!currentConfig.agent?.quality && latestConfig.agent.quality) {
|
|
1431
1522
|
console.log(
|
|
1432
|
-
chalk.cyan(' ⢠Quality subagent for testing and PR management')
|
|
1523
|
+
chalk.cyan(' ⢠Quality subagent for testing and PR management')
|
|
1433
1524
|
)
|
|
1434
1525
|
}
|
|
1435
1526
|
|
|
1436
1527
|
// Check for security subagent mode
|
|
1437
1528
|
if (currentConfig.agent?.security?.mode !== 'subagent') {
|
|
1438
1529
|
console.log(
|
|
1439
|
-
chalk.cyan(
|
|
1440
|
-
' ⢠Security agent converted to subagent (read-only)',
|
|
1441
|
-
),
|
|
1530
|
+
chalk.cyan(' ⢠Security agent converted to subagent (read-only)')
|
|
1442
1531
|
)
|
|
1443
1532
|
}
|
|
1444
1533
|
|
|
1445
|
-
// Check for GLM-4.
|
|
1534
|
+
// Check for GLM-4.7 optimizations
|
|
1446
1535
|
if (
|
|
1447
|
-
!currentConfig.provider?.berget?.models?.[
|
|
1536
|
+
!currentConfig.provider?.berget?.models?.[
|
|
1537
|
+
modelConfig.primary.replace('berget/', '')
|
|
1538
|
+
]?.limit?.context
|
|
1448
1539
|
) {
|
|
1449
1540
|
console.log(
|
|
1450
|
-
chalk.cyan(' ⢠GLM-4.
|
|
1541
|
+
chalk.cyan(' ⢠GLM-4.7 token limits and auto-compaction')
|
|
1451
1542
|
)
|
|
1452
1543
|
}
|
|
1453
1544
|
|
|
@@ -1461,8 +1552,8 @@ All agents follow these principles:
|
|
|
1461
1552
|
if (!options.yes) {
|
|
1462
1553
|
console.log(
|
|
1463
1554
|
chalk.blue(
|
|
1464
|
-
'\nThis will update your OpenCode configuration with the latest improvements.'
|
|
1465
|
-
)
|
|
1555
|
+
'\nThis will update your OpenCode configuration with the latest improvements.'
|
|
1556
|
+
)
|
|
1466
1557
|
)
|
|
1467
1558
|
|
|
1468
1559
|
// Check if user has git for backup
|
|
@@ -1470,12 +1561,12 @@ All agents follow these principles:
|
|
|
1470
1561
|
if (!hasGitRepo) {
|
|
1471
1562
|
console.log(
|
|
1472
1563
|
chalk.yellow(
|
|
1473
|
-
'ā ļø No .git repository detected - backup will be created'
|
|
1474
|
-
)
|
|
1564
|
+
'ā ļø No .git repository detected - backup will be created'
|
|
1565
|
+
)
|
|
1475
1566
|
)
|
|
1476
1567
|
} else {
|
|
1477
1568
|
console.log(
|
|
1478
|
-
chalk.green('ā Git repository detected - changes are tracked')
|
|
1569
|
+
chalk.green('ā Git repository detected - changes are tracked')
|
|
1479
1570
|
)
|
|
1480
1571
|
}
|
|
1481
1572
|
}
|
|
@@ -1484,13 +1575,13 @@ All agents follow these principles:
|
|
|
1484
1575
|
console.log(chalk.blue('\nChoose update strategy:'))
|
|
1485
1576
|
console.log(
|
|
1486
1577
|
chalk.cyan(
|
|
1487
|
-
'1) Replace - Use latest configuration (your customizations will be lost)'
|
|
1488
|
-
)
|
|
1578
|
+
'1) Replace - Use latest configuration (your customizations will be lost)'
|
|
1579
|
+
)
|
|
1489
1580
|
)
|
|
1490
1581
|
console.log(
|
|
1491
1582
|
chalk.cyan(
|
|
1492
|
-
'2) Merge - Combine latest updates with your customizations (recommended)'
|
|
1493
|
-
)
|
|
1583
|
+
'2) Merge - Combine latest updates with your customizations (recommended)'
|
|
1584
|
+
)
|
|
1494
1585
|
)
|
|
1495
1586
|
|
|
1496
1587
|
let mergeChoice: 'replace' | 'merge' = 'merge'
|
|
@@ -1499,7 +1590,7 @@ All agents follow these principles:
|
|
|
1499
1590
|
const choice = await askChoice(
|
|
1500
1591
|
'\nYour choice (1-2, default: 2): ',
|
|
1501
1592
|
['replace', 'merge'],
|
|
1502
|
-
'merge'
|
|
1593
|
+
'merge'
|
|
1503
1594
|
)
|
|
1504
1595
|
mergeChoice = choice as 'replace' | 'merge'
|
|
1505
1596
|
}
|
|
@@ -1516,12 +1607,12 @@ All agents follow these principles:
|
|
|
1516
1607
|
backupPath = `${configPath}.backup.${Date.now()}`
|
|
1517
1608
|
await writeFile(
|
|
1518
1609
|
backupPath,
|
|
1519
|
-
JSON.stringify(currentConfig, null, 2)
|
|
1610
|
+
JSON.stringify(currentConfig, null, 2)
|
|
1520
1611
|
)
|
|
1521
1612
|
console.log(
|
|
1522
1613
|
chalk.green(
|
|
1523
|
-
`ā Backed up current config to ${path.basename(backupPath)}
|
|
1524
|
-
)
|
|
1614
|
+
`ā Backed up current config to ${path.basename(backupPath)}`
|
|
1615
|
+
)
|
|
1525
1616
|
)
|
|
1526
1617
|
}
|
|
1527
1618
|
|
|
@@ -1529,10 +1620,10 @@ All agents follow these principles:
|
|
|
1529
1620
|
// Merge configurations
|
|
1530
1621
|
finalConfig = await mergeConfigurations(
|
|
1531
1622
|
currentConfig,
|
|
1532
|
-
latestConfig
|
|
1623
|
+
latestConfig
|
|
1533
1624
|
)
|
|
1534
1625
|
console.log(
|
|
1535
|
-
chalk.green('ā Merged configurations with latest updates')
|
|
1626
|
+
chalk.green('ā Merged configurations with latest updates')
|
|
1536
1627
|
)
|
|
1537
1628
|
} else {
|
|
1538
1629
|
// Replace with latest
|
|
@@ -1544,8 +1635,8 @@ All agents follow these principles:
|
|
|
1544
1635
|
await writeFile(configPath, JSON.stringify(finalConfig, null, 2))
|
|
1545
1636
|
console.log(
|
|
1546
1637
|
chalk.green(
|
|
1547
|
-
`ā Updated opencode.json with ${mergeChoice} strategy
|
|
1548
|
-
)
|
|
1638
|
+
`ā Updated opencode.json with ${mergeChoice} strategy`
|
|
1639
|
+
)
|
|
1549
1640
|
)
|
|
1550
1641
|
|
|
1551
1642
|
// Update AGENTS.md if it doesn't exist
|
|
@@ -1723,13 +1814,13 @@ All agents follow these principles:
|
|
|
1723
1814
|
console.log(chalk.green('\nā
Update completed successfully!'))
|
|
1724
1815
|
console.log(chalk.blue('New features available:'))
|
|
1725
1816
|
console.log(
|
|
1726
|
-
chalk.cyan(' ⢠@quality subagent for testing and PR management')
|
|
1817
|
+
chalk.cyan(' ⢠@quality subagent for testing and PR management')
|
|
1727
1818
|
)
|
|
1728
1819
|
console.log(
|
|
1729
|
-
chalk.cyan(' ⢠@security subagent for security reviews')
|
|
1820
|
+
chalk.cyan(' ⢠@security subagent for security reviews')
|
|
1730
1821
|
)
|
|
1731
1822
|
console.log(chalk.cyan(' ⢠Improved agent prompts and routing'))
|
|
1732
|
-
console.log(chalk.cyan(' ⢠GLM-4.
|
|
1823
|
+
console.log(chalk.cyan(' ⢠GLM-4.7 token optimizations'))
|
|
1733
1824
|
console.log(chalk.blue('\nTry these new commands:'))
|
|
1734
1825
|
console.log(chalk.cyan(' @quality run tests and create PR'))
|
|
1735
1826
|
console.log(chalk.cyan(' @security review this code'))
|
|
@@ -1741,10 +1832,10 @@ All agents follow these principles:
|
|
|
1741
1832
|
try {
|
|
1742
1833
|
await writeFile(
|
|
1743
1834
|
configPath,
|
|
1744
|
-
JSON.stringify(currentConfig, null, 2)
|
|
1835
|
+
JSON.stringify(currentConfig, null, 2)
|
|
1745
1836
|
)
|
|
1746
1837
|
console.log(
|
|
1747
|
-
chalk.yellow('š Restored original configuration from backup')
|
|
1838
|
+
chalk.yellow('š Restored original configuration from backup')
|
|
1748
1839
|
)
|
|
1749
1840
|
} catch (restoreError) {
|
|
1750
1841
|
console.error(chalk.red('Failed to restore backup:'))
|