Autonomous AI coding agents and terminal assistants have transformed everyday software engineering workflows. Tools like Claude Code, Cursor, Aider, Open Interpreter, and Gemini CLI read workspaces, execute commands, and help debug issues directly in development environments.
However, during active development sessions, developers routinely paste credentials, pass environment variables, or allow agents to inspect configuration files containing secrets. Even when those commands finish, the underlying transcripts and session logs are retained indefinitely on your local machine.
If your workstation is accessed, or if agent history folders are synced, backed up, or shared during collaboration, sensitive authentication material can be exposed. The AgentTrail Scrub tool inspects local AI agent session logs, identifies credentials using regular expressions and Shannon entropy, and helps with redacting them while keeping file backups.
The Problem: Persistent Credentials in Local AI History
Most developers do not realize how much sensitive state AI developer tools store on disk. When you run an agent or use an editor plugin, the tool writes comprehensive logs to record the conversation, tool execution calls, and terminal responses.
Common locations where local history accumulates include:
-
~/.claude/projectsand~/.claude/historyfor Claude Code sessions. -
workspaceStoragefolders under user configuration paths for Cursor. -
~/.aidersession logs. -
~/.geminilogs for terminal-based Gemini tools. -
~/.openinterpreterand~/.codexlocal caches.
When an AI tool prints the output of an environment dump or inspects an uncommitted file, raw strings such as Anthropic keys, OpenAI tokens, AWS secret keys, GitHub personal access tokens, and JSON Web Tokens (JWTs) are embedded directly into session records on disk.
Simply clearing your shell history or adding .env to .gitignore does not touch these assistant logs. Cleaning these records manually across deeply nested directories is tedious and error-prone.
Install & Usage
The tool is self-contained and implemented entirely in Python with zero third-party library dependencies. It uses standard library modules such as argparse, pathlib, re, shutil, math, and collections. You do not need to install additional packages through package managers.
To install the tool, download the source file directly and save it to your machine as agenttrail_scrub_ai_transcript.py.
You can view all available command-line arguments and configuration options by running:
python agenttrail_scrub_ai_transcript.py --help
To try it safely on synthetic transcripts first, run the built-in demo:
python agenttrail_scrub_ai_transcript.py --demo
A typical flow is scan first, then redact (backups go to ~/.agenttrail/backups unless you pass --backup-dir):
python agenttrail_scrub_ai_transcript.py --scan
python agenttrail_scrub_ai_transcript.py --redact
Retention cleanup is a dry run by default; old transcripts are only deleted when you add --apply-retention:
python agenttrail_scrub_ai_transcript.py --retention-days 30
python agenttrail_scrub_ai_transcript.py --retention-days 30 --apply-retention
Because the script runs in user space using your standard Python interpreter, you can run it on macOS, Linux, or Windows environments where your AI coding assistants reside.
How It Works
The cleaner combines agent directory discovery, targeted pattern matching, entropy filtering, safe redaction, and optional retention cleanup.
1. Target Directory Discovery
Rather than forcing you to look up where each individual tool stores session state, the script inspects well-known workspace and history paths:
def discover_agent_transcript_dirs() -> List[Tuple[str, Path]]:
"""Locate common transcript, history, and workspace caches of AI coding agents."""
home = Path.home()
app_data = Path(os.environ.get("APPDATA", home / "AppData" / "Roaming"))
candidates = [
("Claude Code Projects", home / ".claude" / "projects"),
("Claude Code History", home / ".claude" / "history"),
("Claude Code Root", home / ".claude"),
("Cursor Workspaces (Linux/macOS)", home / ".config" / "Cursor" / "User" / "workspaceStorage"),
("Cursor Workspaces (macOS AppSupport)", home / "Library" / "Application Support" / "Cursor" / "User" / "workspaceStorage"),
("Cursor Workspaces (Windows)", app_data / "Cursor" / "User" / "workspaceStorage"),
("Cursor Global Storage", home / ".cursor"),
("Gemini CLI", home / ".gemini"),
("Gemini Config", home / ".config" / "gemini"),
("Aider History", home / ".aider"),
("OpenAI Codex / CLI", home / ".codex"),
("Open Interpreter", home / ".openinterpreter"),
# ... further candidate paths trimmed for brevity
]
discovered = []
for label, path in candidates:
try:
if path.exists() and path.is_dir():
discovered.append((label, path.resolve()))
except (PermissionError, OSError):
continue
return discovered
The tool identifies which directories exist on your machine, then gathers files ending in formats commonly used for logs and chats, such as .json, .jsonl, .log, .txt, and .md.
2. Pattern Matching with Shannon Entropy Filtering
Simple regular expressions frequently trigger false alarms when matching generic token assignments. To distinguish real cryptographic keys from low-randomness dummy values (such as test_token_12345), the tool calculates the Shannon entropy of candidate strings.
def calculate_shannon_entropy(data: str) -> float:
"""Compute the Shannon entropy of a string."""
if not data:
return 0.0
entropy = 0.0
length = len(data)
counts = Counter(data)
for count in counts.values():
p_x = count / length
if p_x > 0:
entropy -= p_x * math.log2(p_x)
return entropy
High-confidence signatures (like AWS access key IDs or structured GitHub tokens) have explicit prefix formats and can match without an entropy check. More generic strings, such as .env variable assignments, must exceed configured entropy thresholds before they are treated as valid secret hits.
3. Overlap Suppression and Safe In-Place Redaction
When an API key matches multiple detection patterns, the scanner suppresses overlapping spans to avoid duplicate notifications. When modifying a file, it preserves a backup before overwriting contents:
def redact_file(file_path: Path, hits: List[SecretHit], backup_dir: Path) -> Tuple[bool, Optional[Path]]:
"""Create a backup and rewrite the file with redacted tokens."""
if not hits:
return False, None
try:
safe_rel = str(file_path.resolve()).lstrip("/\\").replace(":", "_")
target_backup = backup_dir / safe_rel
target_backup.parent.mkdir(parents=True, exist_ok=True)
shutil.copy2(file_path, target_backup)
with open(file_path, "r", encoding="utf-8", errors="replace") as f:
content = f.read()
unique_secrets = sorted(list({h.raw_secret for h in hits}), key=len, reverse=True)
for secret in unique_secrets:
replacement = f"[AGENTTRAIL_REDACTED_{mask_secret(secret)}]"
content = content.replace(secret, replacement)
with open(file_path, "w", encoding="utf-8") as f:
f.write(content)
return True, target_backup
except (PermissionError, OSError):
return False, None
The redaction logic sorts detected secrets by length in descending order before performing replacements. This prevents shorter substrings of a key from being partially replaced before a complete key string is processed.
Limitations
Before incorporating this script into automated cleaning routines, keep these limitations in mind:
-
Large File Skip Limit: The
scan_filefunction skips any file exceeding 100 MB. Very large log files or aggregated session archives above this threshold will not be processed. -
Line-by-Line Text Handling: The script scans files using line-based string reading (
open(..., "r", encoding="utf-8", errors="replace")). While.sqliteand.dbfile extensions are collected, treating database files as plain text can miss secrets split across database blocks, and modifying them via direct string replacement can damage database file formatting. -
Specific Provider Coverage: Secret detection relies on the patterns defined in
SECRET_PATTERNS. Keys from unlisted services or custom corporate authentication tokens will not be recognized unless they conform to generic key-value assignments. - Overlapping Match Suppression: When a regex matches an offset range already claimed by another pattern, subsequent matches within that character span are skipped.
Frequently Asked Questions
Does the script upload transcripts or telemetry to remote servers?
No. The script uses only local Python standard libraries and performs all directory traversal, scanning, and file rewrites locally on your machine.
What happens to my original files when redaction runs?
Before modifying any file containing a secret, redact_file copies the original file into a specified backup directory, mirroring the source path so you can review or restore the original content if needed.
How does the tool avoid flagging placeholder tokens in configuration files?
The detection engine evaluates minimum Shannon entropy thresholds on generic token matches. Repetitive strings, common words, and short sequences are discarded because their calculated randomness falls below the required threshold for cryptographic secrets.
AgentTrail Scrub is a single Python file that helps with finding and redacting secrets in local AI agent transcripts: AgentTrail Scrub on PyForge
Top comments (0)