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