berget 2.2.5 → 2.2.7

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.
Files changed (148) hide show
  1. package/.github/workflows/publish.yml +8 -8
  2. package/.github/workflows/test.yml +12 -6
  3. package/.husky/pre-commit +1 -0
  4. package/.prettierignore +15 -0
  5. package/.prettierrc +5 -3
  6. package/CONTRIBUTING.md +38 -0
  7. package/README.md +2 -148
  8. package/dist/index.js +21 -21
  9. package/dist/package.json +30 -2
  10. package/dist/src/agents/app.js +28 -0
  11. package/dist/src/agents/backend.js +25 -0
  12. package/dist/src/agents/devops.js +34 -0
  13. package/dist/src/agents/frontend.js +25 -0
  14. package/dist/src/agents/fullstack.js +25 -0
  15. package/dist/src/agents/index.js +61 -0
  16. package/dist/src/agents/quality.js +70 -0
  17. package/dist/src/agents/security.js +26 -0
  18. package/dist/src/agents/types.js +2 -0
  19. package/dist/src/client.js +54 -62
  20. package/dist/src/commands/api-keys.js +132 -140
  21. package/dist/src/commands/auth.js +9 -9
  22. package/dist/src/commands/autocomplete.js +9 -9
  23. package/dist/src/commands/billing.js +7 -9
  24. package/dist/src/commands/chat.js +90 -92
  25. package/dist/src/commands/clusters.js +12 -12
  26. package/dist/src/commands/code/__tests__/auth-sync.test.js +348 -0
  27. package/dist/src/commands/code/__tests__/fake-api-key-service.js +23 -0
  28. package/dist/src/commands/code/__tests__/fake-auth-service.js +55 -0
  29. package/dist/src/commands/code/__tests__/fake-command-runner.js +50 -0
  30. package/dist/src/commands/code/__tests__/fake-file-store.js +55 -0
  31. package/dist/src/commands/code/__tests__/fake-prompter.js +133 -0
  32. package/dist/src/commands/code/__tests__/setup-flow.test.js +505 -0
  33. package/dist/src/commands/code/adapters/clack-prompter.js +81 -0
  34. package/dist/src/commands/code/adapters/fs-file-store.js +80 -0
  35. package/dist/src/commands/code/adapters/spawn-command-runner.js +53 -0
  36. package/dist/src/commands/code/auth-sync.js +283 -0
  37. package/dist/src/commands/code/errors.js +27 -0
  38. package/dist/src/commands/code/ports/auth-services.js +2 -0
  39. package/dist/src/commands/code/ports/command-runner.js +2 -0
  40. package/dist/src/commands/code/ports/file-store.js +2 -0
  41. package/dist/src/commands/code/ports/prompter.js +2 -0
  42. package/dist/src/commands/code/setup.js +533 -0
  43. package/dist/src/commands/code.js +223 -779
  44. package/dist/src/commands/models.js +13 -15
  45. package/dist/src/commands/users.js +6 -8
  46. package/dist/src/constants/command-structure.js +116 -114
  47. package/dist/src/services/api-key-service.js +43 -48
  48. package/dist/src/services/auth-service.js +60 -299
  49. package/dist/src/services/browser-auth.js +278 -0
  50. package/dist/src/services/chat-service.js +78 -91
  51. package/dist/src/services/cluster-service.js +6 -6
  52. package/dist/src/services/collaborator-service.js +5 -8
  53. package/dist/src/services/flux-service.js +5 -8
  54. package/dist/src/services/helm-service.js +5 -8
  55. package/dist/src/services/kubectl-service.js +7 -10
  56. package/dist/src/utils/config-checker.js +5 -5
  57. package/dist/src/utils/config-loader.js +25 -25
  58. package/dist/src/utils/default-api-key.js +23 -23
  59. package/dist/src/utils/env-manager.js +7 -7
  60. package/dist/src/utils/error-handler.js +60 -61
  61. package/dist/src/utils/logger.js +7 -7
  62. package/dist/src/utils/markdown-renderer.js +2 -2
  63. package/dist/src/utils/opencode-validator.js +17 -20
  64. package/dist/src/utils/token-manager.js +38 -11
  65. package/dist/tests/commands/chat.test.js +24 -24
  66. package/dist/tests/commands/code.test.js +169 -138
  67. package/dist/tests/utils/config-loader.test.js +114 -114
  68. package/dist/tests/utils/env-manager.test.js +57 -57
  69. package/dist/tests/utils/opencode-validator.test.js +44 -43
  70. package/dist/vitest.config.js +1 -1
  71. package/eslint.config.mjs +47 -0
  72. package/index.ts +42 -48
  73. package/package.json +30 -2
  74. package/src/agents/app.ts +27 -0
  75. package/src/agents/backend.ts +24 -0
  76. package/src/agents/devops.ts +33 -0
  77. package/src/agents/frontend.ts +24 -0
  78. package/src/agents/fullstack.ts +24 -0
  79. package/src/agents/index.ts +71 -0
  80. package/src/agents/quality.ts +69 -0
  81. package/src/agents/security.ts +26 -0
  82. package/src/agents/types.ts +17 -0
  83. package/src/client.ts +125 -167
  84. package/src/commands/api-keys.ts +261 -358
  85. package/src/commands/auth.ts +24 -30
  86. package/src/commands/autocomplete.ts +12 -12
  87. package/src/commands/billing.ts +22 -27
  88. package/src/commands/chat.ts +230 -323
  89. package/src/commands/clusters.ts +33 -33
  90. package/src/commands/code/__tests__/auth-sync.test.ts +481 -0
  91. package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
  92. package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
  93. package/src/commands/code/__tests__/fake-command-runner.ts +44 -0
  94. package/src/commands/code/__tests__/fake-file-store.ts +44 -0
  95. package/src/commands/code/__tests__/fake-prompter.ts +121 -0
  96. package/src/commands/code/__tests__/setup-flow.test.ts +628 -0
  97. package/src/commands/code/adapters/clack-prompter.ts +55 -0
  98. package/src/commands/code/adapters/fs-file-store.ts +37 -0
  99. package/src/commands/code/adapters/spawn-command-runner.ts +40 -0
  100. package/src/commands/code/auth-sync.ts +329 -0
  101. package/src/commands/code/errors.ts +23 -0
  102. package/src/commands/code/ports/auth-services.ts +14 -0
  103. package/src/commands/code/ports/command-runner.ts +10 -0
  104. package/src/commands/code/ports/file-store.ts +7 -0
  105. package/src/commands/code/ports/prompter.ts +29 -0
  106. package/src/commands/code/setup.ts +630 -0
  107. package/src/commands/code.ts +335 -1074
  108. package/src/commands/index.ts +19 -19
  109. package/src/commands/models.ts +32 -37
  110. package/src/commands/users.ts +15 -22
  111. package/src/constants/command-structure.ts +120 -140
  112. package/src/services/api-key-service.ts +96 -113
  113. package/src/services/auth-service.ts +92 -339
  114. package/src/services/browser-auth.ts +296 -0
  115. package/src/services/chat-service.ts +246 -279
  116. package/src/services/cluster-service.ts +29 -32
  117. package/src/services/collaborator-service.ts +13 -18
  118. package/src/services/flux-service.ts +16 -18
  119. package/src/services/helm-service.ts +16 -18
  120. package/src/services/kubectl-service.ts +12 -14
  121. package/src/types/api.d.ts +924 -926
  122. package/src/types/json.d.ts +3 -3
  123. package/src/utils/config-checker.ts +10 -10
  124. package/src/utils/config-loader.ts +110 -127
  125. package/src/utils/default-api-key.ts +81 -93
  126. package/src/utils/env-manager.ts +36 -40
  127. package/src/utils/error-handler.ts +83 -78
  128. package/src/utils/logger.ts +41 -41
  129. package/src/utils/markdown-renderer.ts +11 -11
  130. package/src/utils/opencode-validator.ts +51 -56
  131. package/src/utils/token-manager.ts +84 -64
  132. package/templates/agents/app.md +23 -0
  133. package/templates/agents/backend.md +23 -0
  134. package/templates/agents/devops.md +30 -0
  135. package/templates/agents/frontend.md +25 -0
  136. package/templates/agents/fullstack.md +23 -0
  137. package/templates/agents/quality.md +69 -0
  138. package/templates/agents/security.md +21 -0
  139. package/tests/commands/chat.test.ts +60 -70
  140. package/tests/commands/code.test.ts +346 -345
  141. package/tests/utils/config-loader.test.ts +260 -260
  142. package/tests/utils/env-manager.test.ts +127 -134
  143. package/tests/utils/opencode-validator.test.ts +65 -69
  144. package/tsconfig.json +2 -2
  145. package/vitest.config.ts +3 -3
  146. package/AGENTS.md +0 -374
  147. package/TODO.md +0 -19
  148. package/opencode.json +0 -146
@@ -40,134 +40,22 @@ 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
42
  const error_handler_1 = require("../utils/error-handler");
43
+ const setup_1 = require("./code/setup");
43
44
  const fs = __importStar(require("fs"));
44
45
  const promises_1 = require("fs/promises");
45
46
  const path_1 = __importDefault(require("path"));
46
47
  const child_process_1 = require("child_process");
47
- const client_1 = require("../client");
48
48
  /**
49
49
  * Check if current directory has git
50
50
  */
