@siemens/ix-mcp-angular 3.2.0-v.1.7.0
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/LICENSE.md +20 -0
- package/README.md +330 -0
- package/config.json +48 -0
- package/database/vectors.db +0 -0
- package/dist/src/cli.d.ts +3 -0
- package/dist/src/cli.d.ts.map +1 -0
- package/dist/src/cli.js +298 -0
- package/dist/src/cli.js.map +1 -0
- package/dist/src/config.d.ts +34 -0
- package/dist/src/config.d.ts.map +1 -0
- package/dist/src/config.js +33 -0
- package/dist/src/config.js.map +1 -0
- package/dist/src/db-handler.d.ts +47 -0
- package/dist/src/db-handler.d.ts.map +1 -0
- package/dist/src/db-handler.js +143 -0
- package/dist/src/db-handler.js.map +1 -0
- package/dist/src/embeddings.d.ts +84 -0
- package/dist/src/embeddings.d.ts.map +1 -0
- package/dist/src/embeddings.js +300 -0
- package/dist/src/embeddings.js.map +1 -0
- package/dist/src/index.d.ts +24 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +342 -0
- package/dist/src/index.js.map +1 -0
- package/dist/src/keys.d.ts +4 -0
- package/dist/src/keys.d.ts.map +1 -0
- package/dist/src/keys.js +25 -0
- package/dist/src/keys.js.map +1 -0
- package/dist/src/logger.d.ts +24 -0
- package/dist/src/logger.d.ts.map +1 -0
- package/dist/src/logger.js +185 -0
- package/dist/src/logger.js.map +1 -0
- package/dist/src/prompt.d.ts +19 -0
- package/dist/src/prompt.d.ts.map +1 -0
- package/dist/src/prompt.js +94 -0
- package/dist/src/prompt.js.map +1 -0
- package/dist/src/setup.d.ts +26 -0
- package/dist/src/setup.d.ts.map +1 -0
- package/dist/src/setup.js +718 -0
- package/dist/src/setup.js.map +1 -0
- package/dist/src/token.d.ts +22 -0
- package/dist/src/token.d.ts.map +1 -0
- package/dist/src/token.js +75 -0
- package/dist/src/token.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,718 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Copyright (c) Siemens 2016 - 2025
|
|
3
|
+
* SPDX-License-Identifier: MIT
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'fs';
|
|
6
|
+
import * as path from 'path';
|
|
7
|
+
import * as os from 'os';
|
|
8
|
+
import chalk from 'chalk';
|
|
9
|
+
import which from 'which';
|
|
10
|
+
import * as jsonc from 'jsonc-parser';
|
|
11
|
+
import { confirmPrompt, multiSelectPrompt, passwordPrompt, showSuccess, showError, showWarning, showInfo } from './prompt.js';
|
|
12
|
+
import { setToken, getToken } from './token.js';
|
|
13
|
+
import { exec } from 'child_process';
|
|
14
|
+
import { promisify } from 'util';
|
|
15
|
+
import { fileURLToPath } from 'url';
|
|
16
|
+
import { loadConfig } from './config.js';
|
|
17
|
+
const execAsync = promisify(exec);
|
|
18
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
19
|
+
const __dirname = path.dirname(__filename);
|
|
20
|
+
export class SDLMCPSetup {
|
|
21
|
+
config = loadConfig();
|
|
22
|
+
repoRoot = this.findRepoRoot();
|
|
23
|
+
setupOptions = [
|
|
24
|
+
{
|
|
25
|
+
title: 'Local VS Code / GitHub Copilot (Repository)',
|
|
26
|
+
value: 'local-vscode',
|
|
27
|
+
description: 'Creates .vscode/mcp.json in current repository (recommended for teams)',
|
|
28
|
+
selected: true
|
|
29
|
+
},
|
|
30
|
+
{
|
|
31
|
+
title: 'Cline Global Settings',
|
|
32
|
+
value: 'cline-global',
|
|
33
|
+
description: 'Sets up Cline MCP configuration globally for your user, you should maybe have this package installed globally (as well)'
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
title: 'VS Code / GitHub Copilot Global Config',
|
|
37
|
+
value: 'vscode-user',
|
|
38
|
+
description: 'Sets up VS Code user-level MCP configuration globally, you should maybe have this package installed globally (as well)'
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
title: 'Zed Global Settings',
|
|
42
|
+
value: 'zed-settings',
|
|
43
|
+
description: 'Sets up Zed editor / agent MCP configuration globally, you should maybe have this package installed globally (as well)'
|
|
44
|
+
}
|
|
45
|
+
];
|
|
46
|
+
getRepoConfigOptions() {
|
|
47
|
+
if (!this.config.agentsFile) {
|
|
48
|
+
throw new Error('Agent instruction file setup is not enabled for this package.');
|
|
49
|
+
}
|
|
50
|
+
if (!this.config.instructionFileName) {
|
|
51
|
+
throw new Error('Instruction file name is not defined in the configuration.');
|
|
52
|
+
}
|
|
53
|
+
return [
|
|
54
|
+
{
|
|
55
|
+
title: `Isolated GitHub Copilot Instructions (.github/instructions/${this.config.instructionFileName /* Element.instructions.md */})`,
|
|
56
|
+
value: 'isolated-instructions',
|
|
57
|
+
description: 'Recommended: Create isolated instructions for GitHub Copilot (so you can add yours separately)',
|
|
58
|
+
defaultPath: `.github/instructions/${this.config.instructionFileName /* Element.instructions.md */}`,
|
|
59
|
+
selected: true
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
title: 'AGENTS.md (Repository root)',
|
|
63
|
+
value: 'agents-md',
|
|
64
|
+
description: 'Create AGENTS.md in the repository root for general AI agent instructions (does not allow you to add your own instructions)',
|
|
65
|
+
defaultPath: 'AGENTS.md'
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
title: 'General Copilot Instructions (.github/copilot-instructions.md)',
|
|
69
|
+
value: 'copilot-instructions',
|
|
70
|
+
description: 'Create the general copilot-instructions.md for GitHub Copilot and other agents (does not allow you to add your own instructions)',
|
|
71
|
+
defaultPath: '.github/copilot-instructions.md'
|
|
72
|
+
}
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
findRepoRoot() {
|
|
76
|
+
let currentDir = process.cwd();
|
|
77
|
+
while (currentDir !== path.dirname(currentDir)) {
|
|
78
|
+
if (fs.existsSync(path.join(currentDir, '.git')) || fs.existsSync(path.join(currentDir, 'package.json'))) {
|
|
79
|
+
return currentDir;
|
|
80
|
+
}
|
|
81
|
+
currentDir = path.dirname(currentDir);
|
|
82
|
+
}
|
|
83
|
+
return process.cwd();
|
|
84
|
+
}
|
|
85
|
+
isInstalledInProject() {
|
|
86
|
+
try {
|
|
87
|
+
const packageJsonPath = path.join(__dirname, '../../package.json');
|
|
88
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf-8'));
|
|
89
|
+
const packageName = packageJson.name;
|
|
90
|
+
const projectPackageJsonPath = path.join(this.repoRoot, 'package.json');
|
|
91
|
+
if (!fs.existsSync(projectPackageJsonPath)) {
|
|
92
|
+
return false;
|
|
93
|
+
}
|
|
94
|
+
const projectPackageJson = JSON.parse(fs.readFileSync(projectPackageJsonPath, 'utf8'));
|
|
95
|
+
return !!((projectPackageJson.dependencies && projectPackageJson.dependencies[packageName]) ||
|
|
96
|
+
(projectPackageJson.devDependencies && projectPackageJson.devDependencies[packageName]) ||
|
|
97
|
+
(projectPackageJson.optionalDependencies && projectPackageJson.optionalDependencies[packageName]));
|
|
98
|
+
}
|
|
99
|
+
catch {
|
|
100
|
+
return false;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
getConfigPaths() {
|
|
104
|
+
const platform = os.platform();
|
|
105
|
+
const homeDir = os.homedir();
|
|
106
|
+
const paths = {
|
|
107
|
+
clineGlobal: '',
|
|
108
|
+
vscodeUser: '',
|
|
109
|
+
zedSettings: ''
|
|
110
|
+
};
|
|
111
|
+
switch (platform) {
|
|
112
|
+
case 'darwin':
|
|
113
|
+
paths.clineGlobal = path.join(homeDir, 'Library/Application Support/Code/User/globalStorage/saoudrizwan.claude-dev/settings');
|
|
114
|
+
paths.vscodeUser = path.join(homeDir, 'Library/Application Support/Code/User');
|
|
115
|
+
paths.zedSettings = path.join(homeDir, '.config/zed/settings.json');
|
|
116
|
+
break;
|
|
117
|
+
case 'linux':
|
|
118
|
+
paths.clineGlobal = path.join(homeDir, '.config/Code/User/globalStorage/saoudrizwan.claude-dev/settings');
|
|
119
|
+
paths.vscodeUser = path.join(homeDir, '.config/Code/User');
|
|
120
|
+
paths.zedSettings = path.join(homeDir, '.config/zed/settings.json');
|
|
121
|
+
break;
|
|
122
|
+
case 'win32':
|
|
123
|
+
paths.clineGlobal = path.join(homeDir, 'AppData/Roaming/Code/User/globalStorage/saoudrizwan.claude-dev/settings');
|
|
124
|
+
paths.vscodeUser = path.join(homeDir, 'AppData/Roaming/Code/User');
|
|
125
|
+
paths.zedSettings = path.join(homeDir, 'AppData/Roaming/Zed/settings.json');
|
|
126
|
+
break;
|
|
127
|
+
default:
|
|
128
|
+
throw new Error(`Unsupported platform: ${platform}`);
|
|
129
|
+
}
|
|
130
|
+
return paths;
|
|
131
|
+
}
|
|
132
|
+
updateJsonWithComments(filePath, updates, defaultConfig, allowNewFile = true) {
|
|
133
|
+
let content = '';
|
|
134
|
+
let parsedConfig;
|
|
135
|
+
if (fs.existsSync(filePath)) {
|
|
136
|
+
content = fs.readFileSync(filePath, 'utf8');
|
|
137
|
+
try {
|
|
138
|
+
parsedConfig = jsonc.parse(content) || defaultConfig;
|
|
139
|
+
}
|
|
140
|
+
catch (error) {
|
|
141
|
+
throw new Error(`Failed to parse JSON file: ${error}`);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
if (!allowNewFile) {
|
|
146
|
+
throw new Error(`Configuration file does not exist: ${filePath}`);
|
|
147
|
+
}
|
|
148
|
+
parsedConfig = defaultConfig;
|
|
149
|
+
content = JSON.stringify(defaultConfig, null, 2);
|
|
150
|
+
}
|
|
151
|
+
// If file exists and has content, try to preserve formatting and comments
|
|
152
|
+
if (content.trim()) {
|
|
153
|
+
try {
|
|
154
|
+
let modifiedContent = content;
|
|
155
|
+
// Apply each update sequentially to preserve comments
|
|
156
|
+
for (const update of updates) {
|
|
157
|
+
// Check if the path exists, if not create the parent structure
|
|
158
|
+
const parentPath = update.path.slice(0, -1);
|
|
159
|
+
// Ensure parent path exists
|
|
160
|
+
if (parentPath.length > 0) {
|
|
161
|
+
const tree = jsonc.parseTree(modifiedContent);
|
|
162
|
+
if (!tree) {
|
|
163
|
+
throw new Error('Failed to parse JSON content into tree structure.');
|
|
164
|
+
}
|
|
165
|
+
const parentNode = jsonc.findNodeAtLocation(tree, parentPath);
|
|
166
|
+
if (!parentNode) {
|
|
167
|
+
// Create parent structure
|
|
168
|
+
const edits = jsonc.modify(modifiedContent, parentPath, {}, {
|
|
169
|
+
formattingOptions: {
|
|
170
|
+
insertSpaces: true,
|
|
171
|
+
tabSize: 2
|
|
172
|
+
}
|
|
173
|
+
});
|
|
174
|
+
modifiedContent = jsonc.applyEdits(modifiedContent, edits);
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
// Now set the actual value
|
|
178
|
+
const edits = jsonc.modify(modifiedContent, update.path, update.value, {
|
|
179
|
+
formattingOptions: {
|
|
180
|
+
insertSpaces: true,
|
|
181
|
+
tabSize: 2
|
|
182
|
+
}
|
|
183
|
+
});
|
|
184
|
+
modifiedContent = jsonc.applyEdits(modifiedContent, edits);
|
|
185
|
+
}
|
|
186
|
+
fs.writeFileSync(filePath, modifiedContent);
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
showError('Could not update, skipping to avoid corruption.');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
else {
|
|
193
|
+
// New file - apply updates to default config and use JSON stringify
|
|
194
|
+
for (const update of updates) {
|
|
195
|
+
this.setNestedValue(parsedConfig, update.path, update.value);
|
|
196
|
+
}
|
|
197
|
+
fs.writeFileSync(filePath, JSON.stringify(parsedConfig, null, 2));
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
setNestedValue(obj, path, value) {
|
|
201
|
+
let current = obj;
|
|
202
|
+
for (let i = 0; i < path.length - 1; i++) {
|
|
203
|
+
const key = path[i];
|
|
204
|
+
if (!(key in current) || typeof current[key] !== 'object' || Array.isArray(current[key])) {
|
|
205
|
+
current[key] = {};
|
|
206
|
+
}
|
|
207
|
+
current = current[key];
|
|
208
|
+
}
|
|
209
|
+
current[path[path.length - 1]] = value;
|
|
210
|
+
}
|
|
211
|
+
async setupMCPConfigs() {
|
|
212
|
+
console.log(chalk.bold.blue(`\n${this.config.projectName /* 'Element' */} MCP Setup\n`));
|
|
213
|
+
showInfo(`Repository root: ${this.repoRoot}`);
|
|
214
|
+
console.log();
|
|
215
|
+
console.log(chalk.gray(`This will set up ${this.config.projectName /* 'Element' */} MCP server configurations for your AI tools.`));
|
|
216
|
+
console.log(chalk.gray('You can select multiple options to configure different tools at once.\n'));
|
|
217
|
+
const selectedValues = await multiSelectPrompt('Which tools would you like to configure?', this.setupOptions);
|
|
218
|
+
if (!selectedValues) {
|
|
219
|
+
showWarning('No options selected. Setup cancelled.');
|
|
220
|
+
return;
|
|
221
|
+
}
|
|
222
|
+
if (selectedValues.length === 0) {
|
|
223
|
+
showInfo('No options selected. Nothing to do.');
|
|
224
|
+
// Offer repository configuration setup
|
|
225
|
+
const success = await this.setupAgentInstructions();
|
|
226
|
+
if (success) {
|
|
227
|
+
this.printUsageInstructions([]);
|
|
228
|
+
}
|
|
229
|
+
return;
|
|
230
|
+
}
|
|
231
|
+
const selectedOptions = this.setupOptions.filter(option => selectedValues.includes(option.value));
|
|
232
|
+
console.log();
|
|
233
|
+
showInfo('Selected configurations:');
|
|
234
|
+
selectedOptions.forEach(option => {
|
|
235
|
+
console.log(` ${chalk.green('•')} ${option.title}`);
|
|
236
|
+
});
|
|
237
|
+
console.log();
|
|
238
|
+
const enableLogging = await confirmPrompt('Enable local logging of search queries and retrieval results?', true);
|
|
239
|
+
console.log();
|
|
240
|
+
const proceed = await confirmPrompt('Proceed with setup?', true);
|
|
241
|
+
if (!proceed) {
|
|
242
|
+
showWarning('Setup cancelled.');
|
|
243
|
+
return;
|
|
244
|
+
}
|
|
245
|
+
console.log();
|
|
246
|
+
showInfo('Setting up configurations...');
|
|
247
|
+
let successCount = 0;
|
|
248
|
+
for (const option of selectedOptions) {
|
|
249
|
+
try {
|
|
250
|
+
switch (option.value) {
|
|
251
|
+
case 'local-vscode':
|
|
252
|
+
await this.setupLocalVSCodeConfig(enableLogging);
|
|
253
|
+
break;
|
|
254
|
+
case 'cline-global':
|
|
255
|
+
await this.setupClineGlobalConfig(enableLogging);
|
|
256
|
+
break;
|
|
257
|
+
case 'vscode-user':
|
|
258
|
+
await this.setupVSCodeUserConfig(enableLogging);
|
|
259
|
+
break;
|
|
260
|
+
case 'zed-settings':
|
|
261
|
+
await this.setupZedConfig(enableLogging);
|
|
262
|
+
break;
|
|
263
|
+
}
|
|
264
|
+
successCount++;
|
|
265
|
+
}
|
|
266
|
+
catch (error) {
|
|
267
|
+
showError(`Failed to setup ${option.title}: ${error}`);
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
console.log();
|
|
271
|
+
if (successCount === selectedOptions.length) {
|
|
272
|
+
showSuccess(`All ${successCount} configurations set up successfully!`);
|
|
273
|
+
// Offer repository configuration setup
|
|
274
|
+
const success = await this.setupAgentInstructions();
|
|
275
|
+
if (success) {
|
|
276
|
+
this.printUsageInstructions(selectedOptions);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
else {
|
|
280
|
+
showWarning(`${successCount}/${selectedOptions.length} configurations set up successfully.`);
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async setupToken() {
|
|
284
|
+
console.log(chalk.bold.blue(`\n${this.config.projectName /* 'Element' */} MCP Token Setup\n`));
|
|
285
|
+
showInfo(`Setting up LLM token for ${this.config.designSystem /* 'design system' */} documentation access...`);
|
|
286
|
+
console.log(chalk.gray("Get your token with 'llm' scope from: https://my.siemens.com/"));
|
|
287
|
+
console.log(chalk.gray('This token is stored securely in your system keychain.\n'));
|
|
288
|
+
if (process.env.SDL_MCP_TOKEN_ENV === 'true') {
|
|
289
|
+
showWarning('You are using a token from a environment variable (SDL_MCP_TOKEN_ENV=true), skipping token setup.');
|
|
290
|
+
if (process.env.OPENAI_API_KEY) {
|
|
291
|
+
showSuccess('Using OPENAI_API_KEY from environment.');
|
|
292
|
+
process.exit(0);
|
|
293
|
+
}
|
|
294
|
+
else {
|
|
295
|
+
showError('OPENAI_API_KEY is not set. Please set it.');
|
|
296
|
+
process.exit(1);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
const hasPrevToken = await getToken();
|
|
300
|
+
let token;
|
|
301
|
+
if (hasPrevToken) {
|
|
302
|
+
token = await passwordPrompt('A token is already configured. Enter a new token to overwrite or leave empty to keep existing:', false);
|
|
303
|
+
}
|
|
304
|
+
else {
|
|
305
|
+
token = await passwordPrompt('Enter your LLM token:');
|
|
306
|
+
}
|
|
307
|
+
if (!token && hasPrevToken) {
|
|
308
|
+
showSuccess('Token stored securely in keychain!');
|
|
309
|
+
return;
|
|
310
|
+
}
|
|
311
|
+
if (!token) {
|
|
312
|
+
showWarning('Token setup cancelled.');
|
|
313
|
+
process.exit(1);
|
|
314
|
+
}
|
|
315
|
+
try {
|
|
316
|
+
await setToken(token);
|
|
317
|
+
showSuccess('Token stored securely in keychain!');
|
|
318
|
+
showInfo(`Note: Each team member needs to set up their own token using "${this.config.binName /* 'element-mcp' */} setup-token"`);
|
|
319
|
+
}
|
|
320
|
+
catch (error) {
|
|
321
|
+
showError(`Failed to store token: ${error}`);
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
async setupLocalVSCodeConfig(enableLogging = false) {
|
|
326
|
+
const configDir = path.join(this.repoRoot, '.vscode');
|
|
327
|
+
const configFile = path.join(configDir, 'mcp.json');
|
|
328
|
+
await this.ensureDirectoryExists(configDir);
|
|
329
|
+
const useDevSetup = process.env.SDL_MCP_CONFIG_DEV === 'true';
|
|
330
|
+
const args = [!useDevSetup ? this.config.name /* '@siemens/element-mcp' */ : path.join('dist', 'src', 'cli.js')];
|
|
331
|
+
if (enableLogging) {
|
|
332
|
+
args.push('--log');
|
|
333
|
+
}
|
|
334
|
+
const defaultConfig = { servers: {} };
|
|
335
|
+
const updates = [
|
|
336
|
+
{
|
|
337
|
+
path: ['servers', this.config.name /* '@siemens/element-mcp' */],
|
|
338
|
+
value: {
|
|
339
|
+
type: 'stdio',
|
|
340
|
+
command: !useDevSetup ? 'npx' : 'node',
|
|
341
|
+
args
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
];
|
|
345
|
+
try {
|
|
346
|
+
this.updateJsonWithComments(configFile, updates, defaultConfig);
|
|
347
|
+
showSuccess(`Local VS Code configuration updated`);
|
|
348
|
+
}
|
|
349
|
+
catch (error) {
|
|
350
|
+
showError(`Failed to update VS Code config: ${error}`);
|
|
351
|
+
throw error;
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
async setupClineGlobalConfig(enableLogging = false) {
|
|
355
|
+
const paths = this.getConfigPaths();
|
|
356
|
+
const configDir = paths.clineGlobal;
|
|
357
|
+
const configFile = path.join(configDir, 'cline_mcp_settings.json');
|
|
358
|
+
await this.ensureDirectoryExists(configDir);
|
|
359
|
+
const useDevSetup = process.env.SDL_MCP_CONFIG_DEV === 'true';
|
|
360
|
+
const args = [
|
|
361
|
+
!useDevSetup ? this.config.name /* '@siemens/element-mcp' */ : path.join(this.repoRoot, 'dist', 'src', 'cli.js')
|
|
362
|
+
];
|
|
363
|
+
if (enableLogging) {
|
|
364
|
+
args.push('--log');
|
|
365
|
+
}
|
|
366
|
+
const defaultConfig = { mcpServers: {} };
|
|
367
|
+
const updates = [
|
|
368
|
+
{
|
|
369
|
+
path: ['mcpServers', this.config.name /* '@siemens/element-mcp' */],
|
|
370
|
+
value: {
|
|
371
|
+
command: !useDevSetup ? 'npx' : 'node',
|
|
372
|
+
args
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
];
|
|
376
|
+
try {
|
|
377
|
+
this.updateJsonWithComments(configFile, updates, defaultConfig);
|
|
378
|
+
showSuccess(`Cline global configuration updated`);
|
|
379
|
+
}
|
|
380
|
+
catch (error) {
|
|
381
|
+
showError(`Failed to update Cline config: ${error}`);
|
|
382
|
+
throw error;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
async setupVSCodeUserConfig(enableLogging = false) {
|
|
386
|
+
const paths = this.getConfigPaths();
|
|
387
|
+
const configDir = paths.vscodeUser;
|
|
388
|
+
const configFile = path.join(configDir, 'mcp.json');
|
|
389
|
+
await this.ensureDirectoryExists(configDir);
|
|
390
|
+
const useDevSetup = process.env.SDL_MCP_CONFIG_DEV === 'true';
|
|
391
|
+
const args = [
|
|
392
|
+
!useDevSetup ? this.config.name /* '@siemens/element-mcp' */ : path.join(this.repoRoot, 'dist', 'src', 'cli.js')
|
|
393
|
+
];
|
|
394
|
+
if (enableLogging) {
|
|
395
|
+
args.push('--log');
|
|
396
|
+
}
|
|
397
|
+
const defaultConfig = { servers: {} };
|
|
398
|
+
const updates = [
|
|
399
|
+
{
|
|
400
|
+
path: ['servers', this.config.name /* '@siemens/element-mcp' */],
|
|
401
|
+
value: {
|
|
402
|
+
type: 'stdio',
|
|
403
|
+
command: !useDevSetup ? 'npx' : 'node',
|
|
404
|
+
args
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
];
|
|
408
|
+
try {
|
|
409
|
+
this.updateJsonWithComments(configFile, updates, defaultConfig);
|
|
410
|
+
showSuccess(`VS Code user configuration updated`);
|
|
411
|
+
}
|
|
412
|
+
catch (error) {
|
|
413
|
+
showError(`Failed to update VS Code user config: ${error}`);
|
|
414
|
+
throw error;
|
|
415
|
+
}
|
|
416
|
+
}
|
|
417
|
+
async setupZedConfig(enableLogging = false) {
|
|
418
|
+
const paths = this.getConfigPaths();
|
|
419
|
+
const configFile = paths.zedSettings;
|
|
420
|
+
const configDir = path.dirname(configFile);
|
|
421
|
+
await this.ensureDirectoryExists(configDir);
|
|
422
|
+
// Check if npx is available
|
|
423
|
+
try {
|
|
424
|
+
await execAsync('which npx');
|
|
425
|
+
}
|
|
426
|
+
catch {
|
|
427
|
+
showError('npx is not available in PATH. Please install Node.js/npm first.');
|
|
428
|
+
throw new Error('npx not found');
|
|
429
|
+
}
|
|
430
|
+
const useDevSetup = process.env.SDL_MCP_CONFIG_DEV === 'true';
|
|
431
|
+
const npxPath = await this.findNpxPath();
|
|
432
|
+
const args = [
|
|
433
|
+
!useDevSetup ? this.config.name /* '@siemens/element-mcp' */ : path.join(this.repoRoot, 'dist', 'src', 'cli.js')
|
|
434
|
+
];
|
|
435
|
+
if (enableLogging) {
|
|
436
|
+
args.push('--log');
|
|
437
|
+
}
|
|
438
|
+
const defaultConfig = {};
|
|
439
|
+
const updates = [
|
|
440
|
+
{
|
|
441
|
+
path: ['context_servers', this.config.name /* '@siemens/element-mcp' */],
|
|
442
|
+
value: {
|
|
443
|
+
source: 'command',
|
|
444
|
+
command: npxPath,
|
|
445
|
+
args,
|
|
446
|
+
env: {}
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
];
|
|
450
|
+
try {
|
|
451
|
+
this.updateJsonWithComments(configFile, updates, defaultConfig);
|
|
452
|
+
showSuccess(`Zed configuration updated`);
|
|
453
|
+
}
|
|
454
|
+
catch (error) {
|
|
455
|
+
showError(`Failed to update Zed config: ${error}`);
|
|
456
|
+
throw error;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
async findNpxPath() {
|
|
460
|
+
try {
|
|
461
|
+
return (await which('npx')).replaceAll(path.sep, path.posix.sep);
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
const commonPaths = ['/usr/local/bin/npx', '/usr/bin/npx', 'npx'];
|
|
465
|
+
for (const path of commonPaths) {
|
|
466
|
+
try {
|
|
467
|
+
await execAsync(`${path} --version`);
|
|
468
|
+
return path;
|
|
469
|
+
}
|
|
470
|
+
catch {
|
|
471
|
+
continue;
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
return 'npx';
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
async ensureDirectoryExists(dir) {
|
|
478
|
+
if (!fs.existsSync(dir)) {
|
|
479
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
async checkSetup() {
|
|
483
|
+
console.log(chalk.bold.blue(`\n${this.config.projectName /* 'Element' */} MCP Setup Status\n`));
|
|
484
|
+
showInfo(`Repository root: ${this.repoRoot}`);
|
|
485
|
+
console.log();
|
|
486
|
+
try {
|
|
487
|
+
const token = await getToken();
|
|
488
|
+
if (token) {
|
|
489
|
+
showSuccess('LLM token is configured');
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
showError(`LLM token not found. Run '${this.config.binName /* 'element-mcp' */} setup-token'`);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
catch (error) {
|
|
496
|
+
showWarning(`Could not check token: ${error}`);
|
|
497
|
+
}
|
|
498
|
+
console.log();
|
|
499
|
+
console.log(chalk.bold('MCP Configuration Status:'));
|
|
500
|
+
const paths = this.getConfigPaths();
|
|
501
|
+
const configs = [
|
|
502
|
+
{
|
|
503
|
+
name: 'Local VS Code',
|
|
504
|
+
path: path.join(this.repoRoot, '.vscode', 'mcp.json')
|
|
505
|
+
},
|
|
506
|
+
{
|
|
507
|
+
name: 'Cline Global Settings',
|
|
508
|
+
path: paths.clineGlobal
|
|
509
|
+
},
|
|
510
|
+
{
|
|
511
|
+
name: 'VS Code / GitHub Copilot Global Config',
|
|
512
|
+
path: path.join(paths.vscodeUser, 'mcp.json')
|
|
513
|
+
},
|
|
514
|
+
{
|
|
515
|
+
name: 'Zed Global Settings',
|
|
516
|
+
path: paths.zedSettings
|
|
517
|
+
}
|
|
518
|
+
];
|
|
519
|
+
for (const config of configs) {
|
|
520
|
+
if (fs.existsSync(config.path)) {
|
|
521
|
+
try {
|
|
522
|
+
const content = fs.readFileSync(config.path, 'utf8');
|
|
523
|
+
const parsedContent = jsonc.parse(content);
|
|
524
|
+
const hasSDLMCP = JSON.stringify(parsedContent).includes(this.config.name /* '@siemens/element-mcp' */);
|
|
525
|
+
if (hasSDLMCP) {
|
|
526
|
+
showSuccess(`${config.name} MCP configuration found`);
|
|
527
|
+
}
|
|
528
|
+
else {
|
|
529
|
+
showWarning(`${config.name} configuration exists but missing ${this.config.name /* 'MCP' */} server`);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
catch {
|
|
533
|
+
showError(`${config.name} configuration file is corrupted (parse error)`);
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
showError(`${config.name} MCP configuration not found`);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
console.log();
|
|
541
|
+
console.log(chalk.bold('Configuration file locations:'));
|
|
542
|
+
for (const config of configs) {
|
|
543
|
+
console.log(` ${chalk.gray(config.name)}: ${config.path}`);
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
printUsageInstructions(selectedOptions) {
|
|
547
|
+
console.log(chalk.bold.green('\nNext Steps\n'));
|
|
548
|
+
console.log(chalk.bold('IMPORTANT:'), 'Trust and start the MCP server in your AI tools to enable functionality. If you are using a version manager like nvm or asdf, you may need to replace the command with the full path to `npx`, get it by running `which/where npx`.\n');
|
|
549
|
+
console.log(chalk.bold('Usage Examples:'));
|
|
550
|
+
this.config.exampleQuestions.forEach((question) => {
|
|
551
|
+
console.log(chalk.gray(`• "${question}"`));
|
|
552
|
+
});
|
|
553
|
+
console.log();
|
|
554
|
+
selectedOptions.forEach(option => {
|
|
555
|
+
switch (option.value) {
|
|
556
|
+
case 'local-vscode':
|
|
557
|
+
console.log(chalk.bold('Local VS Code:'));
|
|
558
|
+
console.log(' • Commit the .vscode/mcp.json file to your repository');
|
|
559
|
+
console.log(' • Share with your team for consistent setup');
|
|
560
|
+
console.log(' • Restart VS Code and click "Server" icon in GitHub Copilot Chat\n');
|
|
561
|
+
break;
|
|
562
|
+
case 'cline-global':
|
|
563
|
+
console.log(chalk.bold('Cline:'));
|
|
564
|
+
console.log(' • Configuration is now set up globally');
|
|
565
|
+
console.log(' • Restart Cline to load the new MCP server\n');
|
|
566
|
+
break;
|
|
567
|
+
case 'vscode-user':
|
|
568
|
+
console.log(chalk.bold('VS Code User:'));
|
|
569
|
+
console.log(' • Configuration is now set up globally for your user');
|
|
570
|
+
console.log(' • Restart VS Code and click "Server" icon in GitHub Copilot Chat\n');
|
|
571
|
+
break;
|
|
572
|
+
case 'zed-settings':
|
|
573
|
+
console.log(chalk.bold('Zed:'));
|
|
574
|
+
console.log(' • Configuration is now set up in your Zed settings');
|
|
575
|
+
console.log(' • Restart Zed to load the new MCP server\n');
|
|
576
|
+
break;
|
|
577
|
+
}
|
|
578
|
+
});
|
|
579
|
+
showInfo('The MCP server will auto-start when you use it in supported tools.');
|
|
580
|
+
console.log(chalk.gray(`\nRun this setup command in every project directory where you want to use ${this.config.designSystem /* 'design system' */} documentation search.`));
|
|
581
|
+
}
|
|
582
|
+
async setupAgentInstructions() {
|
|
583
|
+
console.log(chalk.bold.blue('\n\nAI Agent Instruction Setup\n'));
|
|
584
|
+
const proceed = await confirmPrompt(`Would you like to add instruction files for AI agents to work with ${this.config.designSystem /* 'the design system' */} to your repository (recommended)?`, true);
|
|
585
|
+
if (!proceed) {
|
|
586
|
+
showInfo('Skipping AI agent instruction setup.');
|
|
587
|
+
return true;
|
|
588
|
+
}
|
|
589
|
+
console.log(chalk.gray(`These instruction files help AI agents work more effectively with ${this.config.designSystem /* 'the design system' */}.\n`));
|
|
590
|
+
if (!this.config.agentsFile) {
|
|
591
|
+
return true;
|
|
592
|
+
}
|
|
593
|
+
const repoConfigOptions = this.getRepoConfigOptions();
|
|
594
|
+
const selectedValues = await multiSelectPrompt('Which instruction file would you like to add?', repoConfigOptions);
|
|
595
|
+
if (!selectedValues || selectedValues.length === 0) {
|
|
596
|
+
showInfo('No instruction file selected.');
|
|
597
|
+
return true;
|
|
598
|
+
}
|
|
599
|
+
const selectedOptions = repoConfigOptions.filter(option => selectedValues.includes(option.value));
|
|
600
|
+
console.log();
|
|
601
|
+
showInfo('Selected instruction file(s):');
|
|
602
|
+
selectedOptions.forEach(option => {
|
|
603
|
+
console.log(` ${chalk.green('•')} ${option.title}`);
|
|
604
|
+
});
|
|
605
|
+
console.log();
|
|
606
|
+
const isInstalled = this.isInstalledInProject();
|
|
607
|
+
let useSymlink = false;
|
|
608
|
+
if (isInstalled) {
|
|
609
|
+
useSymlink = await confirmPrompt('Use symlinks/aliases? (recommended for getting updates, but requires package to be and stay installed in the package and may not work with some systems or package managers)', true);
|
|
610
|
+
}
|
|
611
|
+
else {
|
|
612
|
+
showInfo(`Package not detected in project dependencies - copying files instead of symlinking. Consider installing ${this.config.name /* 'the MCP package' */} in your project for easier updates.`);
|
|
613
|
+
useSymlink = false;
|
|
614
|
+
}
|
|
615
|
+
console.log();
|
|
616
|
+
showInfo('Setting up AI agent instruction file(s)...');
|
|
617
|
+
let successCount = 0;
|
|
618
|
+
for (const option of selectedOptions) {
|
|
619
|
+
try {
|
|
620
|
+
await this.setupInstructionFile(option, useSymlink);
|
|
621
|
+
successCount++;
|
|
622
|
+
}
|
|
623
|
+
catch (error) {
|
|
624
|
+
showError(`Failed to setup ${option.title}: ${error}`);
|
|
625
|
+
}
|
|
626
|
+
}
|
|
627
|
+
console.log();
|
|
628
|
+
if (successCount === selectedOptions.length) {
|
|
629
|
+
showSuccess(`All ${successCount} instruction files set up successfully!`);
|
|
630
|
+
this.printAgentInstructions(selectedOptions);
|
|
631
|
+
return true;
|
|
632
|
+
}
|
|
633
|
+
else {
|
|
634
|
+
showWarning(`${successCount}/${selectedOptions.length} instruction files set up successfully.`);
|
|
635
|
+
return false;
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
async setupInstructionFile(option, useSymlink) {
|
|
639
|
+
const targetPath = path.join(this.repoRoot, option.defaultPath);
|
|
640
|
+
const targetDir = path.dirname(targetPath);
|
|
641
|
+
// Ensure target directory exists
|
|
642
|
+
await this.ensureDirectoryExists(targetDir);
|
|
643
|
+
// Check if file already exists
|
|
644
|
+
if (fs.existsSync(targetPath)) {
|
|
645
|
+
const overwrite = await confirmPrompt(`${option.defaultPath} already exists. Overwrite?`, false);
|
|
646
|
+
if (!overwrite) {
|
|
647
|
+
showInfo(`Skipping ${option.title} - file already exists.`);
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
}
|
|
651
|
+
const sourcePath = path.resolve(path.join(__dirname, '..', '..', 'AGENTS.md'));
|
|
652
|
+
if (!fs.existsSync(sourcePath)) {
|
|
653
|
+
throw new Error(`AGENTS.md file does not exist at ${sourcePath}`);
|
|
654
|
+
}
|
|
655
|
+
const content = await fs.promises.readFile(sourcePath, 'utf8');
|
|
656
|
+
switch (option.value) {
|
|
657
|
+
case 'isolated-instructions':
|
|
658
|
+
if (useSymlink) {
|
|
659
|
+
await this.createSymlink(sourcePath, targetPath);
|
|
660
|
+
}
|
|
661
|
+
else {
|
|
662
|
+
await fs.promises.writeFile(targetPath, content, 'utf8');
|
|
663
|
+
}
|
|
664
|
+
break;
|
|
665
|
+
case 'copilot-instructions': {
|
|
666
|
+
if (useSymlink) {
|
|
667
|
+
await this.createSymlink(sourcePath, targetPath);
|
|
668
|
+
}
|
|
669
|
+
else {
|
|
670
|
+
await fs.promises.writeFile(targetPath, content, 'utf8');
|
|
671
|
+
}
|
|
672
|
+
break;
|
|
673
|
+
}
|
|
674
|
+
case 'agents-md': {
|
|
675
|
+
if (useSymlink) {
|
|
676
|
+
await this.createSymlink(sourcePath, targetPath);
|
|
677
|
+
}
|
|
678
|
+
else {
|
|
679
|
+
await fs.promises.writeFile(targetPath, content, 'utf8');
|
|
680
|
+
}
|
|
681
|
+
break;
|
|
682
|
+
}
|
|
683
|
+
}
|
|
684
|
+
showSuccess(`${option.title} set up at ${option.defaultPath} ${useSymlink ? '(symlinked)' : '(copied)'}`);
|
|
685
|
+
}
|
|
686
|
+
async createSymlink(sourcePath, targetPath) {
|
|
687
|
+
// Remove existing file/symlink if it exists
|
|
688
|
+
if (fs.existsSync(targetPath)) {
|
|
689
|
+
await fs.promises.unlink(targetPath);
|
|
690
|
+
}
|
|
691
|
+
await fs.promises.symlink(sourcePath, targetPath);
|
|
692
|
+
}
|
|
693
|
+
printAgentInstructions(selectedOptions) {
|
|
694
|
+
console.log(chalk.bold.green('\nAI Agent Instruction Setup Complete\n'));
|
|
695
|
+
selectedOptions.forEach(option => {
|
|
696
|
+
switch (option.value) {
|
|
697
|
+
case 'isolated-instructions':
|
|
698
|
+
console.log(chalk.bold(`${this.config.designSystem /* 'Design System' */} Instructions:`));
|
|
699
|
+
console.log(` • File created at: ${option.defaultPath}`);
|
|
700
|
+
break;
|
|
701
|
+
case 'copilot-instructions':
|
|
702
|
+
console.log(chalk.bold('Copilot Instructions:'));
|
|
703
|
+
console.log(` • File created at: ${option.defaultPath}`);
|
|
704
|
+
break;
|
|
705
|
+
case 'agents-md':
|
|
706
|
+
console.log(chalk.bold('AGENTS.md:'));
|
|
707
|
+
console.log(` • File created at: ${option.defaultPath}`);
|
|
708
|
+
break;
|
|
709
|
+
}
|
|
710
|
+
});
|
|
711
|
+
console.log(chalk.bold('Recommendations:'));
|
|
712
|
+
console.log(chalk.gray('• Commit these instruction files to share consistent AI behavior with your team'));
|
|
713
|
+
console.log(chalk.gray('• Symlinked files will update automatically with package updates'));
|
|
714
|
+
console.log(chalk.gray("• Copied files give you full control but won't receive updates"));
|
|
715
|
+
console.log(chalk.gray('• .github/instructions/ approach allows multiple specialized instruction files'));
|
|
716
|
+
}
|
|
717
|
+
}
|
|
718
|
+
//# sourceMappingURL=setup.js.map
|