berget 2.2.6 → 2.2.8
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 +2 -2
- package/.github/workflows/test.yml +10 -4
- package/.husky/pre-commit +1 -0
- package/.prettierignore +15 -0
- package/.prettierrc +7 -3
- package/CONTRIBUTING.md +38 -0
- package/README.md +2 -148
- package/dist/index.js +10 -11
- package/dist/package.json +30 -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 +97 -117
- package/dist/src/commands/api-keys.js +75 -90
- package/dist/src/commands/auth.js +7 -16
- package/dist/src/commands/autocomplete.js +1 -1
- package/dist/src/commands/billing.js +6 -17
- package/dist/src/commands/chat.js +68 -101
- package/dist/src/commands/clusters.js +9 -18
- package/dist/src/commands/code/__tests__/auth-sync.test.js +351 -0
- package/dist/src/commands/code/__tests__/fake-api-key-service.js +13 -0
- package/dist/src/commands/code/__tests__/fake-auth-service.js +47 -0
- package/dist/src/commands/code/__tests__/fake-command-runner.js +21 -34
- package/dist/src/commands/code/__tests__/fake-file-store.js +20 -33
- package/dist/src/commands/code/__tests__/fake-prompter.js +83 -57
- package/dist/src/commands/code/__tests__/setup-flow.test.js +359 -92
- package/dist/src/commands/code/adapters/clack-prompter.js +15 -22
- package/dist/src/commands/code/adapters/fs-file-store.js +26 -40
- package/dist/src/commands/code/adapters/spawn-command-runner.js +27 -37
- package/dist/src/commands/code/auth-sync.js +270 -0
- package/dist/src/commands/code/errors.js +12 -9
- package/dist/src/commands/code/ports/auth-services.js +2 -0
- package/dist/src/commands/code/setup.js +387 -281
- package/dist/src/commands/code.js +205 -332
- package/dist/src/commands/index.js +5 -5
- package/dist/src/commands/models.js +6 -17
- package/dist/src/commands/users.js +5 -16
- package/dist/src/constants/command-structure.js +104 -104
- package/dist/src/services/api-key-service.js +132 -157
- package/dist/src/services/auth-service.js +89 -342
- package/dist/src/services/browser-auth.js +268 -0
- package/dist/src/services/chat-service.js +371 -401
- package/dist/src/services/cluster-service.js +47 -62
- package/dist/src/services/collaborator-service.js +10 -25
- package/dist/src/services/flux-service.js +14 -29
- package/dist/src/services/helm-service.js +10 -25
- package/dist/src/services/kubectl-service.js +16 -33
- package/dist/src/utils/config-checker.js +3 -3
- package/dist/src/utils/config-loader.js +95 -95
- package/dist/src/utils/default-api-key.js +124 -134
- package/dist/src/utils/env-manager.js +55 -66
- package/dist/src/utils/error-handler.js +20 -21
- package/dist/src/utils/logger.js +72 -65
- package/dist/src/utils/markdown-renderer.js +27 -27
- package/dist/src/utils/opencode-validator.js +63 -68
- package/dist/src/utils/token-manager.js +74 -45
- package/dist/tests/commands/chat.test.js +16 -25
- package/dist/tests/commands/code.test.js +95 -104
- package/dist/tests/utils/config-loader.test.js +48 -48
- package/dist/tests/utils/env-manager.test.js +43 -52
- package/dist/tests/utils/opencode-validator.test.js +22 -21
- package/dist/vitest.config.js +1 -1
- package/eslint.config.mjs +67 -0
- package/index.ts +35 -42
- package/package.json +30 -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 +73 -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 +118 -152
- package/src/commands/api-keys.ts +241 -333
- package/src/commands/auth.ts +22 -27
- package/src/commands/autocomplete.ts +9 -9
- package/src/commands/billing.ts +20 -24
- package/src/commands/chat.ts +248 -338
- package/src/commands/clusters.ts +27 -26
- package/src/commands/code/__tests__/auth-sync.test.ts +482 -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 +45 -42
- package/src/commands/code/__tests__/fake-file-store.ts +32 -23
- package/src/commands/code/__tests__/fake-prompter.ts +116 -77
- package/src/commands/code/__tests__/setup-flow.test.ts +624 -268
- package/src/commands/code/adapters/clack-prompter.ts +53 -39
- package/src/commands/code/adapters/fs-file-store.ts +32 -27
- package/src/commands/code/adapters/spawn-command-runner.ts +38 -29
- package/src/commands/code/auth-sync.ts +329 -0
- package/src/commands/code/errors.ts +18 -18
- 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 +570 -340
- package/src/commands/code.ts +338 -539
- package/src/commands/index.ts +20 -19
- package/src/commands/models.ts +28 -32
- package/src/commands/users.ts +15 -21
- package/src/constants/command-structure.ts +134 -157
- package/src/services/api-key-service.ts +105 -122
- package/src/services/auth-service.ts +99 -345
- package/src/services/browser-auth.ts +296 -0
- package/src/services/chat-service.ts +265 -299
- package/src/services/cluster-service.ts +42 -45
- package/src/services/collaborator-service.ts +14 -19
- package/src/services/flux-service.ts +23 -25
- package/src/services/helm-service.ts +19 -21
- package/src/services/kubectl-service.ts +17 -19
- package/src/types/api.d.ts +1905 -1907
- package/src/types/json.d.ts +2 -2
- package/src/utils/config-checker.ts +10 -10
- package/src/utils/config-loader.ts +162 -178
- package/src/utils/default-api-key.ts +114 -125
- package/src/utils/env-manager.ts +53 -57
- package/src/utils/error-handler.ts +61 -56
- package/src/utils/logger.ts +79 -73
- package/src/utils/markdown-renderer.ts +31 -31
- package/src/utils/opencode-validator.ts +85 -89
- package/src/utils/token-manager.ts +108 -87
- 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 +53 -62
- package/tests/commands/code.test.ts +265 -310
- package/tests/utils/config-loader.test.ts +189 -188
- package/tests/utils/env-manager.test.ts +110 -113
- package/tests/utils/opencode-validator.test.ts +52 -56
- package/tsconfig.json +4 -3
- package/vitest.config.ts +3 -3
- package/AGENTS.md +0 -374
- package/TODO.md +0 -19
|
@@ -22,176 +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
|
|
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");
|
|
32
|
+
const clack_prompter_js_1 = require("./adapters/clack-prompter.js");
|
|
33
|
+
const fs_file_store_js_1 = require("./adapters/fs-file-store.js");
|
|
34
|
+
const spawn_command_runner_js_1 = require("./adapters/spawn-command-runner.js");
|
|
35
|
+
const auth_sync_js_1 = require("./auth-sync.js");
|
|
36
|
+
const errors_1 = require("./errors");
|
|
37
|
+
const OPENCODE_PLUGIN = '@bergetai/opencode-auth';
|
|
39
38
|
const PI_PROVIDER = 'npm:@bergetai/pi-provider';
|
|
40
39
|
const OPENCODE_PLUGIN_NAME = '@bergetai/opencode-auth';
|
|
41
40
|
const PI_PROVIDER_NAME = '@bergetai/pi-provider';
|
|
42
|
-
function runSetup(deps) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
],
|
|
62
|
-
});
|
|
63
|
-
const scope = yield prompter.select({
|
|
64
|
-
message: 'Where should the configuration apply?',
|
|
65
|
-
options: [
|
|
66
|
-
{
|
|
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'),
|
|
72
|
-
},
|
|
73
|
-
{
|
|
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'),
|
|
79
|
-
},
|
|
80
|
-
],
|
|
81
|
-
});
|
|
82
|
-
if (tool === 'opencode') {
|
|
83
|
-
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');
|
|
85
|
-
}
|
|
86
|
-
else {
|
|
87
|
-
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');
|
|
89
|
-
}
|
|
90
|
-
prompter.outro('Setup complete!');
|
|
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
|
+
],
|
|
91
60
|
});
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
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
|
+
],
|
|
87
|
+
});
|
|
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');
|
|
101
94
|
}
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
const newContent = generateModifiedContent(existingContent, configPath);
|
|
105
|
-
if (existingContent && existingContent === newContent) {
|
|
106
|
-
return;
|
|
95
|
+
else {
|
|
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');
|
|
107
97
|
}
|
|
108
|
-
|
|
109
|
-
|
|
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');
|
|
110
104
|
}
|
|
111
105
|
else {
|
|
112
|
-
prompter.note(`
|
|
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');
|
|
113
107
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
? `Write these changes to ${configPath}?`
|
|
117
|
-
: `Create ${configPath}?`,
|
|
118
|
-
initialValue: true,
|
|
119
|
-
});
|
|
120
|
-
if (!shouldWrite)
|
|
121
|
-
throw new errors_1.CancelledError();
|
|
122
|
-
const s = prompter.spinner();
|
|
123
|
-
s.start('Writing OpenCode configuration...');
|
|
124
|
-
yield files.writeFile(configPath, newContent);
|
|
125
|
-
s.stop(`Wrote configuration to ${configPath}.`);
|
|
126
|
-
});
|
|
108
|
+
}
|
|
109
|
+
prompter.outro('Setup complete!');
|
|
127
110
|
}
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
:
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
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(),
|
|
123
|
+
});
|
|
124
|
+
process.exit(0);
|
|
125
|
+
}
|
|
126
|
+
catch (error) {
|
|
127
|
+
if (error instanceof errors_1.CancelledError) {
|
|
128
|
+
process.exit(130);
|
|
144
129
|
}
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
130
|
+
if (error instanceof errors_1.PrerequisiteError) {
|
|
131
|
+
console.error(`Missing required binary: ${error.binary}`);
|
|
132
|
+
process.exit(2);
|
|
148
133
|
}
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
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');
|
|
134
|
+
if (error instanceof errors_1.CommandFailedError) {
|
|
135
|
+
console.error(error.message);
|
|
136
|
+
process.exit(5);
|
|
155
137
|
}
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
const makeDefault = yield prompter.confirm({
|
|
159
|
-
message: `Your default provider is ${settings.defaultProvider}. Switch to Berget AI instead?`,
|
|
160
|
-
initialValue: false,
|
|
161
|
-
});
|
|
162
|
-
if (makeDefault) {
|
|
163
|
-
settings.defaultProvider = 'berget';
|
|
164
|
-
yield writeJsonFile(files, settingsPath, settings);
|
|
165
|
-
prompter.note('Berget AI is now your default provider.', 'Updated default provider');
|
|
166
|
-
}
|
|
167
|
-
}
|
|
168
|
-
else {
|
|
169
|
-
settings.defaultProvider = 'berget';
|
|
170
|
-
yield writeJsonFile(files, settingsPath, settings);
|
|
171
|
-
prompter.note('Berget AI is now your default provider.', 'Updated default provider');
|
|
172
|
-
}
|
|
173
|
-
}
|
|
174
|
-
});
|
|
175
|
-
}
|
|
176
|
-
// ─── Helpers ─────────────────────────────────────────────────────────────────
|
|
177
|
-
function pathJoin(...parts) {
|
|
178
|
-
// Simple path join that avoids importing 'path' module
|
|
179
|
-
// This is good enough for cross-platform testing since tests control the path format
|
|
180
|
-
return parts.join('/');
|
|
181
|
-
}
|
|
182
|
-
function stripJsoncComments(content) {
|
|
183
|
-
content = content.replace(/\/\/.*$/gm, '');
|
|
184
|
-
content = content.replace(/\/\*[\s\S]*?\*\//g, '');
|
|
185
|
-
return content;
|
|
138
|
+
throw error;
|
|
139
|
+
}
|
|
186
140
|
}
|
|
141
|
+
exports.runSetupCommand = runSetupCommand;
|
|
142
|
+
// ─── Pi ────────────────────────────────────────────────────────────────────────
|
|
187
143
|
function generateDiff(oldText, newText, filePath) {
|
|
188
144
|
const oldLines = oldText.split('\n');
|
|
189
145
|
const newLines = newText.split('\n');
|
|
190
146
|
let result = `--- ${filePath}\n+++ ${filePath}\n`;
|
|
191
|
-
const
|
|
192
|
-
for (let
|
|
193
|
-
const oldLine = oldLines[
|
|
194
|
-
const newLine = newLines[
|
|
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];
|
|
195
151
|
if (oldLine !== newLine) {
|
|
196
152
|
if (oldLine !== undefined)
|
|
197
153
|
result += `- ${oldLine}\n`;
|
|
@@ -201,111 +157,14 @@ function generateDiff(oldText, newText, filePath) {
|
|
|
201
157
|
}
|
|
202
158
|
return result.trimEnd();
|
|
203
159
|
}
|
|
204
|
-
function readJsonMaybe(files, filePath) {
|
|
205
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
206
|
-
const content = yield files.readFile(filePath);
|
|
207
|
-
if (!content)
|
|
208
|
-
return null;
|
|
209
|
-
try {
|
|
210
|
-
return JSON.parse(content);
|
|
211
|
-
}
|
|
212
|
-
catch (_a) {
|
|
213
|
-
try {
|
|
214
|
-
return JSON.parse(stripJsoncComments(content));
|
|
215
|
-
}
|
|
216
|
-
catch (_b) {
|
|
217
|
-
return null;
|
|
218
|
-
}
|
|
219
|
-
}
|
|
220
|
-
});
|
|
221
|
-
}
|
|
222
|
-
function writeJsonFile(files, filePath, data) {
|
|
223
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
224
|
-
yield files.writeFile(filePath, JSON.stringify(data, null, 2) + '\n');
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
function hasPluginInConfig(config) {
|
|
228
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
229
|
-
if (!config)
|
|
230
|
-
return false;
|
|
231
|
-
const plugins = config.plugin || config.plugins || [];
|
|
232
|
-
return plugins.some((p) => p.includes(OPENCODE_PLUGIN_NAME));
|
|
233
|
-
});
|
|
234
|
-
}
|
|
235
|
-
function hasPiProviderInSettings(settings) {
|
|
236
|
-
return __awaiter(this, void 0, void 0, function* () {
|
|
237
|
-
if (!settings)
|
|
238
|
-
return false;
|
|
239
|
-
const packages = settings.packages || [];
|
|
240
|
-
return packages.some((p) => {
|
|
241
|
-
if (typeof p === 'string')
|
|
242
|
-
return p.includes(PI_PROVIDER_NAME);
|
|
243
|
-
if (typeof p === 'object' && p.source)
|
|
244
|
-
return p.source.includes(PI_PROVIDER_NAME);
|
|
245
|
-
return false;
|
|
246
|
-
});
|
|
247
|
-
});
|
|
248
|
-
}
|
|
249
|
-
function getOpencodeState(files, homeDir, cwd) {
|
|
250
|
-
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'));
|
|
255
|
-
return {
|
|
256
|
-
project: (yield hasPluginInConfig(projectJsonc)) || (yield hasPluginInConfig(projectJson)),
|
|
257
|
-
global: (yield hasPluginInConfig(globalJsonc)) || (yield hasPluginInConfig(globalJson)),
|
|
258
|
-
};
|
|
259
|
-
});
|
|
260
|
-
}
|
|
261
|
-
function getPiState(files, homeDir, cwd) {
|
|
262
|
-
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'));
|
|
265
|
-
return {
|
|
266
|
-
project: yield hasPiProviderInSettings(projectSettings),
|
|
267
|
-
global: yield hasPiProviderInSettings(globalSettings),
|
|
268
|
-
};
|
|
269
|
-
});
|
|
270
|
-
}
|
|
271
|
-
function getOpencodeLabel(state) {
|
|
272
|
-
if (state.project || state.global)
|
|
273
|
-
return ' (already configured)';
|
|
274
|
-
return '';
|
|
275
|
-
}
|
|
276
|
-
function getPiLabel(state) {
|
|
277
|
-
if (state.project || state.global)
|
|
278
|
-
return ' (already configured)';
|
|
279
|
-
return '';
|
|
280
|
-
}
|
|
281
|
-
function resolveOpencodeConfigPath(files, homeDir, cwd, scope) {
|
|
282
|
-
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');
|
|
286
|
-
if (yield files.exists(jsoncPath))
|
|
287
|
-
return jsoncPath;
|
|
288
|
-
if (yield files.exists(jsonPath))
|
|
289
|
-
return jsonPath;
|
|
290
|
-
return jsonPath;
|
|
291
|
-
}
|
|
292
|
-
else {
|
|
293
|
-
const globalDir = pathJoin(homeDir, '.config', 'opencode');
|
|
294
|
-
const jsoncPath = pathJoin(globalDir, 'opencode.jsonc');
|
|
295
|
-
const jsonPath = pathJoin(globalDir, 'opencode.json');
|
|
296
|
-
if (yield files.exists(jsoncPath))
|
|
297
|
-
return jsoncPath;
|
|
298
|
-
if (yield files.exists(jsonPath))
|
|
299
|
-
return jsonPath;
|
|
300
|
-
return jsonPath;
|
|
301
|
-
}
|
|
302
|
-
});
|
|
303
|
-
}
|
|
304
160
|
function generateModifiedContent(existingContent, configPath) {
|
|
305
161
|
if (configPath.endsWith('.jsonc')) {
|
|
306
162
|
const content = existingContent || '{}';
|
|
307
163
|
const parseErrors = [];
|
|
308
|
-
const parsed = (0, jsonc_parser_1.parse)(content, parseErrors, {
|
|
164
|
+
const parsed = (0, jsonc_parser_1.parse)(content, parseErrors, {
|
|
165
|
+
allowTrailingComma: true,
|
|
166
|
+
disallowComments: false,
|
|
167
|
+
});
|
|
309
168
|
let jsConfig = {};
|
|
310
169
|
const canModifyText = parsed !== undefined &&
|
|
311
170
|
typeof parsed === 'object' &&
|
|
@@ -314,7 +173,7 @@ function generateModifiedContent(existingContent, configPath) {
|
|
|
314
173
|
if (canModifyText) {
|
|
315
174
|
jsConfig = parsed;
|
|
316
175
|
}
|
|
317
|
-
const pluginsKey = jsConfig.plugins
|
|
176
|
+
const pluginsKey = jsConfig.plugins === undefined ? 'plugin' : 'plugins';
|
|
318
177
|
const existing = jsConfig[pluginsKey] || [];
|
|
319
178
|
const filtered = existing.filter((p) => !p.includes(OPENCODE_PLUGIN_NAME));
|
|
320
179
|
filtered.push(OPENCODE_PLUGIN);
|
|
@@ -334,8 +193,8 @@ function generateModifiedContent(existingContent, configPath) {
|
|
|
334
193
|
}
|
|
335
194
|
// Malformed, empty, or non-object JSONC — write a clean config
|
|
336
195
|
const config = {
|
|
337
|
-
[pluginsKey]: filtered,
|
|
338
196
|
$schema: 'https://opencode.ai/config.json',
|
|
197
|
+
[pluginsKey]: filtered,
|
|
339
198
|
};
|
|
340
199
|
return JSON.stringify(config, null, 2) + '\n';
|
|
341
200
|
}
|
|
@@ -345,11 +204,11 @@ function generateModifiedContent(existingContent, configPath) {
|
|
|
345
204
|
try {
|
|
346
205
|
config = JSON.parse(existingContent);
|
|
347
206
|
}
|
|
348
|
-
catch
|
|
207
|
+
catch {
|
|
349
208
|
// ignore malformed, overwrite
|
|
350
209
|
}
|
|
351
210
|
}
|
|
352
|
-
const pluginsKey = config.plugins
|
|
211
|
+
const pluginsKey = config.plugins === undefined ? 'plugin' : 'plugins';
|
|
353
212
|
const existing = config[pluginsKey] || [];
|
|
354
213
|
const filtered = existing.filter((p) => !p.includes(OPENCODE_PLUGIN_NAME));
|
|
355
214
|
filtered.push(OPENCODE_PLUGIN);
|
|
@@ -357,36 +216,283 @@ function generateModifiedContent(existingContent, configPath) {
|
|
|
357
216
|
config.$schema = config.$schema || 'https://opencode.ai/config.json';
|
|
358
217
|
return JSON.stringify(config, null, 2) + '\n';
|
|
359
218
|
}
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
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
|
+
};
|
|
234
|
+
}
|
|
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 {
|
|
367
279
|
try {
|
|
368
|
-
|
|
369
|
-
prompter: new clack_prompter_js_1.ClackPrompter(),
|
|
370
|
-
files: new fs_file_store_js_1.FsFileStore(),
|
|
371
|
-
commands: new spawn_command_runner_js_1.SpawnCommandRunner(),
|
|
372
|
-
homeDir: os.homedir(),
|
|
373
|
-
cwd: process.cwd(),
|
|
374
|
-
});
|
|
280
|
+
return JSON.parse(stripJsoncComments(content));
|
|
375
281
|
}
|
|
376
|
-
catch
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
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');
|
|
387
430
|
}
|
|
388
|
-
throw err;
|
|
389
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
|
+
],
|
|
390
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?',
|
|
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');
|
|
391
498
|
}
|
|
392
|
-
exports.runSetupCommand = runSetupCommand;
|