berget 2.2.6 → 2.2.8

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 (145) hide show
  1. package/.github/workflows/publish.yml +2 -2
  2. package/.github/workflows/test.yml +10 -4
  3. package/.husky/pre-commit +1 -0
  4. package/.prettierignore +15 -0
  5. package/.prettierrc +7 -3
  6. package/CONTRIBUTING.md +38 -0
  7. package/README.md +2 -148
  8. package/dist/index.js +10 -11
  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 +97 -117
  20. package/dist/src/commands/api-keys.js +75 -90
  21. package/dist/src/commands/auth.js +7 -16
  22. package/dist/src/commands/autocomplete.js +1 -1
  23. package/dist/src/commands/billing.js +6 -17
  24. package/dist/src/commands/chat.js +68 -101
  25. package/dist/src/commands/clusters.js +9 -18
  26. package/dist/src/commands/code/__tests__/auth-sync.test.js +351 -0
  27. package/dist/src/commands/code/__tests__/fake-api-key-service.js +13 -0
  28. package/dist/src/commands/code/__tests__/fake-auth-service.js +47 -0
  29. package/dist/src/commands/code/__tests__/fake-command-runner.js +21 -34
  30. package/dist/src/commands/code/__tests__/fake-file-store.js +20 -33
  31. package/dist/src/commands/code/__tests__/fake-prompter.js +83 -57
  32. package/dist/src/commands/code/__tests__/setup-flow.test.js +359 -92
  33. package/dist/src/commands/code/adapters/clack-prompter.js +15 -22
  34. package/dist/src/commands/code/adapters/fs-file-store.js +26 -40
  35. package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -37
  36. package/dist/src/commands/code/auth-sync.js +270 -0
  37. package/dist/src/commands/code/errors.js +12 -9
  38. package/dist/src/commands/code/ports/auth-services.js +2 -0
  39. package/dist/src/commands/code/setup.js +387 -281
  40. package/dist/src/commands/code.js +205 -332
  41. package/dist/src/commands/index.js +5 -5
  42. package/dist/src/commands/models.js +6 -17
  43. package/dist/src/commands/users.js +5 -16
  44. package/dist/src/constants/command-structure.js +104 -104
  45. package/dist/src/services/api-key-service.js +132 -157
  46. package/dist/src/services/auth-service.js +89 -342
  47. package/dist/src/services/browser-auth.js +268 -0
  48. package/dist/src/services/chat-service.js +371 -401
  49. package/dist/src/services/cluster-service.js +47 -62
  50. package/dist/src/services/collaborator-service.js +10 -25
  51. package/dist/src/services/flux-service.js +14 -29
  52. package/dist/src/services/helm-service.js +10 -25
  53. package/dist/src/services/kubectl-service.js +16 -33
  54. package/dist/src/utils/config-checker.js +3 -3
  55. package/dist/src/utils/config-loader.js +95 -95
  56. package/dist/src/utils/default-api-key.js +124 -134
  57. package/dist/src/utils/env-manager.js +55 -66
  58. package/dist/src/utils/error-handler.js +20 -21
  59. package/dist/src/utils/logger.js +72 -65
  60. package/dist/src/utils/markdown-renderer.js +27 -27
  61. package/dist/src/utils/opencode-validator.js +63 -68
  62. package/dist/src/utils/token-manager.js +74 -45
  63. package/dist/tests/commands/chat.test.js +16 -25
  64. package/dist/tests/commands/code.test.js +95 -104
  65. package/dist/tests/utils/config-loader.test.js +48 -48
  66. package/dist/tests/utils/env-manager.test.js +43 -52
  67. package/dist/tests/utils/opencode-validator.test.js +22 -21
  68. package/dist/vitest.config.js +1 -1
  69. package/eslint.config.mjs +67 -0
  70. package/index.ts +35 -42
  71. package/package.json +30 -2
  72. package/src/agents/app.ts +27 -0
  73. package/src/agents/backend.ts +24 -0
  74. package/src/agents/devops.ts +33 -0
  75. package/src/agents/frontend.ts +24 -0
  76. package/src/agents/fullstack.ts +24 -0
  77. package/src/agents/index.ts +73 -0
  78. package/src/agents/quality.ts +69 -0
  79. package/src/agents/security.ts +26 -0
  80. package/src/agents/types.ts +17 -0
  81. package/src/client.ts +118 -152
  82. package/src/commands/api-keys.ts +241 -333
  83. package/src/commands/auth.ts +22 -27
  84. package/src/commands/autocomplete.ts +9 -9
  85. package/src/commands/billing.ts +20 -24
  86. package/src/commands/chat.ts +248 -338
  87. package/src/commands/clusters.ts +27 -26
  88. package/src/commands/code/__tests__/auth-sync.test.ts +482 -0
  89. package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
  90. package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
  91. package/src/commands/code/__tests__/fake-command-runner.ts +45 -42
  92. package/src/commands/code/__tests__/fake-file-store.ts +32 -23
  93. package/src/commands/code/__tests__/fake-prompter.ts +116 -77
  94. package/src/commands/code/__tests__/setup-flow.test.ts +624 -268
  95. package/src/commands/code/adapters/clack-prompter.ts +53 -39
  96. package/src/commands/code/adapters/fs-file-store.ts +32 -27
  97. package/src/commands/code/adapters/spawn-command-runner.ts +38 -29
  98. package/src/commands/code/auth-sync.ts +329 -0
  99. package/src/commands/code/errors.ts +18 -18
  100. package/src/commands/code/ports/auth-services.ts +14 -0
  101. package/src/commands/code/ports/command-runner.ts +8 -4
  102. package/src/commands/code/ports/file-store.ts +5 -4
  103. package/src/commands/code/ports/prompter.ts +24 -18
  104. package/src/commands/code/setup.ts +570 -340
  105. package/src/commands/code.ts +338 -539
  106. package/src/commands/index.ts +20 -19
  107. package/src/commands/models.ts +28 -32
  108. package/src/commands/users.ts +15 -21
  109. package/src/constants/command-structure.ts +134 -157
  110. package/src/services/api-key-service.ts +105 -122
  111. package/src/services/auth-service.ts +99 -345
  112. package/src/services/browser-auth.ts +296 -0
  113. package/src/services/chat-service.ts +265 -299
  114. package/src/services/cluster-service.ts +42 -45
  115. package/src/services/collaborator-service.ts +14 -19
  116. package/src/services/flux-service.ts +23 -25
  117. package/src/services/helm-service.ts +19 -21
  118. package/src/services/kubectl-service.ts +17 -19
  119. package/src/types/api.d.ts +1905 -1907
  120. package/src/types/json.d.ts +2 -2
  121. package/src/utils/config-checker.ts +10 -10
  122. package/src/utils/config-loader.ts +162 -178
  123. package/src/utils/default-api-key.ts +114 -125
  124. package/src/utils/env-manager.ts +53 -57
  125. package/src/utils/error-handler.ts +61 -56
  126. package/src/utils/logger.ts +79 -73
  127. package/src/utils/markdown-renderer.ts +31 -31
  128. package/src/utils/opencode-validator.ts +85 -89
  129. package/src/utils/token-manager.ts +108 -87
  130. package/templates/agents/app.md +1 -0
  131. package/templates/agents/backend.md +1 -0
  132. package/templates/agents/devops.md +2 -0
  133. package/templates/agents/frontend.md +1 -0
  134. package/templates/agents/fullstack.md +1 -0
  135. package/templates/agents/quality.md +45 -40
  136. package/templates/agents/security.md +1 -0
  137. package/tests/commands/chat.test.ts +53 -62
  138. package/tests/commands/code.test.ts +265 -310
  139. package/tests/utils/config-loader.test.ts +189 -188
  140. package/tests/utils/env-manager.test.ts +110 -113
  141. package/tests/utils/opencode-validator.test.ts +52 -56
  142. package/tsconfig.json +4 -3
  143. package/vitest.config.ts +3 -3
  144. package/AGENTS.md +0 -374
  145. package/TODO.md +0 -19
@@ -22,266 +22,20 @@ var __importStar = (this && this.__importStar) || function (mod) {
22
22
  __setModuleDefault(result, mod);
23
23
  return result;
24
24
  };
25
- var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
26
- function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
27
- return new (P || (P = Promise))(function (resolve, reject) {
28
- function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
29
- function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
30
- function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
31
- step((generator = generator.apply(thisArg, _arguments || [])).next());
32
- });
33
- };
34
25
  var __importDefault = (this && this.__importDefault) || function (mod) {
35
26
  return (mod && mod.__esModule) ? mod : { "default": mod };
36
27
  };
37
28
  Object.defineProperty(exports, "__esModule", { value: true });
38
29
  exports.registerCodeCommands = void 0;
39
30
  const chalk_1 = __importDefault(require("chalk"));
40
- const readline_1 = __importDefault(require("readline"));
31
+ const node_child_process_1 = require("node:child_process");
32
+ const fs = __importStar(require("node:fs"));
33
+ const promises_1 = require("node:fs/promises");
34
+ const node_path_1 = __importDefault(require("node:path"));
35
+ const node_readline_1 = __importDefault(require("node:readline"));
41
36
  const command_structure_1 = require("../constants/command-structure");
42
37
  const error_handler_1 = require("../utils/error-handler");
43
38
  const setup_1 = require("./code/setup");
