Stop AI from Scattering Docs: Introducing mkmd
Your AI agent just created 12 temp files. Where did they go?
I wrote a ~100-line bash script that solved this for good.
Investigation notes. Plans. Review results. Every Claude Code / Codex CLI session leaves behind temporary markdown that clutters your repo or vanishes on reboot.
mkmd is a mktemp wrapper. One command. File lands in ~/.local/state/mkmd/, auto-organized by project, date, and branch. Repo stays clean. Agent picks it up autonomously.
The trick: add one line to CLAUDE.md / AGENTS.md. The agent starts creating files in the right place on its own.
Full script included in the post.
#ClaudeCode #CodexCLI #DevTools #DeveloperProductivity #AIAgents #bash #dotfiles
1. The Problem: AI Agent Work Files Are a Mess
If you use Claude Code or Codex CLI on a daily basis, you end up with a pile of temporary Markdown files — investigation notes, plans, review results — that only matter during a session.
Creating them inside the repository means .gitignore headaches. Putting them in /tmp/ means they vanish on reboot. And coming up with a path manually every time is just inefficient.
What I wanted was a tool that checks all these boxes:
- Does not pollute the git repository
- Automatically organized by project
- Sessions distinguished by date and branch
- No filename collisions
- One command to create a file
2. How It Helps
2.1. Easy to Find Later
Work files land under ~/.local/state/mkmd/, organized by project, date, and branch. “Where did I put that investigation?” is solved with a single ls.
~/.local/state/mkmd/
├── i9wa4-dotfiles/
│ └── 2026-03-22-main/
│ ├── research/
│ │ └── nix-investigation-aB3xKq.md
│ └── plans/
│ └── plan-Xk9mZw.md
├── i9wa4-i9wa4.github.io/
│ └── 2026-03-22-feat-auth/
│ └── draft/
│ └── design-doc-pQ7rLs.md
└── mycompany-api/
└── 2026-03-21-issue-42/
└── reviews/
└── completion-nW2vHj.md
The directory name tells you which repository, which date, and which branch. Even if you juggle multiple issues on the same day, nothing gets mixed up.
2.2. Keeps the Repository Clean
Output goes to $XDG_STATE_HOME (defaults to ~/.local/state), so the git repository is never touched. No need to add anything to .gitignore — your repo stays clean at all times.
2.3. AI Agents Use It Autonomously
Just add one line to your CLAUDE.md / AGENTS.md and agents will create files at the right place during research, planning, and review phases.
The agent runs something like:
mkmd --dir research --label api-investigation
writes the results to that file, and references it in subsequent tasks.
2.4. Easy to Clean Up
When files are no longer needed, delete an entire date directory:
rm -rf ~/.local/state/mkmd/i9wa4-dotfiles/2026-03-20-*Deleting by project is just as easy. Nothing breaks even if you wipe everything.
3. mkmd: A Markdown Wrapper for mktemp
mktemp is the standard Unix command for atomically creating temporary files.
mkmd is a thin wrapper around it.
$ mkmd --dir research --label api-investigation
~/.local/state/mkmd/i9wa4-dotfiles/2026-03-22-main/research/api-investigation-xEZZqX.md
The generated path follows this structure:
$MKMD_BASE_DIR/<owner>-<repo>/YYYY-MM-DD-<branch>/<DIR>/<LABEL>-<XXXXXX>.md
| Segment | How It Is Determined |
|---|---|
MKMD_BASE_DIR |
Environment variable (default: $XDG_STATE_HOME/mkmd) |
owner-repo |
Extracted from git remote get-url origin |
YYYY-MM-DD |
Date of execution |
branch |
git rev-parse --abbrev-ref HEAD |
DIR |
--dir argument |
LABEL-XXXXXX |
--label argument + random suffix from mktemp |
When run outside a git repository, it falls back to local-<dirname>/...-local/.
4. Full Script
Place it in a directory on your PATH and make it executable.
chmod +x mkmdmkmd
#!/usr/bin/env bash
set -o errexit
set -o nounset
set -o pipefail
set -o posix
BASE_DIR="${MKMD_BASE_DIR:-${XDG_STATE_HOME:-$HOME/.local/state}/mkmd}"
print_usage() {
cat <<EOF
Usage:
mkmd --dir DIR --label LABEL
mktemp wrapper that creates a markdown working file.
Creates:
\$MKMD_BASE_DIR/<owner>-<repo>/YYYY-MM-DD-<branch>/<DIR>/<LABEL>-<XXXXXX>.md
Base directory: ${BASE_DIR}
Works in git repos (uses owner/repo from remote URL) and outside git (uses "local"/dirname).
Environment:
MKMD_BASE_DIR Override base directory (default: \$XDG_STATE_HOME/mkmd)
Options:
--dir DIR Directory name under the daily session
--label LABEL File label included in the filename
-h, --help Show this help and exit
EOF
}
LABEL=""
DIR=""
# Parse arguments
while [[ $# -gt 0 ]]; do
case $1 in
-h | --help)
print_usage
exit 0
;;
--dir)
DIR="$2"
shift 2
;;
--label)
LABEL="$2"
shift 2
;;
-*)
echo "Unknown option: $1" >&2
exit 1
;;
*)
echo "Error: Unknown argument: $1" >&2
exit 1
;;
esac
done
# Validate DIR and LABEL
if [[ -z $DIR || -z $LABEL ]]; then
echo "Error: --dir and --label are required" >&2
print_usage >&2
exit 1
fi
repo_root="$(git rev-parse --show-toplevel 2>/dev/null || true)"
if [[ -n $repo_root ]]; then
remote_url="$(git remote get-url origin 2>/dev/null || true)"
if [[ -n $remote_url ]]; then
# Extract owner/repo from remote URL (handles both SSH and HTTPS)
remote_url="${remote_url%.git}"
project_name="${remote_url##*/}"
remote_url="${remote_url%/*}"
project_owner="${remote_url##*[:/]}"
else
project_owner="local"
project_name="$(basename "$repo_root")"
fi
branch_name="$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo unknown)"
branch_name="${branch_name//\//-}"
else
project_owner="local"
project_name="$(basename "$(pwd -P)")"
branch_name="local"
fi
date_dir="$(date +%Y-%m-%d)-${branch_name}"
TARGET_DIR="${BASE_DIR}/${project_owner}-${project_name}/${date_dir}/${DIR}"
mkdir -p "$TARGET_DIR"
# BSD mktemp requires XXXXXX at the end of the template (no suffix allowed)
TEMP_PATH=$(mktemp "${TARGET_DIR}/${LABEL}-XXXXXX")
trap 'rm -f "$TEMP_PATH"' EXIT
FILE_PATH="${TEMP_PATH}.md"
mv "$TEMP_PATH" "$FILE_PATH"
trap - EXIT
echo "$FILE_PATH"
echo "Created: $FILE_PATH" >&25. Integration with AI Agents
5.1. Defining in CLAUDE.md / AGENTS.md
In my setup, I include the following in CLAUDE.md / AGENTS.md:
- Create working files (not tracked by git) with `mkmd` (`mkmd --help`)
- Common dir/label combinations:
| --dir | --label |
| --------- | -------------------------------- |
| draft | `${topic}` |
| research | `${feature}-investigation` |
| plans | plan |
| reviews | completion |
| tmp | `${purpose}` |This is all you need. The agent checks mkmd --help on its own and picks the right --dir / --label. No need to spell out usage examples in each skill.
5.2. Working with PreToolUse Hooks
If you use Claude Code’s PreToolUse hooks to control file writes, add mkmd’s output directory to the allow list:
elif [[ -n $FILE_PATH && $FILE_PATH == "$HOME/.local/state/mkmd/"* ]]; then
: # Allow writes to mkmd state directory5.3. Using from Other Scripts
When calling mkmd from another script, stdout contains only the file path, so you can capture it with command substitution:
FILE=$(mkmd --dir tmp --label compact-save)
echo "# Context Save" > "$FILE"The Created: ... message goes to stderr, so it never mixes with the path on stdout.
6. Summary
Managing AI agent work files is unglamorous, but clutter kills productivity.
A thin wrapper around mktemp that automatically applies a “project / date / purpose” structure gives you:
git statusstays clean at all times- Work files automatically organized by project, date, and branch
- Agents create files autonomously; humans never have to think about paths
- Delete an entire directory when you are done
If you have been struggling with where to put AI agent work files, give it a try.