planDirectoryMove
Plan a directory move with automatic, recursive import path updates across all files. Returns actionable edit plans and move suggestions without altering the filesystem.
Instructions
Plan directory move with recursive import updates for all contained files. Returns edit plans and file move suggestions without modifying the filesystem.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| projectRoot | No | Optional base directory for resolving relative paths and limiting tsconfig discovery | |
| workspaceRoot | No | Optional monorepo root used to search multiple tsconfig.json files | |
| tsconfigPath | No | Optional explicit tsconfig.json path for the primary project | |
| oldDir | Yes | Absolute path or path relative to projectRoot/workspaceRoot of the directory to move | |
| newDir | Yes | Absolute path or path relative to projectRoot/workspaceRoot of the destination |
Implementation Reference
- src/tools/planDirectoryMove.ts:54-102 (handler)Main handler function for planDirectoryMove. It resolves paths, collects TypeScript files under the old directory, uses TypeScript language service to compute import-rename edits for each file, and returns both the text edits and file move suggestions.
export function planDirectoryMove( params: PlanDirectoryMoveParams ): PlanDirectoryMoveResult { const baseDir = path.resolve( params.workspaceRoot ?? params.projectRoot ?? process.cwd() ); // 1. パスを絶対パスに正規化 const oldDirAbs = resolveInputPath(params.oldDir, baseDir); const newDirAbs = resolveInputPath(params.newDir, baseDir); const targetFiles = collectMoveTargetFiles(oldDirAbs); const primaryFilePath = targetFiles[0]; const candidateContexts = collectWorkspaceTsServices([oldDirAbs, newDirAbs], { projectRoot: params.projectRoot, workspaceRoot: params.workspaceRoot, tsconfigPath: params.tsconfigPath, primaryFilePath, }); const editBucket = createEditBucket(); const fsMoves: PlanDirectoryMoveResult["fsMoves"] = []; // 2. 各ファイルについて移動先を計算し、各 TS プロジェクトの編集をマージ for (const oldFile of targetFiles) { const rel = path.relative(oldDirAbs, oldFile); const newFile = path.join(newDirAbs, rel); for (const context of candidateContexts) { const fileTextChanges = context.service.getEditsForFileRename( oldFile, newFile, {}, {} ); addFileTextChanges(editBucket, context.tsModule, fileTextChanges); } fsMoves.push({ from: oldFile, to: newFile, }); } const edits = toFileTextEdits(editBucket); return { edits, fsMoves, }; } - src/types.ts:94-102 (schema)Type definitions for PlanDirectoryMoveParams (input) and PlanDirectoryMoveResult (output). Input accepts optional projectRoot/workspaceRoot/tsconfigPath, required oldDir and newDir. Output contains edits (FileTextEdits[]) and fsMoves (FsMove[]).
* planDirectoryMove の入力パラメータ */ export type PlanDirectoryMoveParams = { projectRoot?: string; // 旧互換: 相対パスの解決基準 / ワークスペース境界 workspaceRoot?: string; // monorepo 全体を探索するためのルート tsconfigPath?: string; // 移動元ディレクトリ配下の主 tsconfig を明示したい場合 oldDir: string; // 元ディレクトリパス newDir: string; // 移動先ディレクトリパス }; - src/index.ts:113-147 (registration)Tool registration in the TOOLS array with name 'planDirectoryMove', description, and inputSchema with JSON Schema properties for projectRoot, workspaceRoot, tsconfigPath, oldDir, newDir.
{ name: "planDirectoryMove", description: "Plan directory move with recursive import updates for all contained files. Returns edit plans and file move suggestions without modifying the filesystem.", inputSchema: { type: "object", properties: { projectRoot: { type: "string", description: "Optional base directory for resolving relative paths and limiting tsconfig discovery", }, workspaceRoot: { type: "string", description: "Optional monorepo root used to search multiple tsconfig.json files", }, tsconfigPath: { type: "string", description: "Optional explicit tsconfig.json path for the primary project", }, oldDir: { type: "string", description: "Absolute path or path relative to projectRoot/workspaceRoot of the directory to move", }, newDir: { type: "string", description: "Absolute path or path relative to projectRoot/workspaceRoot of the destination", }, }, required: ["oldDir", "newDir"], }, }, ]; - src/index.ts:205-216 (registration)Dispatch logic in the CallToolRequestSchema handler: case 'planDirectoryMove' casts args to PlanDirectoryMoveParams, calls planDirectoryMove(params), and returns JSON-stringified result.
case "planDirectoryMove": { const params = args as unknown as PlanDirectoryMoveParams; const result = planDirectoryMove(params); return { content: [ { type: "text", text: JSON.stringify(result, null, 2), }, ], }; } - src/tools/planDirectoryMove.ts:10-38 (helper)Helper constant MOVE_TARGET_EXTENSIONS (Set of .ts/.tsx/.mts/.cts) and collectMoveTargetFiles function that recursively collects all TypeScript files under a directory, sorted alphabetically.
const MOVE_TARGET_EXTENSIONS = new Set([".ts", ".tsx", ".mts", ".cts"]); function collectMoveTargetFiles(directoryPath: string): string[] { if (!fs.existsSync(directoryPath) || !fs.statSync(directoryPath).isDirectory()) { return []; } const collected: string[] = []; function visit(currentDirectoryPath: string): void { const entries = fs.readdirSync(currentDirectoryPath, { withFileTypes: true }); for (const entry of entries) { const entryPath = path.join(currentDirectoryPath, entry.name); if (entry.isDirectory()) { visit(entryPath); continue; } if (entry.isFile() && MOVE_TARGET_EXTENSIONS.has(path.extname(entry.name))) { collected.push(entryPath); } } } visit(directoryPath); return collected.sort((left, right) => left.localeCompare(right)); }