berget 2.2.7 → 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 (130) hide show
  1. package/.github/workflows/publish.yml +6 -6
  2. package/.github/workflows/test.yml +1 -1
  3. package/.prettierrc +5 -3
  4. package/dist/index.js +24 -25
  5. package/dist/package.json +5 -3
  6. package/dist/src/agents/app.js +8 -8
  7. package/dist/src/agents/backend.js +3 -3
  8. package/dist/src/agents/devops.js +8 -8
  9. package/dist/src/agents/frontend.js +3 -3
  10. package/dist/src/agents/fullstack.js +3 -3
  11. package/dist/src/agents/index.js +18 -18
  12. package/dist/src/agents/quality.js +8 -8
  13. package/dist/src/agents/security.js +8 -8
  14. package/dist/src/client.js +115 -127
  15. package/dist/src/commands/api-keys.js +195 -202
  16. package/dist/src/commands/auth.js +16 -25
  17. package/dist/src/commands/autocomplete.js +8 -8
  18. package/dist/src/commands/billing.js +10 -19
  19. package/dist/src/commands/chat.js +139 -170
  20. package/dist/src/commands/clusters.js +21 -30
  21. package/dist/src/commands/code/__tests__/auth-sync.test.js +189 -186
  22. package/dist/src/commands/code/__tests__/fake-api-key-service.js +3 -13
  23. package/dist/src/commands/code/__tests__/fake-auth-service.js +21 -29
  24. package/dist/src/commands/code/__tests__/fake-command-runner.js +22 -33
  25. package/dist/src/commands/code/__tests__/fake-file-store.js +19 -41
  26. package/dist/src/commands/code/__tests__/fake-prompter.js +81 -97
  27. package/dist/src/commands/code/__tests__/setup-flow.test.js +295 -295
  28. package/dist/src/commands/code/adapters/clack-prompter.js +15 -32
  29. package/dist/src/commands/code/adapters/fs-file-store.js +25 -44
  30. package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -41
  31. package/dist/src/commands/code/auth-sync.js +215 -228
  32. package/dist/src/commands/code/errors.js +15 -12
  33. package/dist/src/commands/code/setup.js +390 -425
  34. package/dist/src/commands/code.js +279 -294
  35. package/dist/src/commands/index.js +5 -5
  36. package/dist/src/commands/models.js +16 -25
  37. package/dist/src/commands/users.js +9 -18
  38. package/dist/src/constants/command-structure.js +138 -138
  39. package/dist/src/services/api-key-service.js +132 -152
  40. package/dist/src/services/auth-service.js +81 -95
  41. package/dist/src/services/browser-auth.js +121 -131
  42. package/dist/src/services/chat-service.js +369 -386
  43. package/dist/src/services/cluster-service.js +47 -62
  44. package/dist/src/services/collaborator-service.js +9 -21
  45. package/dist/src/services/flux-service.js +13 -25
  46. package/dist/src/services/helm-service.js +9 -21
  47. package/dist/src/services/kubectl-service.js +15 -29
  48. package/dist/src/utils/config-checker.js +7 -7
  49. package/dist/src/utils/config-loader.js +109 -109
  50. package/dist/src/utils/default-api-key.js +129 -139
  51. package/dist/src/utils/env-manager.js +55 -66
  52. package/dist/src/utils/error-handler.js +62 -62
  53. package/dist/src/utils/logger.js +74 -67
  54. package/dist/src/utils/markdown-renderer.js +28 -28
  55. package/dist/src/utils/opencode-validator.js +67 -69
  56. package/dist/src/utils/token-manager.js +67 -65
  57. package/dist/tests/commands/chat.test.js +30 -39
  58. package/dist/tests/commands/code.test.js +186 -195
  59. package/dist/tests/utils/config-loader.test.js +107 -107
  60. package/dist/tests/utils/env-manager.test.js +81 -90
  61. package/dist/tests/utils/opencode-validator.test.js +42 -41
  62. package/dist/vitest.config.js +1 -1
  63. package/eslint.config.mjs +50 -30
  64. package/index.ts +30 -31
  65. package/package.json +5 -3
  66. package/src/agents/app.ts +9 -9
  67. package/src/agents/backend.ts +4 -4
  68. package/src/agents/devops.ts +9 -9
  69. package/src/agents/frontend.ts +4 -4
  70. package/src/agents/fullstack.ts +4 -4
  71. package/src/agents/index.ts +27 -25
  72. package/src/agents/quality.ts +9 -9
  73. package/src/agents/security.ts +9 -9
  74. package/src/agents/types.ts +10 -10
  75. package/src/client.ts +85 -77
  76. package/src/commands/api-keys.ts +190 -185
  77. package/src/commands/auth.ts +15 -14
  78. package/src/commands/autocomplete.ts +10 -10
  79. package/src/commands/billing.ts +13 -12
  80. package/src/commands/chat.ts +145 -142
  81. package/src/commands/clusters.ts +20 -19
  82. package/src/commands/code/__tests__/auth-sync.test.ts +176 -175
  83. package/src/commands/code/__tests__/fake-api-key-service.ts +2 -2
  84. package/src/commands/code/__tests__/fake-auth-service.ts +18 -18
  85. package/src/commands/code/__tests__/fake-command-runner.ts +28 -22
  86. package/src/commands/code/__tests__/fake-file-store.ts +15 -15
  87. package/src/commands/code/__tests__/fake-prompter.ts +86 -85
  88. package/src/commands/code/__tests__/setup-flow.test.ts +253 -251
  89. package/src/commands/code/adapters/clack-prompter.ts +32 -30
  90. package/src/commands/code/adapters/fs-file-store.ts +18 -17
  91. package/src/commands/code/adapters/spawn-command-runner.ts +20 -15
  92. package/src/commands/code/auth-sync.ts +210 -210
  93. package/src/commands/code/errors.ts +11 -11
  94. package/src/commands/code/ports/auth-services.ts +7 -7
  95. package/src/commands/code/ports/command-runner.ts +2 -2
  96. package/src/commands/code/ports/file-store.ts +3 -3
  97. package/src/commands/code/ports/prompter.ts +13 -13
  98. package/src/commands/code/setup.ts +408 -406
  99. package/src/commands/code.ts +288 -287
  100. package/src/commands/index.ts +11 -10
  101. package/src/commands/models.ts +19 -18
  102. package/src/commands/users.ts +11 -10
  103. package/src/constants/command-structure.ts +159 -159
  104. package/src/services/api-key-service.ts +85 -85
  105. package/src/services/auth-service.ts +55 -54
  106. package/src/services/browser-auth.ts +62 -62
  107. package/src/services/chat-service.ts +169 -170
  108. package/src/services/cluster-service.ts +28 -28
  109. package/src/services/collaborator-service.ts +6 -6
  110. package/src/services/flux-service.ts +17 -17
  111. package/src/services/helm-service.ts +11 -11
  112. package/src/services/kubectl-service.ts +12 -12
  113. package/src/types/api.d.ts +1933 -1933
  114. package/src/types/json.d.ts +1 -1
  115. package/src/utils/config-checker.ts +6 -6
  116. package/src/utils/config-loader.ts +130 -129
  117. package/src/utils/default-api-key.ts +81 -80
  118. package/src/utils/env-manager.ts +37 -37
  119. package/src/utils/error-handler.ts +64 -64
  120. package/src/utils/logger.ts +72 -66
  121. package/src/utils/markdown-renderer.ts +28 -28
  122. package/src/utils/opencode-validator.ts +72 -71
  123. package/src/utils/token-manager.ts +69 -68
  124. package/tests/commands/chat.test.ts +32 -31
  125. package/tests/commands/code.test.ts +182 -181
  126. package/tests/utils/config-loader.test.ts +111 -110
  127. package/tests/utils/env-manager.test.ts +83 -79
  128. package/tests/utils/opencode-validator.test.ts +43 -42
  129. package/tsconfig.json +2 -1
  130. package/vitest.config.ts +2 -2
