Last active
August 27, 2025 22:26
-
-
Save grahama1970/66aa97de3c3e8e7c5fdd3b7f711958c1 to your computer and use it in GitHub Desktop.
Codexer: shell helper for codex that pipes a startup checklist plus the last Codex conversation into codex. Supports --resume, --limit, --id, --index, and --list. Finds session JSONL under ~/.codex/sessions, orders by timestamp, prints User/Assistant lines.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # --- Place below in .zshrc --------- | |
| # === Self-contained codexer with resume seed builder === | |
| # --- codexer: simple conversation loader for the Codex CLI -------------------- | |
| # Features: | |
| # --resume Append the last conversation (user + assistant) to the seed | |
| # --limit N Include only the last N lines of that conversation | |
| # --id SESSION_ID Resume a specific session id (instead of the most recent) | |
| # --index N Resume the Nth most-recent session id (1 = newest) | |
| # --list [N] List the N most-recent session ids (default 20) and exit | |
| # | |
| # Behavior: | |
| # - By default, loads the *entire* last conversation (Claude-like). | |
| # - Falls back gracefully if jq or logs are missing. | |
| # - All other args are passed through to the `codex` CLI. | |
| # --- helpers ------------------------------------------------------------------ | |
| # Print recent unique session_ids from history.jsonl, newest first (one per line) | |
| _codex_recent_session_ids() { | |
| local history_file="$HOME/.codex/history.jsonl" | |
| command -v jq >/dev/null 2>&1 || return 0 | |
| [[ -s "$history_file" ]] || return 0 | |
| if command -v tac >/dev/null 2>&1; then | |
| # Reverse order after extracting IDs; unique while preserving order | |
| jq -r '.session_id // empty' "$history_file" \ | |
| | tac | awk 'NF && !seen[$0]++' | |
| else | |
| # Portable reverse if tac unavailable | |
| awk '{ buf[NR]=$0 } END { for (i=NR; i>0; i--) print buf[i] }' "$history_file" \ | |
| | jq -Rr 'fromjson? | .session_id // empty' \ | |
| | awk 'NF && !seen[$0]++' | |
| fi | |
| } | |
| # Return the newest session file (.jsonl) under ~/.codex/sessions containing $1 | |
| _codex_find_session_file_for_id() { | |
| local sid="$1" root="$HOME/.codex/sessions" | |
| [[ -n "$sid" ]] || return 1 | |
| find "$root" -type f -name '*.jsonl' -print0 2>/dev/null \ | |
| | xargs -0 grep -l -- "$sid" 2>/dev/null \ | |
| | sort | tail -n 1 | |
| } | |
| # Emit a labeled transcript (User:/Assistant:) ordered by timestamp for a file | |
| _codex_transcript_for_file() { | |
| local file="$1" | |
| [[ -f "$file" ]] || return 1 | |
| jq -r ' | |
| select(.type=="message" and (.role=="user" or .role=="assistant")) as $m | |
| | [ ($m.timestamp // $m.ts // 0) | |
| , $m.role | |
| , ( ($m.content[]? | select(.text? and (.text|type=="string")) | .text) | |
| // ($m.text // "") ) | |
| ] | |
| | @tsv | |
| ' "$file" \ | |
| | sort -n \ | |
| | awk -F'\t' '{ | |
| role=$2; text=$3; gsub("\r","",text); | |
| if (role=="user") print "User: " text; | |
| else if (role=="assistant") print "Assistant: " text; | |
| }' | |
| } | |
| # --- main --------------------------------------------------------------------- | |
| codexer() { | |
| emulate -L zsh | |
| setopt pipefail | |
| # Flags we handle; everything else is passed to codex | |
| local resume=0 limit="" id="" index="" | |
| local list=0 list_count=20 | |
| local -a passthrough=() | |
| while [[ $# -gt 0 ]]; do | |
| case "$1" in | |
| --resume) resume=1 ;; | |
| --limit) shift; limit="${1:-}" ;; | |
| --id) shift; id="${1:-}" ;; | |
| --index) shift; index="${1:-}" ;; | |
| --list) list=1; if [[ -n "${2:-}" && "$2" == <-> ]]; then list_count="$2"; shift; fi ;; | |
| --) shift; while [[ $# -gt 0 ]]; do passthrough+=("$1"); shift; done; break ;; | |
| *) passthrough+=("$1") ;; | |
| esac | |
| shift || true | |
| done | |
| # Quick list mode | |
| if (( list )); then | |
| local n=0 | |
| _codex_recent_session_ids | while IFS= read -r sid; do | |
| (( ++n )) | |
| printf "%2d. %s\n" "$n" "$sid" | |
| [[ $n -ge $list_count ]] && break | |
| done | |
| return 0 | |
| fi | |
| # Default startup seed (your checklist) | |
| local seed=$'1. Run '\''source .venv/bin/activate && set -a && [ -f .env ] && source .env'\''\n2. Activate the current dir as project using serena.\n3. Peruse the README.md and the pyproject.toml.' | |
| # Optionally append last session transcript | |
| if (( resume )); then | |
| if ! command -v jq >/dev/null 2>&1; then | |
| print -u2 -- "Warning: jq not found; skipping --resume." | |
| else | |
| # Decide which session_id to use | |
| local sid="$id" | |
| if [[ -z "$sid" ]]; then | |
| if [[ -n "$index" && "$index" == <-> ]]; then | |
| sid=$(_codex_recent_session_ids | sed -n "${index}p") | |
| else | |
| sid=$(_codex_recent_session_ids | head -n 1) | |
| fi | |
| fi | |
| if [[ -z "$sid" ]]; then | |
| print -u2 -- "No previous session_id found; skipping --resume." | |
| else | |
| local file; file=$(_codex_find_session_file_for_id "$sid") | |
| if [[ -z "$file" ]]; then | |
| print -u2 -- "No session file found for session_id=$sid." | |
| else | |
| local transcript; transcript=$(_codex_transcript_for_file "$file") | |
| if [[ -n "$limit" && -n "$transcript" ]]; then | |
| transcript=$(printf "%s\n" "$transcript" | tail -n "$limit") | |
| fi | |
| if [[ -n "$transcript" ]]; then | |
| seed+=$'\n\n=== Last Session Transcript ===\n'"$transcript" | |
| else | |
| print -u2 -- "No user/assistant messages found in: $file" | |
| fi | |
| fi | |
| fi | |
| fi | |
| fi | |
| # Pipe seed into codex (stdin). Add your usual codex flags to $passthrough. | |
| printf "%b\n" "$seed" | codex - "${passthrough[@]}" | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment