berget 2.2.7 → 2.2.9

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