berget 2.2.0 → 2.2.2

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.
@@ -39,16 +39,12 @@ exports.registerCodeCommands = void 0;
39
39
  const chalk_1 = __importDefault(require("chalk"));
40
40
  const readline_1 = __importDefault(require("readline"));
41
41
  const command_structure_1 = require("../constants/command-structure");
42
- const api_key_service_1 = require("../services/api-key-service");
43
- const auth_service_1 = require("../services/auth-service");
44
42
  const error_handler_1 = require("../utils/error-handler");
45
43
  const fs = __importStar(require("fs"));
46
44
  const promises_1 = require("fs/promises");
47
45
  const path_1 = __importDefault(require("path"));
48
46
  const child_process_1 = require("child_process");
49
- const env_manager_1 = require("../utils/env-manager");
50
47
  const client_1 = require("../client");
51
- const config_loader_1 = require("../utils/config-loader");
52
48
  /**
53
49
  * Check if current directory has git
54
50
  */
@@ -68,18 +64,6 @@ function mergeConfigurations(currentConfig, latestConfig) {
68
64
  return __awaiter(this, void 0, void 0, function* () {
69
65
  try {
70
66
  const client = (0, client_1.createAuthenticatedClient)();
71
- // Get model config with fallback for init scenario
72
- let modelConfig;
73
- try {
74
- modelConfig = (0, config_loader_1.getModelConfig)();
75
- }
76
- catch (error) {
77
- // Fallback to defaults when no config exists (init scenario)
78
- modelConfig = {
79
- primary: 'berget/glm-4.7',
80
- small: 'berget/gpt-oss',
81
- };
82
- }
83
67
  console.log(chalk_1.default.blue('🤖 Using AI to merge configurations...'));
84
68
  const mergePrompt = `You are a configuration merge specialist. Merge these two OpenCode configurations:
85
69
 
@@ -99,7 +83,7 @@ Merge rules:
99
83
  Return ONLY the merged JSON configuration, no explanations.`;
100
84
  const response = yield client.POST('/v1/chat/completions', {
101
85
  body: {
102
- model: modelConfig.primary,
86
+ model: 'glm-4.7',
103
87
  messages: [
104
88
  {
105
89
  role: 'user',
@@ -285,7 +269,6 @@ function loadLatestAgentConfig() {
285
269
  // Return the latest agent configuration directly - no file reading needed
286
270
  return {
287
271
  fullstack: {
288
- model: 'berget/glm-4.7',
289
272
  temperature: 0.3,
290
273
  top_p: 0.9,
291
274
  mode: 'primary',
@@ -294,7 +277,6 @@ function loadLatestAgentConfig() {
294
277
  prompt: "Voice: Scandinavian calm—precise, concise, confident; no fluff. You are Berget Code Fullstack agent. Act as a router and coordinator in a monorepo. Bottom-up schema: database → OpenAPI → generated types. Top-down types: API → UI → components. Use openapi-fetch and Zod at every boundary; compile-time errors are desired when contracts change. Routing rules: if task/paths match /apps/frontend or React (.tsx) → use frontend; if /apps/app or Expo/React Native → app; if /infra, /k8s, flux-system, kustomization.yaml, Helm values → devops; if /services, Koa routers, services/adapters/domain → backend. If ambiguous, remain fullstack and outline the end-to-end plan, then delegate subtasks to the right persona. Security: validate inputs; secrets via FluxCD SOPS/Sealed Secrets. Documentation is generated from code—never duplicated.\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.",
295
278
  },
296
279
  frontend: {
297
- model: 'berget/glm-4.7',
298
280
  temperature: 0.4,
299
281
  top_p: 0.9,
300
282
  mode: 'primary',
@@ -304,7 +286,6 @@ function loadLatestAgentConfig() {
304
286
  prompt: 'You are Berget Code Frontend agent. Voice: Scandinavian calm—precise, concise, confident. React 18 + TypeScript. Tailwind + Shadcn UI only via the design system (index.css, tailwind.config.ts). Use semantic tokens for color/spacing/typography/motion; never ad-hoc classes or inline colors. Components are pure and responsive; props-first data; minimal global state (Zustand/Jotai). Accessibility and keyboard navigation mandatory. Mock data only at init under /data via typed hooks (e.g., useProducts() reading /data/products.json). Design: minimal, balanced, quiet motion.\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.',
305
287
  },
306
288
  backend: {
307
- model: 'berget/glm-4.7',
308
289
  temperature: 0.3,
309
290
  top_p: 0.9,
310
291
  mode: 'primary',
@@ -313,7 +294,6 @@ function loadLatestAgentConfig() {
313
294
  prompt: "You are Berget Code Backend agent. Voice: Scandinavian calm—precise, concise, confident. TypeScript + Koa. Prefer many small pure functions; avoid big try/catch blocks. Routes thin; logic in services/adapters/domain. Validate with Zod; auto-generate OpenAPI. Adapters isolate external systems; domain never depends on framework. Test with supertest; idempotent and stateless by default. Each microservice emits an OpenAPI contract; changes propagate upward to types. Code Quality & Refactoring Principles: Apply Single Responsibility Principle, fail fast with explicit errors, eliminate code duplication, remove nested complexity, use descriptive error codes, keep functions under 30 lines. Always leave code cleaner and more readable than you found it.\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.",
314
295
  },
315
296
  devops: {
316
- model: 'berget/glm-4.7',
317
297
  temperature: 0.3,
318
298
  top_p: 0.8,
319
299
  mode: 'primary',
@@ -322,7 +302,6 @@ function loadLatestAgentConfig() {
322
302
  prompt: "You are Berget Code DevOps agent. Voice: Scandinavian calm—precise, concise, confident. Start simple: k8s/{deployment,service,ingress}. Add FluxCD sync to repo and image automation. Use Kustomize bases/overlays (staging, production). Add dependencies via Helm from upstream sources; prefer native operators when available (CloudNativePG, cert-manager, external-dns). SemVer with -rc tags keeps CI environments current. Observability with Prometheus/Grafana. No manual kubectl in production—Git is the source of truth.\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.",
323
303
  },
324
304
  app: {
325
- model: 'berget/glm-4.7',
326
305
  temperature: 0.4,
327
306
  top_p: 0.9,
328
307
  mode: 'primary',
@@ -332,7 +311,6 @@ function loadLatestAgentConfig() {
332
311
  prompt: "You are Berget Code App agent. Voice: Scandinavian calm—precise, concise, confident. Expo + React Native + TypeScript. Structure by components/hooks/services/navigation. Components are pure; data via props; refactor shared logic into hooks/stores. Share tokens with frontend. Mock data in /data via typed hooks; later replace with live APIs. Offline via SQLite/MMKV; notifications via Expo. Request permissions only when needed. Subtle, meaningful motion; light/dark parity.\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.",
333
312
  },
334
313
  security: {
335
- model: 'berget/glm-4.7',
336
314
  temperature: 0.2,
337
315
  top_p: 0.8,
338
316
  mode: 'subagent',
@@ -341,7 +319,6 @@ function loadLatestAgentConfig() {
341
319
  prompt: "Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Security agent. Expert in application security, penetration testing, and OWASP standards. Core responsibilities: Conduct security assessments and penetration tests, Validate OWASP Top 10 compliance, Review code for security vulnerabilities, Implement security headers and Content Security Policy (CSP), Audit API security, Check for sensitive data exposure, Validate input sanitization and output encoding, Assess dependency security and supply chain risks. Tools and techniques: OWASP ZAP, Burp Suite, security linters, dependency scanners, manual code review. Always provide specific, actionable security recommendations with priority levels.\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",
342
320
  },
343
321
  quality: {
344
- model: 'berget/glm-4.7',
345
322
  temperature: 0.1,
346
323
  top_p: 0.9,
347
324
  mode: 'subagent',
@@ -376,7 +353,7 @@ function installOpencode() {
376
353
  console.log(chalk_1.default.cyan('Installing OpenCode via npm...'));
377
354
  try {
378
355
  yield new Promise((resolve, reject) => {
379
- const install = (0, child_process_1.spawn)('npm', ['install', '-g', 'opencode-ai'], {
356
+ const install = (0, child_process_1.spawn)('npm', ['install', '-g', 'opencode-ai@1.2.27'], {
380
357
  stdio: 'inherit',
381
358
  });
382
359
  install.on('close', (code) => {
@@ -468,138 +445,20 @@ function registerCodeCommands(program) {
468
445
  if (!(yield ensureOpencodeInstalled(options.yes))) {
469
446
  return;
470
447
  }
471
- // Check if we have an API key in environment first
472
- if (process.env.BERGET_API_KEY) {
473
- console.log(chalk_1.default.blue('🔑 Using BERGET_API_KEY from environment - no authentication required'));
474
- }
475
- else {
476
- // Only require authentication if we don't have an API key
477
- const authService = auth_service_1.AuthService.getInstance();
478
- const profile = yield authService.whoami();
479
- if (!profile) {
480
- console.log(chalk_1.default.red('❌ Not authenticated with Berget AI.'));
481
- console.log(chalk_1.default.blue('To get started, you have two options:'));
482
- console.log('');
483
- console.log(chalk_1.default.yellow('Option 1: Use an existing API key (recommended)'));
484
- console.log(chalk_1.default.cyan(' Set BERGET_API_KEY environment variable:'));
485
- console.log(chalk_1.default.dim(' export BERGET_API_KEY=your_api_key_here'));
486
- console.log(chalk_1.default.cyan(' Or create a .env file in your project:'));
487
- console.log(chalk_1.default.dim(' echo "BERGET_API_KEY=your_api_key_here" > .env'));
488
- console.log('');
489
- console.log(chalk_1.default.yellow('Option 2: Login and create a new API key'));
490
- console.log(chalk_1.default.cyan(' berget auth login'));
491
- console.log(chalk_1.default.cyan(` berget ${command_structure_1.COMMAND_GROUPS.CODE} ${command_structure_1.SUBCOMMANDS.CODE.INIT}`));
492
- console.log('');
493
- console.log(chalk_1.default.blue('Then try again.'));
494
- return;
495
- }
496
- }
497
448
  console.log(chalk_1.default.cyan(`Initializing OpenCode for project: ${projectName}`));
498
- // Handle API key selection or creation
499
- let apiKey;
500
- let keyName;
501
- try {
502
- const apiKeyService = api_key_service_1.ApiKeyService.getInstance();
503
- if (process.env.BERGET_API_KEY) {
504
- if (options.yes) {
505
- console.log(chalk_1.default.blue('🔑 Using BERGET_API_KEY from environment'));
506
- apiKey = process.env.BERGET_API_KEY;
507
- keyName = `env-key-${projectName}`;
508
- }
509
- else {
510
- console.log(chalk_1.default.blue('\n📋 API key setup:'));
511
- console.log(chalk_1.default.dim('─'.repeat(60)));
512
- console.log(`${chalk_1.default.cyan('1')} ${chalk_1.default.bold('Use existing API key')}`);
513
- console.log(chalk_1.default.dim(' Uses BERGET_API_KEY from environment'));
514
- console.log(`${chalk_1.default.cyan('2')} ${chalk_1.default.bold('Create a new API key')}`);
515
- console.log(chalk_1.default.dim('─'.repeat(60)));
516
- const choice = yield new Promise((resolve) => {
517
- const rl = readline_1.default.createInterface({
518
- input: process.stdin,
519
- output: process.stdout,
520
- });
521
- rl.question(chalk_1.default.blue('\nSelect an option (1-2, default: 1): '), (answer) => {
522
- rl.close();
523
- resolve(answer.trim() || '1');
524
- });
525
- });
526
- if (choice === '1') {
527
- console.log(chalk_1.default.blue('🔑 Using BERGET_API_KEY from environment'));
528
- apiKey = process.env.BERGET_API_KEY;
529
- keyName = `env-key-${projectName}`;
530
- }
531
- else if (choice === '2') {
532
- console.log(chalk_1.default.blue('\n🔑 Creating new API key...'));
533
- const defaultKeyName = `opencode-${projectName}-${Date.now()}`;
534
- const customName = yield getInput(chalk_1.default.blue(`Enter key name (default: ${defaultKeyName}): `), defaultKeyName, options.yes);
535
- keyName = customName;
536
- const createOptions = { name: keyName };
537
- const keyData = yield apiKeyService.create(createOptions);
538
- apiKey = keyData.key;
539
- console.log(chalk_1.default.green(`✓ Created new API key: ${keyName}`));
540
- }
541
- else {
542
- console.log(chalk_1.default.red('Invalid selection.'));
543
- return;
544
- }
545
- }
546
- }
547
- else {
548
- if (!options.yes) {
549
- console.log(chalk_1.default.yellow('No BERGET_API_KEY environment variable found.'));
550
- console.log(chalk_1.default.blue('Creating a new API key...'));
551
- console.log(chalk_1.default.dim('\n💡 Tip: Set BERGET_API_KEY environment variable to reuse an existing key:'));
552
- console.log(chalk_1.default.dim(' export BERGET_API_KEY=your_api_key_here'));
553
- }
554
- const defaultKeyName = `opencode-${projectName}-${Date.now()}`;
555
- const customName = yield getInput(chalk_1.default.blue(`Enter key name (default: ${defaultKeyName}): `), defaultKeyName, options.yes);
556
- keyName = customName;
557
- const createOptions = { name: keyName };
558
- const keyData = yield apiKeyService.create(createOptions);
559
- apiKey = keyData.key;
560
- console.log(chalk_1.default.green(`✓ Created new API key: ${keyName}`));
561
- }
562
- }
563
- catch (error) {
564
- if (process.env.BERGET_API_KEY) {
565
- console.log(chalk_1.default.yellow('⚠️ Could not verify API key with Berget API, but continuing with environment key'));
566
- console.log(chalk_1.default.dim('This might be due to network issues or an invalid key'));
567
- }
568
- else {
569
- console.error(chalk_1.default.red('❌ Failed to handle API keys:'));
570
- console.log(chalk_1.default.blue('This could be due to:'));
571
- console.log(chalk_1.default.dim(' • Network connectivity issues'));
572
- console.log(chalk_1.default.dim(' • Invalid authentication credentials'));
573
- console.log(chalk_1.default.dim(' • API service temporarily unavailable'));
574
- console.log('');
575
- console.log(chalk_1.default.blue('Try using an API key directly:'));
576
- console.log(chalk_1.default.cyan(' export BERGET_API_KEY=your_api_key_here'));
577
- console.log(chalk_1.default.cyan(` berget ${command_structure_1.COMMAND_GROUPS.CODE} ${command_structure_1.SUBCOMMANDS.CODE.INIT} --yes`));
578
- (0, error_handler_1.handleError)('API key operation failed', error);
579
- }
580
- return;
581
- }
582
- // Prepare .env file path for safe update
583
- const envPath = path_1.default.join(process.cwd(), '.env');
584
449
  // Load latest agent configuration from our own codebase
585
450
  const latestAgentConfig = yield loadLatestAgentConfig();
586
451
  // Use hardcoded defaults for init - never try to load from project
587
- const modelConfig = {
588
- primary: 'berget/glm-4.7',
589
- small: 'berget/gpt-oss',
590
- };
591
- // Create opencode.json config with optimized agent-based format
452
+ // Create opencode.json config — plugin handles auth, models, and provider
592
453
  const config = {
593
454
  $schema: 'https://opencode.ai/config.json',
455
+ plugin: ['@bergetai/opencode-auth@1.0.9'],
594
456
  username: 'berget-code',
595
457
  theme: 'berget-dark',
596
458
  share: 'manual',
597
459
  autoupdate: true,
598
- model: modelConfig.primary,
599
- small_model: modelConfig.small,
600
460
  agent: {
601
461
  fullstack: {
602
- model: modelConfig.primary,
603
462
  temperature: 0.3,
604
463
  top_p: 0.9,
605
464
  mode: 'primary',
@@ -608,7 +467,6 @@ function registerCodeCommands(program) {
608
467
  prompt: 'Voice: Scandinavian calm—precise, concise, confident; no fluff. You are Berget Code Fullstack agent. Act as a router and coordinator in a monorepo. Bottom-up schema: database → OpenAPI → generated types. Top-down types: API → UI → components. Use openapi-fetch and Zod at every boundary; compile-time errors are desired when contracts change. Routing rules: if task/paths match /apps/frontend or React (.tsx) → use frontend; if /apps/app or Expo/React Native → app; if /infra, /k8s, flux-system, kustomization.yaml, Helm values → devops; if /services, Koa routers, services/adapters/domain → backend. If ambiguous, remain fullstack and outline the end-to-end plan, then delegate subtasks to the right persona. Security: validate inputs; secrets via FluxCD SOPS/Sealed Secrets. Documentation is generated from code—never duplicated. CRITICAL: When all implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
609
468
  },
610
469
  frontend: {
611
- model: modelConfig.primary,
612
470
  temperature: 0.4,
613
471
  top_p: 0.9,
614
472
  mode: 'primary',
@@ -618,7 +476,6 @@ function registerCodeCommands(program) {
618
476
  prompt: 'You are Berget Code Frontend agent. Voice: Scandinavian calm—precise, concise, confident. React 18 + TypeScript. Tailwind + Shadcn UI only via the design system (index.css, tailwind.config.ts). Use semantic tokens for color/spacing/typography/motion; never ad-hoc classes or inline colors. Components are pure and responsive; props-first data; minimal global state (Zustand/Jotai). Accessibility and keyboard navigation mandatory. Mock data only at init under /data via typed hooks (e.g., useProducts() reading /data/products.json). Design: minimal, balanced, quiet motion. CRITICAL: When all frontend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
619
477
  },
620
478
  backend: {
621
- model: modelConfig.primary,
622
479
  temperature: 0.3,
623
480
  top_p: 0.9,
624
481
  mode: 'primary',
@@ -628,7 +485,6 @@ function registerCodeCommands(program) {
628
485
  },
629
486
  // Use centralized devops configuration with Helm guidelines
630
487
  devops: latestAgentConfig.devops || {
631
- model: modelConfig.primary,
632
488
  temperature: 0.3,
633
489
  top_p: 0.8,
634
490
  mode: 'primary',
@@ -637,7 +493,6 @@ function registerCodeCommands(program) {
637
493
  prompt: 'You are Berget Code DevOps agent. Voice: Scandinavian calm—precise, concise, confident. Start simple: k8s/{deployment,service,ingress}. Add FluxCD sync to repo and image automation. Use Kustomize bases/overlays (staging, production). Add dependencies via Helm from upstream sources; prefer native operators when available (CloudNativePG, cert-manager, external-dns). SemVer with -rc tags keeps CI environments current. Observability with Prometheus/Grafana. No manual kubectl in production—Git is the source of truth.',
638
494
  },
639
495
  app: {
640
- model: modelConfig.primary,
641
496
  temperature: 0.4,
642
497
  top_p: 0.9,
643
498
  mode: 'primary',
@@ -647,7 +502,6 @@ function registerCodeCommands(program) {
647
502
  prompt: 'You are Berget Code App agent. Voice: Scandinavian calm—precise, concise, confident. Expo + React Native + TypeScript. Structure by components/hooks/services/navigation. Components are pure; data via props; refactor shared logic into hooks/stores. Share tokens with frontend. Mock data in /data via typed hooks; later replace with live APIs. Offline via SQLite/MMKV; notifications via Expo. Request permissions only when needed. Subtle, meaningful motion; light/dark parity.',
648
503
  },
649
504
  security: {
650
- model: modelConfig.primary,
651
505
  temperature: 0.2,
652
506
  top_p: 0.8,
653
507
  mode: 'subagent',
@@ -656,7 +510,6 @@ function registerCodeCommands(program) {
656
510
  prompt: 'Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Security agent. Expert in application security, penetration testing, and OWASP standards. Core responsibilities: Conduct security assessments and penetration tests, Validate OWASP Top 10 compliance, Review code for security vulnerabilities, Implement security headers and Content Security Policy (CSP), Audit API security, Check for sensitive data exposure, Validate input sanitization and output encoding, Assess dependency security and supply chain risks. Tools and techniques: OWASP ZAP, Burp Suite, security linters, dependency scanners, manual code review. Always provide specific, actionable security recommendations with priority levels.',
657
511
  },
658
512
  quality: {
659
- model: modelConfig.primary,
660
513
  temperature: 0.1,
661
514
  top_p: 0.9,
662
515
  mode: 'subagent',
@@ -706,249 +559,20 @@ function registerCodeCommands(program) {
706
559
  watcher: {
707
560
  ignore: ['node_modules', 'dist', '.git', 'coverage'],
708
561
  },
709
- provider: {
710
- berget: {
711
- npm: '@ai-sdk/openai-compatible',
712
- name: 'Berget AI',
713
- options: {
714
- baseURL: '{env:BERGET_API_URL}',
715
- apiKey: '{env:BERGET_API_KEY}',
716
- },
717
- models: {
718
- 'glm-4.7': {
719
- name: 'GLM-4.7',
720
- limit: { output: 4000, context: 200000 },
721
- modalities: { input: ['text'], output: ['text'] },
722
- },
723
- 'gpt-oss': {
724
- name: 'GPT-OSS',
725
- limit: { output: 4000, context: 128000 },
726
- modalities: { input: ['text', 'image'], output: ['text'] },
727
- },
728
- 'llama-8b': {
729
- name: 'llama-3.1-8b',
730
- limit: { output: 4000, context: 128000 },
731
- },
732
- },
733
- },
734
- },
735
562
  };
736
563
  // Ask for permission to create config files
737
564
  if (!options.yes) {
738
565
  console.log(chalk_1.default.blue('\nAbout to create configuration files:'));
739
566
  console.log(chalk_1.default.dim(`Config: ${configPath}`));
740
- console.log(chalk_1.default.dim(`Environment: ${envPath}`));
741
- console.log(chalk_1.default.dim(`Documentation: ${path_1.default.join(process.cwd(), 'AGENTS.md')} (if not exists)`));
742
- console.log(chalk_1.default.dim(`Environment: ${path_1.default.join(process.cwd(), '.env')} will be updated`));
743
- console.log(chalk_1.default.dim('This will configure OpenCode to use Berget AI models.'));
744
- console.log(chalk_1.default.cyan('\n💡 Benefits:'));
745
- console.log(chalk_1.default.cyan(' • API key stored separately in .env file (not committed to Git)'));
746
- console.log(chalk_1.default.cyan(' • Easy cost separation per project/customer'));
747
- console.log(chalk_1.default.cyan(' • Secure key management with environment variables'));
748
- console.log(chalk_1.default.cyan(" • Project-specific agent documentation (won't overwrite existing)"));
567
+ console.log(chalk_1.default.dim('This will configure OpenCode with the Berget auth plugin.'));
749
568
  }
750
569
  if (yield confirm('\nCreate configuration files? (Y/n): ', options.yes)) {
751
570
  try {
752
- // Safely update .env file using dotenv
753
- yield (0, env_manager_1.updateEnvFile)({
754
- envPath,
755
- key: 'BERGET_API_KEY',
756
- value: apiKey,
757
- comment: `Berget AI Configuration for ${projectName} - Generated by berget code init - Do not commit to version control`,
758
- });
759
571
  // Create opencode.json
760
572
  yield (0, promises_1.writeFile)(configPath, JSON.stringify(config, null, 2));
761
573
  console.log(chalk_1.default.green(`✓ Created opencode.json`));
762
- console.log(chalk_1.default.dim(` Model: ${config.model}`));
763
- console.log(chalk_1.default.dim(` Small Model: ${config.small_model}`));
574
+ console.log(chalk_1.default.dim(` Plugin: @bergetai/opencode-auth`));
764
575
  console.log(chalk_1.default.dim(` Theme: ${config.theme}`));
765
- console.log(chalk_1.default.dim(` API Key: Stored in .env as BERGET_API_KEY`));
766
- // Create AGENTS.md documentation only if it doesn't exist
767
- const agentsMdPath = path_1.default.join(process.cwd(), 'AGENTS.md');
768
- if (!fs.existsSync(agentsMdPath)) {
769
- const agentsMdContent = `# Berget Code Agents
770
-
771
- This document describes the specialized agents available in this project for use with OpenCode.
772
-
773
- ## Available Agents
774
-
775
- ### Primary Agents
776
-
777
- #### fullstack
778
- Router/coordinator agent for full-stack development with schema-driven architecture. Handles routing between different personas based on file paths and task requirements.
779
-
780
- **Use when:**
781
- - Working across multiple parts of a monorepo
782
- - Need to coordinate between frontend, backend, devops, and app
783
- - Starting new projects and need to determine tech stack
784
-
785
- **Key features:**
786
- - Schema-driven development (database → OpenAPI → types)
787
- - Automatic routing to appropriate persona
788
- - Tech stack discovery and recommendations
789
-
790
- #### frontend
791
- Builds Scandinavian, type-safe UIs with React, Tailwind, and Shadcn.
792
-
793
- **Use when:**
794
- - Working with React components (.tsx files)
795
- - Frontend development in /apps/frontend
796
- - UI/UX implementation
797
-
798
- **Key features:**
799
- - Design system integration
800
- - Semantic tokens and accessibility
801
- - Props-first component architecture
802
-
803
- #### backend
804
- Functional, modular Koa + TypeScript services with schema-first approach and code quality focus.
805
-
806
- **Use when:**
807
- - Working with Koa routers and services
808
- - Backend development in /services
809
- - API development and database work
810
-
811
- **Key features:**
812
- - Zod validation and OpenAPI generation
813
- - Code quality and refactoring principles
814
- - PR workflow integration
815
-
816
- #### devops
817
- Declarative GitOps infrastructure with FluxCD, Kustomize, Helm, and operators.
818
-
819
- **Use when:**
820
- - Working with Kubernetes manifests
821
- - Infrastructure in /infra or /k8s
822
- - CI/CD and deployment configurations
823
-
824
- **Key features:**
825
- - GitOps workflows
826
- - Operator-first approach
827
- - SemVer with release candidates
828
-
829
- **Helm Values Configuration Process:**
830
- 1. 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.
831
- 2. 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.
832
- 3. 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.
833
- 4. 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.
834
-
835
- #### app
836
- Expo + React Native applications with props-first architecture and offline awareness.
837
-
838
- **Use when:**
839
- - Mobile app development with Expo
840
- - React Native projects in /apps/app
841
- - Cross-platform mobile development
842
-
843
- **Key features:**
844
- - Shared design tokens with frontend
845
- - Offline-first architecture
846
- - Expo integration
847
-
848
- ### Subagents
849
-
850
- #### security
851
- Security specialist for penetration testing, OWASP compliance, and vulnerability assessments.
852
-
853
- **Use when:**
854
- - Need security review of code changes
855
- - OWASP Top 10 compliance checks
856
- - Vulnerability assessments
857
-
858
- **Key features:**
859
- - OWASP standards compliance
860
- - Security best practices
861
- - Actionable remediation strategies
862
-
863
- #### quality
864
- Quality assurance specialist for testing, building, and PR management.
865
-
866
- **Use when:**
867
- - Need to run test suites and build processes
868
- - Creating or updating pull requests
869
- - Monitoring GitHub for reviewer comments
870
- - Ensuring code quality standards
871
-
872
- **Key features:**
873
- - Comprehensive testing and building workflows
874
- - PR creation and management
875
- - GitHub integration for reviewer feedback
876
- - CLI command expertise for quality assurance
877
-
878
- ## Usage
879
-
880
- ### Switching Agents
881
- Use the \`<tab>\` key to cycle through primary agents during a session.
882
-
883
- ### Manual Agent Selection
884
- Use commands to switch to specific agents:
885
- - \`/fullstack\` - Switch to Fullstack agent
886
- - \`/frontend\` - Switch to Frontend agent
887
- - \`/backend\` - Switch to Backend agent
888
- - \`/devops\` - Switch to DevOps agent
889
- - \`/app\` - Switch to App agent
890
- - \`/quality\` - Switch to Quality agent for testing and PR management
891
-
892
- ### Using Subagents
893
- Mention subagents with \`@\` symbol:
894
- - \`@security review this authentication implementation\`
895
- - \`@quality run tests and create PR for these changes\`
896
-
897
- ## Routing Rules
898
-
899
- The fullstack agent automatically routes tasks based on file patterns:
900
-
901
- - \`/apps/frontend\` or \`.tsx\` files → frontend
902
- - \`/apps/app\` or Expo/React Native → app
903
- - \`/infra\`, \`/k8s\`, FluxCD, Helm → devops
904
- - \`/services\`, Koa routers → backend
905
-
906
- ## Configuration
907
-
908
- All agents are configured in \`opencode.json\` with:
909
- - Specialized prompts and temperature settings
910
- - Appropriate tool permissions
911
- - Model optimizations for their specific tasks
912
-
913
- ## Environment Setup
914
-
915
- Configure \`.env\` with your API key:
916
- \`\`\`
917
- BERGET_API_KEY=your_api_key_here
918
- \`\`\`
919
-
920
- ## Workflow
921
-
922
- All agents follow these principles:
923
- - Never work directly in main branch
924
- - Follow branch strategy and commit conventions
925
- - Create PRs for new functionality
926
- - Run tests before committing
927
- - Address reviewer feedback promptly
928
-
929
- ---
930
-
931
- *Generated by berget code init for ${projectName}*
932
- `;
933
- yield (0, promises_1.writeFile)(agentsMdPath, agentsMdContent);
934
- console.log(chalk_1.default.green(`✓ Created AGENTS.md`));
935
- console.log(chalk_1.default.dim(` Documentation for available agents and usage`));
936
- }
937
- else {
938
- console.log(chalk_1.default.yellow(`⚠ AGENTS.md already exists, skipping creation`));
939
- }
940
- // Check if .gitignore exists and add .env if not already there
941
- const gitignorePath = path_1.default.join(process.cwd(), '.gitignore');
942
- let gitignoreContent = '';
943
- if (fs.existsSync(gitignorePath)) {
944
- gitignoreContent = fs.readFileSync(gitignorePath, 'utf8');
945
- }
946
- if (!gitignoreContent.includes('.env')) {
947
- gitignoreContent +=
948
- (gitignoreContent.endsWith('\n') ? '' : '\n') + '.env\n';
949
- yield (0, promises_1.writeFile)(gitignorePath, gitignoreContent);
950
- console.log(chalk_1.default.green(`✓ Added .env to .gitignore`));
951
- }
952
576
  }
953
577
  catch (error) {
954
578
  console.error(chalk_1.default.red('Failed to create config files:'));
@@ -961,9 +585,12 @@ All agents follow these principles:
961
585
  return;
962
586
  }
963
587
  console.log(chalk_1.default.green('\n✅ Project initialized successfully!'));
964
- console.log(chalk_1.default.blue('Next steps:'));
965
- console.log(chalk_1.default.blue(` berget ${command_structure_1.COMMAND_GROUPS.CODE} ${command_structure_1.SUBCOMMANDS.CODE.RUN}`));
966
- console.log(chalk_1.default.blue(' Or run: opencode'));
588
+ console.log(chalk_1.default.blue('\nNext steps:'));
589
+ console.log(chalk_1.default.cyan(' 1. Run: opencode'));
590
+ console.log(chalk_1.default.cyan(' 2. Type: /connect'));
591
+ console.log(chalk_1.default.cyan(' 3. Choose your auth method:'));
592
+ console.log(chalk_1.default.dim(' • "Login with Berget" — Berget Code team members (SSO)'));
593
+ console.log(chalk_1.default.dim(' • "Enter API Key" — API key users (console.berget.ai)'));
967
594
  }
968
595
  catch (error) {
969
596
  (0, error_handler_1.handleError)('Failed to initialize project', error);
@@ -1001,38 +628,23 @@ All agents follow these principles:
1001
628
  console.log(chalk_1.default.blue(`Run ${chalk_1.default.bold(`berget ${command_structure_1.COMMAND_GROUPS.CODE} ${command_structure_1.SUBCOMMANDS.CODE.INIT}`)} first.`));
1002
629
  return;
1003
630
  }
1004
- // Set environment variables for opencode
631
+ // Prepare opencode command
1005
632
  const env = Object.assign({}, process.env);
1006
- // Set API base URL based on flags (default to production)
1007
- const isLocalMode = process.argv.includes('--local');
1008
- const isStageMode = process.argv.includes('--stage');
1009
- if (isLocalMode) {
1010
- env.BERGET_API_URL = 'http://localhost:3000/v1';
1011
- console.log(chalk_1.default.dim('Using local API: http://localhost:3000/v1'));
1012
- }
1013
- else if (isStageMode) {
1014
- env.BERGET_API_URL = 'https://api.stage.berget.ai/v1';
1015
- console.log(chalk_1.default.dim('Using stage API: https://api.stage.berget.ai/v1'));
1016
- }
1017
- else {
1018
- env.BERGET_API_URL = 'https://api.berget.ai/v1';
1019
- }
1020
- // Auth resolution: JWT first (if valid), then API-key
1021
- // This ensures seat-based users get proper tracking
1022
- const jwtToken = (0, client_1.getAuthToken)();
1023
- if (jwtToken) {
1024
- env.BERGET_API_KEY = jwtToken;
1025
- console.log(chalk_1.default.dim('Using JWT token for authentication'));
1026
- }
1027
- else if (env.BERGET_API_KEY) {
1028
- console.log(chalk_1.default.dim('Using API key for authentication'));
633
+ const opencodeArgs = [];
634
+ // Read --stage and --local from root program options
635
+ // (these flags are registered at program level, not subcommand level)
636
+ const isStage = process.argv.includes('--stage');
637
+ const isLocal = process.argv.includes('--local');
638
+ if (isStage) {
639
+ console.log(chalk_1.default.cyan('Using Berget stage environment'));
640
+ env.BERGET_API_URL = 'https://api.stage.berget.ai';
641
+ env.BERGET_INFERENCE_URL = 'https://api.stage.berget.ai/v1';
1029
642
  }
1030
- else {
1031
- console.log(chalk_1.default.yellow('Warning: No authentication found'));
1032
- console.log(chalk_1.default.dim(' Run `berget auth login` or set BERGET_API_KEY'));
643
+ else if (isLocal) {
644
+ console.log(chalk_1.default.cyan('Using local development environment'));
645
+ env.BERGET_API_URL = 'http://localhost:3000';
646
+ env.BERGET_INFERENCE_URL = 'http://localhost:3000/v1';
1033
647
  }
1034
- // Prepare opencode command
1035
- const opencodeArgs = [];
1036
648
  if (prompt) {
1037
649
  opencodeArgs.push('run', prompt);
1038
650
  }
@@ -1109,7 +721,7 @@ All agents follow these principles:
1109
721
  .option('-f, --force', 'Force update even if already latest')
1110
722
  .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
1111
723
  .action((options) => __awaiter(this, void 0, void 0, function* () {
1112
- var _a, _b, _c, _d, _e, _f, _g, _h;
724
+ var _a, _b, _c;
1113
725
  try {
1114
726
  console.log(chalk_1.default.cyan('🔄 Updating OpenCode configuration...'));
1115
727
  // Ensure opencode is installed first
@@ -1140,30 +752,16 @@ All agents follow these principles:
1140
752
  console.log(chalk_1.default.dim(` Agents: ${Object.keys(currentConfig.agent || {}).length} configured`));
1141
753
  // Load latest agent configuration to ensure consistency
1142
754
  const latestAgentConfig = yield loadLatestAgentConfig();
1143
- // Get model config with fallback for init scenario
1144
- let modelConfig;
1145
- try {
1146
- modelConfig = (0, config_loader_1.getModelConfig)();
1147
- }
1148
- catch (error) {
1149
- // Fallback to defaults when no config exists (init scenario)
1150
- modelConfig = {
1151
- primary: 'berget/glm-4.7',
1152
- small: 'berget/gpt-oss',
1153
- };
1154
- }
1155
- // Create latest configuration with all improvements
755
+ // Create latest configuration plugin handles auth, models, and provider
1156
756
  const latestConfig = {
1157
757
  $schema: 'https://opencode.ai/config.json',
758
+ plugin: ['@bergetai/opencode-auth@1.0.9'],
1158
759
  username: 'berget-code',
1159
760
  theme: 'berget-dark',
1160
761
  share: 'manual',
1161
762
  autoupdate: true,
1162
- model: modelConfig.primary,
1163
- small_model: modelConfig.small,
1164
763
  agent: {
1165
764
  fullstack: {
1166
- model: modelConfig.primary,
1167
765
  temperature: 0.3,
1168
766
  top_p: 0.9,
1169
767
  mode: 'primary',
@@ -1172,7 +770,6 @@ All agents follow these principles:
1172
770
  prompt: 'Voice: Scandinavian calm—precise, concise, confident; no fluff. You are Berget Code Fullstack agent. Act as a router and coordinator in a monorepo. Bottom-up schema: database → OpenAPI → generated types. Top-down types: API → UI → components. Use openapi-fetch and Zod at every boundary; compile-time errors are desired when contracts change. Routing rules: if task/paths match /apps/frontend or React (.tsx) → use frontend; if /apps/app or Expo/React Native → app; if /infra, /k8s, flux-system, kustomization.yaml, Helm values → devops; if /services, Koa routers, services/adapters/domain → backend. If ambiguous, remain fullstack and outline the end-to-end plan, then delegate subtasks to the right persona. Security: validate inputs; secrets via FluxCD SOPS/Sealed Secrets. Documentation is generated from code—never duplicated. CRITICAL: When all implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
1173
771
  },
1174
772
  frontend: {
1175
- model: modelConfig.primary,
1176
773
  temperature: 0.4,
1177
774
  top_p: 0.9,
1178
775
  mode: 'primary',
@@ -1182,7 +779,6 @@ All agents follow these principles:
1182
779
  prompt: 'You are Berget Code Frontend agent. Voice: Scandinavian calm—precise, concise, confident. React 18 + TypeScript. Tailwind + Shadcn UI only via the design system (index.css, tailwind.config.ts). Use semantic tokens for color/spacing/typography/motion; never ad-hoc classes or inline colors. Components are pure and responsive; props-first data; minimal global state (Zustand/Jotai). Accessibility and keyboard navigation mandatory. Mock data only at init under /data via typed hooks (e.g., useProducts() reading /data/products.json). Design: minimal, balanced, quiet motion. CRITICAL: When all frontend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
1183
780
  },
1184
781
  backend: {
1185
- model: modelConfig.primary,
1186
782
  temperature: 0.3,
1187
783
  top_p: 0.9,
1188
784
  mode: 'primary',
@@ -1192,7 +788,6 @@ All agents follow these principles:
1192
788
  },
1193
789
  // Use centralized devops configuration with Helm guidelines
1194
790
  devops: latestAgentConfig.devops || {
1195
- model: modelConfig.primary,
1196
791
  temperature: 0.3,
1197
792
  top_p: 0.8,
1198
793
  mode: 'primary',
@@ -1201,7 +796,6 @@ All agents follow these principles:
1201
796
  prompt: 'You are Berget Code DevOps agent. Voice: Scandinavian calm—precise, concise, confident. Start simple: k8s/{deployment,service,ingress}. Add FluxCD sync to repo and image automation. Use Kustomize bases/overlays (staging, production). Add dependencies via Helm from upstream sources; prefer native operators when available (CloudNativePG, cert-manager, external-dns). SemVer with -rc tags keeps CI environments current. Observability with Prometheus/Grafana. No manual kubectl in production—Git is the source of truth. For testing, building, and PR management, use @quality subagent.',
1202
797
  },
1203
798
  app: {
1204
- model: modelConfig.primary,
1205
799
  temperature: 0.4,
1206
800
  top_p: 0.9,
1207
801
  mode: 'primary',
@@ -1211,7 +805,6 @@ All agents follow these principles:
1211
805
  prompt: 'You are Berget Code App agent. Voice: Scandinavian calm—precise, concise, confident. Expo + React Native + TypeScript. Structure by components/hooks/services/navigation. Components are pure; data via props; refactor shared logic into hooks/stores. Share tokens with frontend. Mock data in /data via typed hooks; later replace with live APIs. Offline via SQLite/MMKV; notifications via Expo. Request permissions only when needed. Subtle, meaningful motion; light/dark parity. For testing, building, and PR management, use @quality subagent.',
1212
806
  },
1213
807
  security: {
1214
- model: modelConfig.primary,
1215
808
  temperature: 0.2,
1216
809
  top_p: 0.8,
1217
810
  mode: 'subagent',
@@ -1220,7 +813,6 @@ All agents follow these principles:
1220
813
  prompt: 'Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Security agent. Expert in application security, penetration testing, and OWASP standards. Core responsibilities: Conduct security assessments and penetration tests, Validate OWASP Top 10 compliance, Review code for security vulnerabilities, Implement security headers and Content Security Policy (CSP), Audit API security, Check for sensitive data exposure, Validate input sanitization and output encoding, Assess dependency security and supply chain risks. Tools and techniques: OWASP ZAP, Burp Suite, security linters, dependency scanners, manual code review. Always provide specific, actionable security recommendations with priority levels. Workflow: Always follow branch_strategy and commit_convention from workflow section. Never work directly in main. Agent awareness: Review code from all personas (frontend, backend, app, devops). If implementation changes are needed, suggest <tab> to switch to appropriate persona after security assessment.',
1221
814
  },
1222
815
  quality: {
1223
- model: modelConfig.primary,
1224
816
  temperature: 0.1,
1225
817
  top_p: 0.9,
1226
818
  mode: 'subagent',
@@ -1275,17 +867,6 @@ All agents follow these principles:
1275
867
  watcher: {
1276
868
  ignore: ['node_modules', 'dist', '.git', 'coverage'],
1277
869
  },
1278
- provider: {
1279
- berget: {
1280
- npm: '@ai-sdk/openai-compatible',
1281
- name: 'Berget AI',
1282
- options: {
1283
- baseURL: '{env:BERGET_API_URL}',
1284
- apiKey: '{env:BERGET_API_KEY}',
1285
- },
1286
- models: (0, config_loader_1.getProviderModels)(),
1287
- },
1288
- },
1289
870
  };
1290
871
  // Check if update is needed
1291
872
  const needsUpdate = JSON.stringify(currentConfig) !== JSON.stringify(latestConfig);
@@ -1310,9 +891,9 @@ All agents follow these principles:
1310
891
  if (((_c = (_b = currentConfig.agent) === null || _b === void 0 ? void 0 : _b.security) === null || _c === void 0 ? void 0 : _c.mode) !== 'subagent') {
1311
892
  console.log(chalk_1.default.cyan(' • Security agent converted to subagent (read-only)'));
1312
893
  }
1313
- // Check for GLM-4.7 optimizations
1314
- if (!((_h = (_g = (_f = (_e = (_d = currentConfig.provider) === null || _d === void 0 ? void 0 : _d.berget) === null || _e === void 0 ? void 0 : _e.models) === null || _f === void 0 ? void 0 : _f[modelConfig.primary.replace('berget/', '')]) === null || _g === void 0 ? void 0 : _g.limit) === null || _h === void 0 ? void 0 : _h.context)) {
1315
- console.log(chalk_1.default.cyan(' • GLM-4.7 token limits and auto-compaction'));
894
+ // Check for plugin migration
895
+ if (!currentConfig.plugin) {
896
+ console.log(chalk_1.default.cyan(' • Plugin-first auth (automatic token refresh + model discovery)'));
1316
897
  }
1317
898
  console.log(chalk_1.default.cyan(' • Latest agent prompts and improvements'));
1318
899
  }
@@ -1511,10 +1092,7 @@ All agents are configured in \`opencode.json\` with:
1511
1092
 
1512
1093
  ## Environment Setup
1513
1094
 
1514
- Configure \`.env\` with your API key:
1515
- \`\`\`
1516
- BERGET_API_KEY=your_api_key_here
1517
- \`\`\`
1095
+ Authentication is handled by the Berget plugin. Run \`/connect\` in OpenCode to authenticate.
1518
1096
 
1519
1097
  ## Workflow
1520
1098