Add portable Codex ops kit
This commit is contained in:
Executable
+36
@@ -0,0 +1,36 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
||||
|
||||
if [[ ! -d "$CODEX_HOME" ]]; then
|
||||
echo "Codex home not found: $CODEX_HOME" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
echo "Codex home: $CODEX_HOME"
|
||||
echo
|
||||
echo "Top-level sizes"
|
||||
du -sh "$CODEX_HOME"/* "$CODEX_HOME"/.[!.]* 2>/dev/null | sort -h
|
||||
|
||||
echo
|
||||
echo "Large session files"
|
||||
python3 - "$CODEX_HOME" <<'PY'
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
root = Path(sys.argv[1]) / "sessions"
|
||||
files = sorted((p for p in root.rglob("*.jsonl") if p.is_file()), key=lambda p: p.stat().st_size, reverse=True)
|
||||
print(f"session files: {len(files)}")
|
||||
for path in files[:10]:
|
||||
size_mb = path.stat().st_size / (1024 * 1024)
|
||||
print(f"{size_mb:9.1f} MB {path}")
|
||||
PY
|
||||
|
||||
echo
|
||||
echo "Config snapshot"
|
||||
sed -n '1,220p' "$CODEX_HOME/config.toml" 2>/dev/null || true
|
||||
|
||||
echo
|
||||
echo "Memory markdown files"
|
||||
find "$CODEX_HOME/memories" -type f -name '*.md' 2>/dev/null | sort || true
|
||||
Executable
+29
@@ -0,0 +1,29 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
DEST="${1:-$HOME/Documents/codex-ops-kit}"
|
||||
|
||||
rm -rf "$DEST"
|
||||
mkdir -p "$DEST"
|
||||
|
||||
copy_path() {
|
||||
local rel="$1"
|
||||
local src="$REPO_ROOT/$rel"
|
||||
local dest="$DEST/$rel"
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
cp -R "$src" "$dest"
|
||||
}
|
||||
|
||||
copy_path README.md
|
||||
copy_path AGENTS.md
|
||||
copy_path .gitignore
|
||||
copy_path docs
|
||||
copy_path memory
|
||||
copy_path templates
|
||||
copy_path scripts
|
||||
|
||||
find "$DEST/scripts" -name '__pycache__' -type d -prune -exec rm -rf {} +
|
||||
find "$DEST" -name '*.pyc' -delete
|
||||
|
||||
echo "Created clean shareable repo at: $DEST"
|
||||
Executable
+51
@@ -0,0 +1,51 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
||||
STAMP="$(date +%Y%m%d-%H%M%S)"
|
||||
DEST="${1:-$REPO_ROOT/export/codex-portable-$STAMP}"
|
||||
|
||||
mkdir -p "$DEST"
|
||||
mkdir -p "$DEST/codex-home"
|
||||
|
||||
copy_if_exists() {
|
||||
local src="$1"
|
||||
local dest="$2"
|
||||
if [[ -e "$src" ]]; then
|
||||
mkdir -p "$(dirname "$dest")"
|
||||
cp -R "$src" "$dest"
|
||||
fi
|
||||
}
|
||||
|
||||
copy_if_exists "$CODEX_HOME/config.toml" "$DEST/codex-home/config.toml"
|
||||
copy_if_exists "$CODEX_HOME/AGENTS.md" "$DEST/codex-home/AGENTS.md"
|
||||
copy_if_exists "$CODEX_HOME/memories" "$DEST/codex-home/memories"
|
||||
copy_if_exists "$CODEX_HOME/rules" "$DEST/codex-home/rules"
|
||||
copy_if_exists "$REPO_ROOT/templates" "$DEST/templates"
|
||||
copy_if_exists "$REPO_ROOT/memory" "$DEST/memory"
|
||||
copy_if_exists "$REPO_ROOT/scripts" "$DEST/scripts"
|
||||
copy_if_exists "$REPO_ROOT/docs" "$DEST/docs"
|
||||
copy_if_exists "$REPO_ROOT/README.md" "$DEST/README.md"
|
||||
|
||||
cat > "$DEST/EXPORT_NOTES.txt" <<EOF
|
||||
Portable Codex bundle created at:
|
||||
$DEST
|
||||
|
||||
Included:
|
||||
- config.toml
|
||||
- AGENTS.md
|
||||
- memories/
|
||||
- rules/
|
||||
- repo templates, memory, docs, and scripts
|
||||
|
||||
Excluded on purpose:
|
||||
- auth.json
|
||||
- sessions/
|
||||
- logs SQLite files
|
||||
- state SQLite files
|
||||
- cache/
|
||||
- plugins/
|
||||
EOF
|
||||
|
||||
echo "Exported portable bundle to: $DEST"
|
||||
Executable
+94
@@ -0,0 +1,94 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)"
|
||||
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
||||
STAMP="$(date +%Y%m%d-%H%M%S)"
|
||||
BACKUP_DIR="$CODEX_HOME/backups/$STAMP"
|
||||
MANAGED_CONFIG="$REPO_ROOT/templates/global/config-managed.toml"
|
||||
|
||||
mkdir -p "$CODEX_HOME" "$BACKUP_DIR" "$CODEX_HOME/memories"
|
||||
|
||||
if [[ -f "$CODEX_HOME/config.toml" ]]; then
|
||||
cp "$CODEX_HOME/config.toml" "$BACKUP_DIR/config.toml.bak"
|
||||
fi
|
||||
|
||||
if [[ -f "$CODEX_HOME/AGENTS.md" ]]; then
|
||||
cp "$CODEX_HOME/AGENTS.md" "$BACKUP_DIR/AGENTS.md.bak"
|
||||
fi
|
||||
|
||||
cp "$REPO_ROOT/templates/global/AGENTS.md" "$CODEX_HOME/AGENTS.md"
|
||||
|
||||
python3 - "$CODEX_HOME/config.toml" "$MANAGED_CONFIG" <<'PY'
|
||||
from pathlib import Path
|
||||
import sys
|
||||
|
||||
config_path = Path(sys.argv[1])
|
||||
managed_path = Path(sys.argv[2])
|
||||
|
||||
managed_start = "# >>> codex-ops-kit managed block >>>"
|
||||
managed_end = "# <<< codex-ops-kit managed block <<<"
|
||||
managed_root_keys = {
|
||||
"model",
|
||||
"model_reasoning_effort",
|
||||
"profile",
|
||||
"project_doc_max_bytes",
|
||||
"web_search",
|
||||
}
|
||||
managed_sections = {
|
||||
"history",
|
||||
"profiles.economy",
|
||||
"profiles.balanced",
|
||||
"profiles.deep",
|
||||
}
|
||||
|
||||
text = config_path.read_text() if config_path.exists() else ""
|
||||
managed = managed_path.read_text().rstrip()
|
||||
|
||||
if managed_start in text and managed_end in text:
|
||||
before = text.split(managed_start, 1)[0].rstrip()
|
||||
else:
|
||||
before = text.rstrip()
|
||||
|
||||
filtered_lines = []
|
||||
current_section = None
|
||||
|
||||
for line in before.splitlines():
|
||||
stripped = line.strip()
|
||||
if stripped.startswith("[") and stripped.endswith("]"):
|
||||
current_section = stripped[1:-1].strip()
|
||||
if current_section in managed_sections:
|
||||
continue
|
||||
filtered_lines.append(line)
|
||||
continue
|
||||
|
||||
if current_section in managed_sections:
|
||||
continue
|
||||
|
||||
if "=" in line and not line.lstrip().startswith("#"):
|
||||
key = line.split("=", 1)[0].strip()
|
||||
if current_section is None and key in managed_root_keys:
|
||||
continue
|
||||
|
||||
filtered_lines.append(line)
|
||||
|
||||
before = "\n".join(filtered_lines).strip()
|
||||
|
||||
block = f"{managed_start}\n{managed}\n{managed_end}\n"
|
||||
|
||||
if before:
|
||||
config_path.write_text(before + "\n\n" + block)
|
||||
else:
|
||||
config_path.write_text(block)
|
||||
PY
|
||||
|
||||
mkdir -p "$CODEX_HOME/memories/portable-kit"
|
||||
cp "$REPO_ROOT"/memory/*.md "$CODEX_HOME/memories/portable-kit/"
|
||||
|
||||
echo "Installed Codex kit into: $CODEX_HOME"
|
||||
echo "Backup created at: $BACKUP_DIR"
|
||||
echo
|
||||
echo "Next steps:"
|
||||
echo "- Start Codex and use the balanced profile by default."
|
||||
echo "- Switch to economy for lighter work and deep for hard tasks."
|
||||
echo "- Run bash scripts/audit_codex_home.sh periodically."
|
||||
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
CODEX_HOME="${CODEX_HOME:-$HOME/.codex}"
|
||||
SESSIONS_DIR="$CODEX_HOME/sessions"
|
||||
ARCHIVE_DIR="${CODEX_ARCHIVE_DIR:-$HOME/.codex_archives/sessions}"
|
||||
OLDER_THAN_DAYS="${OLDER_THAN_DAYS:-30}"
|
||||
MIN_SIZE_MB="${MIN_SIZE_MB:-50}"
|
||||
APPLY=0
|
||||
|
||||
if [[ "${1:-}" == "--apply" ]]; then
|
||||
APPLY=1
|
||||
fi
|
||||
|
||||
if [[ ! -d "$SESSIONS_DIR" ]]; then
|
||||
echo "Sessions directory not found: $SESSIONS_DIR" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
python3 - "$SESSIONS_DIR" "$ARCHIVE_DIR" "$OLDER_THAN_DAYS" "$MIN_SIZE_MB" "$APPLY" <<'PY'
|
||||
from datetime import datetime, timedelta
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
import sys
|
||||
|
||||
sessions_dir = Path(sys.argv[1])
|
||||
archive_dir = Path(sys.argv[2])
|
||||
older_than_days = int(sys.argv[3])
|
||||
min_size_mb = int(sys.argv[4])
|
||||
apply = int(sys.argv[5])
|
||||
|
||||
cutoff = datetime.now() - timedelta(days=older_than_days)
|
||||
candidates = []
|
||||
|
||||
for path in sessions_dir.rglob("*.jsonl"):
|
||||
stat = path.stat()
|
||||
mtime = datetime.fromtimestamp(stat.st_mtime)
|
||||
size_mb = stat.st_size / (1024 * 1024)
|
||||
if mtime < cutoff and size_mb >= min_size_mb:
|
||||
candidates.append((size_mb, mtime, path))
|
||||
|
||||
candidates.sort(reverse=True)
|
||||
|
||||
if not candidates:
|
||||
print("No session files matched the archive rules.")
|
||||
raise SystemExit(0)
|
||||
|
||||
print("Candidates:")
|
||||
for size_mb, mtime, path in candidates:
|
||||
print(f"{size_mb:9.1f} MB {mtime:%Y-%m-%d} {path}")
|
||||
|
||||
if not apply:
|
||||
print()
|
||||
print("Dry run only. Re-run with --apply to move these files to:")
|
||||
print(archive_dir)
|
||||
raise SystemExit(0)
|
||||
|
||||
for _, _, path in candidates:
|
||||
rel = path.relative_to(sessions_dir)
|
||||
dest = archive_dir / rel
|
||||
dest.parent.mkdir(parents=True, exist_ok=True)
|
||||
shutil.move(str(path), str(dest))
|
||||
|
||||
print()
|
||||
print(f"Archived {len(candidates)} session files to {archive_dir}")
|
||||
PY
|
||||
Executable
+109
@@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env python3
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
import re
|
||||
import sys
|
||||
from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def uniq(items: list[str]) -> list[str]:
|
||||
return list(OrderedDict((item, None) for item in items if item))
|
||||
|
||||
|
||||
def extract_texts(obj) -> list[str]:
|
||||
texts: list[str] = []
|
||||
if isinstance(obj, dict):
|
||||
if obj.get("type") in {"input_text", "output_text"} and isinstance(obj.get("text"), str):
|
||||
texts.append(obj["text"])
|
||||
for value in obj.values():
|
||||
texts.extend(extract_texts(value))
|
||||
elif isinstance(obj, list):
|
||||
for value in obj:
|
||||
texts.extend(extract_texts(value))
|
||||
return texts
|
||||
|
||||
|
||||
def main() -> int:
|
||||
if len(sys.argv) not in {2, 3}:
|
||||
print("usage: session_to_memory.py SESSION_JSONL [OUTPUT_MD]", file=sys.stderr)
|
||||
return 2
|
||||
|
||||
session_path = Path(sys.argv[1]).expanduser().resolve()
|
||||
output_path = Path(sys.argv[2]).expanduser().resolve() if len(sys.argv) == 3 else session_path.with_suffix(".memory.md")
|
||||
|
||||
user_texts: list[str] = []
|
||||
assistant_texts: list[str] = []
|
||||
paths: list[str] = []
|
||||
session_meta = {}
|
||||
|
||||
path_pattern = re.compile(r"(\/[A-Za-z0-9._ \-\/]+)")
|
||||
|
||||
with session_path.open() as handle:
|
||||
for raw_line in handle:
|
||||
raw_line = raw_line.strip()
|
||||
if not raw_line:
|
||||
continue
|
||||
event = json.loads(raw_line)
|
||||
payload = event.get("payload", {})
|
||||
if event.get("type") == "session_meta":
|
||||
session_meta = payload
|
||||
if event.get("type") == "response_item" and payload.get("type") == "message":
|
||||
role = payload.get("role")
|
||||
texts = extract_texts(payload.get("content", []))
|
||||
if role == "user":
|
||||
user_texts.extend(texts)
|
||||
elif role == "assistant":
|
||||
assistant_texts.extend(texts)
|
||||
for text in texts:
|
||||
paths.extend(path_pattern.findall(text))
|
||||
|
||||
user_texts = uniq([text.strip() for text in user_texts if text.strip()])
|
||||
assistant_texts = uniq([text.strip() for text in assistant_texts if text.strip()])
|
||||
paths = uniq([path.strip() for path in paths if path.strip()])
|
||||
|
||||
output = []
|
||||
output.append(f"# Session Memory Draft")
|
||||
output.append("")
|
||||
output.append(f"- Session file: `{session_path}`")
|
||||
if session_meta.get("timestamp"):
|
||||
output.append(f"- Session timestamp: `{session_meta['timestamp']}`")
|
||||
if session_meta.get("cwd"):
|
||||
output.append(f"- Working directory: `{session_meta['cwd']}`")
|
||||
output.append("")
|
||||
output.append("## User Requests")
|
||||
output.append("")
|
||||
for text in user_texts[:20]:
|
||||
output.append(f"- {text.replace(chr(10), ' ')}")
|
||||
output.append("")
|
||||
output.append("## Candidate Paths Mentioned")
|
||||
output.append("")
|
||||
for path in paths[:30]:
|
||||
output.append(f"- `{path}`")
|
||||
output.append("")
|
||||
output.append("## Assistant Notes To Distill")
|
||||
output.append("")
|
||||
output.append("- Copy only stable preferences, project facts, and durable decisions into your memory markdown files.")
|
||||
output.append("- Do not copy transient debugging output or large transcript excerpts.")
|
||||
output.append("- Summarize in your own words before adding to long-term memory.")
|
||||
output.append("")
|
||||
output.append("## Sample Durable Summary")
|
||||
output.append("")
|
||||
output.append("- Project:")
|
||||
output.append("- User preference:")
|
||||
output.append("- Important decision:")
|
||||
output.append("- Follow-up worth remembering:")
|
||||
output.append("")
|
||||
output.append("## Assistant Messages Snapshot")
|
||||
output.append("")
|
||||
for text in assistant_texts[:10]:
|
||||
output.append(f"- {text.replace(chr(10), ' ')}")
|
||||
|
||||
output_path.write_text("\n".join(output) + "\n")
|
||||
print(output_path)
|
||||
return 0
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
raise SystemExit(main())
|
||||
Reference in New Issue
Block a user