51
51
  function hasGit() {
52
52
  try {
53
- return fs.existsSync(path_1.default.join(process.cwd(), '.git'));
53
+ return fs.existsSync(path_1.default.join(process.cwd(), ".git"));
54
54
  }
55
55
  catch (_a) {
56
56
  return false;
57
57
  }
58
58
  }
59
- /**
60
- * Merge opencode configurations using chat completions API
61
- */
62
- function mergeConfigurations(currentConfig, latestConfig) {
63
- var _a, _b, _c, _d;
64
- return __awaiter(this, void 0, void 0, function* () {
65
- try {
66
- const client = (0, client_1.createAuthenticatedClient)();
67
- console.log(chalk_1.default.blue('🤖 Using AI to merge configurations...'));
68
- const mergePrompt = `You are a configuration merge specialist. Merge these two OpenCode configurations:
69
-
70
- CURRENT CONFIG (user's customizations):
71
- ${JSON.stringify(currentConfig, null, 2)}
72
-
73
- LATEST CONFIG (new updates):
74
- ${JSON.stringify(latestConfig, null, 2)}
75
-
76
- Merge rules:
77
- 1. Preserve ALL user customizations from current config
78
- 2. Add ALL new features and improvements from latest config
79
- 3. For conflicts, prefer user's customizations but add new latest features
80
- 4. Maintain valid JSON structure
81
- 5. Keep the merged configuration complete and functional
82
-
83
- Return ONLY the merged JSON configuration, no explanations.`;
84
- const response = yield client.POST('/v1/chat/completions', {
85
- body: {
86
- model: 'glm-4.7',
87
- messages: [
88
- {
89
- role: 'user',
90
- content: mergePrompt,
91
- },
92
- ],
93
- temperature: 0.1,
94
- max_tokens: 8000,
95
- },
96
- });
97
- if (response.error) {
98
- console.warn(chalk_1.default.yellow('⚠️ AI merge failed, using fallback merge'));
99
- return fallbackMerge(currentConfig, latestConfig);
100
- }
101
- const content = (_d = (_c = (_b = (_a = response.data) === null || _a === void 0 ? void 0 : _a.choices) === null || _b === void 0 ? void 0 : _b[0]) === null || _c === void 0 ? void 0 : _c.message) === null || _d === void 0 ? void 0 : _d.content;
102
- if (!content) {
103
- console.warn(chalk_1.default.yellow('⚠️ No AI response, using fallback merge'));
104
- return fallbackMerge(currentConfig, latestConfig);
105
- }
106
- try {
107
- const mergedConfig = JSON.parse(content.trim());
108
- console.log(chalk_1.default.green('✓ AI merge completed successfully'));
109
- return mergedConfig;
110
- }
111
- catch (parseError) {
112
- console.warn(chalk_1.default.yellow('⚠️ AI response invalid, using fallback merge'));
113
- return fallbackMerge(currentConfig, latestConfig);
114
- }
115
- }
116
- catch (error) {
117
- console.warn(chalk_1.default.yellow('⚠️ AI merge unavailable, using fallback merge'));
118
- return fallbackMerge(currentConfig, latestConfig);
119
- }
120
- });
121
- }
122
- /**
123
- * Fallback merge logic when AI merge is unavailable
124
- */
125
- function fallbackMerge(currentConfig, latestConfig) {
126
- console.log(chalk_1.default.blue('🔀 Using fallback merge logic...'));
127
- const merged = Object.assign({}, latestConfig);
128
- // Preserve user customizations
129
- if (currentConfig.theme && currentConfig.theme !== latestConfig.theme) {
130
- merged.theme = currentConfig.theme;
131
- }
132
- if (currentConfig.share && currentConfig.share !== latestConfig.share) {
133
- merged.share = currentConfig.share;
134
- }
135
- // Merge custom agents while preserving new ones
136
- if (currentConfig.agent) {
137
- merged.agent = Object.assign({}, latestConfig.agent);
138
- // Add any custom agents from current config
139
- Object.keys(currentConfig.agent).forEach((agentName) => {
140
- if (!latestConfig.agent[agentName]) {
141
- merged.agent[agentName] = currentConfig.agent[agentName];
142
- console.log(chalk_1.default.cyan(` • Preserved custom agent: ${agentName}`));
143
- }
144
- });
145
- }
146
- // Merge custom commands while preserving new ones
147
- if (currentConfig.commands) {
148
- merged.commands = Object.assign({}, latestConfig.commands);
149
- Object.keys(currentConfig.commands).forEach((commandName) => {
150
- if (!latestConfig.commands[commandName]) {
151
- merged.commands[commandName] = currentConfig.commands[commandName];
152
- console.log(chalk_1.default.cyan(` • Preserved custom command: ${commandName}`));
153
- }
154
- });
155
- }
156
- // Preserve custom provider settings if user has modified them
157
- if (currentConfig.provider) {
158
- merged.provider = Object.assign({}, latestConfig.provider);
159
- // Deep merge provider settings
160
- Object.keys(currentConfig.provider).forEach((providerName) => {
161
- if (merged.provider[providerName]) {
162
- merged.provider[providerName] = Object.assign(Object.assign({}, merged.provider[providerName]), currentConfig.provider[providerName]);
163
- }
164
- else {
165
- merged.provider[providerName] = currentConfig.provider[providerName];
166
- }
167
- });
168
- }
169
- return merged;
170
- }
171
59
  /**
172
60
  * Helper function to get user confirmation
173
61
  */
