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.
Files changed (144) hide show
  1. package/.github/workflows/publish.yml +6 -6
  2. package/.github/workflows/test.yml +11 -5
  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 +28 -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 +5 -7
  30. package/dist/src/commands/code/__tests__/fake-file-store.js +9 -0
  31. package/dist/src/commands/code/__tests__/fake-prompter.js +60 -18
  32. package/dist/src/commands/code/__tests__/setup-flow.test.js +374 -107
  33. package/dist/src/commands/code/adapters/clack-prompter.js +10 -0
  34. package/dist/src/commands/code/adapters/fs-file-store.js +8 -3
  35. package/dist/src/commands/code/adapters/spawn-command-runner.js +15 -11
  36. package/dist/src/commands/code/auth-sync.js +283 -0
  37. package/dist/src/commands/code/errors.js +4 -4
  38. package/dist/src/commands/code/ports/auth-services.js +2 -0
  39. package/dist/src/commands/code/setup.js +234 -93
  40. package/dist/src/commands/code.js +139 -251
  41. package/dist/src/commands/models.js +13 -15
  42. package/dist/src/commands/users.js +6 -8
  43. package/dist/src/constants/command-structure.js +116 -116
  44. package/dist/src/services/api-key-service.js +43 -48
  45. package/dist/src/services/auth-service.js +60 -299
  46. package/dist/src/services/browser-auth.js +278 -0
  47. package/dist/src/services/chat-service.js +78 -91
  48. package/dist/src/services/cluster-service.js +6 -6
  49. package/dist/src/services/collaborator-service.js +5 -8
  50. package/dist/src/services/flux-service.js +5 -8
  51. package/dist/src/services/helm-service.js +5 -8
  52. package/dist/src/services/kubectl-service.js +7 -10
  53. package/dist/src/utils/config-checker.js +5 -5
  54. package/dist/src/utils/config-loader.js +25 -25
  55. package/dist/src/utils/default-api-key.js +23 -23
  56. package/dist/src/utils/env-manager.js +7 -7
  57. package/dist/src/utils/error-handler.js +60 -61
  58. package/dist/src/utils/logger.js +7 -7
  59. package/dist/src/utils/markdown-renderer.js +2 -2
  60. package/dist/src/utils/opencode-validator.js +17 -20
  61. package/dist/src/utils/token-manager.js +38 -11
  62. package/dist/tests/commands/chat.test.js +24 -24
  63. package/dist/tests/commands/code.test.js +147 -147
  64. package/dist/tests/utils/config-loader.test.js +114 -114
  65. package/dist/tests/utils/env-manager.test.js +57 -57
  66. package/dist/tests/utils/opencode-validator.test.js +33 -33
  67. package/dist/vitest.config.js +1 -1
  68. package/eslint.config.mjs +47 -0
  69. package/index.ts +42 -48
  70. package/package.json +28 -2
  71. package/src/agents/app.ts +27 -0
  72. package/src/agents/backend.ts +24 -0
  73. package/src/agents/devops.ts +33 -0
  74. package/src/agents/frontend.ts +24 -0
  75. package/src/agents/fullstack.ts +24 -0
  76. package/src/agents/index.ts +71 -0
  77. package/src/agents/quality.ts +69 -0
  78. package/src/agents/security.ts +26 -0
  79. package/src/agents/types.ts +17 -0
  80. package/src/client.ts +125 -167
  81. package/src/commands/api-keys.ts +261 -358
  82. package/src/commands/auth.ts +24 -30
  83. package/src/commands/autocomplete.ts +12 -12
  84. package/src/commands/billing.ts +22 -27
  85. package/src/commands/chat.ts +230 -323
  86. package/src/commands/clusters.ts +33 -33
  87. package/src/commands/code/__tests__/auth-sync.test.ts +481 -0
  88. package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
  89. package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
  90. package/src/commands/code/__tests__/fake-command-runner.ts +39 -42
  91. package/src/commands/code/__tests__/fake-file-store.ts +32 -23
  92. package/src/commands/code/__tests__/fake-prompter.ts +107 -69
  93. package/src/commands/code/__tests__/setup-flow.test.ts +624 -270
  94. package/src/commands/code/adapters/clack-prompter.ts +50 -38
  95. package/src/commands/code/adapters/fs-file-store.ts +31 -27
  96. package/src/commands/code/adapters/spawn-command-runner.ts +33 -29
  97. package/src/commands/code/auth-sync.ts +329 -0
  98. package/src/commands/code/errors.ts +15 -15
  99. package/src/commands/code/ports/auth-services.ts +14 -0
  100. package/src/commands/code/ports/command-runner.ts +8 -4
  101. package/src/commands/code/ports/file-store.ts +5 -4
  102. package/src/commands/code/ports/prompter.ts +24 -18
  103. package/src/commands/code/setup.ts +545 -317
  104. package/src/commands/code.ts +271 -473
  105. package/src/commands/index.ts +19 -19
  106. package/src/commands/models.ts +32 -37
  107. package/src/commands/users.ts +15 -22
  108. package/src/constants/command-structure.ts +119 -142
  109. package/src/services/api-key-service.ts +96 -113
  110. package/src/services/auth-service.ts +92 -339
  111. package/src/services/browser-auth.ts +296 -0
  112. package/src/services/chat-service.ts +246 -279
  113. package/src/services/cluster-service.ts +29 -32
  114. package/src/services/collaborator-service.ts +13 -18
  115. package/src/services/flux-service.ts +16 -18
  116. package/src/services/helm-service.ts +16 -18
  117. package/src/services/kubectl-service.ts +12 -14
  118. package/src/types/api.d.ts +924 -926
  119. package/src/types/json.d.ts +3 -3
  120. package/src/utils/config-checker.ts +10 -10
  121. package/src/utils/config-loader.ts +110 -127
  122. package/src/utils/default-api-key.ts +81 -93
  123. package/src/utils/env-manager.ts +36 -40
  124. package/src/utils/error-handler.ts +83 -78
  125. package/src/utils/logger.ts +41 -41
  126. package/src/utils/markdown-renderer.ts +11 -11
  127. package/src/utils/opencode-validator.ts +51 -56
  128. package/src/utils/token-manager.ts +84 -64
  129. package/templates/agents/app.md +1 -0
  130. package/templates/agents/backend.md +1 -0
  131. package/templates/agents/devops.md +2 -0
  132. package/templates/agents/frontend.md +1 -0
  133. package/templates/agents/fullstack.md +1 -0
  134. package/templates/agents/quality.md +45 -40
  135. package/templates/agents/security.md +1 -0
  136. package/tests/commands/chat.test.ts +60 -70
  137. package/tests/commands/code.test.ts +330 -376
  138. package/tests/utils/config-loader.test.ts +260 -260
  139. package/tests/utils/env-manager.test.ts +127 -134
  140. package/tests/utils/opencode-validator.test.ts +58 -63
  141. package/tsconfig.json +2 -2
  142. package/vitest.config.ts +3 -3
  143. package/AGENTS.md +0 -374
  144. package/TODO.md +0 -19
