makecc 0.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/bin/cli.js +111 -0
- package/dist/assets/index-BcaHeFM8.css +1 -0
- package/dist/assets/index-CryYaA6W.js +57 -0
- package/dist/client/assets/index-BkAJX_uj.js +133 -0
- package/dist/client/assets/index-CRHGYTA2.css +1 -0
- package/dist/client/index.html +14 -0
- package/dist/client/vite.svg +1 -0
- package/dist/index.html +14 -0
- package/dist/server/index.js +207 -0
- package/dist/server/services/claudeService.js +153 -0
- package/dist/server/services/fileService.js +193 -0
- package/dist/server/services/skillExecutionService.js +622 -0
- package/dist/server/services/terminalService.js +216 -0
- package/dist/server/services/workflowAIService.js +191 -0
- package/dist/server/services/workflowExecutionService.js +342 -0
- package/dist/server/types.js +1 -0
- package/dist/vite.svg +1 -0
- package/package.json +84 -0
- package/server/index.ts +253 -0
- package/server/services/claudeService.ts +222 -0
- package/server/services/fileService.ts +277 -0
- package/server/services/skillExecutionService.ts +732 -0
- package/server/services/terminalService.ts +266 -0
- package/server/services/workflowAIService.ts +240 -0
- package/server/services/workflowExecutionService.ts +471 -0
- package/server/types.ts +110 -0
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import Anthropic from '@anthropic-ai/sdk';
|
|
2
|
+
import type {
|
|
3
|
+
ExecutionNode,
|
|
4
|
+
SubagentNodeData,
|
|
5
|
+
SkillNodeData,
|
|
6
|
+
InputNodeData,
|
|
7
|
+
OutputNodeData,
|
|
8
|
+
McpNodeData,
|
|
9
|
+
} from '../types';
|
|
10
|
+
|
|
11
|
+
type ModelId = 'claude-sonnet-4-20250514' | 'claude-opus-4-20250514' | 'claude-3-5-haiku-20241022';
|
|
12
|
+
|
|
13
|
+
export class ClaudeService {
|
|
14
|
+
private client: Anthropic;
|
|
15
|
+
private abortController: AbortController | null = null;
|
|
16
|
+
private executionResults: Map<string, string> = new Map();
|
|
17
|
+
|
|
18
|
+
constructor() {
|
|
19
|
+
this.client = new Anthropic();
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
private getModelId(model?: 'sonnet' | 'opus' | 'haiku'): ModelId {
|
|
23
|
+
switch (model) {
|
|
24
|
+
case 'opus':
|
|
25
|
+
return 'claude-opus-4-20250514';
|
|
26
|
+
case 'haiku':
|
|
27
|
+
return 'claude-3-5-haiku-20241022';
|
|
28
|
+
default:
|
|
29
|
+
return 'claude-sonnet-4-20250514';
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async executeNode(
|
|
34
|
+
node: ExecutionNode,
|
|
35
|
+
onProgress?: (progress: number) => void
|
|
36
|
+
): Promise<string> {
|
|
37
|
+
this.abortController = new AbortController();
|
|
38
|
+
|
|
39
|
+
switch (node.type) {
|
|
40
|
+
case 'input':
|
|
41
|
+
return this.executeInputNode(node.id, node.data as InputNodeData);
|
|
42
|
+
|
|
43
|
+
case 'subagent':
|
|
44
|
+
return this.executeSubagentNode(
|
|
45
|
+
node.id,
|
|
46
|
+
node.data as SubagentNodeData,
|
|
47
|
+
onProgress
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
case 'skill':
|
|
51
|
+
return this.executeSkillNode(node.id, node.data as SkillNodeData);
|
|
52
|
+
|
|
53
|
+
case 'mcp':
|
|
54
|
+
return this.executeMcpNode(node.id, node.data as McpNodeData);
|
|
55
|
+
|
|
56
|
+
case 'output':
|
|
57
|
+
return this.executeOutputNode(node.id, node.data as OutputNodeData);
|
|
58
|
+
|
|
59
|
+
default:
|
|
60
|
+
throw new Error(`Unknown node type: ${node.type}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
private async executeInputNode(
|
|
65
|
+
nodeId: string,
|
|
66
|
+
data: InputNodeData
|
|
67
|
+
): Promise<string> {
|
|
68
|
+
const result = data.value || '';
|
|
69
|
+
this.executionResults.set(nodeId, result);
|
|
70
|
+
return result;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
private async executeSubagentNode(
|
|
74
|
+
nodeId: string,
|
|
75
|
+
data: SubagentNodeData,
|
|
76
|
+
onProgress?: (progress: number) => void
|
|
77
|
+
): Promise<string> {
|
|
78
|
+
onProgress?.(10);
|
|
79
|
+
|
|
80
|
+
// Build context from used inputs
|
|
81
|
+
let context = '';
|
|
82
|
+
if (data.usedInputs && data.usedInputs.length > 0) {
|
|
83
|
+
const inputs = data.usedInputs
|
|
84
|
+
.map((inputId) => this.executionResults.get(inputId))
|
|
85
|
+
.filter(Boolean)
|
|
86
|
+
.join('\n\n');
|
|
87
|
+
context = `Context from previous steps:\n${inputs}\n\n`;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Build system prompt based on role
|
|
91
|
+
let systemPrompt = data.systemPrompt || this.getDefaultSystemPrompt(data.role);
|
|
92
|
+
|
|
93
|
+
// Add tool instructions if tools are specified
|
|
94
|
+
if (data.tools && data.tools.length > 0) {
|
|
95
|
+
systemPrompt += `\n\nYou have access to the following tools: ${data.tools.join(', ')}`;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
onProgress?.(30);
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const response = await this.client.messages.create({
|
|
102
|
+
model: this.getModelId(data.model),
|
|
103
|
+
max_tokens: 4096,
|
|
104
|
+
system: systemPrompt,
|
|
105
|
+
messages: [
|
|
106
|
+
{
|
|
107
|
+
role: 'user',
|
|
108
|
+
content: `${context}${data.description || 'Please complete the assigned task.'}`,
|
|
109
|
+
},
|
|
110
|
+
],
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
onProgress?.(80);
|
|
114
|
+
|
|
115
|
+
// Extract text from response
|
|
116
|
+
const result = response.content
|
|
117
|
+
.filter((block) => block.type === 'text')
|
|
118
|
+
.map((block) => (block as { type: 'text'; text: string }).text)
|
|
119
|
+
.join('\n');
|
|
120
|
+
|
|
121
|
+
this.executionResults.set(nodeId, result);
|
|
122
|
+
onProgress?.(100);
|
|
123
|
+
|
|
124
|
+
return result;
|
|
125
|
+
} catch (error) {
|
|
126
|
+
throw new Error(`Claude API error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private async executeSkillNode(
|
|
131
|
+
nodeId: string,
|
|
132
|
+
data: SkillNodeData
|
|
133
|
+
): Promise<string> {
|
|
134
|
+
// Build context from used inputs
|
|
135
|
+
let context = '';
|
|
136
|
+
if (data.usedInputs && data.usedInputs.length > 0) {
|
|
137
|
+
const inputs = data.usedInputs
|
|
138
|
+
.map((inputId) => this.executionResults.get(inputId))
|
|
139
|
+
.filter(Boolean)
|
|
140
|
+
.join('\n\n');
|
|
141
|
+
context = inputs;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// For now, return the skill configuration as result
|
|
145
|
+
// In a full implementation, this would execute the skill's SKILL.md
|
|
146
|
+
const result = data.mdContent
|
|
147
|
+
? `Skill executed with content:\n${data.mdContent}\n\nContext: ${context}`
|
|
148
|
+
: `Skill ${data.skillId || 'custom'} executed with context: ${context}`;
|
|
149
|
+
|
|
150
|
+
this.executionResults.set(nodeId, result);
|
|
151
|
+
return result;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
private async executeMcpNode(
|
|
155
|
+
nodeId: string,
|
|
156
|
+
data: McpNodeData
|
|
157
|
+
): Promise<string> {
|
|
158
|
+
// MCP server integration would go here
|
|
159
|
+
// For now, return a placeholder result
|
|
160
|
+
const result = `MCP Server ${data.serverName} (${data.serverType}) connection established`;
|
|
161
|
+
this.executionResults.set(nodeId, result);
|
|
162
|
+
return result;
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
private async executeOutputNode(
|
|
166
|
+
nodeId: string,
|
|
167
|
+
data: OutputNodeData
|
|
168
|
+
): Promise<string> {
|
|
169
|
+
// Collect all results from used inputs
|
|
170
|
+
let combinedResult = '';
|
|
171
|
+
if (data.usedInputs && data.usedInputs.length > 0) {
|
|
172
|
+
const inputs = data.usedInputs
|
|
173
|
+
.map((inputId) => this.executionResults.get(inputId))
|
|
174
|
+
.filter(Boolean);
|
|
175
|
+
combinedResult = inputs.join('\n\n---\n\n');
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// Format based on output type
|
|
179
|
+
let formattedResult: string;
|
|
180
|
+
switch (data.outputType) {
|
|
181
|
+
case 'markdown':
|
|
182
|
+
formattedResult = combinedResult;
|
|
183
|
+
break;
|
|
184
|
+
case 'document':
|
|
185
|
+
formattedResult = `# Document Output\n\n${combinedResult}`;
|
|
186
|
+
break;
|
|
187
|
+
default:
|
|
188
|
+
formattedResult = combinedResult;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
this.executionResults.set(nodeId, formattedResult);
|
|
192
|
+
return formattedResult;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
private getDefaultSystemPrompt(role: string): string {
|
|
196
|
+
const prompts: Record<string, string> = {
|
|
197
|
+
researcher:
|
|
198
|
+
'You are an expert researcher. Your task is to gather, analyze, and synthesize information on the given topic. Provide comprehensive, well-structured findings with citations where applicable.',
|
|
199
|
+
writer:
|
|
200
|
+
'You are a professional writer. Your task is to create clear, engaging, and well-structured content. Focus on readability and appropriate tone for the target audience.',
|
|
201
|
+
analyst:
|
|
202
|
+
'You are a data analyst. Your task is to analyze information, identify patterns, and provide insights. Present your findings in a clear, actionable format.',
|
|
203
|
+
coder:
|
|
204
|
+
'You are an expert software developer. Your task is to write clean, efficient, and well-documented code. Follow best practices and consider edge cases.',
|
|
205
|
+
custom:
|
|
206
|
+
'You are a helpful AI assistant. Complete the assigned task to the best of your ability.',
|
|
207
|
+
};
|
|
208
|
+
|
|
209
|
+
return prompts[role] || prompts.custom;
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
cancelExecution(): void {
|
|
213
|
+
if (this.abortController) {
|
|
214
|
+
this.abortController.abort();
|
|
215
|
+
this.abortController = null;
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
clearResults(): void {
|
|
220
|
+
this.executionResults.clear();
|
|
221
|
+
}
|
|
222
|
+
}
|
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import * as fs from 'fs/promises';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
|
|
5
|
+
// Type definitions for save operations
|
|
6
|
+
export interface McpServerConfig {
|
|
7
|
+
type: 'stdio' | 'sse' | 'http';
|
|
8
|
+
command?: string;
|
|
9
|
+
args?: string[];
|
|
10
|
+
url?: string;
|
|
11
|
+
env?: Record<string, string>;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface SkillConfig {
|
|
15
|
+
name: string;
|
|
16
|
+
path: string;
|
|
17
|
+
content: string;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export interface CommandConfig {
|
|
21
|
+
name: string;
|
|
22
|
+
path: string;
|
|
23
|
+
content: string;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface AgentConfig {
|
|
27
|
+
name: string;
|
|
28
|
+
path: string;
|
|
29
|
+
content: string;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface McpSettingsUpdate {
|
|
33
|
+
mcpServers: Record<string, McpServerConfig>;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export interface ClaudeConfigExport {
|
|
37
|
+
skills: SkillConfig[];
|
|
38
|
+
commands: CommandConfig[];
|
|
39
|
+
agents: AgentConfig[];
|
|
40
|
+
mcpSettings: McpSettingsUpdate | null;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export type SaveLocation = 'local' | 'global';
|
|
44
|
+
|
|
45
|
+
export interface SaveOptions {
|
|
46
|
+
location: SaveLocation;
|
|
47
|
+
includeSkills: boolean;
|
|
48
|
+
includeCommands: boolean;
|
|
49
|
+
includeAgents: boolean;
|
|
50
|
+
includeMcpSettings: boolean;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface SaveItemResult {
|
|
54
|
+
name: string;
|
|
55
|
+
path: string;
|
|
56
|
+
success: boolean;
|
|
57
|
+
error?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export interface SaveResult {
|
|
61
|
+
success: boolean;
|
|
62
|
+
skills: SaveItemResult[];
|
|
63
|
+
commands: SaveItemResult[];
|
|
64
|
+
agents: SaveItemResult[];
|
|
65
|
+
mcpSettings: { success: boolean; error?: string } | null;
|
|
66
|
+
errors: { type: string; name: string; error: string }[];
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export class FileService {
|
|
70
|
+
private projectRoot: string;
|
|
71
|
+
|
|
72
|
+
constructor(projectRoot?: string) {
|
|
73
|
+
this.projectRoot = projectRoot || process.cwd();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Resolves a path, expanding ~ to home directory
|
|
78
|
+
*/
|
|
79
|
+
private resolvePath(filePath: string): string {
|
|
80
|
+
if (filePath.startsWith('~')) {
|
|
81
|
+
return path.join(os.homedir(), filePath.slice(1));
|
|
82
|
+
}
|
|
83
|
+
if (path.isAbsolute(filePath)) {
|
|
84
|
+
return filePath;
|
|
85
|
+
}
|
|
86
|
+
return path.join(this.projectRoot, filePath);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Ensures a directory exists
|
|
91
|
+
*/
|
|
92
|
+
private async ensureDir(dirPath: string): Promise<void> {
|
|
93
|
+
const resolvedPath = this.resolvePath(dirPath);
|
|
94
|
+
await fs.mkdir(resolvedPath, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Writes content to a file, creating directories as needed
|
|
99
|
+
*/
|
|
100
|
+
private async writeFile(filePath: string, content: string): Promise<void> {
|
|
101
|
+
const resolvedPath = this.resolvePath(filePath);
|
|
102
|
+
const dirPath = path.dirname(resolvedPath);
|
|
103
|
+
await this.ensureDir(dirPath);
|
|
104
|
+
await fs.writeFile(resolvedPath, content, 'utf-8');
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Saves a skill configuration
|
|
109
|
+
*/
|
|
110
|
+
async saveSkill(config: SkillConfig): Promise<SaveItemResult> {
|
|
111
|
+
try {
|
|
112
|
+
await this.writeFile(config.path, config.content);
|
|
113
|
+
return { name: config.name, path: config.path, success: true };
|
|
114
|
+
} catch (error) {
|
|
115
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
116
|
+
return { name: config.name, path: config.path, success: false, error: errorMessage };
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Saves a command configuration
|
|
122
|
+
*/
|
|
123
|
+
async saveCommand(config: CommandConfig): Promise<SaveItemResult> {
|
|
124
|
+
try {
|
|
125
|
+
await this.writeFile(config.path, config.content);
|
|
126
|
+
return { name: config.name, path: config.path, success: true };
|
|
127
|
+
} catch (error) {
|
|
128
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
129
|
+
return { name: config.name, path: config.path, success: false, error: errorMessage };
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
/**
|
|
134
|
+
* Saves an agent configuration
|
|
135
|
+
*/
|
|
136
|
+
async saveAgent(config: AgentConfig): Promise<SaveItemResult> {
|
|
137
|
+
try {
|
|
138
|
+
await this.writeFile(config.path, config.content);
|
|
139
|
+
return { name: config.name, path: config.path, success: true };
|
|
140
|
+
} catch (error) {
|
|
141
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
142
|
+
return { name: config.name, path: config.path, success: false, error: errorMessage };
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Updates MCP settings in settings.local.json
|
|
148
|
+
*/
|
|
149
|
+
async updateMcpSettings(
|
|
150
|
+
settings: McpSettingsUpdate,
|
|
151
|
+
location: SaveLocation
|
|
152
|
+
): Promise<{ success: boolean; error?: string }> {
|
|
153
|
+
try {
|
|
154
|
+
const settingsPath = location === 'global'
|
|
155
|
+
? '~/.claude/settings.local.json'
|
|
156
|
+
: '.claude/settings.local.json';
|
|
157
|
+
|
|
158
|
+
const resolvedPath = this.resolvePath(settingsPath);
|
|
159
|
+
|
|
160
|
+
// Read existing settings if they exist
|
|
161
|
+
let existingSettings: Record<string, unknown> = {};
|
|
162
|
+
try {
|
|
163
|
+
const content = await fs.readFile(resolvedPath, 'utf-8');
|
|
164
|
+
existingSettings = JSON.parse(content);
|
|
165
|
+
} catch {
|
|
166
|
+
// File doesn't exist or is invalid, start fresh
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Merge MCP servers
|
|
170
|
+
const existingMcpServers = (existingSettings.mcpServers as Record<string, unknown>) || {};
|
|
171
|
+
const mergedSettings = {
|
|
172
|
+
...existingSettings,
|
|
173
|
+
mcpServers: {
|
|
174
|
+
...existingMcpServers,
|
|
175
|
+
...settings.mcpServers,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
// Write updated settings
|
|
180
|
+
const dirPath = path.dirname(resolvedPath);
|
|
181
|
+
await this.ensureDir(dirPath);
|
|
182
|
+
await fs.writeFile(resolvedPath, JSON.stringify(mergedSettings, null, 2), 'utf-8');
|
|
183
|
+
|
|
184
|
+
return { success: true };
|
|
185
|
+
} catch (error) {
|
|
186
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
187
|
+
return { success: false, error: errorMessage };
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Saves an entire workflow configuration
|
|
193
|
+
*/
|
|
194
|
+
async saveWorkflow(config: ClaudeConfigExport, options: SaveOptions): Promise<SaveResult> {
|
|
195
|
+
const result: SaveResult = {
|
|
196
|
+
success: true,
|
|
197
|
+
skills: [],
|
|
198
|
+
commands: [],
|
|
199
|
+
agents: [],
|
|
200
|
+
mcpSettings: null,
|
|
201
|
+
errors: [],
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
// Save skills
|
|
205
|
+
if (options.includeSkills) {
|
|
206
|
+
for (const skill of config.skills) {
|
|
207
|
+
const skillResult = await this.saveSkill(skill);
|
|
208
|
+
result.skills.push(skillResult);
|
|
209
|
+
if (!skillResult.success) {
|
|
210
|
+
result.success = false;
|
|
211
|
+
result.errors.push({
|
|
212
|
+
type: 'skill',
|
|
213
|
+
name: skill.name,
|
|
214
|
+
error: skillResult.error || 'Unknown error',
|
|
215
|
+
});
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
// Save commands
|
|
221
|
+
if (options.includeCommands) {
|
|
222
|
+
for (const command of config.commands) {
|
|
223
|
+
const commandResult = await this.saveCommand(command);
|
|
224
|
+
result.commands.push(commandResult);
|
|
225
|
+
if (!commandResult.success) {
|
|
226
|
+
result.success = false;
|
|
227
|
+
result.errors.push({
|
|
228
|
+
type: 'command',
|
|
229
|
+
name: command.name,
|
|
230
|
+
error: commandResult.error || 'Unknown error',
|
|
231
|
+
});
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Save agents
|
|
237
|
+
if (options.includeAgents) {
|
|
238
|
+
for (const agent of config.agents) {
|
|
239
|
+
const agentResult = await this.saveAgent(agent);
|
|
240
|
+
result.agents.push(agentResult);
|
|
241
|
+
if (!agentResult.success) {
|
|
242
|
+
result.success = false;
|
|
243
|
+
result.errors.push({
|
|
244
|
+
type: 'agent',
|
|
245
|
+
name: agent.name,
|
|
246
|
+
error: agentResult.error || 'Unknown error',
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Update MCP settings
|
|
253
|
+
if (options.includeMcpSettings && config.mcpSettings) {
|
|
254
|
+
const mcpResult = await this.updateMcpSettings(config.mcpSettings, options.location);
|
|
255
|
+
result.mcpSettings = mcpResult;
|
|
256
|
+
if (!mcpResult.success) {
|
|
257
|
+
result.success = false;
|
|
258
|
+
result.errors.push({
|
|
259
|
+
type: 'mcp',
|
|
260
|
+
name: 'settings.local.json',
|
|
261
|
+
error: mcpResult.error || 'Unknown error',
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
return result;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/**
|
|
270
|
+
* Gets the project root path
|
|
271
|
+
*/
|
|
272
|
+
getProjectPath(): string {
|
|
273
|
+
return this.projectRoot;
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
export const fileService = new FileService();
|