Finally solve the 50-year Unix limitation: when you navigate directories in a CLI application, inherit that final location in your shell when the app exits.
You're in /home/user
and run a file manager, editor, or navigation tool. You browse to /home/user/projects/awesome-project/src/components
and then exit the application. You're back in /home/user
. You've lost your final location.
This happens with vim, ranger, fzf, and countless other CLI tools. It's been a limitation since the dawn of Unix.
AutoCD Go is a simple library that lets your CLI application "inherit" its final directory to the parent shell. Add one line to your application's exit handler:
autocd.ExitWithDirectory("/path/to/final/directory")
When your app exits, the user gets a shell in that directory. Finally.
go get github.com/codinganovel/autocd-go
package main
import (
"github.com/codinganovel/autocd-go"
"os"
)
func main() {
// Your application logic here
// User navigates around, final location is in currentDir
currentDir := "/path/to/final/directory"
// When ready to exit, inherit the directory
if err := autocd.ExitWithDirectory(currentDir); err != nil {
// Fallback to normal exit on any error
os.Exit(1)
}
// This line never executes on success
}
package main
import (
"flag"
"os"
"github.com/codinganovel/autocd-go"
)
func main() {
var enableAutoCD = flag.Bool("autocd", false, "Change to directory on exit")
flag.Parse()
app := NewMyApp()
app.Run()
// Only use autocd if requested and directory changed
if *enableAutoCD && app.FinalDirectory() != app.StartDirectory() {
autocd.ExitWithDirectory(app.FinalDirectory())
}
os.Exit(0)
}
The library uses a clever but simple approach:
- Creates a POSIX shell script that changes to your target directory
- Replaces the current process with
/bin/sh
executing this script viasyscall.Exec
- The script changes directory and then replaces itself with the user's shell
- User gets their preferred shell in the final directory
From the user's perspective, they just run your app and end up where they navigated to.
For more control, use the advanced function:
opts := &autocd.Options{
SecurityLevel: autocd.SecurityStrict, // Extra path validation
DebugMode: true, // Verbose logging
Shell: "zsh", // Force specific shell
TempDir: "/custom/temp", // Custom temp directory (also cleaned)
}
err := autocd.ExitWithDirectoryAdvanced("/target/path", opts)
AutoCD automatically warns when you have many nested shells from navigation:
💡 Tip: You have 15 nested shells from navigation.
For better performance, consider opening a fresh terminal.
This feature uses the SHLVL
environment variable to detect shell nesting depth. Disable warnings if needed:
opts := &autocd.Options{DisableDepthWarnings: true}
If you want to guarantee your process exits one way or another:
autocd.ExitWithDirectoryOrFallback("/target/path", func() {
fmt.Println("AutoCD failed, exiting normally")
os.Exit(0)
})
// Never returns
- Linux - bash, zsh, fish, dash, sh
- macOS - bash, zsh, fish, dash, sh
- BSD - sh, bash, zsh
The library automatically detects your shell from the SHELL
environment variable, with automatic fallback to /bin/sh
if the shell is invalid or missing.
Note: AutoCD Go is now focused on Unix-like systems (Linux, macOS, BSD). Windows support has been removed to simplify the architecture and focus on the core Unix use case where directory inheritance is most valuable.
AutoCD includes built-in security features:
- Path validation ensures directories exist and are accessible (execute permission required)
- Shell injection protection using single-quote escaping for all paths and shell commands
- Configurable security levels from permissive to strict
- Automatic cleanup of temporary scripts via periodic removal on subsequent runs
Choose your security level:
SecurityNormal
(default) - Path validation, null byte check, directory verificationSecurityStrict
- Character whitelist, length limits, comprehensive validationSecurityPermissive
- Minimal validation when you handle security yourself
The library never crashes your application. On any error, it returns an error and your app can fallback to normal exit:
if err := autocd.ExitWithDirectory("/path"); err != nil {
fmt.Fprintf(os.Stderr, "Directory inheritance failed: %v\n", err)
fmt.Fprintf(os.Stderr, "Final directory was: %s\n", "/path")
os.Exit(0) // Normal exit
}
// When user presses 'q' to quit
if fm.autoCDEnabled && fm.currentDir != fm.startDir {
autocd.ExitWithDirectory(fm.currentDir)
}
os.Exit(0)
// After user selects a directory
selectedDir := showDirectoryPicker()
autocd.ExitWithDirectory(selectedDir)
// When user types 'exit'
currentWorkingDir, _ := os.Getwd()
autocd.ExitWithDirectory(currentWorkingDir)
AUTOCD_DEBUG=1
- Enable debug outputSHELL
- Override shell detection
None. AutoCD uses only the Go standard library.
For comprehensive technical documentation, implementation details, and advanced integration patterns, see readme-developers.md.
This library is designed to be simple and robust. The core concept is stable, but we welcome:
- Bug reports and fixes
- Additional shell support
- Platform compatibility improvements
- Documentation improvements
under ☕️, check out the-coffee-license
I've included both licenses with the repo, do what you know is right. The licensing works by assuming you're operating under good faith.
After 50 years, CLI applications can finally tell the shell where they've been. Your users will love you for it.
"It's about time." - Every terminal user ever