berget 2.2.6 → 2.2.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/publish.yml +6 -6
- package/.github/workflows/test.yml +11 -5
- package/.husky/pre-commit +1 -0
- package/.prettierignore +15 -0
- package/.prettierrc +5 -3
- package/CONTRIBUTING.md +38 -0
- package/README.md +2 -148
- package/dist/index.js +21 -21
- package/dist/package.json +28 -2
- package/dist/src/agents/app.js +28 -0
- package/dist/src/agents/backend.js +25 -0
- package/dist/src/agents/devops.js +34 -0
- package/dist/src/agents/frontend.js +25 -0
- package/dist/src/agents/fullstack.js +25 -0
- package/dist/src/agents/index.js +61 -0
- package/dist/src/agents/quality.js +70 -0
- package/dist/src/agents/security.js +26 -0
- package/dist/src/agents/types.js +2 -0
- package/dist/src/client.js +54 -62
- package/dist/src/commands/api-keys.js +132 -140
- package/dist/src/commands/auth.js +9 -9
- package/dist/src/commands/autocomplete.js +9 -9
- package/dist/src/commands/billing.js +7 -9
- package/dist/src/commands/chat.js +90 -92
- package/dist/src/commands/clusters.js +12 -12
- package/dist/src/commands/code/__tests__/auth-sync.test.js +348 -0
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +23 -0
- package/dist/src/commands/code/__tests__/fake-auth-service.js +55 -0
- package/dist/src/commands/code/__tests__/fake-command-runner.js +5 -7
- package/dist/src/commands/code/__tests__/fake-file-store.js +9 -0
- package/dist/src/commands/code/__tests__/fake-prompter.js +60 -18
- package/dist/src/commands/code/__tests__/setup-flow.test.js +374 -107
- package/dist/src/commands/code/adapters/clack-prompter.js +10 -0
- package/dist/src/commands/code/adapters/fs-file-store.js +8 -3
- package/dist/src/commands/code/adapters/spawn-command-runner.js +15 -11
- package/dist/src/commands/code/auth-sync.js +283 -0
- package/dist/src/commands/code/errors.js +4 -4
- package/dist/src/commands/code/ports/auth-services.js +2 -0
- package/dist/src/commands/code/setup.js +234 -93
- package/dist/src/commands/code.js +139 -251
- package/dist/src/commands/models.js +13 -15
- package/dist/src/commands/users.js +6 -8
- package/dist/src/constants/command-structure.js +116 -116
- package/dist/src/services/api-key-service.js +43 -48
- package/dist/src/services/auth-service.js +60 -299
- package/dist/src/services/browser-auth.js +278 -0
- package/dist/src/services/chat-service.js +78 -91
- package/dist/src/services/cluster-service.js +6 -6
- package/dist/src/services/collaborator-service.js +5 -8
- package/dist/src/services/flux-service.js +5 -8
- package/dist/src/services/helm-service.js +5 -8
- package/dist/src/services/kubectl-service.js +7 -10
- package/dist/src/utils/config-checker.js +5 -5
- package/dist/src/utils/config-loader.js +25 -25
- package/dist/src/utils/default-api-key.js +23 -23
- package/dist/src/utils/env-manager.js +7 -7
- package/dist/src/utils/error-handler.js +60 -61
- package/dist/src/utils/logger.js +7 -7
- package/dist/src/utils/markdown-renderer.js +2 -2
- package/dist/src/utils/opencode-validator.js +17 -20
- package/dist/src/utils/token-manager.js +38 -11
- package/dist/tests/commands/chat.test.js +24 -24
- package/dist/tests/commands/code.test.js +147 -147
- package/dist/tests/utils/config-loader.test.js +114 -114
- package/dist/tests/utils/env-manager.test.js +57 -57
- package/dist/tests/utils/opencode-validator.test.js +33 -33
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +47 -0
- package/index.ts +42 -48
- package/package.json +28 -2
- package/src/agents/app.ts +27 -0
- package/src/agents/backend.ts +24 -0
- package/src/agents/devops.ts +33 -0
- package/src/agents/frontend.ts +24 -0
- package/src/agents/fullstack.ts +24 -0
- package/src/agents/index.ts +71 -0
- package/src/agents/quality.ts +69 -0
- package/src/agents/security.ts +26 -0
- package/src/agents/types.ts +17 -0
- package/src/client.ts +125 -167
- package/src/commands/api-keys.ts +261 -358
- package/src/commands/auth.ts +24 -30
- package/src/commands/autocomplete.ts +12 -12
- package/src/commands/billing.ts +22 -27
- package/src/commands/chat.ts +230 -323
- package/src/commands/clusters.ts +33 -33
- package/src/commands/code/__tests__/auth-sync.test.ts +481 -0
- package/src/commands/code/__tests__/fake-api-key-service.ts +13 -0
- package/src/commands/code/__tests__/fake-auth-service.ts +50 -0
- package/src/commands/code/__tests__/fake-command-runner.ts +39 -42
- package/src/commands/code/__tests__/fake-file-store.ts +32 -23
- package/src/commands/code/__tests__/fake-prompter.ts +107 -69
- package/src/commands/code/__tests__/setup-flow.test.ts +624 -270
- package/src/commands/code/adapters/clack-prompter.ts +50 -38
- package/src/commands/code/adapters/fs-file-store.ts +31 -27
- package/src/commands/code/adapters/spawn-command-runner.ts +33 -29
- package/src/commands/code/auth-sync.ts +329 -0
- package/src/commands/code/errors.ts +15 -15
- package/src/commands/code/ports/auth-services.ts +14 -0
- package/src/commands/code/ports/command-runner.ts +8 -4
- package/src/commands/code/ports/file-store.ts +5 -4
- package/src/commands/code/ports/prompter.ts +24 -18
- package/src/commands/code/setup.ts +545 -317
- package/src/commands/code.ts +271 -473
- package/src/commands/index.ts +19 -19
- package/src/commands/models.ts +32 -37
- package/src/commands/users.ts +15 -22
- package/src/constants/command-structure.ts +119 -142
- package/src/services/api-key-service.ts +96 -113
- package/src/services/auth-service.ts +92 -339
- package/src/services/browser-auth.ts +296 -0
- package/src/services/chat-service.ts +246 -279
- package/src/services/cluster-service.ts +29 -32
- package/src/services/collaborator-service.ts +13 -18
- package/src/services/flux-service.ts +16 -18
- package/src/services/helm-service.ts +16 -18
- package/src/services/kubectl-service.ts +12 -14
- package/src/types/api.d.ts +924 -926
- package/src/types/json.d.ts +3 -3
- package/src/utils/config-checker.ts +10 -10
- package/src/utils/config-loader.ts +110 -127
- package/src/utils/default-api-key.ts +81 -93
- package/src/utils/env-manager.ts +36 -40
- package/src/utils/error-handler.ts +83 -78
- package/src/utils/logger.ts +41 -41
- package/src/utils/markdown-renderer.ts +11 -11
- package/src/utils/opencode-validator.ts +51 -56
- package/src/utils/token-manager.ts +84 -64
- package/templates/agents/app.md +1 -0
- package/templates/agents/backend.md +1 -0
- package/templates/agents/devops.md +2 -0
- package/templates/agents/frontend.md +1 -0
- package/templates/agents/fullstack.md +1 -0
- package/templates/agents/quality.md +45 -40
- package/templates/agents/security.md +1 -0
- package/tests/commands/chat.test.ts +60 -70
- package/tests/commands/code.test.ts +330 -376
- package/tests/utils/config-loader.test.ts +260 -260
- package/tests/utils/env-manager.test.ts +127 -134
- package/tests/utils/opencode-validator.test.ts +58 -63
- package/tsconfig.json +2 -2
- package/vitest.config.ts +3 -3
- package/AGENTS.md +0 -374
- package/TODO.md +0 -19
|
@@ -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
|
|
39
|
-
const
|
|
40
|
-
const
|
|
41
|
-
const
|
|
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(
|
|
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:
|
|
57
|
+
message: "How do you want to use Berget AI?",
|
|
50
58
|
options: [
|
|
51
59
|
{
|
|
52
|
-
value:
|
|
60
|
+
value: "opencode",
|
|
53
61
|
label: `OpenCode${getOpencodeLabel(ocState)}`,
|
|
54
|
-
hint:
|
|
62
|
+
hint: "Open source AI coding agent",
|
|
55
63
|
},
|
|
56
64
|
{
|
|
57
|
-
value:
|
|
65
|
+
value: "pi",
|
|
58
66
|
label: `Pi${getPiLabel(piState)}`,
|
|
59
|
-
hint:
|
|
67
|
+
hint: "Minimal terminal coding harness",
|
|
60
68
|
},
|
|
61
69
|
],
|
|
62
70
|
});
|
|
63
71
|
const scope = yield prompter.select({
|
|
64
|
-
message:
|
|
72
|
+
message: "Where should the configuration apply?",
|
|
65
73
|
options: [
|
|
66
74
|
{
|
|
67
|
-
value:
|
|
68
|
-
label:
|
|
69
|
-
hint: tool ===
|
|
70
|
-
?
|
|
71
|
-
|
|
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:
|
|
75
|
-
label:
|
|
76
|
-
hint: tool ===
|
|
77
|
-
?
|
|
78
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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(
|
|
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(
|
|
127
|
+
const installed = yield commands.checkInstalled("opencode");
|
|
99
128
|
if (!installed) {
|
|
100
|
-
throw new errors_1.PrerequisiteError(
|
|
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),
|
|
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}`,
|
|
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(
|
|
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(
|
|
160
|
+
const installed = yield commands.checkInstalled("pi");
|
|
134
161
|
if (!installed) {
|
|
135
|
-
throw new errors_1.PrerequisiteError(
|
|
162
|
+
throw new errors_1.PrerequisiteError("pi");
|
|
136
163
|
}
|
|
137
|
-
const installArgs = scope ===
|
|
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(
|
|
143
|
-
s.stop(
|
|
167
|
+
yield commands.run("pi", installArgs);
|
|
168
|
+
s.stop("Installed Pi provider.");
|
|
144
169
|
}
|
|
145
|
-
catch (
|
|
146
|
-
s.stop(
|
|
147
|
-
throw new errors_1.CommandFailedError(`pi ${installArgs.join(
|
|
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 ===
|
|
150
|
-
? pathJoin(cwd,
|
|
151
|
-
: pathJoin(homeDir,
|
|
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 ===
|
|
154
|
-
prompter.note(
|
|
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 =
|
|
188
|
+
settings.defaultProvider = "berget";
|
|
164
189
|
yield writeJsonFile(files, settingsPath, settings);
|
|
165
|
-
prompter.note(
|
|
190
|
+
prompter.note("Berget AI is now your default provider.", "Updated default provider");
|
|
166
191
|
}
|
|
167
192
|
}
|
|
168
193
|
else {
|
|
169
|
-
settings.defaultProvider =
|
|
194
|
+
settings.defaultProvider = "berget";
|
|
170
195
|
yield writeJsonFile(files, settingsPath, settings);
|
|
171
|
-
prompter.note(
|
|
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(
|
|
189
|
-
const newLines = newText.split(
|
|
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) +
|
|
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 ===
|
|
381
|
+
if (typeof p === "string")
|
|
242
382
|
return p.includes(PI_PROVIDER_NAME);
|
|
243
|
-
if (typeof p ===
|
|
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,
|
|
252
|
-
const projectJson = yield readJsonMaybe(files, pathJoin(cwd,
|
|
253
|
-
const globalJsonc = yield readJsonMaybe(files, pathJoin(homeDir,
|
|
254
|
-
const globalJson = yield readJsonMaybe(files, pathJoin(homeDir,
|
|
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,
|
|
264
|
-
const globalSettings = yield readJsonMaybe(files, pathJoin(homeDir,
|
|
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
|
|
274
|
-
return
|
|
413
|
+
return " (already configured)";
|
|
414
|
+
return "";
|
|
275
415
|
}
|
|
276
416
|
function getPiLabel(state) {
|
|
277
417
|
if (state.project || state.global)
|
|
278
|
-
return
|
|
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 ===
|
|
284
|
-
const jsoncPath = pathJoin(cwd,
|
|
285
|
-
const jsonPath = pathJoin(cwd,
|
|
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,
|
|
294
|
-
const jsoncPath = pathJoin(globalDir,
|
|
295
|
-
const jsonPath = pathJoin(globalDir,
|
|
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(
|
|
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, {
|
|
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 ===
|
|
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 ?
|
|
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, [
|
|
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:
|
|
481
|
+
$schema: "https://opencode.ai/config.json",
|
|
339
482
|
};
|
|
340
|
-
return JSON.stringify(config, null, 2) +
|
|
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 ?
|
|
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 ||
|
|
358
|
-
return JSON.stringify(config, null, 2) +
|
|
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) {
|