mcp-code-context 2.1.3 โ†’ 2.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.
Files changed (35) hide show
  1. package/CHANGELOG.md +44 -71
  2. package/CONTRIBUTING.md +243 -0
  3. package/MEJORAS_PROPUESTAS.md +324 -0
  4. package/README.md +31 -7
  5. package/RELEASE_NOTES_v2.2.0.md +286 -0
  6. package/dist/src/index.d.ts +36 -0
  7. package/dist/src/index.js +198 -2
  8. package/dist/src/index.js.map +1 -1
  9. package/dist/src/tools/readFileLines.d.ts +26 -0
  10. package/dist/src/tools/readFileLines.js +77 -0
  11. package/dist/src/tools/readFileLines.js.map +1 -0
  12. package/dist/src/tools/searchCodePattern.d.ts +32 -0
  13. package/dist/src/tools/searchCodePattern.js +80 -0
  14. package/dist/src/tools/searchCodePattern.js.map +1 -0
  15. package/dist/src/utils/backupManager.d.ts +13 -6
  16. package/dist/src/utils/backupManager.js +73 -8
  17. package/dist/src/utils/backupManager.js.map +1 -1
  18. package/dist/src/utils/projectRoot.d.ts +10 -0
  19. package/dist/src/utils/projectRoot.js +40 -0
  20. package/dist/src/utils/projectRoot.js.map +1 -0
  21. package/dist/tests/test-backup-system.d.ts +7 -0
  22. package/dist/tests/test-backup-system.js +152 -0
  23. package/dist/tests/test-backup-system.js.map +1 -0
  24. package/dist/tests/test-new-tools.d.ts +4 -0
  25. package/dist/tests/test-new-tools.js +147 -0
  26. package/dist/tests/test-new-tools.js.map +1 -0
  27. package/llms.txt +62 -7
  28. package/package.json +2 -2
  29. package/src/index.ts +233 -2
  30. package/src/tools/readFileLines.ts +104 -0
  31. package/src/tools/searchCodePattern.ts +129 -0
  32. package/src/utils/backupManager.ts +83 -8
  33. package/src/utils/projectRoot.ts +44 -0
  34. package/tests/test-backup-system.ts +174 -0
  35. package/tests/test-new-tools.ts +165 -0
@@ -0,0 +1,174 @@
1
+ /**
2
+ * Tests for Centralized Backup System
3
+ * - Backup creation in project root
4
+ * - Folder structure preservation
5
+ * - clean_backups functionality
6
+ */
7
+
8
+ import * as fs from 'fs';
9
+ import * as path from 'path';
10
+ import { createBackup, cleanAllBackups } from '../src/utils/backupManager.js';
11
+ import { findProjectRoot } from '../src/utils/projectRoot.js';
12
+
13
+ const TEST_DIR = path.join(process.cwd(), 'tests', 'fixtures', 'backup-test-project');
14
+ const TEST_FILE = path.join(TEST_DIR, 'src', 'components', 'Button.tsx');
15
+
16
+ function setupTestProject() {
17
+ // Create test project structure
18
+ if (fs.existsSync(TEST_DIR)) {
19
+ fs.rmSync(TEST_DIR, { recursive: true, force: true });
20
+ }
21
+
22
+ fs.mkdirSync(path.join(TEST_DIR, 'src', 'components'), { recursive: true });
23
+ fs.writeFileSync(TEST_FILE, 'export const Button = () => <button>Click</button>;');
24
+
25
+ // Create package.json to mark as project root
26
+ fs.writeFileSync(
27
+ path.join(TEST_DIR, 'package.json'),
28
+ JSON.stringify({ name: 'backup-test-project' })
29
+ );
30
+ }
31
+
32
+ function cleanupTestProject() {
33
+ if (fs.existsSync(TEST_DIR)) {
34
+ fs.rmSync(TEST_DIR, { recursive: true, force: true });
35
+ }
36
+ }
37
+
38
+ function runTests() {
39
+ console.log('\n๐Ÿงช Testing Centralized Backup System...\n');
40
+
41
+ let passed = 0;
42
+ let failed = 0;
43
+
44
+ // Setup
45
+ setupTestProject();
46
+
47
+ try {
48
+ // Test 1: Find project root
49
+ console.log('Test 1: Find project root');
50
+ const projectRoot = findProjectRoot(TEST_FILE);
51
+ if (projectRoot === TEST_DIR) {
52
+ console.log('โœ… PASS: Project root found correctly');
53
+ passed++;
54
+ } else {
55
+ console.log(`โŒ FAIL: Expected ${TEST_DIR}, got ${projectRoot}`);
56
+ failed++;
57
+ }
58
+
59
+ // Test 2: Create backup in centralized location
60
+ console.log('\nTest 2: Create backup in centralized location');
61
+ const backupPath = createBackup(TEST_FILE);
62
+
63
+ const expectedBackupDir = path.join(TEST_DIR, '.mcp-backups', 'src', 'components');
64
+ const backupExists = fs.existsSync(backupPath);
65
+ const inCorrectLocation = backupPath.startsWith(expectedBackupDir);
66
+
67
+ if (backupExists && inCorrectLocation) {
68
+ console.log('โœ… PASS: Backup created in centralized location');
69
+ console.log(` Location: ${backupPath}`);
70
+ passed++;
71
+ } else {
72
+ console.log('โŒ FAIL: Backup not in correct location');
73
+ console.log(` Expected dir: ${expectedBackupDir}`);
74
+ console.log(` Actual path: ${backupPath}`);
75
+ failed++;
76
+ }
77
+
78
+ // Test 3: Backup preserves folder structure
79
+ console.log('\nTest 3: Backup preserves folder structure');
80
+ const relativePath = path.relative(TEST_DIR, TEST_FILE);
81
+ const expectedStructure = path.join('.mcp-backups', relativePath);
82
+ const actualStructure = path.relative(TEST_DIR, backupPath.replace(/\.\d+\.backup$/, ''));
83
+
84
+ if (actualStructure.startsWith(expectedStructure)) {
85
+ console.log('โœ… PASS: Folder structure preserved');
86
+ console.log(` Structure: ${expectedStructure}`);
87
+ passed++;
88
+ } else {
89
+ console.log('โŒ FAIL: Folder structure not preserved');
90
+ console.log(` Expected: ${expectedStructure}`);
91
+ console.log(` Actual: ${actualStructure}`);
92
+ failed++;
93
+ }
94
+
95
+ // Test 4: Multiple backups in same location
96
+ console.log('\nTest 4: Multiple backups (rolling system)');
97
+ const backup2 = createBackup(TEST_FILE);
98
+ const backup3 = createBackup(TEST_FILE);
99
+
100
+ const allBackups = fs.readdirSync(expectedBackupDir)
101
+ .filter(f => f.startsWith('Button.tsx.') && f.endsWith('.backup'));
102
+
103
+ if (allBackups.length === 3) {
104
+ console.log('โœ… PASS: Multiple backups created');
105
+ console.log(` Backups: ${allBackups.join(', ')}`);
106
+ passed++;
107
+ } else {
108
+ console.log(`โŒ FAIL: Expected 3 backups, found ${allBackups.length}`);
109
+ failed++;
110
+ }
111
+
112
+ // Test 5: clean_backups removes all backups
113
+ console.log('\nTest 5: clean_backups removes all backups');
114
+ const result = cleanAllBackups(TEST_DIR);
115
+ const backupDirExists = fs.existsSync(path.join(TEST_DIR, '.mcp-backups'));
116
+
117
+ if (result && !backupDirExists) {
118
+ console.log('โœ… PASS: All backups cleaned');
119
+ console.log(` Backup directory removed successfully`);
120
+ passed++;
121
+ } else {
122
+ console.log('โŒ FAIL: Backups not cleaned properly');
123
+ console.log(` Result: ${result}, Dir exists: ${backupDirExists}`);
124
+ failed++;
125
+ }
126
+
127
+ // Test 6: clean_backups on non-existent directory
128
+ console.log('\nTest 6: clean_backups on non-existent directory');
129
+ const result2 = cleanAllBackups(TEST_DIR);
130
+
131
+ if (!result2) {
132
+ console.log('โœ… PASS: Returns false when backup directory not found');
133
+ passed++;
134
+ } else {
135
+ console.log('โŒ FAIL: Should return false when directory not found');
136
+ failed++;
137
+ }
138
+
139
+ // Test 7: Backup in nested project structure
140
+ console.log('\nTest 7: Backup in deeply nested file');
141
+ const deepFile = path.join(TEST_DIR, 'src', 'features', 'auth', 'components', 'LoginForm.tsx');
142
+ fs.mkdirSync(path.dirname(deepFile), { recursive: true });
143
+ fs.writeFileSync(deepFile, 'export const LoginForm = () => <form />;');
144
+
145
+ const deepBackup = createBackup(deepFile);
146
+ const expectedDeepDir = path.join(TEST_DIR, '.mcp-backups', 'src', 'features', 'auth', 'components');
147
+
148
+ if (deepBackup.startsWith(expectedDeepDir)) {
149
+ console.log('โœ… PASS: Deep nesting preserved');
150
+ console.log(` Path: ${path.relative(TEST_DIR, deepBackup)}`);
151
+ passed++;
152
+ } else {
153
+ console.log('โŒ FAIL: Deep nesting not preserved');
154
+ failed++;
155
+ }
156
+
157
+ } catch (error) {
158
+ console.log(`โŒ FAIL: Unexpected error: ${error}`);
159
+ failed++;
160
+ } finally {
161
+ // Cleanup
162
+ cleanupTestProject();
163
+ }
164
+
165
+ console.log('\nโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•');
166
+ console.log(`๐Ÿ“Š Results: ${passed} passed, ${failed} failed (${passed + failed} total)`);
167
+ console.log('โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•\n');
168
+
169
+ if (failed > 0) {
170
+ process.exit(1);
171
+ }
172
+ }
173
+
174
+ runTests();
@@ -0,0 +1,165 @@
1
+ /**
2
+ * Test suite for new tools: read_file_lines and search_code_pattern
3
+ */
4
+
5
+ import { readFileLines } from "../src/tools/readFileLines.js";
6
+ import { searchCodePattern } from "../src/tools/searchCodePattern.js";
7
+ import * as fs from "node:fs";
8
+ import * as path from "node:path";
9
+ import { fileURLToPath } from "node:url";
10
+
11
+ const __filename = fileURLToPath(import.meta.url);
12
+ const __dirname = path.dirname(__filename);
13
+
14
+ // Create test file
15
+ const testDir = path.join(__dirname, "test-data");
16
+ const testFile = path.join(testDir, "sample.ts");
17
+
18
+ if (!fs.existsSync(testDir)) {
19
+ fs.mkdirSync(testDir, { recursive: true });
20
+ }
21
+
22
+ const testContent = `// Sample TypeScript file
23
+ export class Calculator {
24
+ add(a: number, b: number): number {
25
+ return a + b;
26
+ }
27
+
28
+ subtract(a: number, b: number): number {
29
+ return a - b;
30
+ }
31
+
32
+ multiply(a: number, b: number): number {
33
+ return a * b;
34
+ }
35
+
36
+ divide(a: number, b: number): number {
37
+ if (b === 0) throw new Error("Division by zero");
38
+ return a / b;
39
+ }
40
+ }
41
+
42
+ export function greet(name: string): string {
43
+ return \`Hello, \${name}!\`;
44
+ }
45
+ `;
46
+
47
+ fs.writeFileSync(testFile, testContent, "utf-8");
48
+
49
+ console.log("๐Ÿงช Testing read_file_lines...\n");
50
+
51
+ // Test 1: Read exact line range
52
+ console.log("Test 1: Read lines 3-7");
53
+ const result1 = readFileLines({
54
+ filePath: testFile,
55
+ startLine: 3,
56
+ endLine: 7,
57
+ });
58
+
59
+ if (result1.success && result1.content?.includes("add")) {
60
+ console.log("โœ… PASS: Exact line range");
61
+ } else {
62
+ console.error("โŒ FAIL: Exact line range");
63
+ console.error(result1);
64
+ }
65
+
66
+ // Test 2: Read around pattern
67
+ console.log("\nTest 2: Read around pattern 'multiply'");
68
+ const result2 = readFileLines({
69
+ filePath: testFile,
70
+ aroundPattern: "multiply",
71
+ contextLines: 2,
72
+ });
73
+
74
+ if (result2.success && result2.content?.includes("multiply")) {
75
+ console.log("โœ… PASS: Pattern search");
76
+ } else {
77
+ console.error("โŒ FAIL: Pattern search");
78
+ console.error(result2);
79
+ }
80
+
81
+ // Test 3: Invalid line range
82
+ console.log("\nTest 3: Invalid line range (should fail)");
83
+ const result3 = readFileLines({
84
+ filePath: testFile,
85
+ startLine: 100,
86
+ endLine: 200,
87
+ });
88
+
89
+ if (!result3.success && result3.error?.includes("Invalid line range")) {
90
+ console.log("โœ… PASS: Error handling for invalid range");
91
+ } else {
92
+ console.error("โŒ FAIL: Should have failed with invalid range");
93
+ }
94
+
95
+ // Test 4: Pattern not found
96
+ console.log("\nTest 4: Pattern not found (should fail)");
97
+ const result4 = readFileLines({
98
+ filePath: testFile,
99
+ aroundPattern: "nonexistent_pattern_xyz",
100
+ });
101
+
102
+ if (!result4.success && result4.error?.includes("not found")) {
103
+ console.log("โœ… PASS: Error handling for pattern not found");
104
+ } else {
105
+ console.error("โŒ FAIL: Should have failed with pattern not found");
106
+ }
107
+
108
+ console.log("\n๐Ÿงช Testing search_code_pattern...\n");
109
+
110
+ // Test 5: Search for pattern
111
+ console.log("Test 5: Search for 'Calculator'");
112
+ const result5 = searchCodePattern({
113
+ rootDir: testDir,
114
+ pattern: "Calculator",
115
+ fileExtensions: [".ts"],
116
+ maxResults: 10,
117
+ });
118
+
119
+ if (result5.success && result5.matches && result5.matches.length > 0) {
120
+ console.log(`โœ… PASS: Found ${result5.totalMatches} matches`);
121
+ } else {
122
+ console.error("โŒ FAIL: Should have found matches");
123
+ console.error(result5);
124
+ }
125
+
126
+ // Test 6: Search with no matches
127
+ console.log("\nTest 6: Search for non-existent pattern");
128
+ const result6 = searchCodePattern({
129
+ rootDir: testDir,
130
+ pattern: "NonExistentClass123",
131
+ fileExtensions: [".ts"],
132
+ });
133
+
134
+ if (result6.success && result6.totalMatches === 0) {
135
+ console.log("โœ… PASS: No matches found (as expected)");
136
+ } else {
137
+ console.error("โŒ FAIL: Should have found no matches");
138
+ }
139
+
140
+ // Test 7: Search with context
141
+ console.log("\nTest 7: Search with context lines");
142
+ const result7 = searchCodePattern({
143
+ rootDir: testDir,
144
+ pattern: "divide",
145
+ fileExtensions: [".ts"],
146
+ showContext: true,
147
+ contextLines: 2,
148
+ });
149
+
150
+ if (
151
+ result7.success &&
152
+ result7.matches &&
153
+ result7.matches[0]?.context &&
154
+ result7.matches[0].context.length > 0
155
+ ) {
156
+ console.log("โœ… PASS: Context lines included");
157
+ } else {
158
+ console.error("โŒ FAIL: Context lines not included");
159
+ console.error(result7);
160
+ }
161
+
162
+ // Cleanup
163
+ fs.rmSync(testDir, { recursive: true, force: true });
164
+
165
+ console.log("\nโœจ All tests completed!");