44
- const fs = __importStar(require("fs"));
45
- const promises_1 = require("fs/promises");
46
- const path_1 = __importDefault(require("path"));
47
- const child_process_1 = require("child_process");
48
- /**
49
- * Check if current directory has git
50
- */
51
- function hasGit() {
52
- try {
53
- return fs.existsSync(path_1.default.join(process.cwd(), '.git'));
54
- }
55
- catch (_a) {
56
- return false;
57
- }
58
- }
59
- /**
60
- * Helper function to get user confirmation
61
- */
62
- function confirm(question, autoYes = false) {
63
- return __awaiter(this, void 0, void 0, function* () {
64
- if (autoYes) {
65
- return true;
66
- }
67
- return new Promise((resolve) => {
68
- const rl = readline_1.default.createInterface({
69
- input: process.stdin,
70
- output: process.stdout,
71
- });
72
- rl.question(question, (answer) => {
73
- rl.close();
74
- resolve(answer.toLowerCase() === 'y' ||
75
- answer.toLowerCase() === 'yes' ||
76
- answer === '');
77
- });
78
- });
79
- });
80
- }
81
- /**
82
- * Helper function to get user input
83
- */
84
- function getInput(question, defaultValue, autoYes = false) {
85
- return __awaiter(this, void 0, void 0, function* () {
86
- if (autoYes) {
87
- return defaultValue;
88
- }
89
- const rl = readline_1.default.createInterface({
90
- input: process.stdin,
91
- output: process.stdout,
92
- });
93
- return new Promise((resolve) => {
94
- rl.question(question, (answer) => {
95
- rl.close();
96
- resolve(answer.trim() || defaultValue);
97
- });
98
- });
99
- });
100
- }
101
- /**
102
- * Get project name from current directory or package.json
103
- */
104
- function getProjectName() {
105
- try {
106
- const packageJsonPath = path_1.default.join(process.cwd(), 'package.json');
107
- if (fs.existsSync(packageJsonPath)) {
108
- const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
109
- const packageJson = JSON.parse(packageJsonContent);
110
- return packageJson.name || path_1.default.basename(process.cwd());
111
- }
112
- }
113
- catch (error) {
114
- // Ignore error and fallback to directory name
115
- }
116
- return path_1.default.basename(process.cwd());
117
- }
118
- /**
119
- * Get the path to the bundled agent templates directory
120
- */
121
- function getAgentTemplatesDir() {
122
- return path_1.default.resolve(__dirname, '../../templates/agents');
123
- }
124
- /**
125
- * Parse a markdown agent file with YAML frontmatter into an agent config object
126
- */
127
- function parseAgentMarkdown(content) {
128
- const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
129
- if (!frontmatterMatch) {
130
- throw new Error('Invalid agent markdown: missing frontmatter');
131
- }
132
- const yamlStr = frontmatterMatch[1];
133
- const promptBody = frontmatterMatch[2].trim();
134
- const config = { prompt: promptBody };
135
- for (const line of yamlStr.split('\n')) {
136
- const trimmed = line.trim();
137
- if (!trimmed || trimmed.startsWith('#'))
138
- continue;
139
- const colonIdx = trimmed.indexOf(':');
140
- if (colonIdx === -1)
141
- continue;
142
- const key = trimmed.substring(0, colonIdx).trim();
143
- const value = trimmed.substring(colonIdx + 1).trim();
144
- if (key === 'permission')
145
- continue;
146
- if (value === 'true') {
147
- config[key] = true;
148
- }
149
- else if (value === 'false') {
150
- config[key] = false;
151
- }
152
- else if (!isNaN(Number(value)) && value !== '') {
153
- config[key] = Number(value);
154
- }
155
- else {
156
- config[key] = value;
157
- }
158
- }
159
- const permission = {};
160
- const permMatch = yamlStr.match(/permission:\s*\n((?:\s+\w+:.*\n?)*)/);
161
- if (permMatch) {
162
- for (const permLine of permMatch[1].split('\n')) {
163
- const permTrimmed = permLine.trim();
164
- if (!permTrimmed)
165
- continue;
166
- const permColonIdx = permTrimmed.indexOf(':');
167
- if (permColonIdx === -1)
168
- continue;
169
- const permKey = permTrimmed.substring(0, permColonIdx).trim();
170
- const permValue = permTrimmed.substring(permColonIdx + 1).trim();
171
- if (permKey && permValue) {
172
- permission[permKey] = permValue;
173
- }
174
- }
175
- }
176
- if (Object.keys(permission).length > 0) {
177
- config.permission = permission;
178
- }
179
- return config;
180
- }
181
- /**
182
- * Load the latest agent configuration from bundled markdown templates
183
- */
184
- function loadLatestAgentConfig() {
185
- return __awaiter(this, void 0, void 0, function* () {
186
- const templatesDir = getAgentTemplatesDir();
187
- const agents = {};
188
- const files = fs.readdirSync(templatesDir).filter((f) => f.endsWith('.md'));
189
- for (const file of files) {
190
- const agentName = path_1.default.basename(file, '.md');
191
- const filePath = path_1.default.join(templatesDir, file);
192
- const content = fs.readFileSync(filePath, 'utf8');
193
- try {
194
- agents[agentName] = parseAgentMarkdown(content);
195
- }
196
- catch (error) {
197
- console.warn(chalk_1.default.yellow(`Warning: Failed to parse agent template ${file}: ${error}`));
198
- }
199
- }
200
- return agents;
201
- });
202
- }
203
- /**
204
- * Check if opencode is installed
205
- */
206
- function checkOpencodeInstalled() {
207
- return new Promise((resolve) => {
208
- const child = (0, child_process_1.spawn)('which', ['opencode'], {
209
- stdio: 'pipe',
210
- });
211
- child.on('close', (code) => {
212
- resolve(code === 0);
213
- });
214
- child.on('error', () => {
215
- resolve(false);
216
- });
217
- });
218
- }
219
- /**
220
- * Install opencode via npm
221
- */
222
- function installOpencode() {
223
- return __awaiter(this, void 0, void 0, function* () {
224
- console.log(chalk_1.default.cyan('Installing OpenCode via npm...'));
225
- try {
226
- yield new Promise((resolve, reject) => {
227
- const install = (0, child_process_1.spawn)('npm', ['install', '-g', 'opencode-ai@1.3'], {
228
- stdio: 'inherit',
229
- });
230
- install.on('close', (code) => {
231
- if (code === 0) {
232
- console.log(chalk_1.default.green('✓ OpenCode installed successfully!'));
233
- resolve();
234
- }
235
- else {
236
- reject(new Error(`Installation failed with code ${code}`));
237
- }
238
- });
239
- install.on('error', reject);
240
- });
241
- // Verify installation
242
- const opencodeInstalled = yield checkOpencodeInstalled();
243
- if (!opencodeInstalled) {
244
- console.log(chalk_1.default.yellow('Installation completed but opencode command not found.'));
245
- console.log(chalk_1.default.yellow('You may need to restart your terminal or check your PATH.'));
246
- return false;
247
- }
248
- return true;
249
- }
250
- catch (error) {
251
- console.error(chalk_1.default.red('Failed to install OpenCode:'));
252
- console.error(error instanceof Error ? error.message : String(error));
253
- console.log(chalk_1.default.blue('\nAlternative installation methods:'));
254
- console.log(chalk_1.default.blue(' curl -fsSL https://opencode.ai/install | sh'));
255
- console.log(chalk_1.default.blue(' Or visit: https://opencode.ai/docs'));
256
- return false;
257
- }
258
- });
259
- }
260
- /**
261
- * Ensure opencode is installed, offering to install if not
262
- */
263
- function ensureOpencodeInstalled(autoYes = false) {
264
- return __awaiter(this, void 0, void 0, function* () {
265
- let opencodeInstalled = yield checkOpencodeInstalled();
266
- if (!opencodeInstalled) {
267
- if (!autoYes) {
268
- console.log(chalk_1.default.red('OpenCode is not installed.'));
269
- console.log(chalk_1.default.blue('OpenCode is required for the AI coding assistant.'));
270
- }
271
- if (yield confirm('Would you like to install OpenCode automatically? (Y/n): ', autoYes)) {
272
- opencodeInstalled = yield installOpencode();
273
- }
274
- else {
275
- if (!autoYes) {
276
- console.log(chalk_1.default.blue('\nInstallation cancelled.'));
277
- console.log(chalk_1.default.blue('To install manually: curl -fsSL https://opencode.ai/install | bash'));
278
- console.log(chalk_1.default.blue('Or visit: https://opencode.ai/docs'));
279
- }
280
- }
281
- }
282
- return opencodeInstalled;
283
- });
284
- }
285
39
  /**
286
40
  * Register code commands
287
41
  */
