@defai.digital/cli 13.1.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 +214 -0
- package/dist/bin.d.ts +3 -0
- package/dist/bin.d.ts.map +1 -0
- package/dist/bin.js +11 -0
- package/dist/bin.js.map +1 -0
- package/dist/bootstrap.d.ts +144 -0
- package/dist/bootstrap.d.ts.map +1 -0
- package/dist/bootstrap.js +315 -0
- package/dist/bootstrap.js.map +1 -0
- package/dist/cli.d.ts +14 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +84 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/ability.d.ts +17 -0
- package/dist/commands/ability.d.ts.map +1 -0
- package/dist/commands/ability.js +286 -0
- package/dist/commands/ability.js.map +1 -0
- package/dist/commands/agent.d.ts +18 -0
- package/dist/commands/agent.d.ts.map +1 -0
- package/dist/commands/agent.js +361 -0
- package/dist/commands/agent.js.map +1 -0
- package/dist/commands/call.d.ts +15 -0
- package/dist/commands/call.d.ts.map +1 -0
- package/dist/commands/call.js +503 -0
- package/dist/commands/call.js.map +1 -0
- package/dist/commands/cleanup.d.ts +18 -0
- package/dist/commands/cleanup.d.ts.map +1 -0
- package/dist/commands/cleanup.js +300 -0
- package/dist/commands/cleanup.js.map +1 -0
- package/dist/commands/config.d.ts +16 -0
- package/dist/commands/config.d.ts.map +1 -0
- package/dist/commands/config.js +513 -0
- package/dist/commands/config.js.map +1 -0
- package/dist/commands/discuss.d.ts +16 -0
- package/dist/commands/discuss.d.ts.map +1 -0
- package/dist/commands/discuss.js +700 -0
- package/dist/commands/discuss.js.map +1 -0
- package/dist/commands/doctor.d.ts +48 -0
- package/dist/commands/doctor.d.ts.map +1 -0
- package/dist/commands/doctor.js +356 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/guard.d.ts +12 -0
- package/dist/commands/guard.d.ts.map +1 -0
- package/dist/commands/guard.js +225 -0
- package/dist/commands/guard.js.map +1 -0
- package/dist/commands/help.d.ts +11 -0
- package/dist/commands/help.d.ts.map +1 -0
- package/dist/commands/help.js +180 -0
- package/dist/commands/help.js.map +1 -0
- package/dist/commands/history.d.ts +19 -0
- package/dist/commands/history.d.ts.map +1 -0
- package/dist/commands/history.js +200 -0
- package/dist/commands/history.js.map +1 -0
- package/dist/commands/index.d.ts +23 -0
- package/dist/commands/index.d.ts.map +1 -0
- package/dist/commands/index.js +26 -0
- package/dist/commands/index.js.map +1 -0
- package/dist/commands/iterate.d.ts +16 -0
- package/dist/commands/iterate.d.ts.map +1 -0
- package/dist/commands/iterate.js +72 -0
- package/dist/commands/iterate.js.map +1 -0
- package/dist/commands/list.d.ts +6 -0
- package/dist/commands/list.d.ts.map +1 -0
- package/dist/commands/list.js +62 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/mcp.d.ts +16 -0
- package/dist/commands/mcp.d.ts.map +1 -0
- package/dist/commands/mcp.js +57 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/resume.d.ts +18 -0
- package/dist/commands/resume.d.ts.map +1 -0
- package/dist/commands/resume.js +208 -0
- package/dist/commands/resume.js.map +1 -0
- package/dist/commands/review.d.ts +13 -0
- package/dist/commands/review.d.ts.map +1 -0
- package/dist/commands/review.js +450 -0
- package/dist/commands/review.js.map +1 -0
- package/dist/commands/run.d.ts +6 -0
- package/dist/commands/run.d.ts.map +1 -0
- package/dist/commands/run.js +158 -0
- package/dist/commands/run.js.map +1 -0
- package/dist/commands/scaffold.d.ts +20 -0
- package/dist/commands/scaffold.d.ts.map +1 -0
- package/dist/commands/scaffold.js +924 -0
- package/dist/commands/scaffold.js.map +1 -0
- package/dist/commands/session.d.ts +20 -0
- package/dist/commands/session.d.ts.map +1 -0
- package/dist/commands/session.js +504 -0
- package/dist/commands/session.js.map +1 -0
- package/dist/commands/setup.d.ts +14 -0
- package/dist/commands/setup.d.ts.map +1 -0
- package/dist/commands/setup.js +762 -0
- package/dist/commands/setup.js.map +1 -0
- package/dist/commands/status.d.ts +17 -0
- package/dist/commands/status.d.ts.map +1 -0
- package/dist/commands/status.js +227 -0
- package/dist/commands/status.js.map +1 -0
- package/dist/commands/trace.d.ts +6 -0
- package/dist/commands/trace.d.ts.map +1 -0
- package/dist/commands/trace.js +204 -0
- package/dist/commands/trace.js.map +1 -0
- package/dist/commands/update.d.ts +24 -0
- package/dist/commands/update.d.ts.map +1 -0
- package/dist/commands/update.js +296 -0
- package/dist/commands/update.js.map +1 -0
- package/dist/index.d.ts +8 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +14 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +288 -0
- package/dist/parser.js.map +1 -0
- package/dist/types.d.ts +91 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +29 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/dangerous-op-guard.d.ts +33 -0
- package/dist/utils/dangerous-op-guard.d.ts.map +1 -0
- package/dist/utils/dangerous-op-guard.js +112 -0
- package/dist/utils/dangerous-op-guard.js.map +1 -0
- package/dist/utils/database.d.ts +85 -0
- package/dist/utils/database.d.ts.map +1 -0
- package/dist/utils/database.js +184 -0
- package/dist/utils/database.js.map +1 -0
- package/dist/utils/index.d.ts +7 -0
- package/dist/utils/index.d.ts.map +1 -0
- package/dist/utils/index.js +7 -0
- package/dist/utils/index.js.map +1 -0
- package/dist/utils/provider-factory.d.ts +31 -0
- package/dist/utils/provider-factory.d.ts.map +1 -0
- package/dist/utils/provider-factory.js +109 -0
- package/dist/utils/provider-factory.js.map +1 -0
- package/dist/utils/storage-instances.d.ts +19 -0
- package/dist/utils/storage-instances.d.ts.map +1 -0
- package/dist/utils/storage-instances.js +20 -0
- package/dist/utils/storage-instances.js.map +1 -0
- package/package.json +77 -0
|
@@ -0,0 +1,762 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Setup command - Interactive setup wizard for AutomatosX
|
|
3
|
+
*
|
|
4
|
+
* Features:
|
|
5
|
+
* - Provider detection
|
|
6
|
+
* - Configuration creation
|
|
7
|
+
* - Non-interactive mode for CI
|
|
8
|
+
*/
|
|
9
|
+
import { exec } from 'node:child_process';
|
|
10
|
+
import { promisify } from 'node:util';
|
|
11
|
+
import { mkdir, writeFile, readFile, access } from 'node:fs/promises';
|
|
12
|
+
import { join } from 'node:path';
|
|
13
|
+
import { createConfigStore, initConfigDirectory, } from '@defai.digital/config-domain';
|
|
14
|
+
import { DEFAULT_CONFIG, KNOWN_PROVIDERS, PROVIDER_DEFAULTS, DATA_DIR_NAME, CONFIG_FILENAME, TIMEOUT_SETUP_ADD, TIMEOUT_SETUP_REMOVE, TIMEOUT_HEALTH_CHECK, TIMEOUT_WORKFLOW_STEP, DEFAULT_SCHEMA_VERSION, ITERATE_MAX_DEFAULT, ITERATE_TIMEOUT_DEFAULT, } from '@defai.digital/contracts';
|
|
15
|
+
import { CONTEXT_DIRECTORY } from '@defai.digital/context-domain';
|
|
16
|
+
import { PROVIDER_CHECKS, checkProviderCLI, } from './doctor.js';
|
|
17
|
+
const execAsync = promisify(exec);
|
|
18
|
+
// ============================================================================
|
|
19
|
+
// Constants
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// ============================================================================
|
|
22
|
+
// MCP Constants
|
|
23
|
+
// ============================================================================
|
|
24
|
+
/** MCP server name registered with provider CLIs */
|
|
25
|
+
const MCP_SERVER_NAME = 'automatosx';
|
|
26
|
+
/** MCP subcommands */
|
|
27
|
+
const MCP_COMMANDS = {
|
|
28
|
+
add: 'mcp add',
|
|
29
|
+
remove: 'mcp remove',
|
|
30
|
+
serverArgs: 'mcp server',
|
|
31
|
+
};
|
|
32
|
+
/** MCP command flags for different formats */
|
|
33
|
+
const MCP_FLAGS = {
|
|
34
|
+
/** Claude uses -s local for user-scope config */
|
|
35
|
+
claudeScope: '-s local',
|
|
36
|
+
/** ax-wrapper uses -c for command */
|
|
37
|
+
command: '-c',
|
|
38
|
+
/** ax-wrapper uses -a for arguments */
|
|
39
|
+
args: '-a',
|
|
40
|
+
};
|
|
41
|
+
/** Pattern to detect successful MCP server addition in output */
|
|
42
|
+
const MCP_SUCCESS_PATTERN = /Added MCP server|server.*added|successfully added/i;
|
|
43
|
+
// ============================================================================
|
|
44
|
+
// CLI Constants
|
|
45
|
+
// ============================================================================
|
|
46
|
+
/** Fallback CLI command when binary path cannot be determined */
|
|
47
|
+
const CLI_FALLBACK_COMMAND = 'ax';
|
|
48
|
+
/** Node.js executable for running scripts */
|
|
49
|
+
const NODE_EXECUTABLE = 'node';
|
|
50
|
+
/** Conventions file name in context directory */
|
|
51
|
+
const CONVENTIONS_FILENAME = 'conventions.md';
|
|
52
|
+
/** Provider name display width for aligned output */
|
|
53
|
+
const PROVIDER_DISPLAY_WIDTH = 10;
|
|
54
|
+
/** Default error message when error type is unknown */
|
|
55
|
+
const FALLBACK_ERROR_MESSAGE = 'Unknown error';
|
|
56
|
+
/** Stderr redirect suffix for shell commands */
|
|
57
|
+
const STDERR_REDIRECT = '2>&1';
|
|
58
|
+
/** Regex pattern to extract semver version from CLI output */
|
|
59
|
+
const SEMVER_PATTERN = /(\d+\.\d+\.\d+)/;
|
|
60
|
+
/** Platform-specific command to find executables */
|
|
61
|
+
const WHICH_COMMAND = process.platform === 'win32' ? 'where' : 'which';
|
|
62
|
+
/** JSON formatting indentation */
|
|
63
|
+
const JSON_INDENT = 2;
|
|
64
|
+
/** Exit codes for CLI commands */
|
|
65
|
+
const EXIT_CODE = {
|
|
66
|
+
SUCCESS: 0,
|
|
67
|
+
FAILURE: 1,
|
|
68
|
+
};
|
|
69
|
+
/** Config scope values */
|
|
70
|
+
const CONFIG_SCOPE = {
|
|
71
|
+
GLOBAL: 'global',
|
|
72
|
+
LOCAL: 'local',
|
|
73
|
+
};
|
|
74
|
+
/** Health check status values */
|
|
75
|
+
const HEALTH_STATUS = {
|
|
76
|
+
PASS: 'pass',
|
|
77
|
+
FAIL: 'fail',
|
|
78
|
+
};
|
|
79
|
+
/** CLI argument flags */
|
|
80
|
+
const CLI_FLAGS = {
|
|
81
|
+
force: ['--force', '-f'],
|
|
82
|
+
nonInteractive: ['--non-interactive', '-y'],
|
|
83
|
+
local: ['--local', '-l'],
|
|
84
|
+
global: ['--global', '-g'],
|
|
85
|
+
skipProject: ['--skip-project', '--no-project'],
|
|
86
|
+
};
|
|
87
|
+
/** Terminal color codes */
|
|
88
|
+
const COLORS = {
|
|
89
|
+
reset: '\x1b[0m',
|
|
90
|
+
green: '\x1b[32m',
|
|
91
|
+
red: '\x1b[31m',
|
|
92
|
+
yellow: '\x1b[33m',
|
|
93
|
+
cyan: '\x1b[36m',
|
|
94
|
+
bold: '\x1b[1m',
|
|
95
|
+
dim: '\x1b[2m',
|
|
96
|
+
};
|
|
97
|
+
/** Terminal output icons */
|
|
98
|
+
const ICONS = {
|
|
99
|
+
check: `${COLORS.green}\u2713${COLORS.reset}`,
|
|
100
|
+
cross: `${COLORS.red}\u2717${COLORS.reset}`,
|
|
101
|
+
warn: `${COLORS.yellow}\u26A0${COLORS.reset}`,
|
|
102
|
+
arrow: `${COLORS.cyan}\u2192${COLORS.reset}`,
|
|
103
|
+
};
|
|
104
|
+
/** MCP configuration for each provider (null = provider doesn't support MCP) */
|
|
105
|
+
const PROVIDER_MCP_CONFIGS = {
|
|
106
|
+
claude: { cliName: 'claude', format: 'claude' },
|
|
107
|
+
gemini: { cliName: 'gemini', format: 'standard' },
|
|
108
|
+
codex: { cliName: 'codex', format: 'standard' },
|
|
109
|
+
qwen: { cliName: 'qwen', format: 'standard' },
|
|
110
|
+
glm: { cliName: 'ax-glm', format: 'ax-wrapper' },
|
|
111
|
+
grok: { cliName: 'ax-grok', format: 'ax-wrapper' },
|
|
112
|
+
'ax-cli': null, // ax-cli doesn't support MCP yet
|
|
113
|
+
};
|
|
114
|
+
/**
|
|
115
|
+
* Get the absolute path to the CLI binary.
|
|
116
|
+
* Uses process.argv[1] which works for both global and local installations.
|
|
117
|
+
*/
|
|
118
|
+
function getCLIBinaryPath() {
|
|
119
|
+
const binaryPath = process.argv[1];
|
|
120
|
+
return binaryPath || CLI_FALLBACK_COMMAND;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Check if a path is absolute (Unix or Windows style)
|
|
124
|
+
*/
|
|
125
|
+
function isAbsolutePath(filePath) {
|
|
126
|
+
return filePath.startsWith('/') || filePath.includes('\\');
|
|
127
|
+
}
|
|
128
|
+
/**
|
|
129
|
+
* Build the MCP server command parts based on binary path.
|
|
130
|
+
* Returns { executable, arguments } for use in MCP add commands.
|
|
131
|
+
*/
|
|
132
|
+
function buildMCPServerCommand(binaryPath) {
|
|
133
|
+
if (isAbsolutePath(binaryPath)) {
|
|
134
|
+
return { executable: NODE_EXECUTABLE, arguments: `"${binaryPath}" ${MCP_COMMANDS.serverArgs}` };
|
|
135
|
+
}
|
|
136
|
+
return { executable: binaryPath, arguments: MCP_COMMANDS.serverArgs };
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* Get the CLI command to add AutomatosX MCP server for a provider.
|
|
140
|
+
* Uses provider's native CLI to ensure proper formatting and validation.
|
|
141
|
+
*/
|
|
142
|
+
function buildMCPAddCommand(providerId) {
|
|
143
|
+
const mcpConfig = PROVIDER_MCP_CONFIGS[providerId];
|
|
144
|
+
if (!mcpConfig)
|
|
145
|
+
return null;
|
|
146
|
+
const binaryPath = getCLIBinaryPath();
|
|
147
|
+
const { executable, arguments: execArgs } = buildMCPServerCommand(binaryPath);
|
|
148
|
+
const { cliName, format } = mcpConfig;
|
|
149
|
+
switch (format) {
|
|
150
|
+
case 'standard':
|
|
151
|
+
return `${cliName} ${MCP_COMMANDS.add} ${MCP_SERVER_NAME} ${executable} ${execArgs}`;
|
|
152
|
+
case 'claude':
|
|
153
|
+
return `${cliName} ${MCP_COMMANDS.add} ${MCP_SERVER_NAME} ${MCP_FLAGS.claudeScope} ${executable} ${execArgs}`;
|
|
154
|
+
case 'ax-wrapper': {
|
|
155
|
+
const command = isAbsolutePath(binaryPath) ? NODE_EXECUTABLE : CLI_FALLBACK_COMMAND;
|
|
156
|
+
const commandArgs = isAbsolutePath(binaryPath) ? `${binaryPath} ${MCP_COMMANDS.serverArgs}` : MCP_COMMANDS.serverArgs;
|
|
157
|
+
return `${cliName} ${MCP_COMMANDS.add} ${MCP_SERVER_NAME} ${MCP_FLAGS.command} ${command} ${MCP_FLAGS.args} ${commandArgs}`;
|
|
158
|
+
}
|
|
159
|
+
default:
|
|
160
|
+
return null;
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* Get the CLI command to remove MCP server for a provider.
|
|
165
|
+
*/
|
|
166
|
+
function buildMCPRemoveCommand(providerId) {
|
|
167
|
+
const mcpConfig = PROVIDER_MCP_CONFIGS[providerId];
|
|
168
|
+
if (!mcpConfig)
|
|
169
|
+
return null;
|
|
170
|
+
const scopeFlag = mcpConfig.format === 'claude' ? ` ${MCP_FLAGS.claudeScope}` : '';
|
|
171
|
+
return `${mcpConfig.cliName} ${MCP_COMMANDS.remove} ${MCP_SERVER_NAME}${scopeFlag}`;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Get the settings file path for ax-wrapper providers.
|
|
175
|
+
* ax-glm and ax-grok store configs in .ax-glm/ and .ax-grok/ directories.
|
|
176
|
+
*/
|
|
177
|
+
function getAxWrapperSettingsPath(cliName) {
|
|
178
|
+
// For project-local config, use current working directory
|
|
179
|
+
return join(process.cwd(), `.${cliName}`, 'settings.json');
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Configure MCP for ax-wrapper providers by writing config file directly.
|
|
183
|
+
* This is needed because ax-wrapper CLIs default to 'content-length' framing,
|
|
184
|
+
* but automatosx MCP server uses 'ndjson' framing (MCP SDK default).
|
|
185
|
+
*/
|
|
186
|
+
async function configureAxWrapperMCP(cliName) {
|
|
187
|
+
const binaryPath = getCLIBinaryPath();
|
|
188
|
+
const settingsPath = getAxWrapperSettingsPath(cliName);
|
|
189
|
+
const settingsDir = join(process.cwd(), `.${cliName}`);
|
|
190
|
+
try {
|
|
191
|
+
// Ensure settings directory exists
|
|
192
|
+
await mkdir(settingsDir, { recursive: true });
|
|
193
|
+
// Read existing config or create new one
|
|
194
|
+
let existingConfig = { mcpServers: {} };
|
|
195
|
+
try {
|
|
196
|
+
const content = await readFile(settingsPath, 'utf-8');
|
|
197
|
+
existingConfig = JSON.parse(content);
|
|
198
|
+
if (!existingConfig.mcpServers) {
|
|
199
|
+
existingConfig.mcpServers = {};
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
// File doesn't exist or is invalid - use empty config
|
|
204
|
+
}
|
|
205
|
+
// Build args based on binary path
|
|
206
|
+
const args = isAbsolutePath(binaryPath)
|
|
207
|
+
? [binaryPath, 'mcp', 'server']
|
|
208
|
+
: ['mcp', 'server'];
|
|
209
|
+
const command = isAbsolutePath(binaryPath) ? NODE_EXECUTABLE : binaryPath;
|
|
210
|
+
// Add automatosx MCP server config with ndjson framing
|
|
211
|
+
existingConfig.mcpServers[MCP_SERVER_NAME] = {
|
|
212
|
+
name: MCP_SERVER_NAME,
|
|
213
|
+
transport: {
|
|
214
|
+
type: 'stdio',
|
|
215
|
+
command,
|
|
216
|
+
args,
|
|
217
|
+
env: {},
|
|
218
|
+
framing: 'ndjson',
|
|
219
|
+
},
|
|
220
|
+
};
|
|
221
|
+
// Write updated config
|
|
222
|
+
await writeFile(settingsPath, JSON.stringify(existingConfig, null, JSON_INDENT) + '\n');
|
|
223
|
+
return { success: true, skipped: false };
|
|
224
|
+
}
|
|
225
|
+
catch (err) {
|
|
226
|
+
return {
|
|
227
|
+
success: false,
|
|
228
|
+
skipped: false,
|
|
229
|
+
error: err instanceof Error ? err.message : FALLBACK_ERROR_MESSAGE,
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
/**
|
|
234
|
+
* Check if command output indicates successful MCP server addition.
|
|
235
|
+
* Some providers output success message even when validation times out.
|
|
236
|
+
*/
|
|
237
|
+
function isMCPAdditionSuccessful(commandOutput) {
|
|
238
|
+
return MCP_SUCCESS_PATTERN.test(commandOutput);
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Extract clean error message from exec error.
|
|
242
|
+
*/
|
|
243
|
+
function extractErrorMessage(rawError) {
|
|
244
|
+
if (rawError.includes('Command failed')) {
|
|
245
|
+
return rawError.split('\n').pop() || rawError;
|
|
246
|
+
}
|
|
247
|
+
return rawError;
|
|
248
|
+
}
|
|
249
|
+
/**
|
|
250
|
+
* Configure MCP for a detected provider.
|
|
251
|
+
*
|
|
252
|
+
* For ax-wrapper providers (ax-glm, ax-grok):
|
|
253
|
+
* - Write config file directly with ndjson framing (required for MCP SDK compatibility)
|
|
254
|
+
*
|
|
255
|
+
* For other providers:
|
|
256
|
+
* - Use native CLI commands (remove-then-add for clean state)
|
|
257
|
+
*/
|
|
258
|
+
async function configureMCPForProvider(providerId) {
|
|
259
|
+
const mcpConfig = PROVIDER_MCP_CONFIGS[providerId];
|
|
260
|
+
if (!mcpConfig) {
|
|
261
|
+
return { success: true, skipped: true };
|
|
262
|
+
}
|
|
263
|
+
// For ax-wrapper providers, write config file directly with ndjson framing
|
|
264
|
+
// This is needed because ax-wrapper CLIs default to 'content-length' framing,
|
|
265
|
+
// but automatosx MCP server uses 'ndjson' framing (MCP SDK default)
|
|
266
|
+
if (mcpConfig.format === 'ax-wrapper') {
|
|
267
|
+
return configureAxWrapperMCP(mcpConfig.cliName);
|
|
268
|
+
}
|
|
269
|
+
const addCommand = buildMCPAddCommand(providerId);
|
|
270
|
+
const removeCommand = buildMCPRemoveCommand(providerId);
|
|
271
|
+
if (!addCommand) {
|
|
272
|
+
return { success: true, skipped: true };
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
// Step 1: Remove existing config for clean state (ignore if not exists)
|
|
276
|
+
if (removeCommand) {
|
|
277
|
+
try {
|
|
278
|
+
await execAsync(`${removeCommand} ${STDERR_REDIRECT}`, { timeout: TIMEOUT_SETUP_REMOVE });
|
|
279
|
+
}
|
|
280
|
+
catch {
|
|
281
|
+
// Server might not exist - that's expected
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
// Step 2: Add MCP server using provider's native CLI
|
|
285
|
+
const { stdout, stderr } = await execAsync(`${addCommand} ${STDERR_REDIRECT}`, { timeout: TIMEOUT_SETUP_ADD });
|
|
286
|
+
const commandOutput = `${stdout}${stderr}`;
|
|
287
|
+
if (isMCPAdditionSuccessful(commandOutput)) {
|
|
288
|
+
return { success: true, skipped: false };
|
|
289
|
+
}
|
|
290
|
+
return { success: true, skipped: false };
|
|
291
|
+
}
|
|
292
|
+
catch (err) {
|
|
293
|
+
// Node's exec error includes stdout/stderr as properties
|
|
294
|
+
const execResult = err;
|
|
295
|
+
const errorMsg = execResult.message || FALLBACK_ERROR_MESSAGE;
|
|
296
|
+
const fullOutput = `${execResult.stdout || ''}${execResult.stderr || ''}${errorMsg}`;
|
|
297
|
+
// Check if server was added despite command failure (validation timeout)
|
|
298
|
+
if (isMCPAdditionSuccessful(fullOutput)) {
|
|
299
|
+
return { success: true, skipped: false };
|
|
300
|
+
}
|
|
301
|
+
return {
|
|
302
|
+
success: false,
|
|
303
|
+
skipped: false,
|
|
304
|
+
error: extractErrorMessage(errorMsg),
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
/**
|
|
309
|
+
* Use ax doctor logic to check which provider CLIs are installed
|
|
310
|
+
* This ensures consistent detection between 'ax doctor' and 'ax setup'
|
|
311
|
+
*/
|
|
312
|
+
async function getInstalledProviderCLIs() {
|
|
313
|
+
const results = new Map();
|
|
314
|
+
// Run doctor-style checks for all providers
|
|
315
|
+
for (const provider of PROVIDER_CHECKS) {
|
|
316
|
+
const checkResult = await checkProviderCLI(provider);
|
|
317
|
+
results.set(provider.id, checkResult);
|
|
318
|
+
}
|
|
319
|
+
return results;
|
|
320
|
+
}
|
|
321
|
+
/**
|
|
322
|
+
* Configure MCP for all detected providers using their native CLI commands.
|
|
323
|
+
*
|
|
324
|
+
* Uses 'ax doctor' check logic to determine which CLIs are installed.
|
|
325
|
+
* Only configures MCP for providers that pass the doctor check.
|
|
326
|
+
*/
|
|
327
|
+
async function configureMCPForAllProviders() {
|
|
328
|
+
const result = {
|
|
329
|
+
configured: [],
|
|
330
|
+
skipped: [],
|
|
331
|
+
notInstalled: [],
|
|
332
|
+
failed: [],
|
|
333
|
+
};
|
|
334
|
+
const installedProviders = await getInstalledProviderCLIs();
|
|
335
|
+
for (const [providerId, healthCheck] of installedProviders) {
|
|
336
|
+
if (healthCheck.status === HEALTH_STATUS.FAIL) {
|
|
337
|
+
result.notInstalled.push(providerId);
|
|
338
|
+
continue;
|
|
339
|
+
}
|
|
340
|
+
const configResult = await configureMCPForProvider(providerId);
|
|
341
|
+
if (configResult.skipped) {
|
|
342
|
+
result.skipped.push(providerId);
|
|
343
|
+
}
|
|
344
|
+
else if (configResult.success) {
|
|
345
|
+
result.configured.push(providerId);
|
|
346
|
+
}
|
|
347
|
+
else {
|
|
348
|
+
result.failed.push({
|
|
349
|
+
providerId,
|
|
350
|
+
error: configResult.error || FALLBACK_ERROR_MESSAGE,
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
return result;
|
|
355
|
+
}
|
|
356
|
+
// ============================================================================
|
|
357
|
+
// Provider Detection
|
|
358
|
+
// ============================================================================
|
|
359
|
+
/**
|
|
360
|
+
* Checks if a command is available on PATH
|
|
361
|
+
*/
|
|
362
|
+
async function isCommandAvailable(command) {
|
|
363
|
+
try {
|
|
364
|
+
await execAsync(`${WHICH_COMMAND} ${command}`);
|
|
365
|
+
return true;
|
|
366
|
+
}
|
|
367
|
+
catch {
|
|
368
|
+
return false;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
/**
|
|
372
|
+
* Gets the version of a CLI command
|
|
373
|
+
*/
|
|
374
|
+
async function getCommandVersion(command) {
|
|
375
|
+
try {
|
|
376
|
+
const { stdout } = await execAsync(`${command} --version ${STDERR_REDIRECT}`, {
|
|
377
|
+
timeout: TIMEOUT_HEALTH_CHECK,
|
|
378
|
+
});
|
|
379
|
+
const versionMatch = SEMVER_PATTERN.exec(stdout);
|
|
380
|
+
return versionMatch?.[1];
|
|
381
|
+
}
|
|
382
|
+
catch {
|
|
383
|
+
return undefined;
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
/**
|
|
387
|
+
* Detects a single provider
|
|
388
|
+
*
|
|
389
|
+
* Note: AutomatosX does NOT check for authentication.
|
|
390
|
+
* All CLIs handle their own authentication internally.
|
|
391
|
+
* If a CLI is detected, it's considered ready to use.
|
|
392
|
+
*/
|
|
393
|
+
async function detectProvider(providerId) {
|
|
394
|
+
const providerDefaults = PROVIDER_DEFAULTS[providerId];
|
|
395
|
+
const isDetected = await isCommandAvailable(providerDefaults.command);
|
|
396
|
+
if (!isDetected) {
|
|
397
|
+
return {
|
|
398
|
+
providerId,
|
|
399
|
+
detected: false,
|
|
400
|
+
command: providerDefaults.command,
|
|
401
|
+
};
|
|
402
|
+
}
|
|
403
|
+
const version = await getCommandVersion(providerDefaults.command);
|
|
404
|
+
return {
|
|
405
|
+
providerId,
|
|
406
|
+
detected: true,
|
|
407
|
+
command: providerDefaults.command,
|
|
408
|
+
version,
|
|
409
|
+
};
|
|
410
|
+
}
|
|
411
|
+
/**
|
|
412
|
+
* Detects all providers in parallel
|
|
413
|
+
*/
|
|
414
|
+
async function detectAllProviders() {
|
|
415
|
+
const results = await Promise.all(KNOWN_PROVIDERS.map((id) => detectProvider(id)));
|
|
416
|
+
return results;
|
|
417
|
+
}
|
|
418
|
+
// ============================================================================
|
|
419
|
+
// Project Structure Creation
|
|
420
|
+
// ============================================================================
|
|
421
|
+
/**
|
|
422
|
+
* Template for project conventions file
|
|
423
|
+
*/
|
|
424
|
+
const CONVENTIONS_TEMPLATE = `# Project Conventions
|
|
425
|
+
|
|
426
|
+
## Code Style
|
|
427
|
+
<!-- Describe your coding standards -->
|
|
428
|
+
- Example: Use TypeScript strict mode
|
|
429
|
+
- Example: Prefer functional components over class components
|
|
430
|
+
- Example: Use named exports over default exports
|
|
431
|
+
|
|
432
|
+
## Architecture
|
|
433
|
+
<!-- Describe your project structure -->
|
|
434
|
+
- Example: Domain-driven design with packages/core/*/
|
|
435
|
+
- Example: Contract-first: all types in packages/contracts/
|
|
436
|
+
- Example: No circular dependencies between packages
|
|
437
|
+
|
|
438
|
+
## Testing
|
|
439
|
+
<!-- Describe testing practices -->
|
|
440
|
+
- Example: Use vitest for unit tests
|
|
441
|
+
- Example: Co-locate tests with source: *.test.ts
|
|
442
|
+
- Example: Mock external dependencies, not internal modules
|
|
443
|
+
|
|
444
|
+
## Naming Conventions
|
|
445
|
+
<!-- Describe naming conventions -->
|
|
446
|
+
- Example: Use camelCase for variables and functions
|
|
447
|
+
- Example: Use PascalCase for types and classes
|
|
448
|
+
- Example: Prefix interfaces with I (e.g., IUserService)
|
|
449
|
+
`;
|
|
450
|
+
/**
|
|
451
|
+
* Template for project config.json
|
|
452
|
+
*/
|
|
453
|
+
const PROJECT_CONFIG_TEMPLATE = {
|
|
454
|
+
version: DEFAULT_SCHEMA_VERSION,
|
|
455
|
+
iterate: {
|
|
456
|
+
maxIterations: ITERATE_MAX_DEFAULT,
|
|
457
|
+
maxTimeMs: ITERATE_TIMEOUT_DEFAULT,
|
|
458
|
+
autoConfirm: false,
|
|
459
|
+
},
|
|
460
|
+
};
|
|
461
|
+
/**
|
|
462
|
+
* Creates the .automatosx/ project structure
|
|
463
|
+
*/
|
|
464
|
+
async function createProjectStructure(projectDir, force) {
|
|
465
|
+
const created = [];
|
|
466
|
+
const skipped = [];
|
|
467
|
+
const automatosxDir = join(projectDir, DATA_DIR_NAME);
|
|
468
|
+
const contextDir = join(automatosxDir, CONTEXT_DIRECTORY);
|
|
469
|
+
const configPath = join(automatosxDir, CONFIG_FILENAME);
|
|
470
|
+
const conventionsPath = join(contextDir, CONVENTIONS_FILENAME);
|
|
471
|
+
// Create data directory
|
|
472
|
+
try {
|
|
473
|
+
await mkdir(automatosxDir, { recursive: true });
|
|
474
|
+
}
|
|
475
|
+
catch {
|
|
476
|
+
// Directory may already exist
|
|
477
|
+
}
|
|
478
|
+
// Create context directory
|
|
479
|
+
try {
|
|
480
|
+
await mkdir(contextDir, { recursive: true });
|
|
481
|
+
}
|
|
482
|
+
catch {
|
|
483
|
+
// Directory may already exist
|
|
484
|
+
}
|
|
485
|
+
// Create project config.json
|
|
486
|
+
const configExists = await fileExists(configPath);
|
|
487
|
+
if (!configExists || force) {
|
|
488
|
+
await writeFile(configPath, JSON.stringify(PROJECT_CONFIG_TEMPLATE, null, JSON_INDENT) + '\n');
|
|
489
|
+
created.push(`${DATA_DIR_NAME}/${CONFIG_FILENAME}`);
|
|
490
|
+
}
|
|
491
|
+
else {
|
|
492
|
+
skipped.push(`${DATA_DIR_NAME}/${CONFIG_FILENAME} (already exists)`);
|
|
493
|
+
}
|
|
494
|
+
// Create conventions template
|
|
495
|
+
const conventionsExists = await fileExists(conventionsPath);
|
|
496
|
+
if (!conventionsExists || force) {
|
|
497
|
+
await writeFile(conventionsPath, CONVENTIONS_TEMPLATE);
|
|
498
|
+
created.push(`${DATA_DIR_NAME}/${CONTEXT_DIRECTORY}/${CONVENTIONS_FILENAME}`);
|
|
499
|
+
}
|
|
500
|
+
else {
|
|
501
|
+
skipped.push(`${DATA_DIR_NAME}/${CONTEXT_DIRECTORY}/${CONVENTIONS_FILENAME} (already exists)`);
|
|
502
|
+
}
|
|
503
|
+
return { created, skipped };
|
|
504
|
+
}
|
|
505
|
+
/**
|
|
506
|
+
* Check if a file exists
|
|
507
|
+
*/
|
|
508
|
+
async function fileExists(path) {
|
|
509
|
+
try {
|
|
510
|
+
await access(path);
|
|
511
|
+
return true;
|
|
512
|
+
}
|
|
513
|
+
catch {
|
|
514
|
+
return false;
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
/**
|
|
518
|
+
* Runs the setup process
|
|
519
|
+
*/
|
|
520
|
+
async function runSetup(options) {
|
|
521
|
+
const configStore = createConfigStore();
|
|
522
|
+
const configFilePath = configStore.getPath(options.scope);
|
|
523
|
+
// Check if config already exists
|
|
524
|
+
const configExists = await configStore.exists(options.scope);
|
|
525
|
+
if (configExists && !options.force) {
|
|
526
|
+
throw new Error(`Configuration already exists at ${configFilePath}. Use --force to overwrite.`);
|
|
527
|
+
}
|
|
528
|
+
// Initialize directory
|
|
529
|
+
await initConfigDirectory(options.scope);
|
|
530
|
+
// Detect providers
|
|
531
|
+
const allProviders = await detectAllProviders();
|
|
532
|
+
const availableProviders = allProviders.filter((provider) => provider.detected);
|
|
533
|
+
// Build providers config record
|
|
534
|
+
const providersConfig = {};
|
|
535
|
+
for (const provider of availableProviders) {
|
|
536
|
+
const providerDefaults = PROVIDER_DEFAULTS[provider.providerId];
|
|
537
|
+
providersConfig[provider.providerId] = {
|
|
538
|
+
enabled: true,
|
|
539
|
+
priority: providerDefaults.priority,
|
|
540
|
+
timeout: TIMEOUT_WORKFLOW_STEP,
|
|
541
|
+
command: providerDefaults.command,
|
|
542
|
+
};
|
|
543
|
+
}
|
|
544
|
+
// Sort provider IDs by priority to find default (highest first)
|
|
545
|
+
const providersByPriority = Object.keys(providersConfig).sort((a, b) => (providersConfig[b]?.priority ?? 0) - (providersConfig[a]?.priority ?? 0));
|
|
546
|
+
const config = {
|
|
547
|
+
...DEFAULT_CONFIG,
|
|
548
|
+
providers: providersConfig,
|
|
549
|
+
defaultProvider: providersByPriority[0],
|
|
550
|
+
createdAt: new Date().toISOString(),
|
|
551
|
+
updatedAt: new Date().toISOString(),
|
|
552
|
+
};
|
|
553
|
+
// Save config
|
|
554
|
+
await configStore.write(config, options.scope);
|
|
555
|
+
return {
|
|
556
|
+
success: true,
|
|
557
|
+
config,
|
|
558
|
+
providers: allProviders,
|
|
559
|
+
configPath: configFilePath,
|
|
560
|
+
};
|
|
561
|
+
}
|
|
562
|
+
// ============================================================================
|
|
563
|
+
// Command Handler
|
|
564
|
+
// ============================================================================
|
|
565
|
+
/**
|
|
566
|
+
* Check if an argument matches any of the given flags
|
|
567
|
+
*/
|
|
568
|
+
function matchesFlag(arg, flags) {
|
|
569
|
+
return flags.includes(arg);
|
|
570
|
+
}
|
|
571
|
+
/**
|
|
572
|
+
* Parses setup-specific arguments
|
|
573
|
+
*/
|
|
574
|
+
function parseSetupArgs(args) {
|
|
575
|
+
let force = false;
|
|
576
|
+
let nonInteractive = false;
|
|
577
|
+
let scope = CONFIG_SCOPE.GLOBAL;
|
|
578
|
+
let skipProjectStructure = false;
|
|
579
|
+
for (const arg of args) {
|
|
580
|
+
if (matchesFlag(arg, CLI_FLAGS.force)) {
|
|
581
|
+
force = true;
|
|
582
|
+
}
|
|
583
|
+
else if (matchesFlag(arg, CLI_FLAGS.nonInteractive)) {
|
|
584
|
+
nonInteractive = true;
|
|
585
|
+
}
|
|
586
|
+
else if (matchesFlag(arg, CLI_FLAGS.local)) {
|
|
587
|
+
scope = CONFIG_SCOPE.LOCAL;
|
|
588
|
+
}
|
|
589
|
+
else if (matchesFlag(arg, CLI_FLAGS.global)) {
|
|
590
|
+
scope = CONFIG_SCOPE.GLOBAL;
|
|
591
|
+
}
|
|
592
|
+
else if (matchesFlag(arg, CLI_FLAGS.skipProject)) {
|
|
593
|
+
skipProjectStructure = true;
|
|
594
|
+
}
|
|
595
|
+
}
|
|
596
|
+
return { force, nonInteractive, scope, skipProjectStructure };
|
|
597
|
+
}
|
|
598
|
+
/**
|
|
599
|
+
* Formats provider detection result for display.
|
|
600
|
+
*/
|
|
601
|
+
function formatProviderResult(detection) {
|
|
602
|
+
const statusText = detection.detected
|
|
603
|
+
? `${ICONS.check} Detected${detection.version ? ` (v${detection.version})` : ''}`
|
|
604
|
+
: `${ICONS.cross} Not detected`;
|
|
605
|
+
return ` ${detection.providerId.padEnd(PROVIDER_DISPLAY_WIDTH)} ${statusText}`;
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Setup command handler
|
|
609
|
+
*/
|
|
610
|
+
export async function setupCommand(args, options) {
|
|
611
|
+
const setupOptions = parseSetupArgs(args);
|
|
612
|
+
const isJsonFormat = options.format === 'json';
|
|
613
|
+
const outputLines = [];
|
|
614
|
+
try {
|
|
615
|
+
// Header
|
|
616
|
+
if (!isJsonFormat) {
|
|
617
|
+
outputLines.push('');
|
|
618
|
+
outputLines.push(`${COLORS.bold}AutomatosX Setup${COLORS.reset}`);
|
|
619
|
+
outputLines.push('');
|
|
620
|
+
}
|
|
621
|
+
// Step 1: Check prerequisites
|
|
622
|
+
if (!isJsonFormat) {
|
|
623
|
+
outputLines.push(`${COLORS.bold}Step 1: System Check${COLORS.reset}`);
|
|
624
|
+
outputLines.push(` ${ICONS.check} Node.js: ${process.version}`);
|
|
625
|
+
outputLines.push('');
|
|
626
|
+
}
|
|
627
|
+
// Step 2: Detect providers
|
|
628
|
+
if (!isJsonFormat) {
|
|
629
|
+
outputLines.push(`${COLORS.bold}Step 2: Provider Detection${COLORS.reset}`);
|
|
630
|
+
}
|
|
631
|
+
const detectedProviders = await detectAllProviders();
|
|
632
|
+
if (!isJsonFormat) {
|
|
633
|
+
for (const provider of detectedProviders) {
|
|
634
|
+
outputLines.push(formatProviderResult(provider));
|
|
635
|
+
}
|
|
636
|
+
outputLines.push('');
|
|
637
|
+
}
|
|
638
|
+
// Step 3: Create configuration
|
|
639
|
+
if (!isJsonFormat) {
|
|
640
|
+
outputLines.push(`${COLORS.bold}Step 3: Creating Configuration${COLORS.reset}`);
|
|
641
|
+
}
|
|
642
|
+
const setupResult = await runSetup(setupOptions);
|
|
643
|
+
if (!isJsonFormat) {
|
|
644
|
+
outputLines.push(` ${ICONS.check} Configuration saved to: ${setupResult.configPath}`);
|
|
645
|
+
outputLines.push('');
|
|
646
|
+
}
|
|
647
|
+
// Step 4: Create project structure (unless skipped)
|
|
648
|
+
let projectStructure;
|
|
649
|
+
if (!setupOptions.skipProjectStructure) {
|
|
650
|
+
if (!isJsonFormat) {
|
|
651
|
+
outputLines.push(`${COLORS.bold}Step 4: Project Structure${COLORS.reset}`);
|
|
652
|
+
}
|
|
653
|
+
projectStructure = await createProjectStructure(process.cwd(), setupOptions.force);
|
|
654
|
+
if (!isJsonFormat) {
|
|
655
|
+
for (const filePath of projectStructure.created) {
|
|
656
|
+
outputLines.push(` ${ICONS.check} Created ${filePath}`);
|
|
657
|
+
}
|
|
658
|
+
for (const filePath of projectStructure.skipped) {
|
|
659
|
+
outputLines.push(` ${ICONS.warn} Skipped ${filePath}`);
|
|
660
|
+
}
|
|
661
|
+
outputLines.push('');
|
|
662
|
+
}
|
|
663
|
+
}
|
|
664
|
+
// Step 5: Configure MCP for all detected providers
|
|
665
|
+
if (!isJsonFormat) {
|
|
666
|
+
outputLines.push(`${COLORS.bold}Step 5: MCP Configuration${COLORS.reset}`);
|
|
667
|
+
outputLines.push(` ${COLORS.dim}Using 'ax doctor' check to verify installed CLIs...${COLORS.reset}`);
|
|
668
|
+
}
|
|
669
|
+
const mcpResult = await configureMCPForAllProviders();
|
|
670
|
+
if (!isJsonFormat) {
|
|
671
|
+
for (const providerId of mcpResult.configured) {
|
|
672
|
+
outputLines.push(` ${ICONS.check} ${providerId}: AutomatosX MCP configured`);
|
|
673
|
+
}
|
|
674
|
+
for (const providerId of mcpResult.notInstalled) {
|
|
675
|
+
outputLines.push(` ${COLORS.dim} - ${providerId}: CLI not installed, skipped${COLORS.reset}`);
|
|
676
|
+
}
|
|
677
|
+
for (const { providerId, error } of mcpResult.failed) {
|
|
678
|
+
outputLines.push(` ${ICONS.warn} ${providerId}: ${error}`);
|
|
679
|
+
}
|
|
680
|
+
if (mcpResult.configured.length === 0 && mcpResult.failed.length === 0 && mcpResult.notInstalled.length === 0) {
|
|
681
|
+
outputLines.push(` ${ICONS.warn} No providers detected for MCP configuration`);
|
|
682
|
+
}
|
|
683
|
+
outputLines.push('');
|
|
684
|
+
}
|
|
685
|
+
if (!isJsonFormat) {
|
|
686
|
+
// Summary
|
|
687
|
+
const detectedCount = detectedProviders.filter((provider) => provider.detected).length;
|
|
688
|
+
const enabledCount = Object.keys(setupResult.config.providers).length;
|
|
689
|
+
outputLines.push(`${COLORS.bold}Summary${COLORS.reset}`);
|
|
690
|
+
outputLines.push(` Providers detected: ${detectedCount}/${KNOWN_PROVIDERS.length}`);
|
|
691
|
+
outputLines.push(` Providers enabled: ${enabledCount}`);
|
|
692
|
+
if (setupResult.config.defaultProvider !== undefined) {
|
|
693
|
+
outputLines.push(` Default provider: ${setupResult.config.defaultProvider}`);
|
|
694
|
+
}
|
|
695
|
+
if (projectStructure !== undefined) {
|
|
696
|
+
outputLines.push(` Project files created: ${projectStructure.created.length}`);
|
|
697
|
+
}
|
|
698
|
+
outputLines.push(` MCP configured: ${mcpResult.configured.length} provider(s)`);
|
|
699
|
+
outputLines.push('');
|
|
700
|
+
outputLines.push(`${COLORS.bold}Next Steps${COLORS.reset}`);
|
|
701
|
+
outputLines.push(` 1. Run ${COLORS.cyan}ax doctor${COLORS.reset} to verify installation`);
|
|
702
|
+
outputLines.push(` 2. Edit ${COLORS.cyan}${DATA_DIR_NAME}/${CONTEXT_DIRECTORY}/${CONVENTIONS_FILENAME}${COLORS.reset} to add your project conventions`);
|
|
703
|
+
outputLines.push(` 3. Run ${COLORS.cyan}ax call --iterate <provider> "task"${COLORS.reset} to use iterate mode`);
|
|
704
|
+
outputLines.push('');
|
|
705
|
+
}
|
|
706
|
+
if (isJsonFormat) {
|
|
707
|
+
return {
|
|
708
|
+
success: true,
|
|
709
|
+
message: undefined,
|
|
710
|
+
data: {
|
|
711
|
+
success: true,
|
|
712
|
+
configPath: setupResult.configPath,
|
|
713
|
+
providers: {
|
|
714
|
+
detected: detectedProviders.filter((provider) => provider.detected).map((provider) => provider.providerId),
|
|
715
|
+
enabled: Object.keys(setupResult.config.providers).filter(id => setupResult.config.providers[id]?.enabled),
|
|
716
|
+
},
|
|
717
|
+
defaultProvider: setupResult.config.defaultProvider,
|
|
718
|
+
version: setupResult.config.version,
|
|
719
|
+
projectStructure: projectStructure !== undefined
|
|
720
|
+
? {
|
|
721
|
+
created: projectStructure.created,
|
|
722
|
+
skipped: projectStructure.skipped,
|
|
723
|
+
}
|
|
724
|
+
: undefined,
|
|
725
|
+
mcpConfiguration: {
|
|
726
|
+
configured: mcpResult.configured,
|
|
727
|
+
skipped: mcpResult.skipped,
|
|
728
|
+
notInstalled: mcpResult.notInstalled,
|
|
729
|
+
failed: mcpResult.failed,
|
|
730
|
+
},
|
|
731
|
+
},
|
|
732
|
+
exitCode: EXIT_CODE.SUCCESS,
|
|
733
|
+
};
|
|
734
|
+
}
|
|
735
|
+
return {
|
|
736
|
+
success: true,
|
|
737
|
+
message: outputLines.join('\n'),
|
|
738
|
+
data: undefined,
|
|
739
|
+
exitCode: EXIT_CODE.SUCCESS,
|
|
740
|
+
};
|
|
741
|
+
}
|
|
742
|
+
catch (err) {
|
|
743
|
+
const errorMsg = err instanceof Error ? err.message : FALLBACK_ERROR_MESSAGE;
|
|
744
|
+
if (isJsonFormat) {
|
|
745
|
+
return {
|
|
746
|
+
success: false,
|
|
747
|
+
message: undefined,
|
|
748
|
+
data: { error: errorMsg },
|
|
749
|
+
exitCode: EXIT_CODE.FAILURE,
|
|
750
|
+
};
|
|
751
|
+
}
|
|
752
|
+
outputLines.push(`${ICONS.cross} Setup failed: ${errorMsg}`);
|
|
753
|
+
outputLines.push('');
|
|
754
|
+
return {
|
|
755
|
+
success: false,
|
|
756
|
+
message: outputLines.join('\n'),
|
|
757
|
+
data: undefined,
|
|
758
|
+
exitCode: EXIT_CODE.FAILURE,
|
|
759
|
+
};
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
//# sourceMappingURL=setup.js.map
|