@azerate/claudette-mcp 1.0.0 → 1.2.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/dist/__tests__/checkpoints.test.d.ts +1 -0
- package/dist/__tests__/checkpoints.test.js +59 -0
- package/dist/__tests__/utils.test.d.ts +1 -0
- package/dist/__tests__/utils.test.js +28 -0
- package/dist/__tests__/workspace.test.d.ts +1 -0
- package/dist/__tests__/workspace.test.js +30 -0
- package/dist/actions.d.ts +6 -0
- package/dist/actions.js +30 -0
- package/dist/checkpoints.d.ts +21 -0
- package/dist/checkpoints.js +247 -0
- package/dist/errors.d.ts +9 -0
- package/dist/errors.js +37 -0
- package/dist/git.d.ts +6 -0
- package/dist/git.js +34 -0
- package/dist/index.js +557 -431
- package/dist/scripts.d.ts +29 -0
- package/dist/scripts.js +61 -0
- package/dist/utils.d.ts +1 -0
- package/dist/utils.js +12 -0
- package/dist/workspace.d.ts +12 -0
- package/dist/workspace.js +74 -0
- package/package.json +1 -1
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
import { generateCheckpointName } from '../checkpoints.js';
|
|
2
|
+
describe('checkpoints', () => {
|
|
3
|
+
describe('generateCheckpointName', () => {
|
|
4
|
+
it('should generate name for single modified file', () => {
|
|
5
|
+
// Git status format: XY PATH (2 char status + space + path)
|
|
6
|
+
const status = ' M src/file.ts';
|
|
7
|
+
const result = generateCheckpointName(status);
|
|
8
|
+
expect(result).toContain('Edit');
|
|
9
|
+
expect(result).toContain('file.ts'); // Gets filename from path
|
|
10
|
+
});
|
|
11
|
+
it('should generate name for multiple modified files', () => {
|
|
12
|
+
const status = ' M file1.ts\n M file2.ts';
|
|
13
|
+
const result = generateCheckpointName(status);
|
|
14
|
+
expect(result).toContain('Edit');
|
|
15
|
+
});
|
|
16
|
+
it('should summarize when many files modified', () => {
|
|
17
|
+
const status = ' M a.ts\n M b.ts\n M c.ts\n M d.ts';
|
|
18
|
+
const result = generateCheckpointName(status);
|
|
19
|
+
expect(result).toContain('Edit');
|
|
20
|
+
expect(result).toContain('files');
|
|
21
|
+
});
|
|
22
|
+
it('should generate name for added files', () => {
|
|
23
|
+
const status = 'A newfile.ts';
|
|
24
|
+
const result = generateCheckpointName(status);
|
|
25
|
+
expect(result).toContain('Add');
|
|
26
|
+
expect(result).toContain('newfile.ts');
|
|
27
|
+
});
|
|
28
|
+
it('should generate name for deleted files', () => {
|
|
29
|
+
const status = 'D oldfile.ts';
|
|
30
|
+
const result = generateCheckpointName(status);
|
|
31
|
+
expect(result).toContain('Delete');
|
|
32
|
+
expect(result).toContain('oldfile.ts');
|
|
33
|
+
});
|
|
34
|
+
it('should handle untracked files as added', () => {
|
|
35
|
+
const status = '?? untracked.ts';
|
|
36
|
+
const result = generateCheckpointName(status);
|
|
37
|
+
expect(result).toContain('Add');
|
|
38
|
+
});
|
|
39
|
+
it('should truncate long names to 60 chars max', () => {
|
|
40
|
+
const status = ' M verylongfilename1.ts\n M verylongfilename2.ts\n M verylongfilename3.ts';
|
|
41
|
+
const result = generateCheckpointName(status);
|
|
42
|
+
expect(result.length).toBeLessThanOrEqual(63); // 60 + "..."
|
|
43
|
+
});
|
|
44
|
+
it('should combine different change types', () => {
|
|
45
|
+
const status = ' M modified.ts\nA added.ts\nD deleted.ts';
|
|
46
|
+
const result = generateCheckpointName(status);
|
|
47
|
+
// Should contain at least one action
|
|
48
|
+
expect(result.length).toBeGreaterThan(0);
|
|
49
|
+
});
|
|
50
|
+
it('should return timestamp-based name for empty status', () => {
|
|
51
|
+
const result = generateCheckpointName('');
|
|
52
|
+
expect(result).toContain('Checkpoint');
|
|
53
|
+
});
|
|
54
|
+
it('should return timestamp-based name for whitespace-only status', () => {
|
|
55
|
+
const result = generateCheckpointName(' \n \n ');
|
|
56
|
+
expect(result).toContain('Checkpoint');
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { stripAnsi } from '../utils.js';
|
|
2
|
+
describe('stripAnsi', () => {
|
|
3
|
+
it('should remove ANSI color codes', () => {
|
|
4
|
+
const input = '\x1b[31mRed text\x1b[0m';
|
|
5
|
+
const result = stripAnsi(input);
|
|
6
|
+
expect(result).toBe('Red text');
|
|
7
|
+
});
|
|
8
|
+
it('should remove cursor control sequences', () => {
|
|
9
|
+
const input = '\x1b[?25hVisible\x1b[?25l';
|
|
10
|
+
const result = stripAnsi(input);
|
|
11
|
+
expect(result).toBe('Visible');
|
|
12
|
+
});
|
|
13
|
+
it('should remove bell characters', () => {
|
|
14
|
+
const input = 'Hello\x07World';
|
|
15
|
+
const result = stripAnsi(input);
|
|
16
|
+
expect(result).toBe('HelloWorld');
|
|
17
|
+
});
|
|
18
|
+
it('should remove carriage returns', () => {
|
|
19
|
+
const input = 'Line1\rLine2';
|
|
20
|
+
const result = stripAnsi(input);
|
|
21
|
+
expect(result).toBe('Line1Line2');
|
|
22
|
+
});
|
|
23
|
+
it('should handle clean strings', () => {
|
|
24
|
+
const input = 'Clean string';
|
|
25
|
+
const result = stripAnsi(input);
|
|
26
|
+
expect(result).toBe('Clean string');
|
|
27
|
+
});
|
|
28
|
+
});
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { getWorkspaceConfigPath } from '../workspace.js';
|
|
2
|
+
describe('workspace', () => {
|
|
3
|
+
describe('getWorkspaceConfigPath', () => {
|
|
4
|
+
it('should convert workspace path to safe filename', () => {
|
|
5
|
+
const result = getWorkspaceConfigPath('C:\\repo\\MyProject');
|
|
6
|
+
expect(result).toContain('C-repo-MyProject.json');
|
|
7
|
+
});
|
|
8
|
+
it('should handle forward slashes', () => {
|
|
9
|
+
const result = getWorkspaceConfigPath('/home/user/project');
|
|
10
|
+
expect(result).toContain('-home-user-project.json');
|
|
11
|
+
});
|
|
12
|
+
it('should remove invalid filename characters from workspace path', () => {
|
|
13
|
+
const result = getWorkspaceConfigPath('C:\\repo\\test:file*name');
|
|
14
|
+
// Extract just the filename (after last separator)
|
|
15
|
+
const filename = result.split(/[/\\]/).pop() || '';
|
|
16
|
+
// The filename should not contain invalid chars (except drive letter colons are in the full path)
|
|
17
|
+
expect(filename).not.toMatch(/[*?"<>|]/);
|
|
18
|
+
});
|
|
19
|
+
it('should collapse multiple dashes', () => {
|
|
20
|
+
const result = getWorkspaceConfigPath('C:\\\\repo\\\\test');
|
|
21
|
+
expect(result).not.toContain('--');
|
|
22
|
+
});
|
|
23
|
+
it('should produce consistent results', () => {
|
|
24
|
+
const path = '/some/workspace/path';
|
|
25
|
+
const result1 = getWorkspaceConfigPath(path);
|
|
26
|
+
const result2 = getWorkspaceConfigPath(path);
|
|
27
|
+
expect(result1).toBe(result2);
|
|
28
|
+
});
|
|
29
|
+
});
|
|
30
|
+
});
|
package/dist/actions.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
// Quick actions handling for Claudette MCP server
|
|
2
|
+
import { existsSync, readFileSync, unlinkSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
// Read pending action from file (file-based approach for reliability)
|
|
5
|
+
export function getPendingAction(workspacePath) {
|
|
6
|
+
const filePath = join(workspacePath, '.claudette', 'pending-action.json');
|
|
7
|
+
if (!existsSync(filePath)) {
|
|
8
|
+
return null;
|
|
9
|
+
}
|
|
10
|
+
try {
|
|
11
|
+
const content = readFileSync(filePath, 'utf-8');
|
|
12
|
+
const action = JSON.parse(content);
|
|
13
|
+
// Check if action is too old (5 minutes)
|
|
14
|
+
if (Date.now() - action.timestamp > 5 * 60 * 1000) {
|
|
15
|
+
unlinkSync(filePath);
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
// Delete the file after reading (consume it)
|
|
19
|
+
unlinkSync(filePath);
|
|
20
|
+
return action;
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
// Try to clean up corrupted file
|
|
24
|
+
try {
|
|
25
|
+
unlinkSync(filePath);
|
|
26
|
+
}
|
|
27
|
+
catch { }
|
|
28
|
+
return null;
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export interface Checkpoint {
|
|
2
|
+
id: string;
|
|
3
|
+
message: string;
|
|
4
|
+
timestamp: string;
|
|
5
|
+
filesChanged: number;
|
|
6
|
+
}
|
|
7
|
+
export declare function getCheckpoints(workspacePath: string): Checkpoint[];
|
|
8
|
+
export declare function generateCheckpointName(status: string): string;
|
|
9
|
+
export declare function createCheckpoint(workspacePath: string, message?: string): {
|
|
10
|
+
success: boolean;
|
|
11
|
+
error?: string;
|
|
12
|
+
};
|
|
13
|
+
export declare function restoreCheckpoint(workspacePath: string, id: string): {
|
|
14
|
+
success: boolean;
|
|
15
|
+
error?: string;
|
|
16
|
+
autoSaveId?: string;
|
|
17
|
+
};
|
|
18
|
+
export declare function deleteCheckpoint(workspacePath: string, id: string): {
|
|
19
|
+
success: boolean;
|
|
20
|
+
error?: string;
|
|
21
|
+
};
|
|
@@ -0,0 +1,247 @@
|
|
|
1
|
+
// Checkpoint management functions for Claudette MCP server
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
// Get checkpoints (git stashes)
|
|
4
|
+
export function getCheckpoints(workspacePath) {
|
|
5
|
+
const checkpoints = [];
|
|
6
|
+
try {
|
|
7
|
+
const output = execSync('git stash list', {
|
|
8
|
+
cwd: workspacePath,
|
|
9
|
+
encoding: 'utf-8',
|
|
10
|
+
});
|
|
11
|
+
if (!output.trim())
|
|
12
|
+
return [];
|
|
13
|
+
const lines = output.trim().split('\n');
|
|
14
|
+
for (const line of lines) {
|
|
15
|
+
const match = line.match(/^(stash@\{(\d+)\}):\s*(?:On\s+\w+:\s*)?(?:WIP on \w+:\s*\w+\s*)?(.+)$/);
|
|
16
|
+
if (match) {
|
|
17
|
+
const id = match[1];
|
|
18
|
+
let message = match[3].trim().replace(/^[a-f0-9]+\s+/, '');
|
|
19
|
+
try {
|
|
20
|
+
const dateOutput = execSync(`git log -1 --format="%ci" ${id}`, {
|
|
21
|
+
cwd: workspacePath,
|
|
22
|
+
encoding: 'utf-8',
|
|
23
|
+
}).trim();
|
|
24
|
+
let filesChanged = 0;
|
|
25
|
+
try {
|
|
26
|
+
const diffOutput = execSync(`git stash show ${id} --stat`, {
|
|
27
|
+
cwd: workspacePath,
|
|
28
|
+
encoding: 'utf-8',
|
|
29
|
+
});
|
|
30
|
+
const filesMatch = diffOutput.match(/(\d+)\s+files?\s+changed/);
|
|
31
|
+
if (filesMatch)
|
|
32
|
+
filesChanged = parseInt(filesMatch[1]);
|
|
33
|
+
}
|
|
34
|
+
catch { }
|
|
35
|
+
checkpoints.push({ id, message, timestamp: dateOutput, filesChanged });
|
|
36
|
+
}
|
|
37
|
+
catch { }
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
catch { }
|
|
42
|
+
return checkpoints;
|
|
43
|
+
}
|
|
44
|
+
// Generate checkpoint name from git status
|
|
45
|
+
export function generateCheckpointName(status) {
|
|
46
|
+
const lines = status.trim().split('\n').filter(Boolean);
|
|
47
|
+
const changes = {
|
|
48
|
+
modified: [], added: [], deleted: [],
|
|
49
|
+
};
|
|
50
|
+
for (const line of lines) {
|
|
51
|
+
const code = line.substring(0, 2);
|
|
52
|
+
const file = line.substring(3).split('/').pop() || line.substring(3);
|
|
53
|
+
if (code.includes('M'))
|
|
54
|
+
changes.modified.push(file);
|
|
55
|
+
else if (code.includes('A') || code.includes('?'))
|
|
56
|
+
changes.added.push(file);
|
|
57
|
+
else if (code.includes('D'))
|
|
58
|
+
changes.deleted.push(file);
|
|
59
|
+
}
|
|
60
|
+
const parts = [];
|
|
61
|
+
if (changes.modified.length > 0) {
|
|
62
|
+
parts.push(changes.modified.length <= 2 ? `Edit ${changes.modified.join(', ')}` : `Edit ${changes.modified.length} files`);
|
|
63
|
+
}
|
|
64
|
+
if (changes.added.length > 0) {
|
|
65
|
+
parts.push(changes.added.length <= 2 ? `Add ${changes.added.join(', ')}` : `Add ${changes.added.length} files`);
|
|
66
|
+
}
|
|
67
|
+
if (changes.deleted.length > 0) {
|
|
68
|
+
parts.push(changes.deleted.length <= 2 ? `Delete ${changes.deleted.join(', ')}` : `Delete ${changes.deleted.length} files`);
|
|
69
|
+
}
|
|
70
|
+
if (parts.length === 0)
|
|
71
|
+
return `Checkpoint ${new Date().toLocaleTimeString()}`;
|
|
72
|
+
let result = parts.join(', ');
|
|
73
|
+
return result.length > 60 ? result.substring(0, 57) + '...' : result;
|
|
74
|
+
}
|
|
75
|
+
// Create a checkpoint
|
|
76
|
+
export function createCheckpoint(workspacePath, message) {
|
|
77
|
+
try {
|
|
78
|
+
const status = execSync('git status --porcelain', {
|
|
79
|
+
cwd: workspacePath,
|
|
80
|
+
encoding: 'utf-8',
|
|
81
|
+
}).trim();
|
|
82
|
+
if (!status) {
|
|
83
|
+
return { success: false, error: 'No changes to checkpoint' };
|
|
84
|
+
}
|
|
85
|
+
// Stage tracked files
|
|
86
|
+
try {
|
|
87
|
+
execSync('git add -u', { cwd: workspacePath, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
88
|
+
}
|
|
89
|
+
catch { }
|
|
90
|
+
// Create stash without removing changes
|
|
91
|
+
const stashHash = execSync('git stash create', {
|
|
92
|
+
cwd: workspacePath,
|
|
93
|
+
encoding: 'utf-8',
|
|
94
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
95
|
+
}).trim();
|
|
96
|
+
if (!stashHash) {
|
|
97
|
+
return { success: false, error: 'Failed to create checkpoint' };
|
|
98
|
+
}
|
|
99
|
+
// Generate name from changes if not provided
|
|
100
|
+
const stashMessage = message || generateCheckpointName(status);
|
|
101
|
+
execSync(`git stash store -m "${stashMessage.replace(/"/g, '\\"')}" ${stashHash}`, {
|
|
102
|
+
cwd: workspacePath,
|
|
103
|
+
encoding: 'utf-8',
|
|
104
|
+
});
|
|
105
|
+
return { success: true };
|
|
106
|
+
}
|
|
107
|
+
catch (err) {
|
|
108
|
+
return { success: false, error: err.message };
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
// Restore a checkpoint
|
|
112
|
+
export function restoreCheckpoint(workspacePath, id) {
|
|
113
|
+
try {
|
|
114
|
+
// Parse original stash index to calculate new index after auto-save
|
|
115
|
+
const stashMatch = id.match(/stash@\{(\d+)\}/);
|
|
116
|
+
if (!stashMatch) {
|
|
117
|
+
return { success: false, error: 'Invalid checkpoint ID' };
|
|
118
|
+
}
|
|
119
|
+
let stashIndex = parseInt(stashMatch[1], 10);
|
|
120
|
+
let autoSaveCreated = false;
|
|
121
|
+
// Auto-save current changes first
|
|
122
|
+
const status = execSync('git status --porcelain', {
|
|
123
|
+
cwd: workspacePath,
|
|
124
|
+
encoding: 'utf-8',
|
|
125
|
+
}).trim();
|
|
126
|
+
if (status) {
|
|
127
|
+
try {
|
|
128
|
+
execSync('git add -u', { cwd: workspacePath, encoding: 'utf-8', stdio: ['pipe', 'pipe', 'pipe'] });
|
|
129
|
+
}
|
|
130
|
+
catch { }
|
|
131
|
+
const stashHash = execSync('git stash create', {
|
|
132
|
+
cwd: workspacePath,
|
|
133
|
+
encoding: 'utf-8',
|
|
134
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
135
|
+
}).trim();
|
|
136
|
+
if (stashHash) {
|
|
137
|
+
execSync(`git stash store -m "Auto-save before restore" ${stashHash}`, {
|
|
138
|
+
cwd: workspacePath,
|
|
139
|
+
encoding: 'utf-8',
|
|
140
|
+
});
|
|
141
|
+
// Stash indices shifted up by 1
|
|
142
|
+
stashIndex += 1;
|
|
143
|
+
autoSaveCreated = true;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// Reset working directory completely
|
|
147
|
+
execSync('git reset --hard HEAD', {
|
|
148
|
+
cwd: workspacePath,
|
|
149
|
+
encoding: 'utf-8',
|
|
150
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
151
|
+
});
|
|
152
|
+
// Clean untracked files to prevent conflicts
|
|
153
|
+
execSync('git clean -fd', {
|
|
154
|
+
cwd: workspacePath,
|
|
155
|
+
encoding: 'utf-8',
|
|
156
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
157
|
+
});
|
|
158
|
+
// Apply the checkpoint with adjusted index
|
|
159
|
+
try {
|
|
160
|
+
execSync(`git stash apply stash@{${stashIndex}}`, {
|
|
161
|
+
cwd: workspacePath,
|
|
162
|
+
encoding: 'utf-8',
|
|
163
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
164
|
+
});
|
|
165
|
+
}
|
|
166
|
+
catch (applyErr) {
|
|
167
|
+
// Check if there are conflict markers in the working directory
|
|
168
|
+
try {
|
|
169
|
+
const conflictCheck = execSync('git diff --check', {
|
|
170
|
+
cwd: workspacePath,
|
|
171
|
+
encoding: 'utf-8',
|
|
172
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
catch (diffErr) {
|
|
176
|
+
const diffOutput = diffErr.stdout || diffErr.message || '';
|
|
177
|
+
if (diffOutput.includes('conflict') || diffOutput.includes('leftover')) {
|
|
178
|
+
// Conflicts detected - abort and reset to clean state
|
|
179
|
+
execSync('git reset --hard HEAD', {
|
|
180
|
+
cwd: workspacePath,
|
|
181
|
+
encoding: 'utf-8',
|
|
182
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
183
|
+
});
|
|
184
|
+
execSync('git clean -fd', {
|
|
185
|
+
cwd: workspacePath,
|
|
186
|
+
encoding: 'utf-8',
|
|
187
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
188
|
+
});
|
|
189
|
+
return {
|
|
190
|
+
success: false,
|
|
191
|
+
error: 'Checkpoint restore failed due to conflicts. Working directory reset to clean state.' +
|
|
192
|
+
(autoSaveCreated ? ' Your changes were saved as stash@{0}.' : '')
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
// Also check for conflict markers in files directly
|
|
197
|
+
try {
|
|
198
|
+
const grepConflict = execSync('git grep -l "^<<<<<<<" || true', {
|
|
199
|
+
cwd: workspacePath,
|
|
200
|
+
encoding: 'utf-8',
|
|
201
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
202
|
+
}).trim();
|
|
203
|
+
if (grepConflict) {
|
|
204
|
+
// Conflict markers found - abort
|
|
205
|
+
execSync('git reset --hard HEAD', {
|
|
206
|
+
cwd: workspacePath,
|
|
207
|
+
encoding: 'utf-8',
|
|
208
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
209
|
+
});
|
|
210
|
+
execSync('git clean -fd', {
|
|
211
|
+
cwd: workspacePath,
|
|
212
|
+
encoding: 'utf-8',
|
|
213
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
214
|
+
});
|
|
215
|
+
return {
|
|
216
|
+
success: false,
|
|
217
|
+
error: 'Checkpoint restore created merge conflicts. Aborted and reset to clean state.' +
|
|
218
|
+
(autoSaveCreated ? ' Your changes were saved as stash@{0}.' : '')
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
catch { }
|
|
223
|
+
// If we got here, the apply error wasn't about conflicts - re-throw
|
|
224
|
+
throw applyErr;
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
success: true,
|
|
228
|
+
autoSaveId: autoSaveCreated ? 'stash@{0}' : undefined
|
|
229
|
+
};
|
|
230
|
+
}
|
|
231
|
+
catch (err) {
|
|
232
|
+
return { success: false, error: err.message };
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
// Delete a checkpoint
|
|
236
|
+
export function deleteCheckpoint(workspacePath, id) {
|
|
237
|
+
try {
|
|
238
|
+
execSync(`git stash drop ${id}`, {
|
|
239
|
+
cwd: workspacePath,
|
|
240
|
+
encoding: 'utf-8',
|
|
241
|
+
});
|
|
242
|
+
return { success: true };
|
|
243
|
+
}
|
|
244
|
+
catch (err) {
|
|
245
|
+
return { success: false, error: err.message };
|
|
246
|
+
}
|
|
247
|
+
}
|
package/dist/errors.d.ts
ADDED
package/dist/errors.js
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
// TypeScript error checking for Claudette MCP server
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
import { existsSync } from 'fs';
|
|
4
|
+
import { join } from 'path';
|
|
5
|
+
// Check TypeScript errors
|
|
6
|
+
export function getTypeScriptErrors(workspacePath) {
|
|
7
|
+
const errors = [];
|
|
8
|
+
try {
|
|
9
|
+
// Check if tsconfig exists
|
|
10
|
+
if (!existsSync(join(workspacePath, 'tsconfig.json'))) {
|
|
11
|
+
return [];
|
|
12
|
+
}
|
|
13
|
+
// Run TypeScript compiler
|
|
14
|
+
execSync('npx tsc --noEmit 2>&1', {
|
|
15
|
+
cwd: workspacePath,
|
|
16
|
+
encoding: 'utf-8',
|
|
17
|
+
maxBuffer: 10 * 1024 * 1024,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
catch (err) {
|
|
21
|
+
const output = err.stdout || err.message || '';
|
|
22
|
+
// Parse TypeScript errors: src/file.ts(10,5): error TS2322: ...
|
|
23
|
+
const errorRegex = /^(.+?)\((\d+),(\d+)\):\s*(error|warning)\s*(TS\d+):\s*(.+)$/gm;
|
|
24
|
+
let match;
|
|
25
|
+
while ((match = errorRegex.exec(output)) !== null) {
|
|
26
|
+
errors.push({
|
|
27
|
+
file: match[1],
|
|
28
|
+
line: parseInt(match[2], 10),
|
|
29
|
+
column: parseInt(match[3], 10),
|
|
30
|
+
severity: match[4],
|
|
31
|
+
code: match[5],
|
|
32
|
+
message: match[6],
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return errors;
|
|
37
|
+
}
|
package/dist/git.d.ts
ADDED
package/dist/git.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Git operations for Claudette MCP server
|
|
2
|
+
import { execSync } from 'child_process';
|
|
3
|
+
// Get git changes
|
|
4
|
+
export function getGitChanges(workspacePath) {
|
|
5
|
+
const changes = [];
|
|
6
|
+
try {
|
|
7
|
+
const output = execSync('git status --porcelain', {
|
|
8
|
+
cwd: workspacePath,
|
|
9
|
+
encoding: 'utf-8',
|
|
10
|
+
});
|
|
11
|
+
const lines = output.trim().split('\n').filter(Boolean);
|
|
12
|
+
for (const line of lines) {
|
|
13
|
+
const staged = line[0] !== ' ' && line[0] !== '?';
|
|
14
|
+
const statusCode = line.substring(0, 2).trim();
|
|
15
|
+
const file = line.substring(3);
|
|
16
|
+
let status = 'unknown';
|
|
17
|
+
if (statusCode.includes('M'))
|
|
18
|
+
status = 'modified';
|
|
19
|
+
else if (statusCode.includes('A'))
|
|
20
|
+
status = 'added';
|
|
21
|
+
else if (statusCode.includes('D'))
|
|
22
|
+
status = 'deleted';
|
|
23
|
+
else if (statusCode.includes('R'))
|
|
24
|
+
status = 'renamed';
|
|
25
|
+
else if (statusCode.includes('?'))
|
|
26
|
+
status = 'untracked';
|
|
27
|
+
changes.push({ file, status, staged });
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
// Not a git repo or git not available
|
|
32
|
+
}
|
|
33
|
+
return changes;
|
|
34
|
+
}
|