@@ -22,202 +22,57 @@ 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" || answer.toLowerCase() === "yes" || answer === "");
75
- });
76
- });
77
- });
78
- }
79
- /**
80
- * Get project name from current directory or package.json
81
- */
82
- function getProjectName() {
83
- try {
84
- const packageJsonPath = path_1.default.join(process.cwd(), "package.json");
85
- if (fs.existsSync(packageJsonPath)) {
86
- const packageJsonContent = fs.readFileSync(packageJsonPath, "utf8");
87
- const packageJson = JSON.parse(packageJsonContent);
88
- return packageJson.name || path_1.default.basename(process.cwd());
89
- }
90
- }
91
- catch (_a) {
92
- // Ignore error and fallback to directory name
93
- }
94
- return path_1.default.basename(process.cwd());
95
- }
96
- /**
97
- * Get the path to the bundled agent templates directory
98
- */
99
- function getAgentTemplatesDir() {
100
- return path_1.default.resolve(__dirname, "../../templates/agents");
101
- }
102
- /**
103
- * Check if opencode is installed
104
- */
105
- function checkOpencodeInstalled() {
106
- return new Promise(resolve => {
107
- const child = (0, child_process_1.spawn)("which", ["opencode"], {
108
- stdio: "pipe",
109
- });
110
- child.on("close", code => {
111
- resolve(code === 0);
112
- });
113
- child.on("error", () => {
114
- resolve(false);
115
- });
116
- });
117
- }
118
- /**
119
- * Install opencode via npm
120
- */
121
- function installOpencode() {
122
- return __awaiter(this, void 0, void 0, function* () {
123
- console.log(chalk_1.default.cyan("Installing OpenCode via npm..."));
124
- try {
125
- yield new Promise((resolve, reject) => {
126
- const install = (0, child_process_1.spawn)("npm", ["install", "-g", "opencode-ai@1.3"], {
127
- stdio: "inherit",
128
- });
129
- install.on("close", code => {
130
- if (code === 0) {
131
- console.log(chalk_1.default.green("✓ OpenCode installed successfully!"));
132
- resolve();
133
- }
134
- else {
135
- reject(new Error(`Installation failed with code ${code}`));
136
- }
137
- });
138
- install.on("error", reject);
139
- });
140
- // Verify installation
141
- const opencodeInstalled = yield checkOpencodeInstalled();
142
- if (!opencodeInstalled) {
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."));
145
- return false;
146
- }
147
- return true;
148
- }
149
- catch (error) {
150
- console.error(chalk_1.default.red("Failed to install OpenCode:"));
151
- console.error(error instanceof Error ? error.message : String(error));
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"));
155
- return false;
156
- }
157
- });
158
- }
159
- /**
160
- * Ensure opencode is installed, offering to install if not
161
- */
162
- function ensureOpencodeInstalled(autoYes = false) {
163
- return __awaiter(this, void 0, void 0, function* () {
164
- let opencodeInstalled = yield checkOpencodeInstalled();
165
- if (!opencodeInstalled) {
166
- if (!autoYes) {
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."));
169
- }
170
- if (yield confirm("Would you like to install OpenCode automatically? (Y/n): ", autoYes)) {
171
- opencodeInstalled = yield installOpencode();
172
- }
173
- else {
174
- if (!autoYes) {
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"));
178
- }
179
- }
180
- }
181
- return opencodeInstalled;
182
- });
183
- }
184
39
  /**
185
40
  * Register code commands
186
41
  */
