mcp-code-context 2.2.0 → 2.4.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 (132) hide show
  1. package/.io/RULES.md +12 -0
  2. package/CHANGELOG.md +114 -1
  3. package/README.md +28 -1
  4. package/dist/src/ast/writers/safeRename.d.ts +14 -0
  5. package/dist/src/ast/writers/safeRename.js +258 -0
  6. package/dist/src/ast/writers/safeRename.js.map +1 -0
  7. package/dist/src/ast/writers/symbolWriter.d.ts +2 -0
  8. package/dist/src/ast/writers/symbolWriter.js +9 -14
  9. package/dist/src/ast/writers/symbolWriter.js.map +1 -1
  10. package/dist/src/cache/astCache.d.ts +33 -0
  11. package/dist/src/cache/astCache.js +113 -0
  12. package/dist/src/cache/astCache.js.map +1 -0
  13. package/dist/src/handlers/readHandlers.d.ts +70 -0
  14. package/dist/src/handlers/readHandlers.js +431 -0
  15. package/dist/src/handlers/readHandlers.js.map +1 -0
  16. package/dist/src/handlers/utilHandlers.d.ts +31 -0
  17. package/dist/src/handlers/utilHandlers.js +69 -0
  18. package/dist/src/handlers/utilHandlers.js.map +1 -0
  19. package/dist/src/handlers/writeHandlers.d.ts +57 -0
  20. package/dist/src/handlers/writeHandlers.js +475 -0
  21. package/dist/src/handlers/writeHandlers.js.map +1 -0
  22. package/dist/src/index.d.ts +6 -143
  23. package/dist/src/index.js +77 -1091
  24. package/dist/src/index.js.map +1 -1
  25. package/dist/src/tools/searchCodePattern.d.ts +7 -2
  26. package/dist/src/tools/searchCodePattern.js +68 -4
  27. package/dist/src/tools/searchCodePattern.js.map +1 -1
  28. package/dist/src/utils/arrayUtils.d.ts +12 -0
  29. package/dist/src/utils/arrayUtils.js +27 -0
  30. package/dist/src/utils/arrayUtils.js.map +1 -0
  31. package/dist/src/utils/constants.d.ts +46 -0
  32. package/dist/src/utils/constants.js +112 -0
  33. package/dist/src/utils/constants.js.map +1 -0
  34. package/dist/src/utils/ignoreManager.d.ts +8 -0
  35. package/dist/src/utils/ignoreManager.js +49 -0
  36. package/dist/src/utils/ignoreManager.js.map +1 -1
  37. package/dist/src/utils/normalization.d.ts +49 -0
  38. package/dist/src/utils/normalization.js +173 -0
  39. package/dist/src/utils/normalization.js.map +1 -0
  40. package/dist/src/utils/projectRoot.d.ts +12 -5
  41. package/dist/src/utils/projectRoot.js +22 -20
  42. package/dist/src/utils/projectRoot.js.map +1 -1
  43. package/dist/src/utils/secureValidation.d.ts +15 -0
  44. package/dist/src/utils/secureValidation.js +41 -0
  45. package/dist/src/utils/secureValidation.js.map +1 -0
  46. package/dist/src/utils/transactionManager.d.ts +45 -0
  47. package/dist/src/utils/transactionManager.js +102 -0
  48. package/dist/src/utils/transactionManager.js.map +1 -0
  49. package/dist/src/utils/validation.d.ts +87 -0
  50. package/dist/src/utils/validation.js +231 -0
  51. package/dist/src/utils/validation.js.map +1 -0
  52. package/docs/ARCHITECTURE.md +300 -0
  53. package/docs/architecture/ADR-001-modular-handlers.md +119 -0
  54. package/llms.txt +10 -1
  55. package/package.json +5 -5
  56. package/AGENT_INSTRUCTIONS.md +0 -135
  57. package/DEPLOYMENT_INSTRUCTIONS.md +0 -110
  58. package/MEJORAS_PROPUESTAS.md +0 -324
  59. package/RELEASE_NOTES_v2.1.3.md +0 -78
  60. package/RELEASE_NOTES_v2.2.0.md +0 -286
  61. package/dist/src/tests/test-confirmation-flow.d.ts +0 -1
  62. package/dist/src/tests/test-confirmation-flow.js +0 -43
  63. package/dist/src/tests/test-confirmation-flow.js.map +0 -1
  64. package/dist/tests/debug-replace.d.ts +0 -1
  65. package/dist/tests/debug-replace.js +0 -17
  66. package/dist/tests/debug-replace.js.map +0 -1
  67. package/dist/tests/test-backup-system.d.ts +0 -7
  68. package/dist/tests/test-backup-system.js +0 -152
  69. package/dist/tests/test-backup-system.js.map +0 -1
  70. package/dist/tests/test-confirmation-flow.d.ts +0 -1
  71. package/dist/tests/test-confirmation-flow.js +0 -43
  72. package/dist/tests/test-confirmation-flow.js.map +0 -1
  73. package/dist/tests/test-dart.d.ts +0 -1
  74. package/dist/tests/test-dart.js +0 -242
  75. package/dist/tests/test-dart.js.map +0 -1
  76. package/dist/tests/test-new-tools.d.ts +0 -4
  77. package/dist/tests/test-new-tools.js +0 -147
  78. package/dist/tests/test-new-tools.js.map +0 -1
  79. package/dist/tests/test-php-ast.d.ts +0 -1
  80. package/dist/tests/test-php-ast.js +0 -152
  81. package/dist/tests/test-php-ast.js.map +0 -1
  82. package/dist/tests/test-php.d.ts +0 -1
  83. package/dist/tests/test-php.js +0 -234
  84. package/dist/tests/test-php.js.map +0 -1
  85. package/dist/tests/test-smoke.d.ts +0 -5
  86. package/dist/tests/test-smoke.js +0 -83
  87. package/dist/tests/test-smoke.js.map +0 -1
  88. package/dist/tests/writers/test-crlf-handling.d.ts +0 -4
  89. package/dist/tests/writers/test-crlf-handling.js +0 -73
  90. package/dist/tests/writers/test-crlf-handling.js.map +0 -1
  91. package/dist/tests/writers/test-write-smoke.d.ts +0 -9
  92. package/dist/tests/writers/test-write-smoke.js +0 -346
  93. package/dist/tests/writers/test-write-smoke.js.map +0 -1
  94. package/src/ast/dartCompressor.ts +0 -1013
  95. package/src/ast/phpCompressor.ts +0 -814
  96. package/src/ast/semanticCompressor.ts +0 -808
  97. package/src/ast/writers/dartWriter.ts +0 -648
  98. package/src/ast/writers/phpWriter.ts +0 -508
  99. package/src/ast/writers/pyWriter.ts +0 -446
  100. package/src/ast/writers/symbolWriter.ts +0 -277
  101. package/src/ast/writers/tsWriter.ts +0 -418
  102. package/src/index.ts +0 -1784
  103. package/src/tools/readFileLines.ts +0 -104
  104. package/src/tools/searchCodePattern.ts +0 -129
  105. package/src/utils/backupManager.ts +0 -141
  106. package/src/utils/confirmationCache.ts +0 -61
  107. package/src/utils/diffEngine.ts +0 -223
  108. package/src/utils/fuzzyMatch.ts +0 -86
  109. package/src/utils/ignoreManager.ts +0 -144
  110. package/src/utils/projectRoot.ts +0 -44
  111. package/tests/test-backup-system.ts +0 -174
  112. package/tests/test-confirmation-flow.ts +0 -49
  113. package/tests/test-dart.d.ts +0 -1
  114. package/tests/test-dart.js +0 -209
  115. package/tests/test-dart.js.map +0 -1
  116. package/tests/test-dart.ts +0 -265
  117. package/tests/test-new-tools.ts +0 -165
  118. package/tests/test-php-ast.d.ts +0 -1
  119. package/tests/test-php-ast.js +0 -152
  120. package/tests/test-php-ast.js.map +0 -1
  121. package/tests/test-php-ast.ts +0 -155
  122. package/tests/test-php.d.ts +0 -1
  123. package/tests/test-php.js +0 -234
  124. package/tests/test-php.js.map +0 -1
  125. package/tests/test-php.ts +0 -261
  126. package/tests/test-smoke.d.ts +0 -5
  127. package/tests/test-smoke.js +0 -83
  128. package/tests/test-smoke.js.map +0 -1
  129. package/tests/test-smoke.ts +0 -93
  130. package/tests/writers/test-crlf-handling.ts +0 -100
  131. package/tests/writers/test-write-smoke.ts +0 -568
  132. package/tsconfig.json +0 -19
