-
Notifications
You must be signed in to change notification settings - Fork 1.8k
Expand file tree
/
Copy pathcli.ts
More file actions
116 lines (97 loc) · 4.74 KB
/
cli.ts
File metadata and controls
116 lines (97 loc) · 4.74 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
#!/usr/bin/env node
import { existsSync, statSync } from 'node:fs';
import { createRequire } from 'node:module';
import path from 'node:path';
import { Command } from 'commander';
import { listMigrations } from './migrations/index.js';
import { run } from './runner.js';
import { DiagnosticLevel } from './types.js';
import { formatDiagnostic } from './utils/diagnostics.js';
const require = createRequire(import.meta.url);
const { version } = require('../package.json') as { version: string };
const program = new Command();
program.name('mcp-codemod').description('Codemod to migrate MCP TypeScript SDK code between versions').version(version);
for (const [name, migration] of listMigrations()) {
program
.command(`${name} <target-dir>`)
.description(migration.description)
.option('-d, --dry-run', 'Preview changes without writing files')
.option('-t, --transforms <ids>', 'Comma-separated transform IDs to run (default: all)')
.option('-v, --verbose', 'Show detailed per-change output')
.option('--ignore <patterns...>', 'Additional glob patterns to ignore')
.option('--list', 'List available transforms for this migration')
.action((targetDir: string, opts: Record<string, unknown>) => {
try {
if (opts['list']) {
console.log(`\nAvailable transforms for ${name}:\n`);
for (const t of migration.transforms) {
console.log(` ${t.id.padEnd(20)} ${t.name}`);
}
console.log('');
return;
}
const resolvedDir = path.resolve(targetDir);
if (!existsSync(resolvedDir) || !statSync(resolvedDir).isDirectory()) {
console.error(`\nError: "${resolvedDir}" is not a valid directory.\n`);
process.exitCode = 1;
return;
}
console.log(`\n@modelcontextprotocol/codemod — ${migration.name}\n`);
console.log(`Scanning ${resolvedDir}...`);
if (opts['dryRun']) {
console.log('(dry run — no files will be modified)\n');
} else {
console.log('');
}
const transforms = opts['transforms'] ? (opts['transforms'] as string).split(',').map(s => s.trim()) : undefined;
const result = run(migration, {
targetDir: resolvedDir,
dryRun: opts['dryRun'] as boolean | undefined,
verbose: opts['verbose'] as boolean | undefined,
transforms,
ignore: opts['ignore'] as string[] | undefined
});
if (result.filesChanged === 0 && result.diagnostics.length === 0) {
console.log('No changes needed — code already migrated or no SDK imports found.\n');
return;
}
if (result.filesChanged > 0) {
console.log(`Changes: ${result.totalChanges} across ${result.filesChanged} file(s)\n`);
}
if (opts['verbose']) {
console.log('Files modified:');
for (const fr of result.fileResults) {
console.log(` ${fr.filePath} (${fr.changes} change(s))`);
}
console.log('');
}
const errors = result.diagnostics.filter(d => d.level === DiagnosticLevel.Error);
if (errors.length > 0) {
console.log(`Errors (${errors.length}):`);
for (const d of errors) {
console.log(formatDiagnostic(d));
}
console.log('');
process.exitCode = 1;
}
const warnings = result.diagnostics.filter(d => d.level === DiagnosticLevel.Warning);
if (warnings.length > 0) {
console.log(`Warnings (${warnings.length}):`);
for (const d of warnings) {
console.log(formatDiagnostic(d));
}
console.log('');
process.exitCode = 1;
}
if (opts['dryRun']) {
console.log('Run without --dry-run to apply changes.\n');
} else {
console.log('Migration complete. Review the changes and run your build/tests.\n');
}
} catch (error) {
console.error(`\nError: ${error instanceof Error ? error.message : String(error)}\n`);
process.exitCode = 1;
}
});
}
program.parse();