@@ -35,59 +35,88 @@ Object.defineProperty(exports, "__esModule", { value: true });
35
35
  exports.runSetupCommand = exports.runSetup = void 0;
36
36
  const errors_1 = require("./errors");
37
37
  const jsonc_parser_1 = require("jsonc-parser");
38
- const OPENCODE_PLUGIN = '@bergetai/opencode-auth@1.0.16';
39
- const PI_PROVIDER = 'npm:@bergetai/pi-provider';
40
- const OPENCODE_PLUGIN_NAME = '@bergetai/opencode-auth';
41
- const PI_PROVIDER_NAME = '@bergetai/pi-provider';
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";
42
50
  function runSetup(deps) {
43
51
  return __awaiter(this, void 0, void 0, function* () {
44
- const { prompter, files, commands, homeDir, cwd } = deps;
45
- prompter.intro('\uD83D\uDD27 Berget Code Setup');
52
+ const { prompter, files, commands, authService, apiKeyService, homeDir, cwd } = deps;
53
+ prompter.intro("\uD83D\uDD27 Berget Code Setup");
46
54
  const ocState = yield getOpencodeState(files, homeDir, cwd);
47
55
  const piState = yield getPiState(files, homeDir, cwd);
48
56
  const tool = yield prompter.select({
49
- message: 'How do you want to use Berget AI?',
57
+ message: "How do you want to use Berget AI?",
50
58
  options: [
51
59
  {
52
- value: 'opencode',
60
+ value: "opencode",
53
61
  label: `OpenCode${getOpencodeLabel(ocState)}`,
54
- hint: 'Open source AI coding agent',
62
+ hint: "Open source AI coding agent",
55
63
  },
56
64
  {
57
- value: 'pi',
65
+ value: "pi",
58
66
  label: `Pi${getPiLabel(piState)}`,
59
- hint: 'Minimal terminal coding harness',
67
+ hint: "Minimal terminal coding harness",
60
68
  },
61
69
  ],
62
70
  });
63
71
  const scope = yield prompter.select({
64
- message: 'Where should the configuration apply?',
72
+ message: "Where should the configuration apply?",
65
73
  options: [
66
74
  {
67
- value: 'project',
68
- label: 'This project only',
69
- hint: tool === 'opencode'
70
- ? (ocState.project ? 'Already configured' : 'opencode.json in current directory')
71
- : (piState.project ? 'Already configured' : '.pi/settings.json in current directory'),
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",
72
84
  },
73
85
  {
74
- value: 'global',
75
- label: 'Globally for all projects',
76
- hint: tool === 'opencode'
77
- ? (ocState.global ? 'Already configured' : '~/.config/opencode/opencode.json')
78
- : (piState.global ? 'Already configured' : '~/.pi/agent/settings.json'),
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",
79
95
  },
80
96
  ],
81
97
  });
82
- if (tool === 'opencode') {
98
+ const authResult = yield (0, auth_sync_js_1.configureAuth)({ prompter, files, authService, apiKeyService, homeDir }, tool);
99
+ if (tool === "opencode") {
83
100
  yield setupOpenCode({ prompter, files, commands, homeDir, cwd, scope });
84
- prompter.note(`Next steps:\n\n1. Run: opencode\n2. Type: /connect\n3. Choose your auth method:\n \u2022 "Login with Berget" \u2014 Berget Code plan\n \u2022 "Enter Berget API Key manually"\n \u2022 (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');
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
+ }
85
108
  }
86
109
  else {
87
110
  yield setupPi({ prompter, files, commands, homeDir, cwd, scope });
88
- prompter.note(`Next steps:\n\n1. Restart Pi or run /reload\n2. Type: /login\n3. Choose your auth method:\n \u2022 "Use a subscription" \u2192 Berget AI\n \u2022 (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');
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
+ }
89
118
  }
90
- prompter.outro('Setup complete!');
119
+ prompter.outro("Setup complete!");
91
120
  });
92
121
  }
93
122
  exports.runSetup = runSetup;
@@ -95,9 +124,9 @@ exports.runSetup = runSetup;
95
124
  function setupOpenCode(deps) {
96
125
  return __awaiter(this, void 0, void 0, function* () {
97
126
  const { prompter, files, commands, homeDir, cwd, scope } = deps;
98
- const installed = yield commands.checkInstalled('opencode');
127
+ const installed = yield commands.checkInstalled("opencode");
99
128
  if (!installed) {
100
- throw new errors_1.PrerequisiteError('opencode');
129
+ throw new errors_1.PrerequisiteError("opencode");
101
130
  }
102
131
  const configPath = yield resolveOpencodeConfigPath(files, homeDir, cwd, scope);
103
132
  const existingContent = yield files.readFile(configPath);
@@ -106,21 +135,19 @@ function setupOpenCode(deps) {
106
135
  return;
107
136
  }
108
137
  if (existingContent) {
109
- prompter.note(generateDiff(existingContent, newContent, configPath), 'Changes to be written');
138
+ prompter.note(generateDiff(existingContent, newContent, configPath), "Changes to be written");
110
139
  }
111
140
  else {
112
- prompter.note(`New config at ${configPath}:\n\n${newContent}`, 'Config preview');
141
+ prompter.note(`New config at ${configPath}:\n\n${newContent}`, "Config preview");
113
142
  }
114
143
  const shouldWrite = yield prompter.confirm({
115
- message: existingContent
116
- ? `Write these changes to ${configPath}?`
117
- : `Create ${configPath}?`,
144
+ message: existingContent ? `Write these changes to ${configPath}?` : `Create ${configPath}?`,
118
145
  initialValue: true,
119
146
  });
120
147
  if (!shouldWrite)
121
148
  throw new errors_1.CancelledError();
122
149
  const s = prompter.spinner();
123
- s.start('Writing OpenCode configuration...');
150
+ s.start("Writing OpenCode configuration...");
124
151
  yield files.writeFile(configPath, newContent);
125
152
  s.stop(`Wrote configuration to ${configPath}.`);
126
153
  });
@@ -130,28 +157,26 @@ function setupPi(deps) {
130
157
  return __awaiter(this, void 0, void 0, function* () {
131
158
  const { prompter, files, commands, homeDir, cwd, scope } = deps;
132
159
  const s = prompter.spinner();
133
- const installed = yield commands.checkInstalled('pi');
160
+ const installed = yield commands.checkInstalled("pi");
134
161
  if (!installed) {
135
- throw new errors_1.PrerequisiteError('pi');
162
+ throw new errors_1.PrerequisiteError("pi");
136
163
  }
137
- const installArgs = scope === 'project'
138
- ? ['install', '-l', PI_PROVIDER]
139
- : ['install', PI_PROVIDER];
164
+ const installArgs = scope === "project" ? ["install", "-l", PI_PROVIDER] : ["install", PI_PROVIDER];
140
165
  s.start(`Installing Berget AI provider for Pi...`);
141
166
  try {
142
- yield commands.run('pi', installArgs);
143
- s.stop('Installed Pi provider.');
167
+ yield commands.run("pi", installArgs);
168
+ s.stop("Installed Pi provider.");
144
169
  }
145
- catch (err) {
146
- s.stop('Pi provider installation failed. Please try again or install manually.');
147
- throw new errors_1.CommandFailedError(`pi ${installArgs.join(' ')}`, 1);
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);
148
173
  }
149
- const settingsPath = scope === 'project'
150
- ? pathJoin(cwd, '.pi', 'settings.json')
151
- : pathJoin(homeDir, '.pi', 'agent', 'settings.json');
174
+ const settingsPath = scope === "project"
175
+ ? pathJoin(cwd, ".pi", "settings.json")
176
+ : pathJoin(homeDir, ".pi", "agent", "settings.json");
152
177
  let settings = (yield readJsonMaybe(files, settingsPath)) || {};
153
- if (settings.defaultProvider === 'berget') {
154
- prompter.note('Berget AI is already set as your default provider.', 'Default provider already set');
178
+ if (settings.defaultProvider === "berget") {
179
+ prompter.note("Berget AI is already set as your default provider.", "Default provider already set");
155
180
  }
156
181
  else {
157
182
  if (settings.defaultProvider) {
@@ -160,33 +185,148 @@ function setupPi(deps) {
160
185
  initialValue: false,
161
186
  });
162
187
  if (makeDefault) {
163
- settings.defaultProvider = 'berget';
188
+ settings.defaultProvider = "berget";
164
189
  yield writeJsonFile(files, settingsPath, settings);
165
- prompter.note('Berget AI is now your default provider.', 'Updated default provider');
190
+ prompter.note("Berget AI is now your default provider.", "Updated default provider");
166
191
  }
167
192
  }
168
193
  else {
169
- settings.defaultProvider = 'berget';
194
+ settings.defaultProvider = "berget";
170
195
  yield writeJsonFile(files, settingsPath, settings);
171
- prompter.note('Berget AI is now your default provider.', 'Updated default provider');
196
+ prompter.note("Berget AI is now your default provider.", "Updated default provider");
172
197
  }
173
198
  }
174
199
  });
175
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
+ }
176
316
  // ─── Helpers ─────────────────────────────────────────────────────────────────
177
317
  function pathJoin(...parts) {
178
318
  // Simple path join that avoids importing 'path' module
179
319
  // This is good enough for cross-platform testing since tests control the path format
180
- return parts.join('/');
320
+ return parts.join("/");
181
321
  }
182
322
  function stripJsoncComments(content) {
183
- content = content.replace(/\/\/.*$/gm, '');
184
- content = content.replace(/\/\*[\s\S]*?\*\//g, '');
323
+ content = content.replace(/\/\/.*$/gm, "");
324
+ content = content.replace(/\/\*[\s\S]*?\*\//g, "");
185
325
  return content;
186
326
  }
187
327
  function generateDiff(oldText, newText, filePath) {
188
- const oldLines = oldText.split('\n');
189
- const newLines = newText.split('\n');
328
+ const oldLines = oldText.split("\n");
329
+ const newLines = newText.split("\n");
190
330
  let result = `--- ${filePath}\n+++ ${filePath}\n`;
191
331
  const maxLen = Math.max(oldLines.length, newLines.length);
192
332
  for (let i = 0; i < maxLen; i++) {
@@ -221,7 +361,7 @@ function readJsonMaybe(files, filePath) {
221
361
  }
222
362
  function writeJsonFile(files, filePath, data) {
223
363
  return __awaiter(this, void 0, void 0, function* () {
224
- yield files.writeFile(filePath, JSON.stringify(data, null, 2) + '\n');
364
+ yield files.writeFile(filePath, JSON.stringify(data, null, 2) + "\n");
225
365
  });
226
366
  }
227
367
  function hasPluginInConfig(config) {
@@ -238,9 +378,9 @@ function hasPiProviderInSettings(settings) {
238
378
  return false;
239
379
  const packages = settings.packages || [];
240
380
  return packages.some((p) => {
241
- if (typeof p === 'string')
381
+ if (typeof p === "string")
242
382
  return p.includes(PI_PROVIDER_NAME);
243
- if (typeof p === 'object' && p.source)
383
+ if (typeof p === "object" && p.source)
244
384
  return p.source.includes(PI_PROVIDER_NAME);
245
385
  return false;
246
386
  });
@@ -248,10 +388,10 @@ function hasPiProviderInSettings(settings) {
248
388
  }
249
389
  function getOpencodeState(files, homeDir, cwd) {
250
390
  return __awaiter(this, void 0, void 0, function* () {
251
- const projectJsonc = yield readJsonMaybe(files, pathJoin(cwd, 'opencode.jsonc'));
252
- const projectJson = yield readJsonMaybe(files, pathJoin(cwd, 'opencode.json'));
253
- const globalJsonc = yield readJsonMaybe(files, pathJoin(homeDir, '.config', 'opencode', 'opencode.jsonc'));
254
- const globalJson = yield readJsonMaybe(files, pathJoin(homeDir, '.config', 'opencode', 'opencode.json'));
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"));
255
395
  return {
256
396
  project: (yield hasPluginInConfig(projectJsonc)) || (yield hasPluginInConfig(projectJson)),
257
397
  global: (yield hasPluginInConfig(globalJsonc)) || (yield hasPluginInConfig(globalJson)),
@@ -260,8 +400,8 @@ function getOpencodeState(files, homeDir, cwd) {
260
400
  }
261
401
  function getPiState(files, homeDir, cwd) {
262
402
  return __awaiter(this, void 0, void 0, function* () {
263
- const projectSettings = yield readJsonMaybe(files, pathJoin(cwd, '.pi', 'settings.json'));
264
- const globalSettings = yield readJsonMaybe(files, pathJoin(homeDir, '.pi', 'agent', 'settings.json'));
403
+ const projectSettings = yield readJsonMaybe(files, pathJoin(cwd, ".pi", "settings.json"));
404
+ const globalSettings = yield readJsonMaybe(files, pathJoin(homeDir, ".pi", "agent", "settings.json"));
265
405
  return {
266
406
  project: yield hasPiProviderInSettings(projectSettings),
267
407
  global: yield hasPiProviderInSettings(globalSettings),
@@ -270,19 +410,19 @@ function getPiState(files, homeDir, cwd) {
270
410
  }
271
411
  function getOpencodeLabel(state) {
272
412
  if (state.project || state.global)
273
- return ' (already configured)';
274
- return '';
413
+ return " (already configured)";
414
+ return "";
275
415
  }
276
416
  function getPiLabel(state) {
277
417
  if (state.project || state.global)
278
- return ' (already configured)';
279
- return '';
418
+ return " (already configured)";
419
+ return "";
280
420
  }
281
421
  function resolveOpencodeConfigPath(files, homeDir, cwd, scope) {
282
422
  return __awaiter(this, void 0, void 0, function* () {
283
- if (scope === 'project') {
284
- const jsoncPath = pathJoin(cwd, 'opencode.jsonc');
285
- const jsonPath = pathJoin(cwd, 'opencode.json');
423
+ if (scope === "project") {
424
+ const jsoncPath = pathJoin(cwd, "opencode.jsonc");
425
+ const jsonPath = pathJoin(cwd, "opencode.json");
286
426
  if (yield files.exists(jsoncPath))
287
427
  return jsoncPath;
288
428
  if (yield files.exists(jsonPath))
@@ -290,9 +430,9 @@ function resolveOpencodeConfigPath(files, homeDir, cwd, scope) {
290
430
  return jsonPath;
291
431
  }
292
432
  else {
293
- const globalDir = pathJoin(homeDir, '.config', 'opencode');
294
- const jsoncPath = pathJoin(globalDir, 'opencode.jsonc');
295
- const jsonPath = pathJoin(globalDir, 'opencode.json');
433
+ const globalDir = pathJoin(homeDir, ".config", "opencode");
434
+ const jsoncPath = pathJoin(globalDir, "opencode.jsonc");
435
+ const jsonPath = pathJoin(globalDir, "opencode.json");
296
436
  if (yield files.exists(jsoncPath))
297
437
  return jsoncPath;
298
438
  if (yield files.exists(jsonPath))
@@ -302,19 +442,22 @@ function resolveOpencodeConfigPath(files, homeDir, cwd, scope) {
302
442
  });
303
443
  }
304
444
  function generateModifiedContent(existingContent, configPath) {
305
- if (configPath.endsWith('.jsonc')) {
306
- const content = existingContent || '{}';
445
+ if (configPath.endsWith(".jsonc")) {
446
+ const content = existingContent || "{}";
307
447
  const parseErrors = [];
308
- const parsed = (0, jsonc_parser_1.parse)(content, parseErrors, { allowTrailingComma: true, disallowComments: false });
448
+ const parsed = (0, jsonc_parser_1.parse)(content, parseErrors, {
449
+ allowTrailingComma: true,
450
+ disallowComments: false,
451
+ });
309
452
  let jsConfig = {};
310
453
  const canModifyText = parsed !== undefined &&
311
- typeof parsed === 'object' &&
454
+ typeof parsed === "object" &&
312
455
  parsed !== null &&
313
456
  !Array.isArray(parsed);
314
457
  if (canModifyText) {
315
458
  jsConfig = parsed;
316
459
  }
317
- const pluginsKey = jsConfig.plugins !== undefined ? 'plugins' : 'plugin';
460
+ const pluginsKey = jsConfig.plugins !== undefined ? "plugins" : "plugin";
318
461
  const existing = jsConfig[pluginsKey] || [];
319
462
  const filtered = existing.filter((p) => !p.includes(OPENCODE_PLUGIN_NAME));
320
463
  filtered.push(OPENCODE_PLUGIN);
@@ -325,7 +468,7 @@ function generateModifiedContent(existingContent, configPath) {
325
468
  });
326
469
  modifiedContent = (0, jsonc_parser_1.applyEdits)(modifiedContent, pluginEdits);
327
470
  if (!jsConfig.$schema) {
328
- const schemaEdits = (0, jsonc_parser_1.modify)(modifiedContent, ['$schema'], 'https://opencode.ai/config.json', {
471
+ const schemaEdits = (0, jsonc_parser_1.modify)(modifiedContent, ["$schema"], "https://opencode.ai/config.json", {
329
472
  formattingOptions: { insertSpaces: true, tabSize: 2 },
330
473
  });
331
474
  modifiedContent = (0, jsonc_parser_1.applyEdits)(modifiedContent, schemaEdits);
@@ -335,9 +478,9 @@ function generateModifiedContent(existingContent, configPath) {
335
478
  // Malformed, empty, or non-object JSONC — write a clean config
336
479
  const config = {
337
480
  [pluginsKey]: filtered,
338
- $schema: 'https://opencode.ai/config.json',
481
+ $schema: "https://opencode.ai/config.json",
339
482
  };
340
- return JSON.stringify(config, null, 2) + '\n';
483
+ return JSON.stringify(config, null, 2) + "\n";
341
484
  }
342
485
  // Plain JSON
343
486
  let config = {};
@@ -349,19 +492,14 @@ function generateModifiedContent(existingContent, configPath) {
349
492
  // ignore malformed, overwrite
350
493
  }
351
494
  }
352
- const pluginsKey = config.plugins !== undefined ? 'plugins' : 'plugin';
495
+ const pluginsKey = config.plugins !== undefined ? "plugins" : "plugin";
353
496
  const existing = config[pluginsKey] || [];
354
497
  const filtered = existing.filter((p) => !p.includes(OPENCODE_PLUGIN_NAME));
355
498
  filtered.push(OPENCODE_PLUGIN);
356
499
  config[pluginsKey] = filtered;
357
- config.$schema = config.$schema || 'https://opencode.ai/config.json';
358
- return JSON.stringify(config, null, 2) + '\n';
500
+ config.$schema = config.$schema || "https://opencode.ai/config.json";
501
+ return JSON.stringify(config, null, 2) + "\n";
359
502
  }
360
- // ─── Production CLI entry point ──────────────────────────────────────────────
361
- const clack_prompter_js_1 = require("./adapters/clack-prompter.js");
362
- const fs_file_store_js_1 = require("./adapters/fs-file-store.js");
363
- const spawn_command_runner_js_1 = require("./adapters/spawn-command-runner.js");
364
- const os = __importStar(require("os"));
365
503
  function runSetupCommand() {
366
504
  return __awaiter(this, void 0, void 0, function* () {
367
505
  try {
@@ -369,9 +507,12 @@ function runSetupCommand() {
369
507
  prompter: new clack_prompter_js_1.ClackPrompter(),
370
508
  files: new fs_file_store_js_1.FsFileStore(),
371
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(),
372
512
  homeDir: os.homedir(),
373
513
  cwd: process.cwd(),
374
514
  });
515
+ process.exit(0);
375
516
  }
376
517
  catch (err) {
377
518
  if (err instanceof errors_1.CancelledError) {