@@ -1,86 +0,0 @@
1
- export interface SymbolInfo {
2
- name: string;
3
- type: string;
4
- className?: string;
5
- }
6
-
7
- export function levenshteinDistance(a: string, b: string): number {
8
- const matrix = [];
9
- let i, j;
10
-
11
- if (a.length === 0) return b.length;
12
- if (b.length === 0) return a.length;
13
-
14
- for (i = 0; i <= b.length; i++) {
15
- matrix[i] = [i];
16
- }
17
- for (j = 0; j <= a.length; j++) {
18
- matrix[0][j] = j;
19
- }
20
-
21
- for (i = 1; i <= b.length; i++) {
22
- for (j = 1; j <= a.length; j++) {
23
- if (b.charAt(i - 1) === a.charAt(j - 1)) {
24
- matrix[i][j] = matrix[i - 1][j - 1];
25
- } else {
26
- matrix[i][j] = Math.min(
27
- matrix[i - 1][j - 1] + 1,
28
- Math.min(matrix[i][j - 1] + 1, matrix[i - 1][j] + 1)
29
- );
30
- }
31
- }
32
- }
33
-
34
- return matrix[b.length][a.length];
35
- }
36
-
37
- export function findClosestSymbols(target: string, symbols: SymbolInfo[], limit = 5): SymbolInfo[] {
38
- return symbols
39
- .map((sym) => ({
40
- ...sym,
41
- distance: levenshteinDistance(target.toLowerCase(), sym.name.toLowerCase()),
42
- }))
43
- .sort((a, b) => a.distance - b.distance)
44
- .slice(0, limit);
45
- }
46
-
47
- // A simple regex-based extractor to get available symbols when the AST parsing fails
48
- export function extractFallbackSymbols(content: string): SymbolInfo[] {
49
- const symbols: SymbolInfo[] = [];
50
- const lines = content.split('\\n');
51
-
52
- let currentClass: string | undefined = undefined;
53
-
54
- for (let i = 0; i < lines.length; i++) {
55
- const line = lines[i].trim();
56
-
57
- // Check for class definitions (multi-language approx)
58
- const classMatch = line.match(/^\s*(?:export\s+|abstract\s+|final\s+)?class\s+([_a-zA-Z0-9]+)/);
59
- if (classMatch) {
60
- currentClass = classMatch[1];
61
- symbols.push({ name: currentClass, type: 'class' });
62
- continue;
63
- }
64
-
65
- // Check for function / method definitions
66
- // JS/TS: function foo() | foo() { | async foo()
67
- // Python: def foo()
68
- // PHP: function foo()
69
- // Dart: type foo()
70
- const funcMatch = line.match(/^\s*(?:export\s+|async\s+|public\s+|private\s+|protected\s+|static\s+|def\s+|function\s+)*([_a-zA-Z0-9]+)\s*\(/);
71
- if (funcMatch && !['if', 'else', 'for', 'while', 'switch', 'catch'].includes(funcMatch[1])) {
72
- symbols.push({
73
- name: funcMatch[1],
74
- type: currentClass ? 'method' : 'function',
75
- className: currentClass,
76
- });
77
- }
78
-
79
- // Basic assumption: closing brace at start of line might end a class
80
- if (line === '}') {
81
- currentClass = undefined;
82
- }
83
- }
84
-
85
- return symbols;
86
- }
@@ -1,144 +0,0 @@
1
- /**
2
- * IgnoreManager — Reads .gitignore / .repomixignore and walks directories
3
- * filtering out ignored paths. Always excludes common heavy directories.
4
- */
5
-
6
- import * as fs from "node:fs";
7
- import * as path from "node:path";
8
- import { createRequire } from "node:module";
9
- import type { Ignore } from "ignore";
10
-
11
- // CJS interop: 'ignore' uses module.exports which doesn't resolve
12
- // as a callable default export under Node16 module resolution.
13
- const require = createRequire(import.meta.url);
14
- const ignore: (opts?: Record<string, unknown>) => Ignore = require("ignore");
15
-
16
- /** Directories and patterns that are ALWAYS excluded regardless of .gitignore */
17
- const ALWAYS_IGNORE: string[] = [
18
- "node_modules",
19
- ".git",
20
- "dist",
21
- "build",
22
- "vendor",
23
- ".next",
24
- ".nuxt",
25
- ".output",
26
- "__pycache__",
27
- ".pytest_cache",
28
- "coverage",
29
- ".nyc_output",
30
- ".turbo",
31
- ".cache",
32
- ".parcel-cache",
33
- ".venv",
34
- "venv",
35
- "env",
36
- ".env.local",
37
- ".tsbuildinfo",
38
- "*.lock",
39
- "package-lock.json",
40
- "yarn.lock",
41
- "pnpm-lock.yaml",
42
- "bun.lockb",
43
- "*.min.js",
44
- "*.min.css",
45
- "*.map",
46
- "*.chunk.js",
47
- "*.bundle.js",
48
- ];
49
-
50
- /** Max file size to read (512 KB). Files larger than this are skipped. */
51
- const MAX_FILE_SIZE_BYTES = 512 * 1024;
52
-
53
- export class IgnoreManager {
54
- private ig: Ignore;
55
- private rootDir: string;
56
-
57
- constructor(rootDir: string) {
58
- this.rootDir = path.resolve(rootDir);
59
- this.ig = ignore();
60
-
61
- // Load hardcoded ignores first
62
- this.ig.add(ALWAYS_IGNORE);
63
-
64
- // Load .gitignore
65
- this.loadIgnoreFile(path.join(this.rootDir, ".gitignore"));
66
-
67
- // Load .repomixignore (Repomix-style overrides)
68
- this.loadIgnoreFile(path.join(this.rootDir, ".repomixignore"));
69
- }
70
-
71
- /**
72
- * Reads an ignore-pattern file and adds its rules to the filter.
73
- * Silently skips if the file doesn't exist or is unreadable.
74
- */
75
- private loadIgnoreFile(filePath: string): void {
76
- try {
77
- if (!fs.existsSync(filePath)) return;
78
-
79
- const content = fs.readFileSync(filePath, "utf-8");
80
- const patterns = content
81
- .split("\n")
82
- .map((line) => line.trim())
83
- .filter((line) => line.length > 0 && !line.startsWith("#"));
84
-
85
- if (patterns.length > 0) {
86
- this.ig.add(patterns);
87
- }
88
- } catch {
89
- // File unreadable — skip silently
90
- }
91
- }
92
-
93
- /** Check if a relative path should be ignored. */
94
- public isIgnored(relativePath: string): boolean {
95
- if (!relativePath) return false;
96
- return this.ig.ignores(relativePath);
97
- }
98
-
99
- /**
100
- * Recursively walk a directory, returning absolute paths to all
101
- * non-ignored files.
102
- */
103
- public walkDirectory(dir?: string): string[] {
104
- const baseDir = dir ? path.resolve(dir) : this.rootDir;
105
- const files: string[] = [];
106
- this.walk(baseDir, files);
107
- return files;
108
- }
109
-
110
- private walk(currentDir: string, collected: string[]): void {
111
- let entries: fs.Dirent[];
112
- try {
113
- entries = fs.readdirSync(currentDir, { withFileTypes: true });
114
- } catch {
115
- return; // Permission denied, broken symlink, etc.
116
- }
117
-
118
- for (const entry of entries) {
119
- const fullPath = path.join(currentDir, entry.name);
120
- const relativePath = path
121
- .relative(this.rootDir, fullPath)
122
- .replace(/\\/g, "/");
123
-
124
- // Check ignore rules
125
- if (this.isIgnored(relativePath)) continue;
126
-
127
- if (entry.isDirectory()) {
128
- // Also test with trailing slash for directory-level patterns
129
- if (this.isIgnored(relativePath + "/")) continue;
130
- this.walk(fullPath, collected);
131
- } else if (entry.isFile()) {
132
- // Skip files that are too large
133
- try {
134
- const stat = fs.statSync(fullPath);
135
- if (stat.size > MAX_FILE_SIZE_BYTES) continue;
136
- } catch {
137
- continue;
138
- }
139
- collected.push(fullPath);
140
- }
141
- // Symlinks and other entries are silently skipped
142
- }
143
- }
144
- }
@@ -1,44 +0,0 @@
1
- /**
2
- * projectRoot.ts — Project Root Discovery Utility
3
- *
4
- * Finds the root directory of a project by looking for common markers.
5
- */
6
-
7
- import * as fs from "node:fs";
8
- import * as path from "node:path";
9
-
10
- const ROOT_MARKERS = [
11
- "package.json",
12
- ".git",
13
- "pyproject.toml",
14
- "setup.py",
15
- "Cargo.toml",
16
- "go.mod",
17
- "pom.xml",
18
- "build.gradle",
19
- "CMakeLists.txt",
20
- ".gitignore",
21
- "Gemfile",
22
- "composer.json",
23
- "pubspec.yaml",
24
- ];
25
-
26
- /**
27
- * Find the project root by walking up from a file path.
28
- * Returns undefined if no root marker is found.
29
- */
30
- export function findProjectRoot(filePath: string): string | undefined {
31
- let current = path.dirname(path.resolve(filePath));
32
- const root = path.parse(current).root;
33
-
34
- while (current !== root) {
35
- for (const marker of ROOT_MARKERS) {
36
- if (fs.existsSync(path.join(current, marker))) {
37
- return current;
38
- }
39
- }
40
- current = path.dirname(current);
41
- }
42
-
43
- return undefined;
44
- }
@@ -1,174 +0,0 @@
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();
@@ -1,49 +0,0 @@
1
- import * as fs from 'node:fs';
2
- import * as path from 'node:path';
3
- import { handleWriteFileSurgical, handleRollbackFile } from '../src/index.js';
4
-
5
- async function testWorkflow() {
6
- const testFile = path.resolve('test-artifact.ts');
7
- const initialContent = 'export class Old { build() { return 1; } }';
8
- fs.writeFileSync(testFile, initialContent, 'utf-8');
9
-
10
- console.log('--- Step 1: Preview (Dry-Run) ---');
11
- const previewArgs = {
12
- filePath: testFile,
13
- symbolName: 'build',
14
- newContent: ' build() { return "new value"; }',
15
- className: 'Old'
16
- };
17
-
18
- const p1 = await handleWriteFileSurgical(previewArgs);
19
- const p1Text = p1.content[0].text;
20
- console.log('Preview Result:', p1Text.split('\n')[0]);
21
-
22
- const tokenMatch = p1Text.match(/confirmationToken: "([^"]+)"/);
23
- if (!tokenMatch) throw new Error('No token found');
24
- const token = tokenMatch[1];
25
- console.log('Token found:', token);
26
-
27
- console.log('\n--- Step 2: Confirm ---');
28
- const confirmArgs = {
29
- ...previewArgs,
30
- confirmationToken: token,
31
- confirm: true
32
- };
33
- const p2 = await handleWriteFileSurgical(confirmArgs);
34
- console.log('Confirm Result:', p2.content[0].text.split('\n')[0]);
35
- console.log('File Content:', fs.readFileSync(testFile, 'utf-8'));
36
-
37
- console.log('\n--- Step 3: Rollback ---');
38
- await handleRollbackFile({ filePath: testFile, steps: 1 });
39
- console.log('After Rollback:', fs.readFileSync(testFile, 'utf-8'));
40
-
41
- // Clean up
42
- fs.unlinkSync(testFile);
43
- const backupDir = path.join(path.dirname(testFile), '.mcp-backups');
44
- if (fs.existsSync(backupDir)) {
45
- fs.rmSync(backupDir, { recursive: true, force: true });
46
- }
47
- }
48
-
49
- testWorkflow().catch(console.error);
@@ -1 +0,0 @@
1
- export {};