diff --git a/.auto-claude-security.json b/.auto-claude-security.json
new file mode 100644
index 0000000..fe53dfe
--- /dev/null
+++ b/.auto-claude-security.json
@@ -0,0 +1,168 @@
+{
+ "base_commands": [
+ ".",
+ "[",
+ "[[",
+ "ag",
+ "awk",
+ "basename",
+ "bash",
+ "bc",
+ "break",
+ "cat",
+ "cd",
+ "chmod",
+ "clear",
+ "cmp",
+ "column",
+ "comm",
+ "command",
+ "continue",
+ "cp",
+ "curl",
+ "cut",
+ "date",
+ "df",
+ "diff",
+ "dig",
+ "dirname",
+ "du",
+ "echo",
+ "egrep",
+ "env",
+ "eval",
+ "exec",
+ "exit",
+ "expand",
+ "export",
+ "expr",
+ "false",
+ "fd",
+ "fgrep",
+ "file",
+ "find",
+ "fmt",
+ "fold",
+ "gawk",
+ "gh",
+ "git",
+ "grep",
+ "gunzip",
+ "gzip",
+ "head",
+ "help",
+ "host",
+ "iconv",
+ "id",
+ "jobs",
+ "join",
+ "jq",
+ "kill",
+ "killall",
+ "less",
+ "let",
+ "ln",
+ "ls",
+ "lsof",
+ "man",
+ "mkdir",
+ "mktemp",
+ "more",
+ "mv",
+ "nl",
+ "paste",
+ "pgrep",
+ "ping",
+ "pkill",
+ "popd",
+ "printenv",
+ "printf",
+ "ps",
+ "pushd",
+ "pwd",
+ "read",
+ "readlink",
+ "realpath",
+ "reset",
+ "return",
+ "rev",
+ "rg",
+ "rm",
+ "rmdir",
+ "sed",
+ "seq",
+ "set",
+ "sh",
+ "shuf",
+ "sleep",
+ "sort",
+ "source",
+ "split",
+ "stat",
+ "tail",
+ "tar",
+ "tee",
+ "test",
+ "time",
+ "timeout",
+ "touch",
+ "tr",
+ "tree",
+ "true",
+ "type",
+ "uname",
+ "unexpand",
+ "uniq",
+ "unset",
+ "unzip",
+ "watch",
+ "wc",
+ "wget",
+ "whereis",
+ "which",
+ "whoami",
+ "xargs",
+ "yes",
+ "yq",
+ "zip",
+ "zsh"
+ ],
+ "stack_commands": [
+ "dotnet",
+ "msbuild",
+ "node",
+ "npm",
+ "npx",
+ "nuget",
+ "ts-node",
+ "tsc",
+ "tsx"
+ ],
+ "script_commands": [],
+ "custom_commands": [],
+ "detected_stack": {
+ "languages": [
+ "javascript",
+ "typescript",
+ "csharp"
+ ],
+ "package_managers": [],
+ "frameworks": [],
+ "databases": [],
+ "infrastructure": [],
+ "cloud_providers": [],
+ "code_quality_tools": [],
+ "version_managers": []
+ },
+ "custom_scripts": {
+ "npm_scripts": [],
+ "make_targets": [],
+ "poetry_scripts": [],
+ "cargo_aliases": [],
+ "shell_scripts": []
+ },
+ "project_dir": "/Users/phmatray/Repositories/github-atyp/VirtualFileSystem",
+ "created_at": "2026-01-23T16:06:29.737577",
+ "project_hash": "7416031e5a474c1bcba54ef0d9d353ac",
+ "inherited_from": "/Users/phmatray/Repositories/github-atyp/VirtualFileSystem"
+}
\ No newline at end of file
diff --git a/.auto-claude-status b/.auto-claude-status
new file mode 100644
index 0000000..12e48fb
--- /dev/null
+++ b/.auto-claude-status
@@ -0,0 +1,25 @@
+{
+ "active": true,
+ "spec": "003-fix-rename-directory-operation",
+ "state": "building",
+ "subtasks": {
+ "completed": 9,
+ "total": 10,
+ "in_progress": 1,
+ "failed": 0
+ },
+ "phase": {
+ "current": "Integration Verification",
+ "id": null,
+ "total": 2
+ },
+ "workers": {
+ "active": 0,
+ "max": 1
+ },
+ "session": {
+ "number": 11,
+ "started_at": "2026-01-23T16:17:25.470510"
+ },
+ "last_update": "2026-01-23T16:49:14.315697"
+}
\ No newline at end of file
diff --git a/.claude_settings.json b/.claude_settings.json
new file mode 100644
index 0000000..7828bbe
--- /dev/null
+++ b/.claude_settings.json
@@ -0,0 +1,39 @@
+{
+ "sandbox": {
+ "enabled": true,
+ "autoAllowBashIfSandboxed": true
+ },
+ "permissions": {
+ "defaultMode": "acceptEdits",
+ "allow": [
+ "Read(./**)",
+ "Write(./**)",
+ "Edit(./**)",
+ "Glob(./**)",
+ "Grep(./**)",
+ "Read(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/worktrees/tasks/003-fix-rename-directory-operation/**)",
+ "Write(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/worktrees/tasks/003-fix-rename-directory-operation/**)",
+ "Edit(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/worktrees/tasks/003-fix-rename-directory-operation/**)",
+ "Glob(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/worktrees/tasks/003-fix-rename-directory-operation/**)",
+ "Grep(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/worktrees/tasks/003-fix-rename-directory-operation/**)",
+ "Read(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/worktrees/tasks/003-fix-rename-directory-operation/.auto-claude/specs/003-fix-rename-directory-operation/**)",
+ "Write(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/worktrees/tasks/003-fix-rename-directory-operation/.auto-claude/specs/003-fix-rename-directory-operation/**)",
+ "Edit(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/worktrees/tasks/003-fix-rename-directory-operation/.auto-claude/specs/003-fix-rename-directory-operation/**)",
+ "Read(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/**)",
+ "Write(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/**)",
+ "Edit(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/**)",
+ "Glob(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/**)",
+ "Grep(/Users/phmatray/Repositories/github-atyp/VirtualFileSystem/.auto-claude/**)",
+ "Bash(*)",
+ "WebFetch(*)",
+ "WebSearch(*)",
+ "mcp__context7__resolve-library-id(*)",
+ "mcp__context7__get-library-docs(*)",
+ "mcp__graphiti-memory__search_nodes(*)",
+ "mcp__graphiti-memory__search_facts(*)",
+ "mcp__graphiti-memory__add_episode(*)",
+ "mcp__graphiti-memory__get_episodes(*)",
+ "mcp__graphiti-memory__get_entity_edge(*)"
+ ]
+ }
+}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index dfcfd56..64e2fd6 100644
--- a/.gitignore
+++ b/.gitignore
@@ -348,3 +348,6 @@ MigrationBackup/
# Ionide (cross platform F# VS Code tools) working folder
.ionide/
+
+# Auto Claude data directory
+.auto-claude/
diff --git a/docs/api/VFSDirectoryRenamedArgs.NewName.md b/docs/api/VFSDirectoryRenamedArgs.NewName.md
index 3622fd0..efa09c2 100644
--- a/docs/api/VFSDirectoryRenamedArgs.NewName.md
+++ b/docs/api/VFSDirectoryRenamedArgs.NewName.md
@@ -3,7 +3,7 @@
## VFSDirectoryRenamedArgs\.NewName Property
-Gets the new name of the renamed file\.
+Gets the new name of the renamed directory\.
```csharp
public string NewName { get; }
diff --git a/docs/api/VFSDirectoryRenamedArgs.NewPath.md b/docs/api/VFSDirectoryRenamedArgs.NewPath.md
new file mode 100644
index 0000000..610b6f0
--- /dev/null
+++ b/docs/api/VFSDirectoryRenamedArgs.NewPath.md
@@ -0,0 +1,13 @@
+#### [Atypical\.VirtualFileSystem\.Core](VirtualFileSystem.md 'VirtualFileSystem')
+### [Atypical\.VirtualFileSystem\.Core](VirtualFileSystem.md#Atypical.VirtualFileSystem.Core 'Atypical\.VirtualFileSystem\.Core').[VFSDirectoryRenamedArgs](VFSDirectoryRenamedArgs.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs')
+
+## VFSDirectoryRenamedArgs\.NewPath Property
+
+Gets the new path of the renamed directory\.
+
+```csharp
+public Atypical.VirtualFileSystem.Core.VFSDirectoryPath NewPath { get; }
+```
+
+#### Property Value
+[VFSDirectoryPath](VFSDirectoryPath.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath')
\ No newline at end of file
diff --git a/docs/api/VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string).md b/docs/api/VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string,VFSDirectoryPath).md
similarity index 61%
rename from docs/api/VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string).md
rename to docs/api/VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string,VFSDirectoryPath).md
index 2a10f73..ea3cb87 100644
--- a/docs/api/VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string).md
+++ b/docs/api/VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string,VFSDirectoryPath).md
@@ -1,29 +1,35 @@
#### [Atypical\.VirtualFileSystem\.Core](VirtualFileSystem.md 'VirtualFileSystem')
### [Atypical\.VirtualFileSystem\.Core](VirtualFileSystem.md#Atypical.VirtualFileSystem.Core 'Atypical\.VirtualFileSystem\.Core').[VFSDirectoryRenamedArgs](VFSDirectoryRenamedArgs.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs')
-## VFSDirectoryRenamedArgs\(VFSDirectoryPath, string, string\) Constructor
+## VFSDirectoryRenamedArgs\(VFSDirectoryPath, string, string, VFSDirectoryPath\) Constructor
Initializes a new instance of the [VFSDirectoryRenamedArgs](VFSDirectoryRenamedArgs.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs') class\.
```csharp
-public VFSDirectoryRenamedArgs(Atypical.VirtualFileSystem.Core.VFSDirectoryPath path, string oldName, string newName);
+public VFSDirectoryRenamedArgs(Atypical.VirtualFileSystem.Core.VFSDirectoryPath path, string oldName, string newName, Atypical.VirtualFileSystem.Core.VFSDirectoryPath newPath);
```
#### Parameters
-
+
`path` [VFSDirectoryPath](VFSDirectoryPath.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath')
The path of the renamed directory\.
-
+
`oldName` [System\.String](https://learn.microsoft.com/en-us/dotnet/api/system.string 'System\.String')
The old name of the renamed directory\.
-
+
`newName` [System\.String](https://learn.microsoft.com/en-us/dotnet/api/system.string 'System\.String')
-The new name of the renamed directory\.
\ No newline at end of file
+The new name of the renamed directory\.
+
+
+
+`newPath` [VFSDirectoryPath](VFSDirectoryPath.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath')
+
+The new path of the renamed directory\.
\ No newline at end of file
diff --git a/docs/api/VFSDirectoryRenamedArgs.md b/docs/api/VFSDirectoryRenamedArgs.md
index 1779aa8..20f8448 100644
--- a/docs/api/VFSDirectoryRenamedArgs.md
+++ b/docs/api/VFSDirectoryRenamedArgs.md
@@ -13,14 +13,15 @@ Inheritance [System\.Object](https://learn.microsoft.com/en-us/dotnet/api/system
| Constructors | |
| :--- | :--- |
-| [VFSDirectoryRenamedArgs\(VFSDirectoryPath, string, string\)](VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string).md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.VFSDirectoryRenamedArgs\(Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath, string, string\)') | Initializes a new instance of the [VFSDirectoryRenamedArgs](VFSDirectoryRenamedArgs.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs') class\. |
+| [VFSDirectoryRenamedArgs\(VFSDirectoryPath, string, string, VFSDirectoryPath\)](VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string,VFSDirectoryPath).md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.VFSDirectoryRenamedArgs\(Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath, string, string, Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath\)') | Initializes a new instance of the [VFSDirectoryRenamedArgs](VFSDirectoryRenamedArgs.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs') class\. |
| Properties | |
| :--- | :--- |
| [Message](VFSDirectoryRenamedArgs.Message.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.Message') | Gets the message\. |
| [MessageTemplate](VFSDirectoryRenamedArgs.MessageTemplate.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.MessageTemplate') | Gets the message template\. |
| [MessageWithMarkup](VFSDirectoryRenamedArgs.MessageWithMarkup.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.MessageWithMarkup') | Gets the message with markup\. |
-| [NewName](VFSDirectoryRenamedArgs.NewName.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.NewName') | Gets the new name of the renamed file\. |
+| [NewName](VFSDirectoryRenamedArgs.NewName.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.NewName') | Gets the new name of the renamed directory\. |
+| [NewPath](VFSDirectoryRenamedArgs.NewPath.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.NewPath') | Gets the new path of the renamed directory\. |
| [OldName](VFSDirectoryRenamedArgs.OldName.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.OldName') | Gets the old name of the renamed directory\. |
| [Path](VFSDirectoryRenamedArgs.Path.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.Path') | Gets the old path of the renamed directory\. |
| [Timestamp](VFSDirectoryRenamedArgs.Timestamp.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.Timestamp') | Gets the timestamp when the directory was renamed\. |
diff --git a/docs/api/VirtualFileSystem.md b/docs/api/VirtualFileSystem.md
index 8137b49..71ab475 100644
--- a/docs/api/VirtualFileSystem.md
+++ b/docs/api/VirtualFileSystem.md
@@ -149,11 +149,12 @@
- **[implicit operator VFSDirectoryPath\(string\)](VFSDirectoryPath.implicitoperatorVFSDirectoryPath(string).md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath\.op\_Implicit Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath\(string\)')** `Operator` Implicit conversion from string\. This allows you to use a string as a [VFSDirectoryPath](VFSDirectoryPath.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath')\.
- **[implicit operator string\(VFSDirectoryPath\)](VFSDirectoryPath.implicitoperatorstring(VFSDirectoryPath).md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath\.op\_Implicit string\(Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath\)')** `Operator` Implicit conversion to string This allows you to use a [VFSDirectoryPath](VFSDirectoryPath.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath') as a string\.
- **[VFSDirectoryRenamedArgs](VFSDirectoryRenamedArgs.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs')** `Class` Provides data for the DirectoryRenamed event\.
- - **[VFSDirectoryRenamedArgs\(VFSDirectoryPath, string, string\)](VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string).md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.VFSDirectoryRenamedArgs\(Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath, string, string\)')** `Constructor` Initializes a new instance of the [VFSDirectoryRenamedArgs](VFSDirectoryRenamedArgs.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs') class\.
+ - **[VFSDirectoryRenamedArgs\(VFSDirectoryPath, string, string, VFSDirectoryPath\)](VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string,VFSDirectoryPath).md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.VFSDirectoryRenamedArgs\(Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath, string, string, Atypical\.VirtualFileSystem\.Core\.VFSDirectoryPath\)')** `Constructor` Initializes a new instance of the [VFSDirectoryRenamedArgs](VFSDirectoryRenamedArgs.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs') class\.
- **[Message](VFSDirectoryRenamedArgs.Message.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.Message')** `Property` Gets the message\.
- **[MessageTemplate](VFSDirectoryRenamedArgs.MessageTemplate.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.MessageTemplate')** `Property` Gets the message template\.
- **[MessageWithMarkup](VFSDirectoryRenamedArgs.MessageWithMarkup.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.MessageWithMarkup')** `Property` Gets the message with markup\.
- - **[NewName](VFSDirectoryRenamedArgs.NewName.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.NewName')** `Property` Gets the new name of the renamed file\.
+ - **[NewName](VFSDirectoryRenamedArgs.NewName.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.NewName')** `Property` Gets the new name of the renamed directory\.
+ - **[NewPath](VFSDirectoryRenamedArgs.NewPath.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.NewPath')** `Property` Gets the new path of the renamed directory\.
- **[OldName](VFSDirectoryRenamedArgs.OldName.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.OldName')** `Property` Gets the old name of the renamed directory\.
- **[Path](VFSDirectoryRenamedArgs.Path.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.Path')** `Property` Gets the old path of the renamed directory\.
- **[Timestamp](VFSDirectoryRenamedArgs.Timestamp.md 'Atypical\.VirtualFileSystem\.Core\.VFSDirectoryRenamedArgs\.Timestamp')** `Property` Gets the timestamp when the directory was renamed\.
diff --git a/docs/links b/docs/links
index c8dd6ba..5cde5cc 100644
--- a/docs/links
+++ b/docs/links
@@ -251,11 +251,12 @@ T:Atypical.VirtualFileSystem.Core.VFSFileMovedArgs|VFSFileMovedArgs.md|VFSFileMo
P:Atypical.VirtualFileSystem.Core.VFSDirectoryRenamedArgs.Path|VFSDirectoryRenamedArgs.Path.md|Path
P:Atypical.VirtualFileSystem.Core.VFSDirectoryRenamedArgs.OldName|VFSDirectoryRenamedArgs.OldName.md|OldName
P:Atypical.VirtualFileSystem.Core.VFSDirectoryRenamedArgs.NewName|VFSDirectoryRenamedArgs.NewName.md|NewName
+P:Atypical.VirtualFileSystem.Core.VFSDirectoryRenamedArgs.NewPath|VFSDirectoryRenamedArgs.NewPath.md|NewPath
P:Atypical.VirtualFileSystem.Core.VFSDirectoryRenamedArgs.Timestamp|VFSDirectoryRenamedArgs.Timestamp.md|Timestamp
P:Atypical.VirtualFileSystem.Core.VFSDirectoryRenamedArgs.MessageTemplate|VFSDirectoryRenamedArgs.MessageTemplate.md|MessageTemplate
P:Atypical.VirtualFileSystem.Core.VFSDirectoryRenamedArgs.Message|VFSDirectoryRenamedArgs.Message.md|Message
P:Atypical.VirtualFileSystem.Core.VFSDirectoryRenamedArgs.MessageWithMarkup|VFSDirectoryRenamedArgs.MessageWithMarkup.md|MessageWithMarkup
-M:Atypical.VirtualFileSystem.Core.VFSDirectoryRenamedArgs.#ctor(Atypical.VirtualFileSystem.Core.VFSDirectoryPath,System.String,System.String)|VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string).md|VFSDirectoryRenamedArgs(VFSDirectoryPath, string, string)
+M:Atypical.VirtualFileSystem.Core.VFSDirectoryRenamedArgs.#ctor(Atypical.VirtualFileSystem.Core.VFSDirectoryPath,System.String,System.String,Atypical.VirtualFileSystem.Core.VFSDirectoryPath)|VFSDirectoryRenamedArgs.VFSDirectoryRenamedArgs(VFSDirectoryPath,string,string,VFSDirectoryPath).md|VFSDirectoryRenamedArgs(VFSDirectoryPath, string, string, VFSDirectoryPath)
T:Atypical.VirtualFileSystem.Core.VFSDirectoryRenamedArgs|VFSDirectoryRenamedArgs.md|VFSDirectoryRenamedArgs
P:Atypical.VirtualFileSystem.Core.VFSFileRenamedArgs.Path|VFSFileRenamedArgs.Path.md|Path
P:Atypical.VirtualFileSystem.Core.VFSFileRenamedArgs.OldName|VFSFileRenamedArgs.OldName.md|OldName
diff --git a/src/Atypical.VirtualFileSystem.Core/SystemOperations/Commands/Rename/VFS.Rename.cs b/src/Atypical.VirtualFileSystem.Core/SystemOperations/Commands/Rename/VFS.Rename.cs
index 59f6b52..d981fea 100644
--- a/src/Atypical.VirtualFileSystem.Core/SystemOperations/Commands/Rename/VFS.Rename.cs
+++ b/src/Atypical.VirtualFileSystem.Core/SystemOperations/Commands/Rename/VFS.Rename.cs
@@ -37,6 +37,11 @@ public IVirtualFileSystem RenameDirectory(
// update the directory node with the new path
var oldName = directoryNode.Name;
var newPath = new VFSDirectoryPath($"{directoryPath.Parent}/{newName}");
+
+ // Validate that the destination path doesn't already exist
+ if (Index.ContainsKey(newPath))
+ ThrowVirtualNodeAlreadyExists(newPath);
+
var updatedDirectoryNode = directoryNode.UpdatePath(newPath);
// Add the directory to its old parent directory with the new name
@@ -93,7 +98,7 @@ public IVirtualFileSystem RenameDirectory(
}
}
- DirectoryRenamed?.Invoke(new VFSDirectoryRenamedArgs(directoryPath, oldName, newName));
+ DirectoryRenamed?.Invoke(new VFSDirectoryRenamedArgs(directoryPath, oldName, newName, newPath));
return this;
}
diff --git a/src/Atypical.VirtualFileSystem.Core/SystemOperations/Commands/Rename/VFSDirectoryRenamedArgs.cs b/src/Atypical.VirtualFileSystem.Core/SystemOperations/Commands/Rename/VFSDirectoryRenamedArgs.cs
index c446620..0d50684 100644
--- a/src/Atypical.VirtualFileSystem.Core/SystemOperations/Commands/Rename/VFSDirectoryRenamedArgs.cs
+++ b/src/Atypical.VirtualFileSystem.Core/SystemOperations/Commands/Rename/VFSDirectoryRenamedArgs.cs
@@ -17,11 +17,13 @@ public sealed class VFSDirectoryRenamedArgs : VFSEventArgs
/// The path of the renamed directory.
/// The old name of the renamed directory.
/// The new name of the renamed directory.
- public VFSDirectoryRenamedArgs(VFSDirectoryPath path, string oldName, string newName)
+ /// The new path of the renamed directory.
+ public VFSDirectoryRenamedArgs(VFSDirectoryPath path, string oldName, string newName, VFSDirectoryPath newPath)
{
Path = path;
OldName = oldName;
NewName = newName;
+ NewPath = newPath;
Timestamp = DateTimeOffset.Now;
}
@@ -36,10 +38,15 @@ public VFSDirectoryRenamedArgs(VFSDirectoryPath path, string oldName, string new
public string OldName { get; }
///
- /// Gets the new name of the renamed file.
+ /// Gets the new name of the renamed directory.
///
public string NewName { get; }
+ ///
+ /// Gets the new path of the renamed directory.
+ ///
+ public VFSDirectoryPath NewPath { get; }
+
///
/// Gets the timestamp when the directory was renamed.
///
@@ -51,11 +58,11 @@ public override string MessageTemplate
///
public override string Message
- => string.Format(MessageTemplate, Path, NewName, Timestamp);
+ => string.Format(MessageTemplate, Path, NewPath, Timestamp);
///
public override string MessageWithMarkup
- => ToMarkup("blue", Path, NewName, Timestamp);
+ => ToMarkup("blue", Path, NewPath, Timestamp);
///
public override string ToString()
diff --git a/src/Atypical.VirtualFileSystem.Core/UndoRedo/ChangeHistory.cs b/src/Atypical.VirtualFileSystem.Core/UndoRedo/ChangeHistory.cs
index 06bfd77..32281c2 100644
--- a/src/Atypical.VirtualFileSystem.Core/UndoRedo/ChangeHistory.cs
+++ b/src/Atypical.VirtualFileSystem.Core/UndoRedo/ChangeHistory.cs
@@ -122,7 +122,7 @@ public IVirtualFileSystem Undo()
_vfs.MoveDirectory(directoryMoved.DestinationPath, directoryMoved.SourcePath);
break;
case VFSDirectoryRenamedArgs directoryRenamed:
- _vfs.RenameDirectory(directoryRenamed.Path, directoryRenamed.OldName);
+ _vfs.RenameDirectory(directoryRenamed.NewPath, directoryRenamed.OldName);
break;
case VFSFileCreatedArgs fileCreated:
_vfs.DeleteFile(fileCreated.Path);
diff --git a/src/Atypical.VirtualFileSystem.DemoCli/Commands/DemonstrateVFS.cs b/src/Atypical.VirtualFileSystem.DemoCli/Commands/DemonstrateVFS.cs
index e167e72..1b34469 100644
--- a/src/Atypical.VirtualFileSystem.DemoCli/Commands/DemonstrateVFS.cs
+++ b/src/Atypical.VirtualFileSystem.DemoCli/Commands/DemonstrateVFS.cs
@@ -62,9 +62,8 @@ public override int Execute(CommandContext context, CancellationToken cancellati
() => vfs.MoveDirectory(new VFSDirectoryPath("/heroes"), new VFSDirectoryPath("/avengers")));
// Rename a directory
- // TODO: fix rename directory
- // ProcessStep(vfs, "RENAME DIRECTORY",
- // () => vfs.RenameDirectory(new VFSDirectoryPath("/avengers"), new VFSDirectoryPath("/heroes")));
+ ProcessStep(vfs, "RENAME DIRECTORY",
+ () => vfs.RenameDirectory(new VFSDirectoryPath("/avengers"), "heroes"));
return 0;
}
diff --git a/tests/Atypical.VirtualFileSystem.UnitTests/SystemOperations/Commands/VirtualFileSystem_MethodRenameDirectory_Tests.cs b/tests/Atypical.VirtualFileSystem.UnitTests/SystemOperations/Commands/VirtualFileSystem_MethodRenameDirectory_Tests.cs
index 2c488f4..d99c158 100644
--- a/tests/Atypical.VirtualFileSystem.UnitTests/SystemOperations/Commands/VirtualFileSystem_MethodRenameDirectory_Tests.cs
+++ b/tests/Atypical.VirtualFileSystem.UnitTests/SystemOperations/Commands/VirtualFileSystem_MethodRenameDirectory_Tests.cs
@@ -80,7 +80,7 @@ public void RenameDirectory_raises_a_DirectoryRenamed_event()
_vfs.CreateDirectory(_directoryPath);
var eventRaised = false;
- _vfs.DirectoryRenamed += args =>
+ _vfs.DirectoryRenamed += args =>
{
eventRaised = true;
args.Path.Should().Be(_directoryPath);
@@ -93,7 +93,31 @@ public void RenameDirectory_raises_a_DirectoryRenamed_event()
// Assert
eventRaised.Should().BeTrue();
}
-
+
+ [Fact]
+ public void RenameDirectory_event_args_contain_correct_paths()
+ {
+ // Arrange
+ _vfs.CreateDirectory(_directoryPath);
+ VFSDirectoryRenamedArgs? capturedArgs = null;
+
+ _vfs.DirectoryRenamed += args =>
+ {
+ capturedArgs = args;
+ };
+
+ // Act
+ Act();
+
+ // Assert
+ capturedArgs.Should().NotBeNull();
+ capturedArgs!.Path.Should().Be(new VFSDirectoryPath("vfs://dir1/dir2/dir3"));
+ capturedArgs.OldName.Should().Be("dir3");
+ capturedArgs.NewName.Should().Be("new_dir");
+ capturedArgs.NewPath.Should().Be(new VFSDirectoryPath("vfs://dir1/dir2/new_dir"));
+ capturedArgs.Timestamp.Should().BeCloseTo(DateTimeOffset.Now, TimeSpan.FromSeconds(1));
+ }
+
[Fact]
public void RenameDirectory_adds_a_change_to_the_ChangeHistory()
{
@@ -105,10 +129,184 @@ public void RenameDirectory_adds_a_change_to_the_ChangeHistory()
// Retrieve the change from the UndoStack
var change = _vfs.ChangeHistory.UndoStack.First();
-
+
// Assert
_vfs.ChangeHistory.UndoStack.Should().ContainEquivalentOf(change);
_vfs.ChangeHistory.UndoStack.Should().HaveCount(4);
_vfs.ChangeHistory.RedoStack.Should().BeEmpty();
}
+
+ [Fact]
+ public void RenameDirectory_can_be_undone_and_redone()
+ {
+ // Arrange
+ _vfs.CreateDirectory(_directoryPath);
+ var originalPath = _directoryPath;
+ var newPath = new VFSDirectoryPath("dir1/dir2/new_dir");
+
+ // Act - Rename directory
+ Act();
+
+ // Assert - Directory should be renamed
+ _vfs.Index.RawIndex.Should().NotContainKey(originalPath);
+ _vfs.Index.RawIndex.Should().ContainKey(newPath);
+
+ // Act - Undo rename
+ _vfs.ChangeHistory.Undo();
+
+ // Assert - Directory should be back to original name
+ _vfs.Index.RawIndex.Should().ContainKey(originalPath);
+ _vfs.Index.RawIndex.Should().NotContainKey(newPath);
+
+ // Act - Redo rename
+ _vfs.ChangeHistory.Redo();
+
+ // Assert - Directory should be renamed again
+ _vfs.Index.RawIndex.Should().NotContainKey(originalPath);
+ _vfs.Index.RawIndex.Should().ContainKey(newPath);
+ }
+
+ [Fact]
+ public void RenameDirectory_updates_nested_file_paths()
+ {
+ // Arrange
+ _vfs.CreateDirectory(_directoryPath);
+ var nestedFilePath = new VFSFilePath("dir1/dir2/dir3/file.txt");
+ var deepNestedFilePath = new VFSFilePath("dir1/dir2/dir3/subdir/nested.txt");
+ _vfs.CreateFile(nestedFilePath, "content1");
+ _vfs.CreateDirectory(new VFSDirectoryPath("dir1/dir2/dir3/subdir"));
+ _vfs.CreateFile(deepNestedFilePath, "content2");
+ var indexLength = _vfs.Index.Count;
+
+ // Act
+ Act();
+
+ // Assert
+ _vfs.Index.Count.Should().Be(indexLength);
+
+ // Verify old paths no longer exist
+ _vfs.Index.RawIndex.Should().NotContainKey(nestedFilePath);
+ _vfs.Index.RawIndex.Should().NotContainKey(deepNestedFilePath);
+
+ // Verify new paths exist
+ var newNestedFilePath = new VFSFilePath("dir1/dir2/new_dir/file.txt");
+ var newDeepNestedFilePath = new VFSFilePath("dir1/dir2/new_dir/subdir/nested.txt");
+ _vfs.Index.RawIndex.Should().ContainKey(newNestedFilePath);
+ _vfs.Index.RawIndex.Should().ContainKey(newDeepNestedFilePath);
+
+ // Verify file contents are preserved
+ _vfs.Index[newNestedFilePath].As().Content.Should().Be("content1");
+ _vfs.Index[newDeepNestedFilePath].As().Content.Should().Be("content2");
+
+ // Verify file paths are updated
+ _vfs.Index[newNestedFilePath].Path.Value.Should().Be("vfs://dir1/dir2/new_dir/file.txt");
+ _vfs.Index[newDeepNestedFilePath].Path.Value.Should().Be("vfs://dir1/dir2/new_dir/subdir/nested.txt");
+ }
+
+ [Fact]
+ public void RenameDirectory_handles_deeply_nested_structure()
+ {
+ // Arrange - Create deeply nested structure: /a/b/c/d/e with files at each level
+ var deepPath = new VFSDirectoryPath("a/b/c/d/e");
+ _vfs.CreateDirectory(deepPath);
+
+ // Create files at various levels
+ var fileAtB = new VFSFilePath("a/b/file_b.txt");
+ var fileAtC = new VFSFilePath("a/b/c/file_c.txt");
+ var fileAtD = new VFSFilePath("a/b/c/d/file_d.txt");
+ var fileAtE = new VFSFilePath("a/b/c/d/e/file_e.txt");
+
+ _vfs.CreateFile(fileAtB, "content_b");
+ _vfs.CreateFile(fileAtC, "content_c");
+ _vfs.CreateFile(fileAtD, "content_d");
+ _vfs.CreateFile(fileAtE, "content_e");
+
+ // Create additional subdirectories at different levels
+ var subdirAtC = new VFSDirectoryPath("a/b/c/subdir_c");
+ var subdirAtE = new VFSDirectoryPath("a/b/c/d/e/subdir_e");
+ _vfs.CreateDirectory(subdirAtC);
+ _vfs.CreateDirectory(subdirAtE);
+
+ // Create files in subdirectories
+ var fileInSubdirC = new VFSFilePath("a/b/c/subdir_c/nested.txt");
+ var fileInSubdirE = new VFSFilePath("a/b/c/d/e/subdir_e/deep.txt");
+ _vfs.CreateFile(fileInSubdirC, "nested_content");
+ _vfs.CreateFile(fileInSubdirE, "deep_content");
+
+ var indexLength = _vfs.Index.Count;
+
+ // Act - Rename directory 'c' to 'renamed_c'
+ var pathToRename = new VFSDirectoryPath("a/b/c");
+ _vfs.RenameDirectory(pathToRename, "renamed_c");
+
+ // Assert
+ _vfs.Index.Count.Should().Be(indexLength);
+
+ // Verify old paths no longer exist
+ _vfs.Index.RawIndex.Should().NotContainKey(new VFSDirectoryPath("vfs://a/b/c"));
+ _vfs.Index.RawIndex.Should().NotContainKey(fileAtC);
+ _vfs.Index.RawIndex.Should().NotContainKey(fileAtD);
+ _vfs.Index.RawIndex.Should().NotContainKey(fileAtE);
+ _vfs.Index.RawIndex.Should().NotContainKey(subdirAtC);
+ _vfs.Index.RawIndex.Should().NotContainKey(subdirAtE);
+ _vfs.Index.RawIndex.Should().NotContainKey(fileInSubdirC);
+ _vfs.Index.RawIndex.Should().NotContainKey(fileInSubdirE);
+
+ // Verify new paths exist
+ var newFileAtC = new VFSFilePath("a/b/renamed_c/file_c.txt");
+ var newFileAtD = new VFSFilePath("a/b/renamed_c/d/file_d.txt");
+ var newFileAtE = new VFSFilePath("a/b/renamed_c/d/e/file_e.txt");
+ var newSubdirAtC = new VFSDirectoryPath("a/b/renamed_c/subdir_c");
+ var newSubdirAtE = new VFSDirectoryPath("a/b/renamed_c/d/e/subdir_e");
+ var newFileInSubdirC = new VFSFilePath("a/b/renamed_c/subdir_c/nested.txt");
+ var newFileInSubdirE = new VFSFilePath("a/b/renamed_c/d/e/subdir_e/deep.txt");
+
+ _vfs.Index.RawIndex.Should().ContainKey(new VFSDirectoryPath("vfs://a/b/renamed_c"));
+ _vfs.Index.RawIndex.Should().ContainKey(newFileAtC);
+ _vfs.Index.RawIndex.Should().ContainKey(newFileAtD);
+ _vfs.Index.RawIndex.Should().ContainKey(newFileAtE);
+ _vfs.Index.RawIndex.Should().ContainKey(newSubdirAtC);
+ _vfs.Index.RawIndex.Should().ContainKey(newSubdirAtE);
+ _vfs.Index.RawIndex.Should().ContainKey(newFileInSubdirC);
+ _vfs.Index.RawIndex.Should().ContainKey(newFileInSubdirE);
+
+ // Verify file contents are preserved
+ _vfs.Index[fileAtB].As().Content.Should().Be("content_b"); // Not affected by rename
+ _vfs.Index[newFileAtC].As().Content.Should().Be("content_c");
+ _vfs.Index[newFileAtD].As().Content.Should().Be("content_d");
+ _vfs.Index[newFileAtE].As().Content.Should().Be("content_e");
+ _vfs.Index[newFileInSubdirC].As().Content.Should().Be("nested_content");
+ _vfs.Index[newFileInSubdirE].As().Content.Should().Be("deep_content");
+
+ // Verify file paths are correctly updated
+ _vfs.Index[newFileAtC].Path.Value.Should().Be("vfs://a/b/renamed_c/file_c.txt");
+ _vfs.Index[newFileAtD].Path.Value.Should().Be("vfs://a/b/renamed_c/d/file_d.txt");
+ _vfs.Index[newFileAtE].Path.Value.Should().Be("vfs://a/b/renamed_c/d/e/file_e.txt");
+ _vfs.Index[newFileInSubdirC].Path.Value.Should().Be("vfs://a/b/renamed_c/subdir_c/nested.txt");
+ _vfs.Index[newFileInSubdirE].Path.Value.Should().Be("vfs://a/b/renamed_c/d/e/subdir_e/deep.txt");
+
+ // Verify directory paths are correctly updated
+ _vfs.Index[new VFSDirectoryPath("vfs://a/b/renamed_c")].Path.Value.Should().Be("vfs://a/b/renamed_c");
+ _vfs.Index[newSubdirAtC].Path.Value.Should().Be("vfs://a/b/renamed_c/subdir_c");
+ _vfs.Index[newSubdirAtE].Path.Value.Should().Be("vfs://a/b/renamed_c/d/e/subdir_e");
+ }
+
+ [Fact]
+ public void RenameDirectory_throws_exception_when_target_name_already_exists()
+ {
+ // Arrange
+ _vfs.CreateDirectory(new VFSDirectoryPath("dir1/dir2/dir3"));
+ _vfs.CreateDirectory(new VFSDirectoryPath("dir1/dir2/existing"));
+
+ // Act
+ Action action = () => _vfs.RenameDirectory(
+ new VFSDirectoryPath("dir1/dir2/dir3"),
+ "existing"
+ );
+
+ // Assert
+ action.Should()
+ .Throw()
+ .WithMessage("The node 'vfs://dir1/dir2/existing' already exists in the index.");
+ }
}
\ No newline at end of file