187
42
  function registerCodeCommands(program) {
188
43
  const code = program
189
44
  .command(command_structure_1.COMMAND_GROUPS.CODE)
190
- .description("AI-powered coding assistant with OpenCode");
45
+ .description('AI-powered coding assistant with OpenCode');
191
46
  if (process.env.BERGET_EXPERIMENTAL) {
192
47
  code
193
- .command("setup")
194
- .description("Interactive setup for Berget AI coding tools")
195
- .action(() => __awaiter(this, void 0, void 0, function* () {
48
+ .command('setup')
49
+ .description('Interactive setup for Berget AI coding tools')
50
+ .action(async () => {
196
51
  try {
197
- yield (0, setup_1.runSetupCommand)();
52
+ await (0, setup_1.runSetupCommand)();
198
53
  }
199
54
  catch (error) {
200
- (0, error_handler_1.handleError)("Setup failed", error);
55
+ (0, error_handler_1.handleError)('Setup failed', error);
201
56
  }
202
- }));
57
+ });
203
58
  }
204
59
  code
205
60
  .command(command_structure_1.SUBCOMMANDS.CODE.INIT)
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)")
210
- .action((options) => __awaiter(this, void 0, void 0, function* () {
61
+ .description('Initialize project for AI coding assistant')
62
+ .option('-n, --name <name>', 'Project name (defaults to directory name)')
63
+ .option('-f, --force', 'Overwrite existing configuration')
64
+ .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
65
+ .action(async (options) => {
211
66
  try {
212
67
  const projectName = options.name || getProjectName();
213
- const configPath = path_1.default.join(process.cwd(), "opencode.json");
68
+ const configPath = node_path_1.default.join(process.cwd(), 'opencode.json');
214
69
  // Check if already initialized
215
70
  if (fs.existsSync(configPath) && !options.force) {
216
71
  if (!options.yes) {
217
- console.log(chalk_1.default.yellow("Project already initialized for OpenCode."));
72
+ console.log(chalk_1.default.yellow('Project already initialized for OpenCode.'));
218
73
  console.log(chalk_1.default.dim(`Config file: ${configPath}`));
219
74
  }
220
- 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)) {
221
76
  // Continue with reinitialization
222
77
  }
223
78
  else {
@@ -225,109 +80,109 @@ function registerCodeCommands(program) {
225
80
  }
226
81
  }
227
82
  // Ensure opencode is installed
228
- if (!(yield ensureOpencodeInstalled(options.yes))) {
83
+ if (!(await ensureOpencodeInstalled(options.yes))) {
229
84
  return;
230
85
  }
231
86
  console.log(chalk_1.default.cyan(`Initializing OpenCode for project: ${projectName}`));
232
87
  const config = {
233
- $schema: "https://opencode.ai/config.json",
234
- plugin: ["@bergetai/opencode-auth@1.0.16"],
88
+ $schema: 'https://opencode.ai/config.json',
89
+ plugin: ['@bergetai/opencode-auth@1.0.16'],
235
90
  };
236
- const agentsDir = path_1.default.join(process.cwd(), ".opencode", "agents");
91
+ const agentsDir = node_path_1.default.join(process.cwd(), '.opencode', 'agents');
237
92
  const templatesDir = getAgentTemplatesDir();
238
93
  if (!options.yes) {
239
- console.log(chalk_1.default.blue("\nAbout to create configuration files:"));
94
+ console.log(chalk_1.default.blue('\nAbout to create configuration files:'));
240
95
  console.log(chalk_1.default.dim(`Config: ${configPath}`));
241
96
  console.log(chalk_1.default.dim(`Agents: ${agentsDir}/`));
242
- console.log(chalk_1.default.dim("This will configure OpenCode with the Berget auth plugin."));
97
+ console.log(chalk_1.default.dim('This will configure OpenCode with the Berget auth plugin.'));
243
98
  }
244
- if (yield confirm("\nCreate configuration files? (Y/n): ", options.yes)) {
99
+ if (await confirm('\nCreate configuration files? (Y/n): ', options.yes)) {
245
100
  try {
246
- yield (0, promises_1.writeFile)(configPath, JSON.stringify(config, null, 2));
247
- console.log(chalk_1.default.green("✓ Created opencode.json"));
248
- console.log(chalk_1.default.dim(" Plugin: @bergetai/opencode-auth"));
101
+ await (0, promises_1.writeFile)(configPath, JSON.stringify(config, null, 2));
102
+ console.log(chalk_1.default.green('✓ Created opencode.json'));
103
+ console.log(chalk_1.default.dim(' Plugin: @bergetai/opencode-auth'));
249
104
  fs.mkdirSync(agentsDir, { recursive: true });
250
- const templateFiles = fs.readdirSync(templatesDir).filter(f => f.endsWith(".md"));
105
+ const templateFiles = fs.readdirSync(templatesDir).filter((f) => f.endsWith('.md'));
251
106
  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);
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);
255
110
  }
256
111
  console.log(chalk_1.default.green(`✓ Created ${templateFiles.length} agent definitions in .opencode/agents/`));
257
112
  }
258
113
  catch (error) {
259
- console.error(chalk_1.default.red("Failed to create config files:"));
260
- (0, error_handler_1.handleError)("Config file creation failed", error);
114
+ console.error(chalk_1.default.red('Failed to create config files:'));
115
+ (0, error_handler_1.handleError)('Config file creation failed', error);
261
116
  return;
262
117
  }
263
118
  }
264
119
  else {
265
- console.log(chalk_1.default.yellow("Configuration file creation cancelled."));
120
+ console.log(chalk_1.default.yellow('Configuration file creation cancelled.'));
266
121
  return;
267
122
  }
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:"));
123
+ console.log(chalk_1.default.green('\n✅ Project initialized successfully!'));
124
+ console.log(chalk_1.default.blue('\nNext steps:'));
125
+ console.log(chalk_1.default.cyan(' 1. Run: opencode'));
126
+ console.log(chalk_1.default.cyan(' 2. Type: /connect'));
127
+ console.log(chalk_1.default.cyan(' 3. Choose your auth method:'));
273
128
  console.log(chalk_1.default.dim(' • "Login with Berget" — Berget Code team members (SSO)'));
274
129
  console.log(chalk_1.default.dim(' • "Enter API Key" — API key users (console.berget.ai)'));
275
130
  }
276
131
  catch (error) {
277
- (0, error_handler_1.handleError)("Failed to initialize project", error);
132
+ (0, error_handler_1.handleError)('Failed to initialize project', error);
278
133
  }
279
- }));
134
+ });
280
135
  code