@@ -176,70 +64,14 @@ function confirm(question, autoYes = false) {
176
64
  if (autoYes) {
177
65
  return true;
178
66
  }
179
- return new Promise((resolve) => {
180
- const rl = readline_1.default.createInterface({
181
- input: process.stdin,
182
- output: process.stdout,
183
- });
184
- rl.question(question, (answer) => {
185
- rl.close();
186
- resolve(answer.toLowerCase() === 'y' ||
187
- answer.toLowerCase() === 'yes' ||
188
- answer === '');
189
- });
190
- });
191
- });
192
- }
193
- /**
194
- * Helper function to get user choice from options
195
- */
196
- function askChoice(question, options, defaultChoice) {
197
- return __awaiter(this, void 0, void 0, function* () {
198
- return new Promise((resolve) => {
67
+ return new Promise(resolve => {
199
68
  const rl = readline_1.default.createInterface({
200
69
  input: process.stdin,
201
70
  output: process.stdout,
202
71
  });
203
- rl.question(question, (answer) => {
72
+ rl.question(question, answer => {
204
73
  rl.close();
205
- const trimmed = answer.trim().toLowerCase();
206
- // Handle numeric input (1, 2, etc.)
207
- const numericIndex = parseInt(trimmed) - 1;
208
- if (numericIndex >= 0 && numericIndex < options.length) {
209
- resolve(options[numericIndex]);
210
- return;
211
- }
212
- // Handle text input
213
- const matchingOption = options.find((option) => option.toLowerCase().startsWith(trimmed));
214
- if (matchingOption) {
215
- resolve(matchingOption);
216
- }
217
- else if (defaultChoice) {
218
- resolve(defaultChoice);
219
- }
220
- else {
221
- resolve(options[0]); // Default to first option
222
- }
223
- });
224
- });
225
- });
226
- }
227
- /**
228
- * Helper function to get user input
229
- */
230
- function getInput(question, defaultValue, autoYes = false) {
231
- return __awaiter(this, void 0, void 0, function* () {
232
- if (autoYes) {
233
- return defaultValue;
234
- }
235
- const rl = readline_1.default.createInterface({
236
- input: process.stdin,
237
- output: process.stdout,
238
- });
239
- return new Promise((resolve) => {
240
- rl.question(question, (answer) => {
241
- rl.close();
242
- resolve(answer.trim() || defaultValue);
74
+ resolve(answer.toLowerCase() === "y" || answer.toLowerCase() === "yes" || answer === "");
243
75
  });
244
76
  });
245
77
  });
@@ -249,98 +81,36 @@ function getInput(question, defaultValue, autoYes = false) {
249
81
  */
250
82
  function getProjectName() {
251
83
  try {
252
- const packageJsonPath = path_1.default.join(process.cwd(), 'package.json');
84
+ const packageJsonPath = path_1.default.join(process.cwd(), "package.json");
253
85
  if (fs.existsSync(packageJsonPath)) {
254
- const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
86
+ const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8");
255
87
  const packageJson = JSON.parse(packageJsonContent);
256
88
  return packageJson.name || path_1.default.basename(process.cwd());
257
89
  }
258
90
  }
259
- catch (error) {
91
+ catch (_a) {
260
92
  // Ignore error and fallback to directory name
261
93
  }
262
94
  return path_1.default.basename(process.cwd());
263
95
  }
264
96
  /**
265
- * Load the latest agent configuration from embedded config
97
+ * Get the path to the bundled agent templates directory
266
98
  */
267
- function loadLatestAgentConfig() {
268
- return __awaiter(this, void 0, void 0, function* () {
269
- // Return the latest agent configuration directly - no file reading needed
270
- return {
271
- fullstack: {
272
- temperature: 0.3,
273
- top_p: 0.9,
274
- mode: 'primary',
275
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
276
- description: 'Router/coordinator agent for full-stack development with schema-driven architecture',
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.",
278
- },
279
- frontend: {
280
- temperature: 0.4,
281
- top_p: 0.9,
282
- mode: 'primary',
283
- permission: { edit: 'allow', bash: 'deny', webfetch: 'allow' },
284
- note: 'Bash access is denied for frontend persona to prevent shell command execution in UI environments. This restriction enforces security and architectural boundaries.',
285
- description: 'Builds Scandinavian, type-safe UIs with React, Tailwind, Shadcn.',
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.',
287
- },
288
- backend: {
289
- temperature: 0.3,
290
- top_p: 0.9,
291
- mode: 'primary',
292
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
293
- description: 'Functional, modular Koa + TypeScript services; schema-first with code quality focus.',
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.",
295
- },
296
- devops: {
297
- temperature: 0.3,
298
- top_p: 0.8,
299
- mode: 'primary',
300
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
301
- description: 'Declarative GitOps infra with FluxCD, Kustomize, Helm, operators.',
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.",
303
- },
304
- app: {
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 app persona to prevent shell command execution in mobile/Expo environments. This restriction enforces security and architectural boundaries.',
310
- description: 'Expo + React Native apps; props-first, offline-aware, shared tokens.',
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.",
312
- },
313
- security: {
314
- temperature: 0.2,
315
- top_p: 0.8,
316
- mode: 'subagent',
317
- permission: { edit: 'deny', bash: 'allow', webfetch: 'allow' },
318
- description: 'Security specialist for pentesting, OWASP compliance, and vulnerability assessments.',
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",
320
- },
321
- quality: {
322
- temperature: 0.1,
323
- top_p: 0.9,
324
- mode: 'subagent',
325
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
326
- description: 'Quality assurance specialist for testing, building, and PR management.',
327
- prompt: "Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Quality agent. Specialist in code quality assurance, testing, building, and pull request management.\\n\\nCore responsibilities:\\n - Run comprehensive test suites (npm test, npm run test, jest, vitest)\\n - Execute build processes (npm run build, webpack, vite, tsc)\\n - Create and manage pull requests with proper descriptions\\n - Monitor GitHub for Copilot/reviewer comments\\n - Ensure code quality standards are met\\n - Validate linting and formatting (npm run lint, prettier)\\n - Check test coverage and performance benchmarks\\n - Handle CI/CD pipeline validation\\n\\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.",
328
- },
329
- };
330
- });
99
+ function getAgentTemplatesDir() {
100
+ return path_1.default.resolve(__dirname, "../../templates/agents");
331
101
  }
332
102
  /**
333
103
  * Check if opencode is installed
334
104
  */
335
105
  function checkOpencodeInstalled() {
336
- return new Promise((resolve) => {
337
- const child = (0, child_process_1.spawn)('which', ['opencode'], {
338
- stdio: 'pipe',
106
+ return new Promise(resolve => {
107
+ const child = (0, child_process_1.spawn)("which", ["opencode"], {
108
+ stdio: "pipe",
339
109
  });
340
- child.on('close', (code) => {
110
+ child.on("close", code => {
341
111
  resolve(code === 0);
342
112
  });
343
- child.on('error', () => {
113
+ child.on("error", () => {
344
114
  resolve(false);
345
115
  });
346
116
  });
@@ -350,38 +120,38 @@ function checkOpencodeInstalled() {
350
120
  */
351
121
  function installOpencode() {
352
122
  return __awaiter(this, void 0, void 0, function* () {
353
- console.log(chalk_1.default.cyan('Installing OpenCode via npm...'));
123
+ console.log(chalk_1.default.cyan("Installing OpenCode via npm..."));
354
124
  try {
355
125
  yield new Promise((resolve, reject) => {
356
- const install = (0, child_process_1.spawn)('npm', ['install', '-g', 'opencode-ai@1.3'], {
357
- stdio: 'inherit',
126
+ const install = (0, child_process_1.spawn)("npm", ["install", "-g", "opencode-ai@1.3"], {
127
+ stdio: "inherit",
358
128
  });
359
- install.on('close', (code) => {
129
+ install.on("close", code => {
360
130
  if (code === 0) {
361
- console.log(chalk_1.default.green('✓ OpenCode installed successfully!'));
131
+ console.log(chalk_1.default.green("✓ OpenCode installed successfully!"));
362
132
  resolve();
363
133
  }
364
134
  else {
365
135
  reject(new Error(`Installation failed with code ${code}`));
366
136
  }
367
137
  });
368
- install.on('error', reject);
138
+ install.on("error", reject);
369
139
  });
370
140
  // Verify installation
371
141
  const opencodeInstalled = yield checkOpencodeInstalled();
372
142
  if (!opencodeInstalled) {
373
- console.log(chalk_1.default.yellow('Installation completed but opencode command not found.'));
374
- console.log(chalk_1.default.yellow('You may need to restart your terminal or check your PATH.'));
143
+ console.log(chalk_1.default.yellow("Installation completed but opencode command not found."));
144
+ console.log(chalk_1.default.yellow("You may need to restart your terminal or check your PATH."));
375
145
  return false;
376
146
  }
377
147
  return true;
378
148
  }
379
149
  catch (error) {
380
- console.error(chalk_1.default.red('Failed to install OpenCode:'));
150
+ console.error(chalk_1.default.red("Failed to install OpenCode:"));
381
151
  console.error(error instanceof Error ? error.message : String(error));
382
- console.log(chalk_1.default.blue('\nAlternative installation methods:'));
383
- console.log(chalk_1.default.blue(' curl -fsSL https://opencode.ai/install | sh'));
384
- console.log(chalk_1.default.blue(' Or visit: https://opencode.ai/docs'));
152
+ console.log(chalk_1.default.blue("\nAlternative installation methods:"));
153
+ console.log(chalk_1.default.blue(" curl -fsSL https://opencode.ai/install | sh"));
154
+ console.log(chalk_1.default.blue(" Or visit: https://opencode.ai/docs"));
385
155
  return false;
386
156
  }
387
157
  });
@@ -394,17 +164,17 @@ function ensureOpencodeInstalled(autoYes = false) {
394
164
  let opencodeInstalled = yield checkOpencodeInstalled();
395
165
  if (!opencodeInstalled) {
396
166
  if (!autoYes) {
397
- console.log(chalk_1.default.red('OpenCode is not installed.'));
398
- console.log(chalk_1.default.blue('OpenCode is required for the AI coding assistant.'));
167
+ console.log(chalk_1.default.red("OpenCode is not installed."));
168
+ console.log(chalk_1.default.blue("OpenCode is required for the AI coding assistant."));
399
169
  }
400
- if (yield confirm('Would you like to install OpenCode automatically? (Y/n): ', autoYes)) {
170
+ if (yield confirm("Would you like to install OpenCode automatically? (Y/n): ", autoYes)) {
401
171
  opencodeInstalled = yield installOpencode();
402
172
  }
403
173
  else {
404
174
  if (!autoYes) {
405
- console.log(chalk_1.default.blue('\nInstallation cancelled.'));
406
- console.log(chalk_1.default.blue('To install manually: curl -fsSL https://opencode.ai/install | bash'));
407
- console.log(chalk_1.default.blue('Or visit: https://opencode.ai/docs'));
175
+ console.log(chalk_1.default.blue("\nInstallation cancelled."));
176
+ console.log(chalk_1.default.blue("To install manually: curl -fsSL https://opencode.ai/install | bash"));
177
+ console.log(chalk_1.default.blue("Or visit: https://opencode.ai/docs"));
408
178
  }
409
179
  }
410
180
  }
@@ -417,24 +187,37 @@ function ensureOpencodeInstalled(autoYes = false) {
417
187
  function registerCodeCommands(program) {
418
188
  const code = program
419
189
  .command(command_structure_1.COMMAND_GROUPS.CODE)
420
- .description('AI-powered coding assistant with OpenCode');
190
+ .description("AI-powered coding assistant with OpenCode");
191
+ if (process.env.BERGET_EXPERIMENTAL) {
192
+ code
193
+ .command("setup")
194
+ .description("Interactive setup for Berget AI coding tools")
195
+ .action(() => __awaiter(this, void 0, void 0, function* () {
196
+ try {
197
+ yield (0, setup_1.runSetupCommand)();
198
+ }
199
+ catch (error) {
200
+ (0, error_handler_1.handleError)("Setup failed", error);
201
+ }
202
+ }));
203
+ }
421
204
  code
422
205
  .command(command_structure_1.SUBCOMMANDS.CODE.INIT)
423
- .description('Initialize project for AI coding assistant')
424
- .option('-n, --name <name>', 'Project name (defaults to directory name)')
425
- .option('-f, --force', 'Overwrite existing configuration')
426
- .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
206
+ .description("Initialize project for AI coding assistant")
207
+ .option("-n, --name <name>", "Project name (defaults to directory name)")
208
+ .option("-f, --force", "Overwrite existing configuration")
209
+ .option("-y, --yes", "Automatically answer yes to all prompts (for automation)")
427
210
  .action((options) => __awaiter(this, void 0, void 0, function* () {
428
211
  try {
429
212
  const projectName = options.name || getProjectName();
430
- const configPath = path_1.default.join(process.cwd(), 'opencode.json');
213
+ const configPath = path_1.default.join(process.cwd(), "opencode.json");
431
214
  // Check if already initialized
432
215
  if (fs.existsSync(configPath) && !options.force) {
433
216
  if (!options.yes) {
434
- console.log(chalk_1.default.yellow('Project already initialized for OpenCode.'));
217
+ console.log(chalk_1.default.yellow("Project already initialized for OpenCode."));
435
218
  console.log(chalk_1.default.dim(`Config file: ${configPath}`));
436
219
  }
437
- if (yield confirm('Do you want to reinitialize? (Y/n): ', options.yes)) {
220
+ if (yield confirm("Do you want to reinitialize? (Y/n): ", options.yes)) {
438
221
  // Continue with reinitialization
439
222
  }
440
223
  else {
@@ -446,167 +229,65 @@ function registerCodeCommands(program) {
446
229
  return;
447
230
  }
448
231
  console.log(chalk_1.default.cyan(`Initializing OpenCode for project: ${projectName}`));
449
- // Load latest agent configuration from our own codebase
450
- const latestAgentConfig = yield loadLatestAgentConfig();
451
- // Use hardcoded defaults for init - never try to load from project
452
- // Create opencode.json config — plugin handles auth, models, and provider
453
232
  const config = {
454
- $schema: 'https://opencode.ai/config.json',
455
- plugin: ['@bergetai/opencode-auth@1.0.16'],
456
- username: 'berget-code',
457
- theme: 'berget-dark',
458
- share: 'manual',
459
- autoupdate: true,
460
- agent: {
461
- fullstack: {
462
- temperature: 0.3,
463
- top_p: 0.9,
464
- mode: 'primary',
465
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
466
- description: 'Router/coordinator agent for full-stack development with schema-driven architecture',
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.',
468
- },
469
- frontend: {
470
- temperature: 0.4,
471
- top_p: 0.9,
472
- mode: 'primary',
473
- permission: { edit: 'allow', bash: 'deny', webfetch: 'allow' },
474
- note: 'Bash access is denied for frontend persona to prevent shell command execution in UI environments. This restriction enforces security and architectural boundaries.',
475
- description: 'Builds Scandinavian, type-safe UIs with React, Tailwind, Shadcn.',
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.',
477
- },
478
- backend: {
479
- temperature: 0.3,
480
- top_p: 0.9,
481
- mode: 'primary',
482
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
483
- description: 'Functional, modular Koa + TypeScript services; schema-first with code quality focus.',
484
- prompt: 'You are Berget Code Backend agent. Voice: Scandinavian calm—precise, concise, confident. TypeScript + Koa. Prefer many small pure functions; avoid big try/catch blocks. Routes thin; logic in services/adapters/domain. Validate with Zod; auto-generate OpenAPI. Adapters isolate external systems; domain never depends on framework. Test with supertest; idempotent and stateless by default. Each microservice emits an OpenAPI contract; changes propagate upward to types. Code Quality & Refactoring Principles: Apply Single Responsibility Principle, fail fast with explicit errors, eliminate code duplication, remove nested complexity, use descriptive error codes, keep functions under 30 lines. Always leave code cleaner and more readable than you found it. CRITICAL: When all backend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
485
- },
486
- // Use centralized devops configuration with Helm guidelines
487
- devops: latestAgentConfig.devops || {
488
- temperature: 0.3,
489
- top_p: 0.8,
490
- mode: 'primary',
491
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
492
- description: 'Declarative GitOps infra with FluxCD, Kustomize, Helm, operators.',
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.',
494
- },
495
- app: {
496
- temperature: 0.4,
497
- top_p: 0.9,
498
- mode: 'primary',
499
- permission: { edit: 'allow', bash: 'deny', webfetch: 'allow' },
500
- note: 'Bash access is denied for app persona to prevent shell command execution in mobile/Expo environments. This restriction enforces security and architectural boundaries.',
501
- description: 'Expo + React Native apps; props-first, offline-aware, shared tokens.',
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.',
503
- },
504
- security: {
505
- temperature: 0.2,
506
- top_p: 0.8,
507
- mode: 'subagent',
508
- permission: { edit: 'deny', bash: 'allow', webfetch: 'allow' },
509
- description: 'Security specialist for pentesting, OWASP compliance, and vulnerability assessments.',
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.',
511
- },
512
- quality: {
513
- temperature: 0.1,
514
- top_p: 0.9,
515
- mode: 'subagent',
516
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
517
- description: 'Quality assurance specialist for testing, building, and PR management.',
518
- prompt: 'Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Quality agent. Specialist in code quality assurance, testing, building, and pull request management.\n\nCore responsibilities:\n - Run comprehensive test suites (npm test, npm run test, jest, vitest)\n - Execute build processes (npm run build, webpack, vite, tsc)\n - Create and manage pull requests with proper descriptions\n - Monitor GitHub for Copilot/reviewer comments\n - Ensure code quality standards are met\n - Validate linting and formatting (npm run lint, prettier)\n - Check test coverage and performance benchmarks\n - Handle CI/CD pipeline validation\n\nCommon CLI commands:\n - npm test or npm run test (run test suite)\n - npm run build (build project)\n - npm run lint (run linting)\n - npm run format (format code)\n - npm run test:coverage (check coverage)\n - gh pr create (create pull request)\n - gh pr view --comments (check PR comments)\n - git add . && git commit -m "message" && git push (commit and push)\n\nPR Workflow:\n 1. Ensure all tests pass: npm test\n 2. Build successfully: npm run build\n 3. Create/update PR with clear description\n 4. Monitor for reviewer comments\n 5. Address feedback promptly\n 6. Update PR with fixes\n 7. Ensure CI checks pass\n\nAlways provide specific command examples and wait for processes to complete before proceeding.',
519
- },
520
- },
521
- command: {
522
- fullstack: {
523
- description: 'Switch to Fullstack (router)',
524
- template: '{{input}}',
525
- agent: 'fullstack',
526
- },
527
- route: {
528
- description: 'Let Fullstack auto-route to the right persona based on files/intent',
529
- template: 'ROUTE {{input}}',
530
- agent: 'fullstack',
531
- subtask: true,
532
- },
533
- frontend: {
534
- description: 'Switch to Frontend persona',
535
- template: '{{input}}',
536
- agent: 'frontend',
537
- },
538
- backend: {
539
- description: 'Switch to Backend persona',
540
- template: '{{input}}',
541
- agent: 'backend',
542
- },
543
- devops: {
544
- description: 'Switch to DevOps persona',
545
- template: '{{input}}',
546
- agent: 'devops',
547
- },
548
- app: {
549
- description: 'Switch to App persona',
550
- template: '{{input}}',
551
- agent: 'app',
552
- },
553
- quality: {
554
- description: 'Switch to Quality agent for testing, building, and PR management',
555
- template: '{{input}}',
556
- agent: 'quality',
557
- },
558
- },
559
- watcher: {
560
- ignore: ['node_modules', 'dist', '.git', 'coverage'],
561
- },
233
+ $schema: "https://opencode.ai/config.json",
234
+ plugin: ["@bergetai/opencode-auth@1.0.16"],
562
235
  };
563
- // Ask for permission to create config files
236
+ const agentsDir = path_1.default.join(process.cwd(), ".opencode", "agents");
237
+ const templatesDir = getAgentTemplatesDir();
564
238
  if (!options.yes) {
565
- console.log(chalk_1.default.blue('\nAbout to create configuration files:'));
239
+ console.log(chalk_1.default.blue("\nAbout to create configuration files:"));
566
240
  console.log(chalk_1.default.dim(`Config: ${configPath}`));
567
- console.log(chalk_1.default.dim('This will configure OpenCode with the Berget auth plugin.'));
241
+ console.log(chalk_1.default.dim(`Agents: ${agentsDir}/`));
242
+ console.log(chalk_1.default.dim("This will configure OpenCode with the Berget auth plugin."));
568
243
  }
569
- if (yield confirm('\nCreate configuration files? (Y/n): ', options.yes)) {
244
+ if (yield confirm("\nCreate configuration files? (Y/n): ", options.yes)) {
570
245
  try {
571
- // Create opencode.json
572
246
  yield (0, promises_1.writeFile)(configPath, JSON.stringify(config, null, 2));
573
- console.log(chalk_1.default.green(`✓ Created opencode.json`));
574
- console.log(chalk_1.default.dim(` Plugin: @bergetai/opencode-auth`));
575
- console.log(chalk_1.default.dim(` Theme: ${config.theme}`));
247
+ console.log(chalk_1.default.green("✓ Created opencode.json"));
248
+ console.log(chalk_1.default.dim(" Plugin: @bergetai/opencode-auth"));
249
+ fs.mkdirSync(agentsDir, { recursive: true });
250
+ const templateFiles = fs.readdirSync(templatesDir).filter(f => f.endsWith(".md"));
251
+ for (const file of templateFiles) {
252
+ const src = path_1.default.join(templatesDir, file);
253
+ const dest = path_1.default.join(agentsDir, file);
254
+ fs.copyFileSync(src, dest);
255
+ }
256
+ console.log(chalk_1.default.green(`✓ Created ${templateFiles.length} agent definitions in .opencode/agents/`));
576
257
  }
577
258
  catch (error) {
578
- console.error(chalk_1.default.red('Failed to create config files:'));
579
- (0, error_handler_1.handleError)('Config file creation failed', error);
259
+ console.error(chalk_1.default.red("Failed to create config files:"));
260
+ (0, error_handler_1.handleError)("Config file creation failed", error);
580
261
  return;
581
262
  }
582
263
  }
583
264
  else {
584
- console.log(chalk_1.default.yellow('Configuration file creation cancelled.'));
265
+ console.log(chalk_1.default.yellow("Configuration file creation cancelled."));
585
266
  return;
586
267
  }
587
- console.log(chalk_1.default.green('\n✅ Project initialized successfully!'));
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:'));
268
+ console.log(chalk_1.default.green("\n✅ Project initialized successfully!"));
269
+ console.log(chalk_1.default.blue("\nNext steps:"));
270
+ console.log(chalk_1.default.cyan(" 1. Run: opencode"));
271
+ console.log(chalk_1.default.cyan(" 2. Type: /connect"));
272
+ console.log(chalk_1.default.cyan(" 3. Choose your auth method:"));
592
273
  console.log(chalk_1.default.dim(' • "Login with Berget" — Berget Code team members (SSO)'));
593
274
  console.log(chalk_1.default.dim(' • "Enter API Key" — API key users (console.berget.ai)'));
594
275
  }
595
276
  catch (error) {
596
- (0, error_handler_1.handleError)('Failed to initialize project', error);
277
+ (0, error_handler_1.handleError)("Failed to initialize project", error);
597
278
  }
598
279
  }));
599
280
  code
600
281
  .command(command_structure_1.SUBCOMMANDS.CODE.RUN)
601
- .description('Run AI coding assistant')
602
- .argument('[prompt]', 'Prompt to send directly to OpenCode')
603
- .option('-m, --model <model>', 'Model to use (overrides config)')
604
- .option('-a, --analysis', 'Use fast analysis model for context building')
605
- .option('--no-config', 'Run without loading project config')
606
- .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
282
+ .description("Run AI coding assistant")
283
+ .argument("[prompt]", "Prompt to send directly to OpenCode")
284
+ .option("-m, --model <model>", "Model to use (overrides config)")
285
+ .option("-a, --analysis", "Use fast analysis model for context building")
286
+ .option("--no-config", "Run without loading project config")
287
+ .option("-y, --yes", "Automatically answer yes to all prompts (for automation)")
607
288
  .action((prompt, options) => __awaiter(this, void 0, void 0, function* () {
608
289
  try {
609
- const configPath = path_1.default.join(process.cwd(), 'opencode.json');
290
+ const configPath = path_1.default.join(process.cwd(), "opencode.json");
610
291
  // Ensure opencode is installed
611
292
  if (!(yield ensureOpencodeInstalled(options.yes))) {
612
293
  return;
@@ -614,17 +295,17 @@ function registerCodeCommands(program) {
614
295
  let config = null;
615
296
  if (!options.noConfig && fs.existsSync(configPath)) {
616
297
  try {
617
- const configContent = yield (0, promises_1.readFile)(configPath, 'utf8');
298
+ const configContent = yield (0, promises_1.readFile)(configPath, "utf8");
618
299
  config = JSON.parse(configContent);
619
300
  console.log(chalk_1.default.dim(`Loaded config for project: ${config.projectName}`));
620
301
  console.log(chalk_1.default.dim(`Models: Analysis=${config.analysisModel}, Build=${config.buildModel}`));
621
302
  }
622
- catch (error) {
623
- console.log(chalk_1.default.yellow('Warning: Failed to load opencode.json'));
303
+ catch (_a) {
304
+ console.log(chalk_1.default.yellow("Warning: Failed to load opencode.json"));
624
305
  }
625
306
  }
626
307
  if (!config) {
627
- console.log(chalk_1.default.yellow('No project configuration found.'));
308
+ console.log(chalk_1.default.yellow("No project configuration found."));
628
309
  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.`));
629
310
  return;
630
311
  }
@@ -633,20 +314,20 @@ function registerCodeCommands(program) {
633
314
  const opencodeArgs = [];
634
315
  // Read --stage and --local from root program options
635
316
  // (these flags are registered at program level, not subcommand level)
636
- const isStage = process.argv.includes('--stage');
637
- const isLocal = process.argv.includes('--local');
317
+ const isStage = process.argv.includes("--stage");
318
+ const isLocal = process.argv.includes("--local");
638
319
  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';
320
+ console.log(chalk_1.default.cyan("Using Berget stage environment"));
321
+ env.BERGET_API_URL = "https://api.stage.berget.ai";
322
+ env.BERGET_INFERENCE_URL = "https://api.stage.berget.ai/v1";
642
323
  }
643
324
  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';
325
+ console.log(chalk_1.default.cyan("Using local development environment"));
326
+ env.BERGET_API_URL = "http://localhost:3000";
327
+ env.BERGET_INFERENCE_URL = "http://localhost:3000/v1";
647
328
  }
648
329
  if (prompt) {
649
- opencodeArgs.push('run', prompt);
330
+ opencodeArgs.push("run", prompt);
650
331
  }
651
332
  // Choose model based on analysis flag or override
652
333
  let selectedModel = options.model || config.buildModel;
@@ -654,491 +335,254 @@ function registerCodeCommands(program) {
654
335
  selectedModel = config.analysisModel;
655
336
  }
656
337
  if (selectedModel) {
657
- opencodeArgs.push('--model', selectedModel);
338
+ opencodeArgs.push("--model", selectedModel);
658
339
  }
659
- console.log(chalk_1.default.cyan('Starting OpenCode...'));
340
+ console.log(chalk_1.default.cyan("Starting OpenCode..."));
660
341
  // Spawn opencode process
661
- const opencode = (0, child_process_1.spawn)('opencode', opencodeArgs, {
662
- stdio: 'inherit',
342
+ const opencode = (0, child_process_1.spawn)("opencode", opencodeArgs, {
343
+ stdio: "inherit",
663
344
  env: env,
664
345
  });
665
- opencode.on('close', (code) => {
346
+ opencode.on("close", code => {
666
347
  if (code !== 0) {
667
348
  console.log(chalk_1.default.red(`OpenCode exited with code ${code}`));
668
349
  }
669
350
  });
670
- opencode.on('error', (error) => {
671
- console.error(chalk_1.default.red('Failed to start OpenCode:'));
351
+ opencode.on("error", error => {
352
+ console.error(chalk_1.default.red("Failed to start OpenCode:"));
672
353
  console.error(error.message);
673
354
  });
674
355
  }
675
356
  catch (error) {
676
- (0, error_handler_1.handleError)('Failed to run OpenCode', error);
357
+ (0, error_handler_1.handleError)("Failed to run OpenCode", error);
677
358
  }
678
359
  }));
679
360
  code
680
361
  .command(command_structure_1.SUBCOMMANDS.CODE.SERVE)
681
- .description('Start OpenCode web server')
682
- .option('-p, --port <port>', 'Port to run the server on (default: 3000)')
683
- .option('-h, --host <host>', 'Host to bind the server to (default: localhost)')
684
- .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
362
+ .description("Start OpenCode web server")
363
+ .option("-p, --port <port>", "Port to run the server on (default: 3000)")
364
+ .option("-h, --host <host>", "Host to bind the server to (default: localhost)")
365
+ .option("-y, --yes", "Automatically answer yes to all prompts (for automation)")
685
366
  .action((options) => __awaiter(this, void 0, void 0, function* () {
686
367
  try {
687
368
  // Ensure opencode is installed
688
369
  if (!(yield ensureOpencodeInstalled(options.yes))) {
689
370
  return;
690
371
  }
691
- console.log(chalk_1.default.cyan('🚀 Starting OpenCode web server...'));
372
+ console.log(chalk_1.default.cyan("🚀 Starting OpenCode web server..."));
692
373
  // Prepare opencode serve command
693
- const serveArgs = ['serve'];
374
+ const serveArgs = ["serve"];
694
375
  if (options.port) {
695
- serveArgs.push('--port', options.port);
376
+ serveArgs.push("--port", options.port);
696
377
  }
697
378
  if (options.host) {
698
- serveArgs.push('--host', options.host);
379
+ serveArgs.push("--host", options.host);
699
380
  }
700
381
  // Spawn opencode serve process
701
- const opencode = (0, child_process_1.spawn)('opencode', serveArgs, {
702
- stdio: 'inherit',
382
+ const opencode = (0, child_process_1.spawn)("opencode", serveArgs, {
383
+ stdio: "inherit",
703
384
  });
704
- opencode.on('close', (code) => {
385
+ opencode.on("close", code => {
705
386
  if (code !== 0) {
706
387
  console.log(chalk_1.default.red(`OpenCode server exited with code ${code}`));
707
388
  }
708
389
  });
709
- opencode.on('error', (error) => {
710
- console.error(chalk_1.default.red('Failed to start OpenCode server:'));
390
+ opencode.on("error", error => {
391
+ console.error(chalk_1.default.red("Failed to start OpenCode server:"));
711
392
  console.error(error.message);
712
393
  });
713
394
  }
714
395
  catch (error) {
715
- (0, error_handler_1.handleError)('Failed to start OpenCode server', error);
396
+ (0, error_handler_1.handleError)("Failed to start OpenCode server", error);
716
397
  }
717
398
  }));
718
399
  code
719
400
  .command(command_structure_1.SUBCOMMANDS.CODE.UPDATE)
720
- .description('Update OpenCode and agents to latest versions')
721
- .option('-f, --force', 'Force update even if already latest')
722
- .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
401
+ .description("Update OpenCode and agents to latest versions")
402
+ .option("-f, --force", "Force update even if already latest")
403
+ .option("-y, --yes", "Automatically answer yes to all prompts (for automation)")
723
404
  .action((options) => __awaiter(this, void 0, void 0, function* () {
724
- var _a, _b, _c;
725
405
  try {
726
- console.log(chalk_1.default.cyan('🔄 Updating OpenCode configuration...'));
406
+ console.log(chalk_1.default.cyan("🔄 Updating OpenCode configuration..."));
727
407
  // Ensure opencode is installed first
728
408
  if (!(yield ensureOpencodeInstalled(options.yes))) {
729
409
  return;
730
410
  }
731
- const configPath = path_1.default.join(process.cwd(), 'opencode.json');
411
+ const configPath = path_1.default.join(process.cwd(), "opencode.json");
732
412
  // Check if project is initialized
733
413
  if (!fs.existsSync(configPath)) {
734
- console.log(chalk_1.default.red('❌ No OpenCode configuration found.'));
414
+ console.log(chalk_1.default.red("❌ No OpenCode configuration found."));
735
415
  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.`));
736
416
  return;
737
417
  }
738
418
  // Read current configuration
739
419
  let currentConfig;
740
420
  try {
741
- const configContent = yield (0, promises_1.readFile)(configPath, 'utf8');
421
+ const configContent = yield (0, promises_1.readFile)(configPath, "utf8");
742
422
  currentConfig = JSON.parse(configContent);
743
423
  }
744
424
  catch (error) {
745
- console.error(chalk_1.default.red('Failed to read current opencode.json:'));
746
- (0, error_handler_1.handleError)('Config read failed', error);
425
+ console.error(chalk_1.default.red("Failed to read current opencode.json:"));
426
+ (0, error_handler_1.handleError)("Config read failed", error);
747
427
  return;
748
428
  }
749
- console.log(chalk_1.default.blue('📋 Current configuration:'));
750
- console.log(chalk_1.default.dim(` Model: ${currentConfig.model}`));
751
- console.log(chalk_1.default.dim(` Theme: ${currentConfig.theme}`));
752
- console.log(chalk_1.default.dim(` Agents: ${Object.keys(currentConfig.agent || {}).length} configured`));
753
- // Load latest agent configuration to ensure consistency
754
- const latestAgentConfig = yield loadLatestAgentConfig();
755
- // Create latest configuration plugin handles auth, models, and provider
756
- const latestConfig = {
757
- $schema: 'https://opencode.ai/config.json',
758
- plugin: ['@bergetai/opencode-auth@1.0.16'],
759
- username: 'berget-code',
760
- theme: 'berget-dark',
761
- share: 'manual',
762
- autoupdate: true,
763
- agent: {
764
- fullstack: {
765
- temperature: 0.3,
766
- top_p: 0.9,
767
- mode: 'primary',
768
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
769
- description: 'Router/coordinator agent for full-stack development with schema-driven architecture',
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.',
771
- },
772
- frontend: {
773
- temperature: 0.4,
774
- top_p: 0.9,
775
- mode: 'primary',
776
- permission: { edit: 'allow', bash: 'deny', webfetch: 'allow' },
777
- note: 'Bash access is denied for frontend persona to prevent shell command execution in UI environments. This restriction enforces security and architectural boundaries.',
778
- description: 'Builds Scandinavian, type-safe UIs with React, Tailwind, Shadcn.',
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.',
780
- },
781
- backend: {
782
- temperature: 0.3,
783
- top_p: 0.9,
784
- mode: 'primary',
785
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
786
- description: 'Functional, modular Koa + TypeScript services; schema-first with code quality focus.',
787
- prompt: 'You are Berget Code Backend agent. Voice: Scandinavian calm—precise, concise, confident. TypeScript + Koa. Prefer many small pure functions; avoid big try/catch blocks. Routes thin; logic in services/adapters/domain. Validate with Zod; auto-generate OpenAPI. Adapters isolate external systems; domain never depends on framework. Test with supertest; idempotent and stateless by default. Each microservice emits an OpenAPI contract; changes propagate upward to types. Code Quality & Refactoring Principles: Apply Single Responsibility Principle, fail fast with explicit errors, eliminate code duplication, remove nested complexity, use descriptive error codes, keep functions under 30 lines. Always leave code cleaner and more readable than you found it. CRITICAL: When all backend implementation tasks are complete and ready for merge, ALWAYS invoke @quality subagent to handle testing, building, and complete PR management including URL provision.',
788
- },
789
- // Use centralized devops configuration with Helm guidelines
790
- devops: latestAgentConfig.devops || {
791
- temperature: 0.3,
792
- top_p: 0.8,
793
- mode: 'primary',
794
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
795
- description: 'Declarative GitOps infra with FluxCD, Kustomize, Helm, operators.',
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.',
797
- },
798
- app: {
799
- temperature: 0.4,
800
- top_p: 0.9,
801
- mode: 'primary',
802
- permission: { edit: 'allow', bash: 'deny', webfetch: 'allow' },
803
- note: 'Bash access is denied for app persona to prevent shell command execution in mobile/Expo environments. This restriction enforces security and architectural boundaries.',
804
- description: 'Expo + React Native apps; props-first, offline-aware, shared tokens.',
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.',
806
- },
807
- security: {
808
- temperature: 0.2,
809
- top_p: 0.8,
810
- mode: 'subagent',
811
- permission: { edit: 'deny', bash: 'allow', webfetch: 'allow' },
812
- description: 'Security specialist for pentesting, OWASP compliance, and vulnerability assessments.',
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.',
814
- },
815
- quality: {
816
- temperature: 0.1,
817
- top_p: 0.9,
818
- mode: 'subagent',
819
- permission: { edit: 'allow', bash: 'allow', webfetch: 'allow' },
820
- description: 'Quality assurance specialist for testing, building, and complete PR management.',
821
- prompt: 'Voice: Scandinavian calm—precise, concise, confident. You are Berget Code Quality agent. Specialist in code quality assurance, testing, building, and complete pull request lifecycle management.\n\nCore responsibilities:\n - Run comprehensive test suites (npm test, npm run test, jest, vitest)\n - Execute build processes (npm run build, webpack, vite, tsc)\n - Create and manage pull requests with proper descriptions\n - Handle merge conflicts and keep main updated\n - Monitor GitHub for reviewer comments and address them\n - Ensure code quality standards are met\n - Validate linting and formatting (npm run lint, prettier)\n - Check test coverage and performance benchmarks\n - Handle CI/CD pipeline validation\n\nComplete PR Workflow:\n 1. Ensure all tests pass: npm test\n 2. Build successfully: npm run build\n 3. Commit all changes with proper message\n 4. Push to feature branch\n 5. Update main branch and handle merge conflicts\n 6. Create or update PR with comprehensive description\n 7. Monitor for reviewer comments\n 8. Address feedback and push updates\n 9. Always provide PR URL for user review\n\nEssential CLI commands:\n - npm test or npm run test (run test suite)\n - npm run build (build project)\n - npm run lint (run linting)\n - npm run format (format code)\n - npm run test:coverage (check coverage)\n - git add . && git commit -m "message" && git push (commit and push)\n - git checkout main && git pull origin main (update main)\n - git checkout feature-branch && git merge main (handle conflicts)\n - gh pr create --title "title" --body "body" (create PR)\n - gh pr view --comments (check PR comments)\n - gh pr edit --title "title" --body "body" (update PR)\n\nPR Creation Process:\n - Always include clear summary of changes\n - List technical details and improvements\n - Include testing and validation results\n - Add any breaking changes or migration notes\n - Provide PR URL immediately after creation\n\nMerge Conflict Resolution:\n - Always update main before creating/updating PR\n - Handle conflicts automatically when possible\n - If conflicts require human input, clearly explain what\'s needed\n - Re-run tests after conflict resolution\n - Ensure CI checks pass before finalizing\n\nReviewer Comment Handling:\n - Monitor PR for new comments regularly\n - Address each comment specifically\n - Push fixes and update PR accordingly\n - Always provide updated PR URL after changes\n - Continue monitoring until all feedback is addressed\n\nCRITICAL: When invoked by other agents (@quality), you MUST:\n - Complete all testing and building tasks\n - Handle entire PR creation/update process\n - Provide PR URL at the end\n - Ensure main branch is properly merged\n - Handle any merge conflicts automatically\n\nAlways provide specific command examples and wait for processes to complete before proceeding.\nWorkflow: Always follow branch_strategy and commit_convention from workflow section. Never work directly in main.\nAgent awareness: Can be invoked by any primary agent (@quality) for complete testing, building, and PR management. You are the final step before user review - ensure everything is perfect.',
822
- },
823
- },
824
- command: {
825
- fullstack: {
826
- description: 'Switch to Fullstack (router)',
827
- template: '{{input}}',
828
- agent: 'fullstack',
829
- },
830
- route: {
831
- description: 'Let Fullstack auto-route to the right persona based on files/intent',
832
- template: 'ROUTE {{input}}',
833
- agent: 'fullstack',
834
- subtask: true,
835
- },
836
- frontend: {
837
- description: 'Switch to Frontend persona',
838
- template: '{{input}}',
839
- agent: 'frontend',
840
- },
841
- backend: {
842
- description: 'Switch to Backend persona',
843
- template: '{{input}}',
844
- agent: 'backend',
845
- },
846
- devops: {
847
- description: 'Switch to DevOps persona',
848
- template: '{{input}}',
849
- agent: 'devops',
850
- },
851
- app: {
852
- description: 'Switch to App persona',
853
- template: '{{input}}',
854
- agent: 'app',
855
- },
856
- security: {
857
- description: 'Switch to Security persona for pentesting and OWASP compliance',
858
- template: '{{input}}',
859
- agent: 'security',
860
- },
861
- quality: {
862
- description: 'Switch to Quality agent for testing, building, and PR management',
863
- template: '{{input}}',
864
- agent: 'quality',
865
- },
866
- },
867
- watcher: {
868
- ignore: ['node_modules', 'dist', '.git', 'coverage'],
869
- },
870
- };
871
- // Check if update is needed
872
- const needsUpdate = JSON.stringify(currentConfig) !== JSON.stringify(latestConfig);
873
- if (!needsUpdate && !options.force) {
874
- console.log(chalk_1.default.green('✅ Already using the latest configuration!'));
875
- return;
876
- }
877
- if (needsUpdate) {
878
- console.log(chalk_1.default.blue('\n🔄 Updates available:'));
879
- // Compare agents
880
- const currentAgents = Object.keys(currentConfig.agent || {});
881
- const latestAgents = Object.keys(latestConfig.agent);
882
- const newAgents = latestAgents.filter((agent) => !currentAgents.includes(agent));
883
- if (newAgents.length > 0) {
884
- console.log(chalk_1.default.cyan(` • New agents: ${newAgents.join(', ')}`));
429
+ console.log(chalk_1.default.blue("📋 Current configuration:"));
430
+ if (currentConfig.model) {
431
+ console.log(chalk_1.default.dim(` Model: ${currentConfig.model}`));
432
+ }
433
+ const agentsDir = path_1.default.join(process.cwd(), ".opencode", "agents");
434
+ const templatesDir = getAgentTemplatesDir();
435
+ const templateFiles = fs.readdirSync(templatesDir).filter(f => f.endsWith(".md"));
436
+ // Check if agent definitions need updating
437
+ let agentsNeedUpdate = false;
438
+ for (const file of templateFiles) {
439
+ const src = path_1.default.join(templatesDir, file);
440
+ const dest = path_1.default.join(agentsDir, file);
441
+ if (!fs.existsSync(dest)) {
442
+ agentsNeedUpdate = true;
443
+ break;
885
444
  }
886
- // Check for quality agent specifically
887
- if (!((_a = currentConfig.agent) === null || _a === void 0 ? void 0 : _a.quality) && latestConfig.agent.quality) {
888
- console.log(chalk_1.default.cyan(' • Quality subagent for testing and PR management'));
445
+ const srcContent = fs.readFileSync(src, "utf8");
446
+ const destContent = fs.readFileSync(dest, "utf8");
447
+ if (srcContent !== destContent) {
448
+ agentsNeedUpdate = true;
449
+ break;
889
450
  }
890
- // Check for security subagent mode
891
- if (((_c = (_b = currentConfig.agent) === null || _b === void 0 ? void 0 : _b.security) === null || _c === void 0 ? void 0 : _c.mode) !== 'subagent') {
892
- console.log(chalk_1.default.cyan(' • Security agent converted to subagent (read-only)'));
451
+ }
452
+ // Check if opencode.json still has inline agent config (needs migration)
453
+ const needsMigration = !!currentConfig.agent;
454
+ if (!agentsNeedUpdate && !needsMigration && !options.force) {
455
+ console.log(chalk_1.default.green("✅ Already using the latest configuration!"));
456
+ return;
457
+ }
458
+ if (agentsNeedUpdate || needsMigration) {
459
+ console.log(chalk_1.default.blue("\n🔄 Updates available:"));
460
+ if (needsMigration) {
461
+ console.log(chalk_1.default.cyan(" • Migrate agents from opencode.json to .opencode/agents/"));
893
462
  }
894
- // Check for plugin migration
895
- if (!currentConfig.plugin) {
896
- console.log(chalk_1.default.cyan(' • Plugin-first auth (automatic token refresh + model discovery)'));
463
+ if (agentsNeedUpdate) {
464
+ console.log(chalk_1.default.cyan(" • Latest agent prompts and improvements"));
897
465
  }
898
- console.log(chalk_1.default.cyan(' • Latest agent prompts and improvements'));
899
466
  }
900
467
  if (options.force) {
901
- console.log(chalk_1.default.yellow('🔧 Force update requested'));
468
+ console.log(chalk_1.default.yellow("🔧 Force update requested"));
902
469
  }
903
470
  if (!options.yes) {
904
- console.log(chalk_1.default.blue('\nThis will update your OpenCode configuration with the latest improvements.'));
905
- // Check if user has git for backup
471
+ console.log(chalk_1.default.blue("\nThis will update your agent definitions and OpenCode configuration."));
906
472
  const hasGitRepo = hasGit();
907
473
  if (!hasGitRepo) {
908
- console.log(chalk_1.default.yellow('⚠️ No .git repository detected - backup will be created'));
474
+ console.log(chalk_1.default.yellow("⚠️ No .git repository detected - backup will be created"));
909
475
  }
910
476
  else {
911
- console.log(chalk_1.default.green('✓ Git repository detected - changes are tracked'));
477
+ console.log(chalk_1.default.green("✓ Git repository detected - changes are tracked"));
912
478
  }
913
479
  }
914
- // Ask user what they want to do
915
- console.log(chalk_1.default.blue('\nChoose update strategy:'));
916
- console.log(chalk_1.default.cyan('1) Replace - Use latest configuration (your customizations will be lost)'));
917
- console.log(chalk_1.default.cyan('2) Merge - Combine latest updates with your customizations (recommended)'));
918
- let mergeChoice = 'merge';
919
- if (!options.yes) {
920
- const choice = yield askChoice('\nYour choice (1-2, default: 2): ', ['replace', 'merge'], 'merge');
921
- mergeChoice = choice;
922
- }
923
- if (yield confirm(`\nProceed with ${mergeChoice}? (Y/n): `, options.yes)) {
480
+ if (yield confirm("\nProceed with update? (Y/n): ", options.yes)) {
924
481
  try {
925
- let finalConfig;
926
482
  let backupPath = null;
927
- // Create backup if no git
928
483
  if (!hasGit()) {
929
484
  backupPath = `${configPath}.backup.${Date.now()}`;
930
485
  yield (0, promises_1.writeFile)(backupPath, JSON.stringify(currentConfig, null, 2));
931
486
  console.log(chalk_1.default.green(`✓ Backed up current config to ${path_1.default.basename(backupPath)}`));
932
487
  }
933
- if (mergeChoice === 'merge') {
934
- // Merge configurations
935
- finalConfig = yield mergeConfigurations(currentConfig, latestConfig);
936
- console.log(chalk_1.default.green('✓ Merged configurations with latest updates'));
488
+ // Remove inline agent section from opencode.json if present
489
+ if (currentConfig.agent) {
490
+ delete currentConfig.agent;
491
+ yield (0, promises_1.writeFile)(configPath, JSON.stringify(currentConfig, null, 2));
492
+ console.log(chalk_1.default.green("✓ Removed inline agent config from opencode.json"));
937
493
  }
938
- else {
939
- // Replace with latest
940
- finalConfig = latestConfig;
941
- console.log(chalk_1.default.green('✓ Replaced with latest configuration'));
494
+ // Sync agent markdown files from templates
495
+ fs.mkdirSync(agentsDir, { recursive: true });
496
+ let updatedCount = 0;
497
+ for (const file of templateFiles) {
498
+ const src = path_1.default.join(templatesDir, file);
499
+ const dest = path_1.default.join(agentsDir, file);
500
+ const agentName = path_1.default.basename(file, ".md");
501
+ if (!fs.existsSync(dest) ||
502
+ fs.readFileSync(src, "utf8") !== fs.readFileSync(dest, "utf8")) {
503
+ fs.copyFileSync(src, dest);
504
+ updatedCount++;
505
+ console.log(chalk_1.default.cyan(` • Updated agent: ${agentName}`));
506
+ }
507
+ }
508
+ if (updatedCount > 0) {
509
+ console.log(chalk_1.default.green(`✓ Updated ${updatedCount} agent definition(s)`));
942
510
  }
943
- // Write final configuration
944
- yield (0, promises_1.writeFile)(configPath, JSON.stringify(finalConfig, null, 2));
945
- console.log(chalk_1.default.green(`✓ Updated opencode.json with ${mergeChoice} strategy`));
946
511
  // Update AGENTS.md if it doesn't exist
947
- const agentsMdPath = path_1.default.join(process.cwd(), 'AGENTS.md');
512
+ const agentsMdPath = path_1.default.join(process.cwd(), "AGENTS.md");
948
513
  if (!fs.existsSync(agentsMdPath)) {
949
514
  const agentsMdContent = `# Berget Code Agents
950
515
 
951
516
  This document describes the specialized agents available in this project for use with OpenCode.
952
517
 
518
+ Agents are defined as markdown files in \`.opencode/agents/\` with YAML frontmatter.
519
+
953
520
  ## Available Agents
954
521
 
955
522
  ### Primary Agents
956
523
 
957
- #### fullstack
958
- Router/coordinator agent for full-stack development with schema-driven architecture. Handles routing between different personas based on file paths and task requirements.
959
-
960
- **Use when:**
961
- - Working across multiple parts of a monorepo
962
- - Need to coordinate between frontend, backend, devops, and app
963
- - Starting new projects and need to determine tech stack
964
-
965
- **Key features:**
966
- - Schema-driven development (database → OpenAPI → types)
967
- - Automatic routing to appropriate persona
968
- - Tech stack discovery and recommendations
969
-
970
- #### frontend
971
- Builds Scandinavian, type-safe UIs with React, Tailwind, and Shadcn.
972
-
973
- **Use when:**
974
- - Working with React components (.tsx files)
975
- - Frontend development in /apps/frontend
976
- - UI/UX implementation
977
-
978
- **Key features:**
979
- - Design system integration
980
- - Semantic tokens and accessibility
981
- - Props-first component architecture
982
-
983
- #### backend
984
- Functional, modular Koa + TypeScript services with schema-first approach and code quality focus.
985
-
986
- **Use when:**
987
- - Working with Koa routers and services
988
- - Backend development in /services
989
- - API development and database work
990
-
991
- **Key features:**
992
- - Zod validation and OpenAPI generation
993
- - Code quality and refactoring principles
994
- - PR workflow integration
995
-
996
- #### devops
997
- Declarative GitOps infrastructure with FluxCD, Kustomize, Helm, and operators.
998
-
999
- **Use when:**
1000
- - Working with Kubernetes manifests
1001
- - Infrastructure in /infra or /k8s
1002
- - CI/CD and deployment configurations
1003
-
1004
- **Key features:**
1005
- - GitOps workflows
1006
- - Operator-first approach
1007
- - SemVer with release candidates
1008
-
1009
- **Helm Values Configuration Process:**
1010
- 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.
1011
- 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.
1012
- 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.
1013
- 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.
1014
-
1015
- #### app
1016
- Expo + React Native applications with props-first architecture and offline awareness.
1017
-
1018
- **Use when:**
1019
- - Mobile app development with Expo
1020
- - React Native projects in /apps/app
1021
- - Cross-platform mobile development
1022
-
1023
- **Key features:**
1024
- - Shared design tokens with frontend
1025
- - Offline-first architecture
1026
- - Expo integration
524
+ | Agent | Description | Temperature |
525
+ |-------|-------------|-------------|
526
+ | fullstack | Router/coordinator for full-stack development | 0.3 |
527
+ | frontend | Scandinavian, type-safe UIs with React, Tailwind, Shadcn | 0.4 |
528
+ | backend | Functional, modular Koa + TypeScript services | 0.3 |
529
+ | devops | Declarative GitOps infra with FluxCD, Kustomize, Helm | 0.3 |
530
+ | app | Expo + React Native apps; props-first, offline-aware | 0.4 |
1027
531
 
1028
532
  ### Subagents
1029
533
 
1030
- #### security
1031
- Security specialist for penetration testing, OWASP compliance, and vulnerability assessments.
1032
-
1033
- **Use when:**
1034
- - Need security review of code changes
1035
- - OWASP Top 10 compliance checks
1036
- - Vulnerability assessments
1037
-
1038
- **Key features:**
1039
- - OWASP standards compliance
1040
- - Security best practices
1041
- - Actionable remediation strategies
1042
-
1043
- #### quality
1044
- Quality assurance specialist for testing, building, and PR management.
1045
-
1046
- **Use when:**
1047
- - Need to run test suites and build processes
1048
- - Creating or updating pull requests
1049
- - Monitoring GitHub for reviewer comments
1050
- - Ensuring code quality standards
1051
-
1052
- **Key features:**
1053
- - Comprehensive testing and building workflows
1054
- - PR creation and management
1055
- - GitHub integration for reviewer feedback
1056
- - CLI command expertise for quality assurance
534
+ | Agent | Description | Temperature |
535
+ |-------|-------------|-------------|
536
+ | security | Security specialist for pentesting and OWASP compliance | 0.2 |
537
+ | quality | QA specialist for testing, building, and PR management | 0.1 |
1057
538
 
1058
539
  ## Usage
1059
540
 
1060
- ### Switching Agents
1061
- Use the \`<tab>\` key to cycle through primary agents during a session.
1062
-
1063
- ### Manual Agent Selection
1064
- Use commands to switch to specific agents:
1065
- - \`/fullstack\` - Switch to Fullstack agent
1066
- - \`/frontend\` - Switch to Frontend agent
1067
- - \`/backend\` - Switch to Backend agent
1068
- - \`/devops\` - Switch to DevOps agent
1069
- - \`/app\` - Switch to App agent
1070
- - \`/quality\` - Switch to Quality agent for testing and PR management
1071
-
1072
- ### Using Subagents
1073
- Mention subagents with \`@\` symbol:
1074
- - \`@security review this authentication implementation\`
1075
- - \`@quality run tests and create PR for these changes\`
541
+ - **Tab** key to cycle between primary agents
542
+ - **@mention** to invoke subagents (e.g. \`@security review this code\`)
543
+ - \`/fullstack\`, \`/frontend\`, \`/backend\`, \`/devops\`, \`/app\` to switch agents
1076
544
 
1077
545
  ## Routing Rules
1078
546
 
1079
547
  The fullstack agent automatically routes tasks based on file patterns:
1080
548
 
1081
549
  - \`/apps/frontend\` or \`.tsx\` files → frontend
1082
- - \`/apps/app\` or Expo/React Native → app
550
+ - \`/apps/app\` or Expo/React Native → app
1083
551
  - \`/infra\`, \`/k8s\`, FluxCD, Helm → devops
1084
552
  - \`/services\`, Koa routers → backend
1085
553
 
1086
- ## Configuration
1087
-
1088
- All agents are configured in \`opencode.json\` with:
1089
- - Specialized prompts and temperature settings
1090
- - Appropriate tool permissions
1091
- - Model optimizations for their specific tasks
1092
-
1093
- ## Environment Setup
554
+ ## Customization
1094
555
 
1095
- Authentication is handled by the Berget plugin. Run \`/connect\` in OpenCode to authenticate.
1096
-
1097
- ## Workflow
1098
-
1099
- All agents follow these principles:
1100
- - Never work directly in main branch
1101
- - Follow branch strategy and commit conventions
1102
- - Create PRs for new functionality
1103
- - Run tests before committing
1104
- - Address reviewer feedback promptly
556
+ Edit the markdown files in \`.opencode/agents/\` to customize agent behavior.
557
+ See https://opencode.ai/docs/agents/ for available options.
1105
558
 
1106
559
  ---
1107
560
 
1108
561
  *Updated by berget code update*
1109
562
  `;
1110
563
  yield (0, promises_1.writeFile)(agentsMdPath, agentsMdContent);
1111
- console.log(chalk_1.default.green('Updated AGENTS.md documentation'));
564
+ console.log(chalk_1.default.green("Created AGENTS.md documentation"));
1112
565
  }
1113
- console.log(chalk_1.default.green('\n✅ Update completed successfully!'));
1114
- console.log(chalk_1.default.blue('New features available:'));
1115
- console.log(chalk_1.default.cyan(' • @quality subagent for testing and PR management'));
1116
- console.log(chalk_1.default.cyan(' • @security subagent for security reviews'));
1117
- console.log(chalk_1.default.cyan(' • Improved agent prompts and routing'));
1118
- console.log(chalk_1.default.cyan(' • GLM-4.7 token optimizations'));
1119
- console.log(chalk_1.default.blue('\nTry these new commands:'));
1120
- console.log(chalk_1.default.cyan(' @quality run tests and create PR'));
1121
- console.log(chalk_1.default.cyan(' @security review this code'));
566
+ console.log(chalk_1.default.green("\n✅ Update completed successfully!"));
1122
567
  }
1123
568
  catch (error) {
1124
- console.error(chalk_1.default.red('Failed to update configuration:'));
1125
- (0, error_handler_1.handleError)('Update failed', error);
1126
- // Restore from backup if update failed
569
+ console.error(chalk_1.default.red("Failed to update configuration:"));
570
+ (0, error_handler_1.handleError)("Update failed", error);
1127
571
  try {
1128
572
  yield (0, promises_1.writeFile)(configPath, JSON.stringify(currentConfig, null, 2));
1129
- console.log(chalk_1.default.yellow('📁 Restored original configuration from backup'));
573
+ console.log(chalk_1.default.yellow("📁 Restored original configuration from backup"));
1130
574
  }
1131
- catch (restoreError) {
1132
- console.error(chalk_1.default.red('Failed to restore backup:'));
575
+ catch (_b) {
576
+ console.error(chalk_1.default.red("Failed to restore backup:"));
1133
577
  }
1134
578
  }
1135
579
  }
1136
580
  else {
1137
- console.log(chalk_1.default.yellow('Update cancelled.'));
581
+ console.log(chalk_1.default.yellow("Update cancelled."));
1138
582
  }
1139
583
  }
1140
- catch (error) {
1141
- (0, error_handler_1.handleError)('Failed to update OpenCode configuration', error);
584
+ catch (_c) {
585
+ console.error(chalk_1.default.red("Failed to update OpenCode configuration"));
1142
586
  }
1143
587
  }));
1144
588
  }