In the previous chapter, we taught our system what commands are by building a dictionary (Registry).
Now, we face a new problem. Even if the system knows what git is, it doesn't know how you specifically use it.
Every developer has a unique setup. You might have:
alias g='git' or alias ll='ls -la'export API_KEY='secret123'deploy() that runs a complex script.
If our AI agent tries to run g status on your behalf, it will crash with command not found: g. The AI is like a guest entering your house; it doesn't know where the light switches are.
To fix this, we need Shell Environment Snapshotting.
Imagine a photographer entering a room (your terminal) and taking a high-resolution 360ยฐ photo.
Instead of trying to guess your configuration, our system:
.bashrc or .zshrc).Whenever the AI wants to run a command later, it simply says: "Load the snapshot file, then run the command."
We can't simply read your .bashrc file as text. Why? Because config files contain logic!
# .bashrc example
if [ -d "$HOME/bin" ]; then
export PATH="$HOME/bin:$PATH"
fi
A text parser wouldn't know if that if statement is true or false.
The Solution: We must let the shell execute the config, and then ask the shell to report its final state.
Here is how the snapshot process works:
We generate a small shell script dynamically using TypeScript. Let's look at how we handle the differences between shells (like Bash vs Zsh).
First, we need to know where the user keeps their settings.
// From ShellSnapshot.ts
function getConfigFile(shellPath: string): string {
// If using zsh, look for .zshrc
if (shellPath.includes('zsh')) return '.zshrc'
// If using bash, look for .bashrc
if (shellPath.includes('bash')) return '.bashrc'
// Fallback
return '.profile'
}
This is the tricky part. Bash and Zsh use different commands to list defined functions.
In Zsh, we use typeset -f. In Bash, we use declare -f.
// Simplified from ShellSnapshot.ts
function getUserSnapshotContent(configFile: string): string {
const isZsh = configFile.endsWith('.zshrc')
let content = ''
if (isZsh) {
// Zsh specific command to dump functions
content += `typeset -f >> "$SNAPSHOT_FILE"`
} else {
// Bash specific command to dump functions
content += `declare -f >> "$SNAPSHOT_FILE"`
}
return content
}
Explanation: We are building a string of shell commands. When this runs, it appends the definition of every function in your memory to our $SNAPSHOT_FILE.
Next, we want to capture shortcuts like alias g='git'.
// Simplified from ShellSnapshot.ts
// We add this to our content string
content += `
echo "# Aliases" >> "$SNAPSHOT_FILE"
# List aliases, remove 'alias ' prefix, and re-format them
alias | sed 's/^alias //g' | sed 's/^/alias -- /' >> "$SNAPSHOT_FILE"
`
Explanation: The alias command lists aliases. We use sed (stream editor) to format them so they can be easily re-read by another shell later.
Sometimes, we want to give the AI super-powers that the user doesn't have installed. For example, we might want to use a high-performance search tool like ripgrep (rg), even if the user doesn't have it.
We can inject this into the snapshot!
// Simplified from ShellSnapshot.ts
export function createRipgrepShellIntegration() {
const binaryPath = '/path/to/bundled/rg'
// Create an alias inside the snapshot
return {
type: 'alias',
snippet: `alias rg='${binaryPath}'`
}
}
Now, when the AI runs rg, it uses our bundled, high-speed version, but it feels native.
Finally, we run the script we just built. We use Node.js's execFile to run the shell and pass our script.
// Simplified from ShellSnapshot.ts
export const createAndSaveSnapshot = async (binShell: string) => {
// 1. Generate the script text
const script = await getSnapshotScript(binShell, snapshotPath, true)
// 2. Execute the shell with the script
execFile(binShell, ['-c', script], {
env: process.env,
timeout: 10000 // Give it 10 seconds to finish
}, (error) => {
if (!error) console.log("Snapshot created!")
})
}
Once this process is done, we have a file at /tmp/snapshot-123.sh.
Whenever the AI Agent needs to execute a command, it constructs a command line like this:
source /tmp/snapshot-123.sh && g status
Because we source the snapshot first:
g becomes available.deploy becomes available.API_KEY is loaded.The AI now acts exactly like you.
In this chapter, we learned:
declare -f vs typeset -f).Now that we know what commands are (Chapter 1) and where they run (Chapter 2), we need to ensure we can parse complex, nested commands safely.
Next Chapter: Robust Command Parsing (Tree-Sitter & AST)
Generated by Code IQ