281
136
  .command(command_structure_1.SUBCOMMANDS.CODE.RUN)
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)")
288
- .action((prompt, options) => __awaiter(this, void 0, void 0, function* () {
137
+ .description('Run AI coding assistant')
138
+ .argument('[prompt]', 'Prompt to send directly to OpenCode')
139
+ .option('-m, --model <model>', 'Model to use (overrides config)')
140
+ .option('-a, --analysis', 'Use fast analysis model for context building')
141
+ .option('--no-config', 'Run without loading project config')
142
+ .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
143
+ .action(async (prompt, options) => {
289
144
  try {
290
- const configPath = path_1.default.join(process.cwd(), "opencode.json");
145
+ const configPath = node_path_1.default.join(process.cwd(), 'opencode.json');
291
146
  // Ensure opencode is installed
292
- if (!(yield ensureOpencodeInstalled(options.yes))) {
147
+ if (!(await ensureOpencodeInstalled(options.yes))) {
293
148
  return;
294
149
  }
295
150
  let config = null;
296
151
  if (!options.noConfig && fs.existsSync(configPath)) {
297
152
  try {
298
- const configContent = yield (0, promises_1.readFile)(configPath, "utf8");
153
+ const configContent = await (0, promises_1.readFile)(configPath, 'utf8');
299
154
  config = JSON.parse(configContent);
300
155
  console.log(chalk_1.default.dim(`Loaded config for project: ${config.projectName}`));
301
156
  console.log(chalk_1.default.dim(`Models: Analysis=${config.analysisModel}, Build=${config.buildModel}`));
302
157
  }
303
- catch (_a) {
304
- console.log(chalk_1.default.yellow("Warning: Failed to load opencode.json"));
158
+ catch {
159
+ console.log(chalk_1.default.yellow('Warning: Failed to load opencode.json'));
305
160
  }
306
161
  }
307
162
  if (!config) {
308
- console.log(chalk_1.default.yellow("No project configuration found."));
163
+ console.log(chalk_1.default.yellow('No project configuration found.'));
309
164
  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.`));
310
165
  return;
311
166
  }
312
167
  // Prepare opencode command
313
- const env = Object.assign({}, process.env);
314
- const opencodeArgs = [];
168
+ const environment = { ...process.env };
169
+ const opencodeArguments = [];
315
170
  // Read --stage and --local from root program options
316
171
  // (these flags are registered at program level, not subcommand level)
317
- const isStage = process.argv.includes("--stage");
318
- const isLocal = process.argv.includes("--local");
172
+ const isStage = process.argv.includes('--stage');
173
+ const isLocal = process.argv.includes('--local');
319
174
  if (isStage) {
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";
175
+ console.log(chalk_1.default.cyan('Using Berget stage environment'));
176
+ environment.BERGET_API_URL = 'https://api.stage.berget.ai';
177
+ environment.BERGET_INFERENCE_URL = 'https://api.stage.berget.ai/v1';
323
178
  }
324
179
  else if (isLocal) {
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";
180
+ console.log(chalk_1.default.cyan('Using local development environment'));
181
+ environment.BERGET_API_URL = 'http://localhost:3000';
182
+ environment.BERGET_INFERENCE_URL = 'http://localhost:3000/v1';
328
183
  }
329
184
  if (prompt) {
330
- opencodeArgs.push("run", prompt);
185
+ opencodeArguments.push('run', prompt);
331
186
  }
332
187
  // Choose model based on analysis flag or override
333
188
  let selectedModel = options.model || config.buildModel;
@@ -335,116 +190,116 @@ function registerCodeCommands(program) {
335
190
  selectedModel = config.analysisModel;
336
191
  }
337
192
  if (selectedModel) {
338
- opencodeArgs.push("--model", selectedModel);
193
+ opencodeArguments.push('--model', selectedModel);
339
194
  }
340
- console.log(chalk_1.default.cyan("Starting OpenCode..."));
195
+ console.log(chalk_1.default.cyan('Starting OpenCode...'));
341
196
  // Spawn opencode process
342
- const opencode = (0, child_process_1.spawn)("opencode", opencodeArgs, {
343
- stdio: "inherit",
344
- env: env,
197
+ const opencode = (0, node_child_process_1.spawn)('opencode', opencodeArguments, {
198
+ env: environment,
199
+ stdio: 'inherit',
345
200
  });
346
- opencode.on("close", code => {
201
+ opencode.on('close', (code) => {
347
202
  if (code !== 0) {
348
203
  console.log(chalk_1.default.red(`OpenCode exited with code ${code}`));
349
204
  }
350
205
  });
351
- opencode.on("error", error => {
352
- console.error(chalk_1.default.red("Failed to start OpenCode:"));
206
+ opencode.on('error', (error) => {
207
+ console.error(chalk_1.default.red('Failed to start OpenCode:'));
353
208
  console.error(error.message);
354
209
  });
355
210
  }
356
211
  catch (error) {
357
- (0, error_handler_1.handleError)("Failed to run OpenCode", error);
212
+ (0, error_handler_1.handleError)('Failed to run OpenCode', error);
358
213
  }
359
- }));
214
+ });
360
215
  code
361
216
  .command(command_structure_1.SUBCOMMANDS.CODE.SERVE)
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)")
366
- .action((options) => __awaiter(this, void 0, void 0, function* () {
217
+ .description('Start OpenCode web server')
218
+ .option('-p, --port <port>', 'Port to run the server on (default: 3000)')
219
+ .option('-h, --host <host>', 'Host to bind the server to (default: localhost)')
220
+ .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
221
+ .action(async (options) => {
367
222
  try {
368
223
  // Ensure opencode is installed
369
- if (!(yield ensureOpencodeInstalled(options.yes))) {
224
+ if (!(await ensureOpencodeInstalled(options.yes))) {
370
225
  return;
371
226
  }
372
- console.log(chalk_1.default.cyan("🚀 Starting OpenCode web server..."));
227
+ console.log(chalk_1.default.cyan('🚀 Starting OpenCode web server...'));
373
228
  // Prepare opencode serve command
374
- const serveArgs = ["serve"];
229
+ const serveArguments = ['serve'];
375
230
  if (options.port) {
376
- serveArgs.push("--port", options.port);
231
+ serveArguments.push('--port', options.port);
377
232
  }
378
233
  if (options.host) {
379
- serveArgs.push("--host", options.host);
234
+ serveArguments.push('--host', options.host);
380
235
  }
381
236
  // Spawn opencode serve process
382
- const opencode = (0, child_process_1.spawn)("opencode", serveArgs, {
383
- stdio: "inherit",
237
+ const opencode = (0, node_child_process_1.spawn)('opencode', serveArguments, {
238
+ stdio: 'inherit',
384
239
  });
385
- opencode.on("close", code => {
240
+ opencode.on('close', (code) => {
386
241
  if (code !== 0) {
387
242
  console.log(chalk_1.default.red(`OpenCode server exited with code ${code}`));
388
243
  }
389
244
  });
390
- opencode.on("error", error => {
391
- console.error(chalk_1.default.red("Failed to start OpenCode server:"));
245
+ opencode.on('error', (error) => {
246
+ console.error(chalk_1.default.red('Failed to start OpenCode server:'));
392
247
  console.error(error.message);
393
248
  });
394
249
  }
395
250
  catch (error) {
396
- (0, error_handler_1.handleError)("Failed to start OpenCode server", error);
251
+ (0, error_handler_1.handleError)('Failed to start OpenCode server', error);
397
252
  }
398
- }));
253
+ });
399
254
  code
400
255
  .command(command_structure_1.SUBCOMMANDS.CODE.UPDATE)
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)")
404
- .action((options) => __awaiter(this, void 0, void 0, function* () {
256
+ .description('Update OpenCode and agents to latest versions')
257
+ .option('-f, --force', 'Force update even if already latest')
258
+ .option('-y, --yes', 'Automatically answer yes to all prompts (for automation)')
259
+ .action(async (options) => {
405
260
  try {
406
- console.log(chalk_1.default.cyan("🔄 Updating OpenCode configuration..."));
261
+ console.log(chalk_1.default.cyan('🔄 Updating OpenCode configuration...'));
407
262
  // Ensure opencode is installed first
408
- if (!(yield ensureOpencodeInstalled(options.yes))) {
263
+ if (!(await ensureOpencodeInstalled(options.yes))) {
409
264
  return;
410
265
  }
411
- const configPath = path_1.default.join(process.cwd(), "opencode.json");
266
+ const configPath = node_path_1.default.join(process.cwd(), 'opencode.json');
412
267
  // Check if project is initialized
413
268
  if (!fs.existsSync(configPath)) {
414
- console.log(chalk_1.default.red("❌ No OpenCode configuration found."));
269
+ console.log(chalk_1.default.red('❌ No OpenCode configuration found.'));
415
270
  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.`));
416
271
  return;
417
272
  }
418
273
  // Read current configuration
419
274
  let currentConfig;
420
275
  try {
421
- const configContent = yield (0, promises_1.readFile)(configPath, "utf8");
276
+ const configContent = await (0, promises_1.readFile)(configPath, 'utf8');
422
277
  currentConfig = JSON.parse(configContent);
423
278
  }
424
279
  catch (error) {
425
- console.error(chalk_1.default.red("Failed to read current opencode.json:"));
426
- (0, error_handler_1.handleError)("Config read failed", error);
280
+ console.error(chalk_1.default.red('Failed to read current opencode.json:'));
281
+ (0, error_handler_1.handleError)('Config read failed', error);
427
282
  return;
428
283
  }
429
- console.log(chalk_1.default.blue("📋 Current configuration:"));
284
+ console.log(chalk_1.default.blue('📋 Current configuration:'));
430
285
  if (currentConfig.model) {
431
286
  console.log(chalk_1.default.dim(` Model: ${currentConfig.model}`));
432
287
  }
433
- const agentsDir = path_1.default.join(process.cwd(), ".opencode", "agents");
288
+ const agentsDir = node_path_1.default.join(process.cwd(), '.opencode', 'agents');
434
289
  const templatesDir = getAgentTemplatesDir();
435
- const templateFiles = fs.readdirSync(templatesDir).filter(f => f.endsWith(".md"));
290
+ const templateFiles = fs.readdirSync(templatesDir).filter((f) => f.endsWith('.md'));
436
291
  // Check if agent definitions need updating
437
292
  let agentsNeedUpdate = false;
438
293
  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)) {
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)) {
442
297
  agentsNeedUpdate = true;
443
298
  break;
444
299
  }
445
- const srcContent = fs.readFileSync(src, "utf8");
446
- const destContent = fs.readFileSync(dest, "utf8");
447
- if (srcContent !== destContent) {
300
+ const sourceContent = fs.readFileSync(source, 'utf8');
301
+ const destinationContent = fs.readFileSync(destination, 'utf8');
302
+ if (sourceContent !== destinationContent) {
448
303
  agentsNeedUpdate = true;
449
304
  break;
450
305
  }
@@ -452,55 +307,55 @@ function registerCodeCommands(program) {
452
307
  // Check if opencode.json still has inline agent config (needs migration)
453
308
  const needsMigration = !!currentConfig.agent;
454
309
  if (!agentsNeedUpdate && !needsMigration && !options.force) {
455
- console.log(chalk_1.default.green("✅ Already using the latest configuration!"));
310
+ console.log(chalk_1.default.green('✅ Already using the latest configuration!'));
456
311
  return;
457
312
  }
458
313
  if (agentsNeedUpdate || needsMigration) {
459
- console.log(chalk_1.default.blue("\n🔄 Updates available:"));
314
+ console.log(chalk_1.default.blue('\n🔄 Updates available:'));
460
315
  if (needsMigration) {
461
- console.log(chalk_1.default.cyan(" • Migrate agents from opencode.json to .opencode/agents/"));
316
+ console.log(chalk_1.default.cyan(' • Migrate agents from opencode.json to .opencode/agents/'));
462
317
  }
463
318
  if (agentsNeedUpdate) {
464
- console.log(chalk_1.default.cyan(" • Latest agent prompts and improvements"));
319
+ console.log(chalk_1.default.cyan(' • Latest agent prompts and improvements'));
465
320
  }
466
321
  }
467
322
  if (options.force) {
468
- console.log(chalk_1.default.yellow("🔧 Force update requested"));
323
+ console.log(chalk_1.default.yellow('🔧 Force update requested'));
469
324
  }
470
325
  if (!options.yes) {
471
- console.log(chalk_1.default.blue("\nThis will update your agent definitions and OpenCode configuration."));
326
+ console.log(chalk_1.default.blue('\nThis will update your agent definitions and OpenCode configuration.'));
472
327
  const hasGitRepo = hasGit();
473
- if (!hasGitRepo) {
474
- 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'));
475
330
  }
476
331
  else {
477
- 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'));
478
333
  }
479
334
  }
480
- if (yield confirm("\nProceed with update? (Y/n): ", options.yes)) {
335
+ if (await confirm('\nProceed with update? (Y/n): ', options.yes)) {
481
336
  try {
482
337
  let backupPath = null;
483
338
  if (!hasGit()) {
484
339
  backupPath = `${configPath}.backup.${Date.now()}`;
485
- yield (0, promises_1.writeFile)(backupPath, JSON.stringify(currentConfig, null, 2));
486
- 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)}`));
487
342
  }
488
343
  // Remove inline agent section from opencode.json if present
489
344
  if (currentConfig.agent) {
490
345
  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"));
346
+ await (0, promises_1.writeFile)(configPath, JSON.stringify(currentConfig, null, 2));
347
+ console.log(chalk_1.default.green('✓ Removed inline agent config from opencode.json'));
493
348
  }
494
349
  // Sync agent markdown files from templates
495
350
  fs.mkdirSync(agentsDir, { recursive: true });
496
351
  let updatedCount = 0;
497
352
  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);
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);
504
359
  updatedCount++;
505
360
  console.log(chalk_1.default.cyan(` • Updated agent: ${agentName}`));
506
361
  }
@@ -509,7 +364,7 @@ function registerCodeCommands(program) {
509
364
  console.log(chalk_1.default.green(`✓ Updated ${updatedCount} agent definition(s)`));
510
365
  }
511
366
  // Update AGENTS.md if it doesn't exist
512
- const agentsMdPath = path_1.default.join(process.cwd(), "AGENTS.md");
367
+ const agentsMdPath = node_path_1.default.join(process.cwd(), 'AGENTS.md');
513
368
  if (!fs.existsSync(agentsMdPath)) {
514
369
  const agentsMdContent = `# Berget Code Agents
515
370
 
@@ -560,30 +415,160 @@ See https://opencode.ai/docs/agents/ for available options.
560
415
 
561
416
  *Updated by berget code update*
562
417
  `;
563
- yield (0, promises_1.writeFile)(agentsMdPath, agentsMdContent);
564
- console.log(chalk_1.default.green("✓ Created AGENTS.md documentation"));
418
+ await (0, promises_1.writeFile)(agentsMdPath, agentsMdContent);
419
+ console.log(chalk_1.default.green('✓ Created AGENTS.md documentation'));
565
420
  }
566
- console.log(chalk_1.default.green("\n✅ Update completed successfully!"));
421
+ console.log(chalk_1.default.green('\n✅ Update completed successfully!'));
567
422
  }
568
423
  catch (error) {
569
- console.error(chalk_1.default.red("Failed to update configuration:"));
570
- (0, error_handler_1.handleError)("Update failed", error);
424
+ console.error(chalk_1.default.red('Failed to update configuration:'));
425
+ (0, error_handler_1.handleError)('Update failed', error);
571
426
  try {
572
- yield (0, promises_1.writeFile)(configPath, JSON.stringify(currentConfig, null, 2));
573
- console.log(chalk_1.default.yellow("📁 Restored original configuration from backup"));
427
+ await (0, promises_1.writeFile)(configPath, JSON.stringify(currentConfig, null, 2));
428
+ console.log(chalk_1.default.yellow('📁 Restored original configuration from backup'));
574
429
  }
575
- catch (_b) {
576
- console.error(chalk_1.default.red("Failed to restore backup:"));
430
+ catch {
431
+ console.error(chalk_1.default.red('Failed to restore backup:'));
577
432
  }
578
433
  }
579
434
  }
580
435
  else {
581
- console.log(chalk_1.default.yellow("Update cancelled."));
436
+ console.log(chalk_1.default.yellow('Update cancelled.'));
582
437
  }
583
438
  }
584
- catch (_c) {
585
- console.error(chalk_1.default.red("Failed to update OpenCode configuration"));
439
+ catch {
440
+ console.error(chalk_1.default.red('Failed to update OpenCode configuration'));
586
441
  }
587
- }));
442
+ });
588
443
  }
589
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
+ }