berget 2.2.5 → 2.2.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/.github/workflows/publish.yml +8 -8
  2. package/.github/workflows/test.yml +12 -6
  3. package/.husky/pre-commit +1 -0
  4. package/.prettierignore +15 -0
  5. package/.prettierrc +5 -3
  6. package/CONTRIBUTING.md +38 -0
  7. package/README.md +2 -148
  8. package/dist/index.js +21 -21
  9. package/dist/package.json +30 -2
  10. package/dist/src/agents/app.js +28 -0
  11. package/dist/src/agents/backend.js +25 -0
  12. package/dist/src/agents/devops.js +34 -0
  13. package/dist/src/agents/frontend.js +25 -0
  14. package/dist/src/agents/fullstack.js +25 -0
  15. package/dist/src/agents/index.js +61 -0
  16. package/dist/src/agents/quality.js +70 -0
  17. package/dist/src/agents/security.js +26 -0
  18. package/dist/src/agents/types.js +2 -0
  19. package/dist/src/client.js +54 -62
  20. package/dist/src/commands/api-keys.js +132 -140
  21. package/dist/src/commands/auth.js +9 -9
  22. package/dist/src/commands/autocomplete.js +9 -9
  23. package/dist/src/commands/billing.js +7 -9
  24. package/dist/src/commands/chat.js +90 -92
  25. package/dist/src/commands/clusters.js +12 -12
  26. package/dist/src/commands/code/__tests__/auth-sync.test.js +348 -0
  27. package/dist/src/commands/code/__tests__/fake-api-key-service.js +23 -0
  28. package/dist/src/commands/code/__tests__/fake-auth-service.js +55 -0
  29. package/dist/src/commands/code/__tests__/fake-command-runner.js +50 -0
  30. package/dist/src/commands/code/__tests__/fake-file-store.js +55 -0
  31. package/dist/src/commands/code/__tests__/fake-prompter.js +133 -0
  32. package/dist/src/commands/code/__tests__/setup-flow.test.js +505 -0
  33. package/dist/src/commands/code/adapters/clack-prompter.js +81 -0
  34. package/dist/src/commands/code/adapters/fs-file-store.js +80 -0
  35. package/dist/src/commands/code/adapters/spawn-command-runner.js +53 -0
  36. package/dist/src/commands/code/auth-sync.js +283 -0
  37. package/dist/src/commands/code/errors.js +27 -0
  38. package/dist/src/commands/code/ports/auth-services.js +2 -0
  39. package/dist/src/commands/code/ports/command-runner.js +2 -0
  40. package/dist/src/commands/code/ports/file-store.js +2 -0
  41. package/dist/src/commands/code/ports/prompter.js +2 -0
  42. package/dist/src/commands/code/setup.js +533 -0
  43. package/dist/src/commands/code.js +223 -779
  44. package/dist/src/commands/models.js +13 -15
  45. package/dist/src/commands/users.js +6 -8
  46. package/dist/src/constants/command-structure.js +116 -114
  47. package/dist/src/services/api-key-service.js +43 -48
  48. package/dist/src/services/auth-service.js +60 -299
  49. package/dist/src/services/browser-auth.js +278 -0
  50. package/dist/src/services/chat-service.js +78 -91
  51. package/dist/src/services/cluster-service.js +6 -6
  52. package/dist/src/services/collaborator-service.js +5 -8
  53. package/dist/src/services/flux-service.js +5 -8
  54. package/dist/src/services/helm-service.js +5 -8
  55. package/dist/src/services/kubectl-service.js +7 -10
  56. package/dist/src/utils/config-checker.js +5 -5
  57. package/dist/src/utils/config-loader.js +25 -25
  58. package/dist/src/utils/default-api-key.js +23 -23
  59. package/dist/src/utils/env-manager.js +7 -7
  60. package/dist/src/utils/error-handler.js +60 -61
  61. package/dist/src/utils/logger.js +7 -7
  62. package/dist/src/utils/markdown-renderer.js +2 -2
  63. package/dist/src/utils/opencode-validator.js +17 -20
  64. package/dist/src/utils/token-manager.js +38 -11
  65. package/dist/tests/commands/chat.test.js +24 -24
  66. package/dist/tests/commands/code.test.js +169 -138
  67. package/dist/tests/utils/config-loader.test.js +114 -114
  68. package/dist/tests/utils/env-manager.test.js +57 -57
  69. package/dist/tests/utils/opencode-validator.test.js +44 -43
  70. package/dist/vitest.config.js +1 -1
  71. package/eslint.config.mjs +47 -0
  72. package/index.ts +42 -48
  73. package/package.json +30 -2
  74. package/src/agents/app.ts +27 -0
  75. package/src/agents/backend.ts +24 -0
  76. package/src/agents/devops.ts +33 -0
  77. package/src/agents/frontend.ts +24 -0
  78. package/src/agents/fullstack.ts +24 -0
  79. package/src/agents/index.ts +71 -0
  80. package/src/agents/quality.ts +69 -0
  81. package/src/agents/security.ts +26 -0
  82. package/src/agents/types.ts +17 -0
  83. package/src/client.ts +125 -167
  84. package/src/commands/api-keys.ts +261 -358
  85. package/src/commands/auth.ts +24 -30
  86. package/src/commands/autocomplete.ts +12 -12
  87. package/src/commands/billing.ts +22 -27
  88. package/src/commands/chat.ts +230 -323
  89. package/src/commands/clusters.ts +33 -33
  90. package/src/commands/code/__tests__/auth-sync.test.ts +481 -0
  91. package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
  92. package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
  93. package/src/commands/code/__tests__/fake-command-runner.ts +44 -0
  94. package/src/commands/code/__tests__/fake-file-store.ts +44 -0
  95. package/src/commands/code/__tests__/fake-prompter.ts +121 -0
  96. package/src/commands/code/__tests__/setup-flow.test.ts +628 -0
  97. package/src/commands/code/adapters/clack-prompter.ts +55 -0
  98. package/src/commands/code/adapters/fs-file-store.ts +37 -0
  99. package/src/commands/code/adapters/spawn-command-runner.ts +40 -0
  100. package/src/commands/code/auth-sync.ts +329 -0
  101. package/src/commands/code/errors.ts +23 -0
  102. package/src/commands/code/ports/auth-services.ts +14 -0
  103. package/src/commands/code/ports/command-runner.ts +10 -0
  104. package/src/commands/code/ports/file-store.ts +7 -0
  105. package/src/commands/code/ports/prompter.ts +29 -0
  106. package/src/commands/code/setup.ts +630 -0
  107. package/src/commands/code.ts +335 -1074
  108. package/src/commands/index.ts +19 -19
  109. package/src/commands/models.ts +32 -37
  110. package/src/commands/users.ts +15 -22
  111. package/src/constants/command-structure.ts +120 -140
  112. package/src/services/api-key-service.ts +96 -113
  113. package/src/services/auth-service.ts +92 -339
  114. package/src/services/browser-auth.ts +296 -0
  115. package/src/services/chat-service.ts +246 -279
  116. package/src/services/cluster-service.ts +29 -32
  117. package/src/services/collaborator-service.ts +13 -18
  118. package/src/services/flux-service.ts +16 -18
  119. package/src/services/helm-service.ts +16 -18
  120. package/src/services/kubectl-service.ts +12 -14
  121. package/src/types/api.d.ts +924 -926
  122. package/src/types/json.d.ts +3 -3
  123. package/src/utils/config-checker.ts +10 -10
  124. package/src/utils/config-loader.ts +110 -127
  125. package/src/utils/default-api-key.ts +81 -93
  126. package/src/utils/env-manager.ts +36 -40
  127. package/src/utils/error-handler.ts +83 -78
  128. package/src/utils/logger.ts +41 -41
  129. package/src/utils/markdown-renderer.ts +11 -11
  130. package/src/utils/opencode-validator.ts +51 -56
  131. package/src/utils/token-manager.ts +84 -64
  132. package/templates/agents/app.md +23 -0
  133. package/templates/agents/backend.md +23 -0
  134. package/templates/agents/devops.md +30 -0
  135. package/templates/agents/frontend.md +25 -0
  136. package/templates/agents/fullstack.md +23 -0
  137. package/templates/agents/quality.md +69 -0
  138. package/templates/agents/security.md +21 -0
  139. package/tests/commands/chat.test.ts +60 -70
  140. package/tests/commands/code.test.ts +346 -345
  141. package/tests/utils/config-loader.test.ts +260 -260
  142. package/tests/utils/env-manager.test.ts +127 -134
  143. package/tests/utils/opencode-validator.test.ts +65 -69
  144. package/tsconfig.json +2 -2
  145. package/vitest.config.ts +3 -3
  146. package/AGENTS.md +0 -374
  147. package/TODO.md +0 -19
  148. package/opencode.json +0 -146
@@ -0,0 +1,533 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
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
+ Object.defineProperty(exports, "__esModule", { value: true });
35
+ exports.runSetupCommand = exports.runSetup = void 0;
36
+ const errors_1 = require("./errors");
37
+ const jsonc_parser_1 = require("jsonc-parser");
38
+ const auth_sync_js_1 = require("./auth-sync.js");
39
+ const clack_prompter_js_1 = require("./adapters/clack-prompter.js");
40
+ const fs_file_store_js_1 = require("./adapters/fs-file-store.js");
41
+ const spawn_command_runner_js_1 = require("./adapters/spawn-command-runner.js");
42
+ const auth_service_js_1 = require("../../services/auth-service.js");
43
+ const api_key_service_js_1 = require("../../services/api-key-service.js");
44
+ const index_js_1 = require("../../agents/index.js");
45
+ const os = __importStar(require("os"));
46
+ const OPENCODE_PLUGIN = "@bergetai/opencode-auth@1.0.16";
47
+ const PI_PROVIDER = "npm:@bergetai/pi-provider";
48
+ const OPENCODE_PLUGIN_NAME = "@bergetai/opencode-auth";
49
+ const PI_PROVIDER_NAME = "@bergetai/pi-provider";
50
+ function runSetup(deps) {
51
+ return __awaiter(this, void 0, void 0, function* () {
52
+ const { prompter, files, commands, authService, apiKeyService, homeDir, cwd } = deps;
53
+ prompter.intro("\uD83D\uDD27 Berget Code Setup");
54
+ const ocState = yield getOpencodeState(files, homeDir, cwd);
55
+ const piState = yield getPiState(files, homeDir, cwd);
56
+ const tool = yield prompter.select({
57
+ message: "How do you want to use Berget AI?",
58
+ options: [
59
+ {
60
+ value: "opencode",
61
+ label: `OpenCode${getOpencodeLabel(ocState)}`,
62
+ hint: "Open source AI coding agent",
63
+ },
64
+ {
65
+ value: "pi",
66
+ label: `Pi${getPiLabel(piState)}`,
67
+ hint: "Minimal terminal coding harness",
68
+ },
69
+ ],
70
+ });
71
+ const scope = yield prompter.select({
72
+ message: "Where should the configuration apply?",
73
+ options: [
74
+ {
75
+ value: "project",
76
+ label: "This project only",
77
+ hint: tool === "opencode"
78
+ ? ocState.project
79
+ ? "Already configured"
80
+ : "opencode.json in current directory"
81
+ : piState.project
82
+ ? "Already configured"
83
+ : ".pi/settings.json in current directory",
84
+ },
85
+ {
86
+ value: "global",
87
+ label: "Globally for all projects",
88
+ hint: tool === "opencode"
89
+ ? ocState.global
90
+ ? "Already configured"
91
+ : "~/.config/opencode/opencode.json"
92
+ : piState.global
93
+ ? "Already configured"
94
+ : "~/.pi/agent/settings.json",
95
+ },
96
+ ],
97
+ });
98
+ const authResult = yield (0, auth_sync_js_1.configureAuth)({ prompter, files, authService, apiKeyService, homeDir }, tool);
99
+ if (tool === "opencode") {
100
+ yield setupOpenCode({ prompter, files, commands, homeDir, cwd, scope });
101
+ yield setupOpenCodeAgents({ prompter, files, homeDir, cwd, scope });
102
+ if (authResult.authenticated) {
103
+ prompter.note(`You're all set!\n\n1. Run: opencode\n2. Select model: /models\n\nFor more information, see official docs:\n\nhttps://github.com/berget-ai/opencode-berget-auth`, "Successfully configured Berget AI for OpenCode");
104
+ }
105
+ else {
106
+ prompter.note(`Next steps:\n\n1. Run: opencode\n2. Type: /connect\n3. Choose your auth method:\n • "Login with Berget" — Berget Code plan\n • "Enter Berget API Key manually"\n • (or set BERGET_API_KEY env var)\n4. Select model: /models\n\nFor more information, see official docs:\n\nhttps://github.com/berget-ai/opencode-berget-auth`, "Successfully configured Berget AI for OpenCode");
107
+ }
108
+ }
109
+ else {
110
+ yield setupPi({ prompter, files, commands, homeDir, cwd, scope });
111
+ yield setupPiAgent({ prompter, files, homeDir, cwd, scope });
112
+ if (authResult.authenticated) {
113
+ prompter.note(`You're all set!\n\n1. Restart Pi or run /reload\n2. Select model: /model\n\nFor more information, see official docs:\n\nhttps://github.com/berget-ai/pi-provider`, "Successfully configured Berget AI for Pi");
114
+ }
115
+ else {
116
+ prompter.note(`Next steps:\n\n1. Restart Pi or run /reload\n2. Type: /login\n3. Choose your auth method:\n • "Use a subscription" → Berget AI\n • (or set BERGET_API_KEY env var)\n4. Select model: /model\n\nFor more information, see official docs:\n\nhttps://github.com/berget-ai/pi-provider`, "Successfully configured Berget AI for Pi");
117
+ }
118
+ }
119
+ prompter.outro("Setup complete!");
120
+ });
121
+ }
122
+ exports.runSetup = runSetup;
123
+ // ─── OpenCode ────────────────────────────────────────────────────────────────
124
+ function setupOpenCode(deps) {
125
+ return __awaiter(this, void 0, void 0, function* () {
126
+ const { prompter, files, commands, homeDir, cwd, scope } = deps;
127
+ const installed = yield commands.checkInstalled("opencode");
128
+ if (!installed) {
129
+ throw new errors_1.PrerequisiteError("opencode");
130
+ }
131
+ const configPath = yield resolveOpencodeConfigPath(files, homeDir, cwd, scope);
132
+ const existingContent = yield files.readFile(configPath);
133
+ const newContent = generateModifiedContent(existingContent, configPath);
134
+ if (existingContent && existingContent === newContent) {
135
+ return;
136
+ }
137
+ if (existingContent) {
138
+ prompter.note(generateDiff(existingContent, newContent, configPath), "Changes to be written");
139
+ }
140
+ else {
141
+ prompter.note(`New config at ${configPath}:\n\n${newContent}`, "Config preview");
142
+ }
143
+ const shouldWrite = yield prompter.confirm({
144
+ message: existingContent ? `Write these changes to ${configPath}?` : `Create ${configPath}?`,
145
+ initialValue: true,
146
+ });
147
+ if (!shouldWrite)
148
+ throw new errors_1.CancelledError();
149
+ const s = prompter.spinner();
150
+ s.start("Writing OpenCode configuration...");
151
+ yield files.writeFile(configPath, newContent);
152
+ s.stop(`Wrote configuration to ${configPath}.`);
153
+ });
154
+ }
155
+ // ─── Pi ────────────────────────────────────────────────────────────────────────
156
+ function setupPi(deps) {
157
+ return __awaiter(this, void 0, void 0, function* () {
158
+ const { prompter, files, commands, homeDir, cwd, scope } = deps;
159
+ const s = prompter.spinner();
160
+ const installed = yield commands.checkInstalled("pi");
161
+ if (!installed) {
162
+ throw new errors_1.PrerequisiteError("pi");
163
+ }
164
+ const installArgs = scope === "project" ? ["install", "-l", PI_PROVIDER] : ["install", PI_PROVIDER];
165
+ s.start(`Installing Berget AI provider for Pi...`);
166
+ try {
167
+ yield commands.run("pi", installArgs);
168
+ s.stop("Installed Pi provider.");
169
+ }
170
+ catch (_a) {
171
+ s.stop("Pi provider installation failed. Please try again or install manually.");
172
+ throw new errors_1.CommandFailedError(`pi ${installArgs.join(" ")}`, 1);
173
+ }
174
+ const settingsPath = scope === "project"
175
+ ? pathJoin(cwd, ".pi", "settings.json")
176
+ : pathJoin(homeDir, ".pi", "agent", "settings.json");
177
+ let settings = (yield readJsonMaybe(files, settingsPath)) || {};
178
+ if (settings.defaultProvider === "berget") {
179
+ prompter.note("Berget AI is already set as your default provider.", "Default provider already set");
180
+ }
181
+ else {
182
+ if (settings.defaultProvider) {
183
+ const makeDefault = yield prompter.confirm({
184
+ message: `Your default provider is ${settings.defaultProvider}. Switch to Berget AI instead?`,
185
+ initialValue: false,
186
+ });
187
+ if (makeDefault) {
188
+ settings.defaultProvider = "berget";
189
+ yield writeJsonFile(files, settingsPath, settings);
190
+ prompter.note("Berget AI is now your default provider.", "Updated default provider");
191
+ }
192
+ }
193
+ else {
194
+ settings.defaultProvider = "berget";
195
+ yield writeJsonFile(files, settingsPath, settings);
196
+ prompter.note("Berget AI is now your default provider.", "Updated default provider");
197
+ }
198
+ }
199
+ });
200
+ }
201
+ function setupOpenCodeAgents(deps) {
202
+ return __awaiter(this, void 0, void 0, function* () {
203
+ const { prompter, files, homeDir, cwd, scope } = deps;
204
+ const agents = (0, index_js_1.getAllAgents)().filter(a => a.config.mode === "primary");
205
+ if (agents.length === 0) {
206
+ return;
207
+ }
208
+ const selectedAgents = yield prompter.multiselect({
209
+ message: "Select agents to set up (optional - press enter to skip):",
210
+ options: agents.map(agent => ({
211
+ value: agent.config.name,
212
+ label: agent.config.name,
213
+ hint: agent.config.description,
214
+ })),
215
+ });
216
+ if (selectedAgents.length === 0) {
217
+ return;
218
+ }
219
+ const agentsDir = scope === "project"
220
+ ? pathJoin(cwd, ".opencode", "agents")
221
+ : pathJoin(homeDir, ".config", "opencode", "agents");
222
+ yield files.mkdir(agentsDir);
223
+ const hasChanges = yield Promise.all(selectedAgents.map((agentName) => __awaiter(this, void 0, void 0, function* () {
224
+ const agent = agents.find(a => a.config.name === agentName);
225
+ if (!agent)
226
+ return false;
227
+ const agentPath = pathJoin(agentsDir, `${agentName}.md`);
228
+ const existing = yield files.readFile(agentPath);
229
+ const newContent = (0, index_js_1.toMarkdown)(agent);
230
+ if (existing === newContent) {
231
+ return false;
232
+ }
233
+ if (existing) {
234
+ prompter.note(generateDiff(existing, newContent, agentPath), `Changes to ${agentName} agent`);
235
+ }
236
+ return true;
237
+ })));
238
+ if (!hasChanges.some(Boolean)) {
239
+ prompter.note("Agent files are already up to date.", "No changes needed");
240
+ return;
241
+ }
242
+ const shouldWrite = yield prompter.confirm({
243
+ message: "Write agent configuration files?",
244
+ initialValue: true,
245
+ });
246
+ if (!shouldWrite) {
247
+ throw new errors_1.CancelledError();
248
+ }
249
+ const s = prompter.spinner();
250
+ s.start("Writing agent configurations...");
251
+ for (const agentName of selectedAgents) {
252
+ const agent = agents.find(a => a.config.name === agentName);
253
+ if (!agent)
254
+ continue;
255
+ const agentPath = pathJoin(agentsDir, `${agentName}.md`);
256
+ const content = (0, index_js_1.toMarkdown)(agent);
257
+ yield files.writeFile(agentPath, content);
258
+ }
259
+ s.stop(`Wrote ${selectedAgents.length} agent(s) to ${agentsDir}`);
260
+ });
261
+ }
262
+ function setupPiAgent(deps) {
263
+ return __awaiter(this, void 0, void 0, function* () {
264
+ const { prompter, files, homeDir, cwd, scope } = deps;
265
+ const agents = (0, index_js_1.getAllAgents)().filter(a => a.config.mode === "primary");
266
+ if (agents.length === 0) {
267
+ return;
268
+ }
269
+ const selectedAgentName = yield prompter.select({
270
+ message: "Select an agent (optional - press enter to skip):",
271
+ options: [
272
+ { value: "__skip__", label: "Skip agent setup" },
273
+ ...agents.map(agent => ({
274
+ value: agent.config.name,
275
+ label: agent.config.name,
276
+ hint: agent.config.description,
277
+ })),
278
+ ],
279
+ });
280
+ if (selectedAgentName === "__skip__") {
281
+ return;
282
+ }
283
+ const agent = agents.find(a => a.config.name === selectedAgentName);
284
+ if (!agent)
285
+ return;
286
+ const systemPath = scope === "project"
287
+ ? pathJoin(cwd, ".pi", "SYSTEM.md")
288
+ : pathJoin(homeDir, ".pi", "agent", "SYSTEM.md");
289
+ const existing = yield files.readFile(systemPath);
290
+ const newContent = (0, index_js_1.toPiPrompt)(agent);
291
+ if (existing === newContent) {
292
+ prompter.note("Agent configuration is already up to date.", "No changes needed");
293
+ return;
294
+ }
295
+ if (existing) {
296
+ prompter.note(generateDiff(existing, newContent, systemPath), "Changes to agent configuration");
297
+ }
298
+ else {
299
+ prompter.note(newContent, "New agent configuration");
300
+ }
301
+ const shouldWrite = yield prompter.confirm({
302
+ message: existing ? "Overwrite existing agent configuration?" : "Create agent configuration?",
303
+ initialValue: true,
304
+ });
305
+ if (!shouldWrite) {
306
+ throw new errors_1.CancelledError();
307
+ }
308
+ const s = prompter.spinner();
309
+ s.start("Writing agent configuration...");
310
+ const systemDir = scope === "project" ? pathJoin(cwd, ".pi") : pathJoin(homeDir, ".pi", "agent");
311
+ yield files.mkdir(systemDir);
312
+ yield files.writeFile(systemPath, newContent);
313
+ s.stop(`Wrote agent configuration to ${systemPath}`);
314
+ });
315
+ }
316
+ // ─── Helpers ─────────────────────────────────────────────────────────────────
317
+ function pathJoin(...parts) {
318
+ // Simple path join that avoids importing 'path' module
319
+ // This is good enough for cross-platform testing since tests control the path format
320
+ return parts.join("/");
321
+ }
322
+ function stripJsoncComments(content) {
323
+ content = content.replace(/\/\/.*$/gm, "");
324
+ content = content.replace(/\/\*[\s\S]*?\*\//g, "");
325
+ return content;
326
+ }
327
+ function generateDiff(oldText, newText, filePath) {
328
+ const oldLines = oldText.split("\n");
329
+ const newLines = newText.split("\n");
330
+ let result = `--- ${filePath}\n+++ ${filePath}\n`;
331
+ const maxLen = Math.max(oldLines.length, newLines.length);
332
+ for (let i = 0; i < maxLen; i++) {
333
+ const oldLine = oldLines[i];
334
+ const newLine = newLines[i];
335
+ if (oldLine !== newLine) {
336
+ if (oldLine !== undefined)
337
+ result += `- ${oldLine}\n`;
338
+ if (newLine !== undefined)
339
+ result += `+ ${newLine}\n`;
340
+ }
341
+ }
342
+ return result.trimEnd();
343
+ }
344
+ function readJsonMaybe(files, filePath) {
345
+ return __awaiter(this, void 0, void 0, function* () {
346
+ const content = yield files.readFile(filePath);
347
+ if (!content)
348
+ return null;
349
+ try {
350
+ return JSON.parse(content);
351
+ }
352
+ catch (_a) {
353
+ try {
354
+ return JSON.parse(stripJsoncComments(content));
355
+ }
356
+ catch (_b) {
357
+ return null;
358
+ }
359
+ }
360
+ });
361
+ }
362
+ function writeJsonFile(files, filePath, data) {
363
+ return __awaiter(this, void 0, void 0, function* () {
364
+ yield files.writeFile(filePath, JSON.stringify(data, null, 2) + "\n");
365
+ });
366
+ }
367
+ function hasPluginInConfig(config) {
368
+ return __awaiter(this, void 0, void 0, function* () {
369
+ if (!config)
370
+ return false;
371
+ const plugins = config.plugin || config.plugins || [];
372
+ return plugins.some((p) => p.includes(OPENCODE_PLUGIN_NAME));
373
+ });
374
+ }
375
+ function hasPiProviderInSettings(settings) {
376
+ return __awaiter(this, void 0, void 0, function* () {
377
+ if (!settings)
378
+ return false;
379
+ const packages = settings.packages || [];
380
+ return packages.some((p) => {
381
+ if (typeof p === "string")
382
+ return p.includes(PI_PROVIDER_NAME);
383
+ if (typeof p === "object" && p.source)
384
+ return p.source.includes(PI_PROVIDER_NAME);
385
+ return false;
386
+ });
387
+ });
388
+ }
389
+ function getOpencodeState(files, homeDir, cwd) {
390
+ return __awaiter(this, void 0, void 0, function* () {
391
+ const projectJsonc = yield readJsonMaybe(files, pathJoin(cwd, "opencode.jsonc"));
392
+ const projectJson = yield readJsonMaybe(files, pathJoin(cwd, "opencode.json"));
393
+ const globalJsonc = yield readJsonMaybe(files, pathJoin(homeDir, ".config", "opencode", "opencode.jsonc"));
394
+ const globalJson = yield readJsonMaybe(files, pathJoin(homeDir, ".config", "opencode", "opencode.json"));
395
+ return {
396
+ project: (yield hasPluginInConfig(projectJsonc)) || (yield hasPluginInConfig(projectJson)),
397
+ global: (yield hasPluginInConfig(globalJsonc)) || (yield hasPluginInConfig(globalJson)),
398
+ };
399
+ });
400
+ }
401
+ function getPiState(files, homeDir, cwd) {
402
+ return __awaiter(this, void 0, void 0, function* () {
403
+ const projectSettings = yield readJsonMaybe(files, pathJoin(cwd, ".pi", "settings.json"));
404
+ const globalSettings = yield readJsonMaybe(files, pathJoin(homeDir, ".pi", "agent", "settings.json"));
405
+ return {
406
+ project: yield hasPiProviderInSettings(projectSettings),
407
+ global: yield hasPiProviderInSettings(globalSettings),
408
+ };
409
+ });
410
+ }
411
+ function getOpencodeLabel(state) {
412
+ if (state.project || state.global)
413
+ return " (already configured)";
414
+ return "";
415
+ }
416
+ function getPiLabel(state) {
417
+ if (state.project || state.global)
418
+ return " (already configured)";
419
+ return "";
420
+ }
421
+ function resolveOpencodeConfigPath(files, homeDir, cwd, scope) {
422
+ return __awaiter(this, void 0, void 0, function* () {
423
+ if (scope === "project") {
424
+ const jsoncPath = pathJoin(cwd, "opencode.jsonc");
425
+ const jsonPath = pathJoin(cwd, "opencode.json");
426
+ if (yield files.exists(jsoncPath))
427
+ return jsoncPath;
428
+ if (yield files.exists(jsonPath))
429
+ return jsonPath;
430
+ return jsonPath;
431
+ }
432
+ else {
433
+ const globalDir = pathJoin(homeDir, ".config", "opencode");
434
+ const jsoncPath = pathJoin(globalDir, "opencode.jsonc");
435
+ const jsonPath = pathJoin(globalDir, "opencode.json");
436
+ if (yield files.exists(jsoncPath))
437
+ return jsoncPath;
438
+ if (yield files.exists(jsonPath))
439
+ return jsonPath;
440
+ return jsonPath;
441
+ }
442
+ });
443
+ }
444
+ function generateModifiedContent(existingContent, configPath) {
445
+ if (configPath.endsWith(".jsonc")) {
446
+ const content = existingContent || "{}";
447
+ const parseErrors = [];
448
+ const parsed = (0, jsonc_parser_1.parse)(content, parseErrors, {
449
+ allowTrailingComma: true,
450
+ disallowComments: false,
451
+ });
452
+ let jsConfig = {};
453
+ const canModifyText = parsed !== undefined &&
454
+ typeof parsed === "object" &&
455
+ parsed !== null &&
456
+ !Array.isArray(parsed);
457
+ if (canModifyText) {
458
+ jsConfig = parsed;
459
+ }
460
+ const pluginsKey = jsConfig.plugins !== undefined ? "plugins" : "plugin";
461
+ const existing = jsConfig[pluginsKey] || [];
462
+ const filtered = existing.filter((p) => !p.includes(OPENCODE_PLUGIN_NAME));
463
+ filtered.push(OPENCODE_PLUGIN);
464
+ if (canModifyText) {
465
+ let modifiedContent = content;
466
+ const pluginEdits = (0, jsonc_parser_1.modify)(modifiedContent, [pluginsKey], filtered, {
467
+ formattingOptions: { insertSpaces: true, tabSize: 2 },
468
+ });
469
+ modifiedContent = (0, jsonc_parser_1.applyEdits)(modifiedContent, pluginEdits);
470
+ if (!jsConfig.$schema) {
471
+ const schemaEdits = (0, jsonc_parser_1.modify)(modifiedContent, ["$schema"], "https://opencode.ai/config.json", {
472
+ formattingOptions: { insertSpaces: true, tabSize: 2 },
473
+ });
474
+ modifiedContent = (0, jsonc_parser_1.applyEdits)(modifiedContent, schemaEdits);
475
+ }
476
+ return modifiedContent;
477
+ }
478
+ // Malformed, empty, or non-object JSONC — write a clean config
479
+ const config = {
480
+ [pluginsKey]: filtered,
481
+ $schema: "https://opencode.ai/config.json",
482
+ };
483
+ return JSON.stringify(config, null, 2) + "\n";
484
+ }
485
+ // Plain JSON
486
+ let config = {};
487
+ if (existingContent) {
488
+ try {
489
+ config = JSON.parse(existingContent);
490
+ }
491
+ catch (_a) {
492
+ // ignore malformed, overwrite
493
+ }
494
+ }
495
+ const pluginsKey = config.plugins !== undefined ? "plugins" : "plugin";
496
+ const existing = config[pluginsKey] || [];
497
+ const filtered = existing.filter((p) => !p.includes(OPENCODE_PLUGIN_NAME));
498
+ filtered.push(OPENCODE_PLUGIN);
499
+ config[pluginsKey] = filtered;
500
+ config.$schema = config.$schema || "https://opencode.ai/config.json";
501
+ return JSON.stringify(config, null, 2) + "\n";
502
+ }
503
+ function runSetupCommand() {
504
+ return __awaiter(this, void 0, void 0, function* () {
505
+ try {
506
+ yield runSetup({
507
+ prompter: new clack_prompter_js_1.ClackPrompter(),
508
+ files: new fs_file_store_js_1.FsFileStore(),
509
+ commands: new spawn_command_runner_js_1.SpawnCommandRunner(),
510
+ authService: auth_service_js_1.AuthService.getInstance(),
511
+ apiKeyService: api_key_service_js_1.ApiKeyService.getInstance(),
512
+ homeDir: os.homedir(),
513
+ cwd: process.cwd(),
514
+ });
515
+ process.exit(0);
516
+ }
517
+ catch (err) {
518
+ if (err instanceof errors_1.CancelledError) {
519
+ process.exit(130);
520
+ }
521
+ if (err instanceof errors_1.PrerequisiteError) {
522
+ console.error(`Missing required binary: ${err.binary}`);
523
+ process.exit(2);
524
+ }
525
+ if (err instanceof errors_1.CommandFailedError) {
526
+ console.error(err.message);
527
+ process.exit(5);
528
+ }
529
+ throw err;
530
+ }
531
+ });
532
+ }
533
+ exports.runSetupCommand = runSetupCommand;