Skip to content

Commit 4d1a0c5

Browse files
ibrahimkteishclaude
andcommitted
Make install command non-destructive with merge support
- Remove destructive `removeItem()` call - all installs now merge files - Add support for `--path .` or `--path current` to install into current directory - Add safety check to verify current directory is in expected `.codex/skills` or `.claude/skills` location - Add user confirmation prompt when installing to current directory - Update help text to document current directory option This change ensures users never lose existing work when installing skills, whether using default paths, custom paths, or current directory installation. Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
1 parent 0cb2927 commit 4d1a0c5

File tree

1 file changed

+41
-4
lines changed

1 file changed

+41
-4
lines changed

Sources/pfw/Install.swift

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ struct Install: AsyncParsableCommand {
2727
)
2828
var tool: Tool = .codex
2929

30-
@Option(help: "Directory to install skills into.")
30+
@Option(help: "Directory to install skills into. Use '.' or 'current' for current directory.")
3131
var path: String?
3232

3333
func run() async throws {
@@ -68,9 +68,46 @@ struct Install: AsyncParsableCommand {
6868
let zipURL = URL.temporaryDirectory.appending(path: UUID().uuidString)
6969
try data.write(to: zipURL)
7070

71-
let installURL = URL(fileURLWithPath: path ?? tool.defaultInstallPath.path)
72-
try? FileManager.default.removeItem(at: installURL)
71+
// Determine if installing to current directory
72+
let isCurrentDirectory = path == "." || path == "current"
73+
let installURL: URL
74+
75+
if isCurrentDirectory {
76+
installURL = URL(fileURLWithPath: FileManager.default.currentDirectoryPath)
77+
78+
// Verify the current directory is in the expected location
79+
let currentPath = installURL.path
80+
let expectedPattern = ".\(tool.rawValue)/skills"
81+
82+
guard currentPath.contains(expectedPattern) else {
83+
print("Error: Current directory is not in the expected location.")
84+
print("Expected to be inside: ~/.\(tool.rawValue)/skills/")
85+
print("Current directory: \(currentPath)")
86+
throw ExitCode.failure
87+
}
88+
89+
// Ask for confirmation
90+
print("You are about to install into the current directory:")
91+
print(" \(currentPath)")
92+
print("\nThis will merge new skills with existing ones without removing current files.")
93+
print("Continue? (yes/no): ", terminator: "")
94+
95+
guard let response = readLine()?.lowercased(),
96+
response == "yes" || response == "y" else {
97+
print("Installation cancelled.")
98+
throw ExitCode.success
99+
}
100+
} else {
101+
installURL = URL(fileURLWithPath: path ?? tool.defaultInstallPath.path)
102+
}
103+
104+
// Always merge - never remove existing files
73105
try FileManager.default.unzipItem(at: zipURL, to: installURL)
74-
print("Installed skills for \(tool.rawValue) into \(installURL.path)")
106+
107+
if isCurrentDirectory {
108+
print("Successfully merged skills into \(installURL.path)")
109+
} else {
110+
print("Installed and merged skills for \(tool.rawValue) into \(installURL.path)")
111+
}
75112
}
76113
}

0 commit comments

Comments
 (0)