@@ -293,14 +47,14 @@ function registerCodeCommands(program) {
293
47
  code
294
48
  .command('setup')
295
49
  .description('Interactive setup for Berget AI coding tools')
296
- .action(() => __awaiter(this, void 0, void 0, function* () {
50
+ .action(async () => {
297
51
  try {
298
- yield (0, setup_1.runSetupCommand)();
52
+ await (0, setup_1.runSetupCommand)();
299
53
  }
300
54
  catch (error) {
301
55
  (0, error_handler_1.handleError)('Setup failed', error);
302
56
  }
303
- }));
57
+ });
304
58
  }
305
59
  code
306
60
  .command(command_structure_1.SUBCOMMANDS.CODE.INIT)
@@ -308,17 +62,17 @@ function registerCodeCommands(program) {
308
62
  .option('-n, --name <name>', 'Project name (defaults to directory name)')
309
63
  .option('-f, --force', 'Overwrite existing configuration')
310
64
  .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
311
- .action((options) => __awaiter(this, void 0, void 0, function* () {
65
+ .action(async (options) => {
312
66
  try {
313
67
  const projectName = options.name || getProjectName();
314
- const configPath = path_1.default.join(process.cwd(), 'opencode.json');
68
+ const configPath = node_path_1.default.join(process.cwd(), 'opencode.json');
315
69
  // Check if already initialized
316
70
  if (fs.existsSync(configPath) && !options.force) {
317
71
  if (!options.yes) {
318
72
  console.log(chalk_1.default.yellow('Project already initialized for OpenCode.'));
319
73
  console.log(chalk_1.default.dim(`Config file: ${configPath}`));
320
74
  }
321
- if (yield confirm('Do you want to reinitialize? (Y/n): ', options.yes)) {
75
+ if (await confirm('Do you want to reinitialize? (Y/n): ', options.yes)) {
322
76
  // Continue with reinitialization
323
77
  }
324
78
  else {
@@ -326,7 +80,7 @@ function registerCodeCommands(program) {
326
80
  }
327
81
  }
328
82
  // Ensure opencode is installed
329
- if (!(yield ensureOpencodeInstalled(options.yes))) {
83
+ if (!(await ensureOpencodeInstalled(options.yes))) {
330
84
  return;
331
85
  }
332
86
  console.log(chalk_1.default.cyan(`Initializing OpenCode for project: ${projectName}`));
@@ -334,7 +88,7 @@ function registerCodeCommands(program) {
334
88
  $schema: 'https://opencode.ai/config.json',
335
89
  plugin: ['@bergetai/opencode-auth@1.0.16'],
336
90
  };
337
- const agentsDir = path_1.default.join(process.cwd(), '.opencode', 'agents');
91
+ const agentsDir = node_path_1.default.join(process.cwd(), '.opencode', 'agents');
338
92
  const templatesDir = getAgentTemplatesDir();
339
93
  if (!options.yes) {
340
94
  console.log(chalk_1.default.blue('\nAbout to create configuration files:'));
@@ -342,19 +96,17 @@ function registerCodeCommands(program) {
342
96
  console.log(chalk_1.default.dim(`Agents: ${agentsDir}/`));
343
97
  console.log(chalk_1.default.dim('This will configure OpenCode with the Berget auth plugin.'));
344
98
  }
345
- if (yield confirm('\nCreate configuration files? (Y/n): ', options.yes)) {
99
+ if (await confirm('\nCreate configuration files? (Y/n): ', options.yes)) {
346
100
  try {
347
- yield (0, promises_1.writeFile)(configPath, JSON.stringify(config, null, 2));
101
+ await (0, promises_1.writeFile)(configPath, JSON.stringify(config, null, 2));
348
102
  console.log(chalk_1.default.green('✓ Created opencode.json'));
349
103
  console.log(chalk_1.default.dim(' Plugin: @bergetai/opencode-auth'));
350
104
  fs.mkdirSync(agentsDir, { recursive: true });
351
- const templateFiles = fs
352
- .readdirSync(templatesDir)
353
- .filter((f) => f.endsWith('.md'));
105
+ const templateFiles = fs.readdirSync(templatesDir).filter((f) => f.endsWith('.md'));
354
106
  for (const file of templateFiles) {
355
- const src = path_1.default.join(templatesDir, file);
356
- const dest = path_1.default.join(agentsDir, file);
357
- fs.copyFileSync(src, dest);
107
+ const source = node_path_1.default.join(templatesDir, file);
108
+ const destination = node_path_1.default.join(agentsDir, file);
109
+ fs.copyFileSync(source, destination);
358
110
  }
359
111
  console.log(chalk_1.default.green(`✓ Created ${templateFiles.length} agent definitions in .opencode/agents/`));
360
112
  }
@@ -379,7 +131,7 @@ function registerCodeCommands(program) {
379
131
  catch (error) {
380
132
  (0, error_handler_1.handleError)('Failed to initialize project', error);
381
133
  }
382
- }));
134
+ });
383
135
  code
384
136
  .command(command_structure_1.SUBCOMMANDS.CODE.RUN)
385
137
  .description('Run AI coding assistant')
@@ -388,22 +140,22 @@ function registerCodeCommands(program) {
388
140
  .option('-a, --analysis', 'Use fast analysis model for context building')
389
141
  .option('--no-config', 'Run without loading project config')
390
142
  .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
391
- .action((prompt, options) => __awaiter(this, void 0, void 0, function* () {
143
+ .action(async (prompt, options) => {
392
144
  try {
393
- const configPath = path_1.default.join(process.cwd(), 'opencode.json');
145
+ const configPath = node_path_1.default.join(process.cwd(), 'opencode.json');
394
146
  // Ensure opencode is installed
395
- if (!(yield ensureOpencodeInstalled(options.yes))) {
147
+ if (!(await ensureOpencodeInstalled(options.yes))) {
396
148
  return;
397
149
  }
398
150
  let config = null;
399
151
  if (!options.noConfig && fs.existsSync(configPath)) {
400
152
  try {
401
- const configContent = yield (0, promises_1.readFile)(configPath, 'utf8');
153
+ const configContent = await (0, promises_1.readFile)(configPath, 'utf8');
402
154
  config = JSON.parse(configContent);
403
155
  console.log(chalk_1.default.dim(`Loaded config for project: ${config.projectName}`));
404
156
  console.log(chalk_1.default.dim(`Models: Analysis=${config.analysisModel}, Build=${config.buildModel}`));
405
157
  }
406
- catch (error) {
158
+ catch {
407
159
  console.log(chalk_1.default.yellow('Warning: Failed to load opencode.json'));
408
160
  }
409
161
  }
@@ -413,24 +165,24 @@ function registerCodeCommands(program) {
413
165
  return;
414
166
  }
415
167
  // Prepare opencode command
416
- const env = Object.assign({}, process.env);
417
- const opencodeArgs = [];
168
+ const environment = { ...process.env };
169
+ const opencodeArguments = [];
418
170
  // Read --stage and --local from root program options
419
171
  // (these flags are registered at program level, not subcommand level)
420
172
  const isStage = process.argv.includes('--stage');
421
173
  const isLocal = process.argv.includes('--local');
422
174
  if (isStage) {
423
175
  console.log(chalk_1.default.cyan('Using Berget stage environment'));
424
- env.BERGET_API_URL = 'https://api.stage.berget.ai';
425
- env.BERGET_INFERENCE_URL = 'https://api.stage.berget.ai/v1';
176
+ environment.BERGET_API_URL = 'https://api.stage.berget.ai';
177
+ environment.BERGET_INFERENCE_URL = 'https://api.stage.berget.ai/v1';
426
178
  }
427
179
  else if (isLocal) {
428
180
  console.log(chalk_1.default.cyan('Using local development environment'));
429
- env.BERGET_API_URL = 'http://localhost:3000';
430
- env.BERGET_INFERENCE_URL = 'http://localhost:3000/v1';
181
+ environment.BERGET_API_URL = 'http://localhost:3000';
182
+ environment.BERGET_INFERENCE_URL = 'http://localhost:3000/v1';
431
183
  }
432
184
  if (prompt) {
433
- opencodeArgs.push('run', prompt);
185
+ opencodeArguments.push('run', prompt);
434
186
  }
435
187
  // Choose model based on analysis flag or override
436
188
  let selectedModel = options.model || config.buildModel;
@@ -438,13 +190,13 @@ function registerCodeCommands(program) {
438
190
  selectedModel = config.analysisModel;
439
191
  }
440
192
  if (selectedModel) {
441
- opencodeArgs.push('--model', selectedModel);
193
+ opencodeArguments.push('--model', selectedModel);
442
194
  }
443
195
  console.log(chalk_1.default.cyan('Starting OpenCode...'));
444
196
  // Spawn opencode process
445
- const opencode = (0, child_process_1.spawn)('opencode', opencodeArgs, {
197
+ const opencode = (0, node_child_process_1.spawn)('opencode', opencodeArguments, {
198
+ env: environment,
446
199
  stdio: 'inherit',
447
- env: env,
448
200
  });
449
201
  opencode.on('close', (code) => {
450
202
  if (code !== 0) {
@@ -459,30 +211,30 @@ function registerCodeCommands(program) {
459
211
  catch (error) {
460
212
  (0, error_handler_1.handleError)('Failed to run OpenCode', error);
461
213
  }
462
- }));
214
+ });
463
215
  code
464
216
  .command(command_structure_1.SUBCOMMANDS.CODE.SERVE)
465
217
  .description('Start OpenCode web server')
466
218
  .option('-p, --port <port>', 'Port to run the server on (default: 3000)')
467
219
  .option('-h, --host <host>', 'Host to bind the server to (default: localhost)')
468
220
  .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
469
- .action((options) => __awaiter(this, void 0, void 0, function* () {
221
+ .action(async (options) => {
470
222
  try {
471
223
  // Ensure opencode is installed
472
- if (!(yield ensureOpencodeInstalled(options.yes))) {
224
+ if (!(await ensureOpencodeInstalled(options.yes))) {
473
225
  return;
474
226
  }
475
227
  console.log(chalk_1.default.cyan('🚀 Starting OpenCode web server...'));
476
228
  // Prepare opencode serve command
477
- const serveArgs = ['serve'];
229
+ const serveArguments = ['serve'];
478
230
  if (options.port) {
479
- serveArgs.push('--port', options.port);
231
+ serveArguments.push('--port', options.port);
480
232
  }
481
233
  if (options.host) {
482
- serveArgs.push('--host', options.host);
234
+ serveArguments.push('--host', options.host);
483
235
  }
484
236
  // Spawn opencode serve process
485
- const opencode = (0, child_process_1.spawn)('opencode', serveArgs, {
237
+ const opencode = (0, node_child_process_1.spawn)('opencode', serveArguments, {
486
238
  stdio: 'inherit',
487
239
  });
488
240
  opencode.on('close', (code) => {
@@ -498,20 +250,20 @@ function registerCodeCommands(program) {
498
250
  catch (error) {
499
251
  (0, error_handler_1.handleError)('Failed to start OpenCode server', error);
500
252
  }
501
- }));
253
+ });
502
254
  code
503
255
  .command(command_structure_1.SUBCOMMANDS.CODE.UPDATE)
504
256
  .description('Update OpenCode and agents to latest versions')
505
257
  .option('-f, --force', 'Force update even if already latest')
506
258
  .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
507
- .action((options) => __awaiter(this, void 0, void 0, function* () {
259
+ .action(async (options) => {
508
260
  try {
509
261
  console.log(chalk_1.default.cyan('🔄 Updating OpenCode configuration...'));
510
262
  // Ensure opencode is installed first
511
- if (!(yield ensureOpencodeInstalled(options.yes))) {
263
+ if (!(await ensureOpencodeInstalled(options.yes))) {
512
264
  return;
513
265
  }
514
- const configPath = path_1.default.join(process.cwd(), 'opencode.json');
266
+ const configPath = node_path_1.default.join(process.cwd(), 'opencode.json');
515
267
  // Check if project is initialized
516
268
  if (!fs.existsSync(configPath)) {
517
269
  console.log(chalk_1.default.red('❌ No OpenCode configuration found.'));
@@ -521,7 +273,7 @@ function registerCodeCommands(program) {
521
273
  // Read current configuration
522
274
  let currentConfig;
523
275
  try {
524
- const configContent = yield (0, promises_1.readFile)(configPath, 'utf8');
276
+ const configContent = await (0, promises_1.readFile)(configPath, 'utf8');
525
277
  currentConfig = JSON.parse(configContent);
526
278
  }
527
279
  catch (error) {
@@ -533,30 +285,21 @@ function registerCodeCommands(program) {
533
285
  if (currentConfig.model) {
534
286
  console.log(chalk_1.default.dim(` Model: ${currentConfig.model}`));
535
287
  }
536
- const agentsDir = path_1.default.join(process.cwd(), '.opencode', 'agents');
288
+ const agentsDir = node_path_1.default.join(process.cwd(), '.opencode', 'agents');
537
289
  const templatesDir = getAgentTemplatesDir();
538
- const templateFiles = fs
539
- .readdirSync(templatesDir)
540
- .filter((f) => f.endsWith('.md'));
541
- const latestConfig = {
542
- $schema: 'https://opencode.ai/config.json',
543
- plugin: ['@bergetai/opencode-auth@1.0.16'],
544
- };
290
+ const templateFiles = fs.readdirSync(templatesDir).filter((f) => f.endsWith('.md'));
545
291
  // Check if agent definitions need updating
546
292
  let agentsNeedUpdate = false;
547
- const existingAgentFiles = fs.existsSync(agentsDir)
548
- ? fs.readdirSync(agentsDir).filter((f) => f.endsWith('.md'))
549
- : [];
550
293
  for (const file of templateFiles) {
551
- const src = path_1.default.join(templatesDir, file);
552
- const dest = path_1.default.join(agentsDir, file);
553
- if (!fs.existsSync(dest)) {
294
+ const source = node_path_1.default.join(templatesDir, file);
295
+ const destination = node_path_1.default.join(agentsDir, file);
296
+ if (!fs.existsSync(destination)) {
554
297
  agentsNeedUpdate = true;
555
298
  break;
556
299
  }
557
- const srcContent = fs.readFileSync(src, 'utf8');
558
- const destContent = fs.readFileSync(dest, 'utf8');
559
- if (srcContent !== destContent) {
300
+ const sourceContent = fs.readFileSync(source, 'utf8');
301
+ const destinationContent = fs.readFileSync(destination, 'utf8');
302
+ if (sourceContent !== destinationContent) {
560
303
  agentsNeedUpdate = true;
561
304
  break;
562
305
  }
@@ -582,37 +325,37 @@ function registerCodeCommands(program) {
582
325
  if (!options.yes) {
583
326
  console.log(chalk_1.default.blue('\nThis will update your agent definitions and OpenCode configuration.'));
584
327
  const hasGitRepo = hasGit();
585
- if (!hasGitRepo) {
586
- console.log(chalk_1.default.yellow('⚠️ No .git repository detected - backup will be created'));
328
+ if (hasGitRepo) {
329
+ console.log(chalk_1.default.green(' Git repository detected - changes are tracked'));
587
330
  }
588
331
  else {
589
- console.log(chalk_1.default.green(' Git repository detected - changes are tracked'));
332
+ console.log(chalk_1.default.yellow('⚠️ No .git repository detected - backup will be created'));
590
333
  }
591
334
  }
592
- if (yield confirm('\nProceed with update? (Y/n): ', options.yes)) {
335
+ if (await confirm('\nProceed with update? (Y/n): ', options.yes)) {
593
336
  try {
594
337
  let backupPath = null;
595
338
  if (!hasGit()) {
596
339
  backupPath = `${configPath}.backup.${Date.now()}`;
597
- yield (0, promises_1.writeFile)(backupPath, JSON.stringify(currentConfig, null, 2));
598
- console.log(chalk_1.default.green(`✓ Backed up current config to ${path_1.default.basename(backupPath)}`));
340
+ await (0, promises_1.writeFile)(backupPath, JSON.stringify(currentConfig, null, 2));
341
+ console.log(chalk_1.default.green(`✓ Backed up current config to ${node_path_1.default.basename(backupPath)}`));
599
342
  }
600
343
  // Remove inline agent section from opencode.json if present
601
344
  if (currentConfig.agent) {
602
345
  delete currentConfig.agent;
603
- yield (0, promises_1.writeFile)(configPath, JSON.stringify(currentConfig, null, 2));
346
+ await (0, promises_1.writeFile)(configPath, JSON.stringify(currentConfig, null, 2));
604
347
  console.log(chalk_1.default.green('✓ Removed inline agent config from opencode.json'));
605
348
  }
606
349
  // Sync agent markdown files from templates
607
350
  fs.mkdirSync(agentsDir, { recursive: true });
608
351
  let updatedCount = 0;
609
352
  for (const file of templateFiles) {
610
- const src = path_1.default.join(templatesDir, file);
611
- const dest = path_1.default.join(agentsDir, file);
612
- const agentName = path_1.default.basename(file, '.md');
613
- if (!fs.existsSync(dest) ||
614
- fs.readFileSync(src, 'utf8') !== fs.readFileSync(dest, 'utf8')) {
615
- fs.copyFileSync(src, dest);
353
+ const source = node_path_1.default.join(templatesDir, file);
354
+ const destination = node_path_1.default.join(agentsDir, file);
355
+ const agentName = node_path_1.default.basename(file, '.md');
356
+ if (!fs.existsSync(destination) ||
357
+ fs.readFileSync(source, 'utf8') !== fs.readFileSync(destination, 'utf8')) {
358
+ fs.copyFileSync(source, destination);
616
359
  updatedCount++;
617
360
  console.log(chalk_1.default.cyan(` • Updated agent: ${agentName}`));
618
361
  }
@@ -621,7 +364,7 @@ function registerCodeCommands(program) {
621
364
  console.log(chalk_1.default.green(`✓ Updated ${updatedCount} agent definition(s)`));
622
365
  }
623
366
  // Update AGENTS.md if it doesn't exist
624
- const agentsMdPath = path_1.default.join(process.cwd(), 'AGENTS.md');
367
+ const agentsMdPath = node_path_1.default.join(process.cwd(), 'AGENTS.md');
625
368
  if (!fs.existsSync(agentsMdPath)) {
626
369
  const agentsMdContent = `# Berget Code Agents
627
370
 
@@ -672,7 +415,7 @@ See https://opencode.ai/docs/agents/ for available options.
672
415
 
673
416
  *Updated by berget code update*
674
417
  `;
675
- yield (0, promises_1.writeFile)(agentsMdPath, agentsMdContent);
418
+ await (0, promises_1.writeFile)(agentsMdPath, agentsMdContent);
676
419
  console.log(chalk_1.default.green('✓ Created AGENTS.md documentation'));
677
420
  }
678
421
  console.log(chalk_1.default.green('\n✅ Update completed successfully!'));
@@ -681,10 +424,10 @@ See https://opencode.ai/docs/agents/ for available options.
681
424
  console.error(chalk_1.default.red('Failed to update configuration:'));
682
425
  (0, error_handler_1.handleError)('Update failed', error);
683
426
  try {
684
- yield (0, promises_1.writeFile)(configPath, JSON.stringify(currentConfig, null, 2));
427
+ await (0, promises_1.writeFile)(configPath, JSON.stringify(currentConfig, null, 2));
685
428
  console.log(chalk_1.default.yellow('📁 Restored original configuration from backup'));
686
429
  }
687
- catch (restoreError) {
430
+ catch {
688
431
  console.error(chalk_1.default.red('Failed to restore backup:'));
689
432
  }
690
433
  }
@@ -693,9 +436,139 @@ See https://opencode.ai/docs/agents/ for available options.
693
436
  console.log(chalk_1.default.yellow('Update cancelled.'));
694
437
  }
695
438
  }
696
- catch (error) {
697
- (0, error_handler_1.handleError)('Failed to update OpenCode configuration', error);
439
+ catch {
440
+ console.error(chalk_1.default.red('Failed to update OpenCode configuration'));
698
441
  }
699
- }));
442
+ });
700
443
  }
701
444
  exports.registerCodeCommands = registerCodeCommands;
445
+ /**
446
+ * Check if opencode is installed
447
+ */
448
+ function checkOpencodeInstalled() {
449
+ return new Promise((resolve) => {
450
+ const child = (0, node_child_process_1.spawn)('which', ['opencode'], {
451
+ stdio: 'pipe',
452
+ });
453
+ child.on('close', (code) => {
454
+ resolve(code === 0);
455
+ });
456
+ child.on('error', () => {
457
+ resolve(false);
458
+ });
459
+ });
460
+ }
461
+ /**
462
+ * Helper function to get user confirmation
463
+ */
464
+ async function confirm(question, autoYes = false) {
465
+ if (autoYes) {
466
+ return true;
467
+ }
468
+ return new Promise((resolve) => {
469
+ const rl = node_readline_1.default.createInterface({
470
+ input: process.stdin,
471
+ output: process.stdout,
472
+ });
473
+ rl.question(question, (answer) => {
474
+ rl.close();
475
+ resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes' || answer === '');
476
+ });
477
+ });
478
+ }
479
+ /**
480
+ * Ensure opencode is installed, offering to install if not
481
+ */
482
+ async function ensureOpencodeInstalled(autoYes = false) {
483
+ let opencodeInstalled = await checkOpencodeInstalled();
484
+ if (!opencodeInstalled) {
485
+ if (!autoYes) {
486
+ console.log(chalk_1.default.red('OpenCode is not installed.'));
487
+ console.log(chalk_1.default.blue('OpenCode is required for the AI coding assistant.'));
488
+ }
489
+ if (await confirm('Would you like to install OpenCode automatically? (Y/n): ', autoYes)) {
490
+ opencodeInstalled = await installOpencode();
491
+ }
492
+ else {
493
+ if (!autoYes) {
494
+ console.log(chalk_1.default.blue('\nInstallation cancelled.'));
495
+ console.log(chalk_1.default.blue('To install manually: curl -fsSL https://opencode.ai/install | bash'));
496
+ console.log(chalk_1.default.blue('Or visit: https://opencode.ai/docs'));
497
+ }
498
+ }
499
+ }
500
+ return opencodeInstalled;
501
+ }
502
+ /**
503
+ * Get the path to the bundled agent templates directory
504
+ */
505
+ function getAgentTemplatesDir() {
506
+ return node_path_1.default.resolve(__dirname, '../../templates/agents');
507
+ }
508
+ /**
509
+ * Get project name from current directory or package.json
510
+ */
511
+ function getProjectName() {
512
+ try {
513
+ const packageJsonPath = node_path_1.default.join(process.cwd(), 'package.json');
514
+ if (fs.existsSync(packageJsonPath)) {
515
+ const packageJsonContent = fs.readFileSync(packageJsonPath, 'utf8');
516
+ const packageJson = JSON.parse(packageJsonContent);
517
+ return packageJson.name || node_path_1.default.basename(process.cwd());
518
+ }
519
+ }
520
+ catch {
521
+ // Ignore error and fallback to directory name
522
+ }
523
+ return node_path_1.default.basename(process.cwd());
524
+ }
525
+ /**
526
+ * Check if current directory has git
527
+ */
528
+ function hasGit() {
529
+ try {
530
+ return fs.existsSync(node_path_1.default.join(process.cwd(), '.git'));
531
+ }
532
+ catch {
533
+ return false;
534
+ }
535
+ }
536
+ /**
537
+ * Install opencode via npm
538
+ */
539
+ async function installOpencode() {
540
+ console.log(chalk_1.default.cyan('Installing OpenCode via npm...'));
541
+ try {
542
+ await new Promise((resolve, reject) => {
543
+ const install = (0, node_child_process_1.spawn)('npm', ['install', '-g', 'opencode-ai@1.3'], {
544
+ stdio: 'inherit',
545
+ });
546
+ install.on('close', (code) => {
547
+ if (code === 0) {
548
+ console.log(chalk_1.default.green('✓ OpenCode installed successfully!'));
549
+ resolve();
550
+ }
551
+ else {
552
+ reject(new Error(`Installation failed with code ${code}`));
553
+ }
554
+ });
555
+ install.on('error', reject);
556
+ });
557
+ // Verify installation
558
+ const opencodeInstalled = await checkOpencodeInstalled();
559
+ if (!opencodeInstalled) {
560
+ console.log(chalk_1.default.yellow('Installation completed but opencode command not found.'));
561
+ console.log(chalk_1.default.yellow('You may need to restart your terminal or check your PATH.'));
562
+ return false;
563
+ }
564
+ return true;
565
+ }
566
+ catch (error) {
567
+ console.error(chalk_1.default.red('Failed to install OpenCode:'));
568
+ console.error(error instanceof Error ? error.message : String(error));
569
+ console.log(chalk_1.default.blue('\nAlternative installation methods:'));
570
+ console.log(chalk_1.default.blue(' curl -fsSL https://opencode.ai/install | sh'));
571
+ console.log(chalk_1.default.blue(' Or visit: https://opencode.ai/docs'));
572
+ return false;
573
+ }
574
+ }