mirror of
https://github.com/SeriousBug/dotfiles
synced 2026-07-27 12:35:37 -05:00
cc-compact: auto-select latest session when no selector given
Default to the most recently active session in the current project, excluding the caller's own session (found via CLAUDE_CODE_SESSION_ID). Lets you run /clear then cc-compact to reload the just-cleared session, emulating Claude Code's built-in /compact.
This commit is contained in:
parent
705e914ea0
commit
876ac92432
|
|
@ -16,8 +16,13 @@ The helper script lives at `~/.claude/skills/cc-compact/compact_session.py`.
|
|||
|
||||
## Step 1: Resolve the session
|
||||
|
||||
The argument is one of two forms:
|
||||
The argument is one of three forms:
|
||||
|
||||
- **Nothing** — no id or title given. This is the common case: the user ran
|
||||
`/clear` and then `cc-compact` to reload the session they just cleared. Run
|
||||
the script with no selector; it auto-picks the most recently active session
|
||||
in the current project, **excluding this session** (the fresh one `/clear`
|
||||
created). This emulates Claude Code's built-in `/compact`.
|
||||
- **A session id** — when invoked like `/resume claude --resume <id>` or
|
||||
`cc-compact <id>`, the user already gave you the UUID. Pass it as `--id`.
|
||||
- **A session name / title** — free text. Pass it as `--title`; the script
|
||||
|
|
@ -27,6 +32,9 @@ The argument is one of two forms:
|
|||
Run the script exactly once with the matching form:
|
||||
|
||||
```sh
|
||||
# Latest session before the /clear (default, no selector):
|
||||
python3 ~/.claude/skills/cc-compact/compact_session.py
|
||||
|
||||
# By id:
|
||||
python3 ~/.claude/skills/cc-compact/compact_session.py --id <session-uuid>
|
||||
|
||||
|
|
|
|||
|
|
@ -18,6 +18,10 @@ Session resolution (pick one):
|
|||
--id UUID find <UUID>.jsonl under the projects dir
|
||||
--title TEXT find the session whose ai-title contains TEXT
|
||||
(case-insensitive substring; newest match wins)
|
||||
--latest the most recently active session in the current project,
|
||||
excluding the caller's own session — this is the default
|
||||
when no selector is given, so `cc-compact` right after
|
||||
`/clear` picks up the session you just cleared
|
||||
|
||||
Sessions live at: ~/.claude/projects/<encoded-cwd>/<session-id>.jsonl
|
||||
"""
|
||||
|
|
@ -27,6 +31,7 @@ import glob
|
|||
import json
|
||||
import os
|
||||
import random
|
||||
import re
|
||||
import sys
|
||||
|
||||
PROJECTS_DIR = os.path.expanduser("~/.claude/projects")
|
||||
|
|
@ -84,7 +89,45 @@ def is_genuine_prompt(rec):
|
|||
return not s.startswith(skip_prefixes)
|
||||
|
||||
|
||||
def current_session():
|
||||
"""(project_dir, session_id) for the session invoking this script.
|
||||
|
||||
Claude Code exports CLAUDE_CODE_SESSION_ID; its log lives at
|
||||
<project_dir>/<id>.jsonl. Fall back to the encoded cwd when the env var is
|
||||
missing (script run by hand outside a session). Either value may be None."""
|
||||
sid = os.environ.get("CLAUDE_CODE_SESSION_ID")
|
||||
if sid:
|
||||
matches = glob.glob(os.path.join(PROJECTS_DIR, "**", f"{sid}.jsonl"), recursive=True)
|
||||
if matches:
|
||||
return os.path.dirname(matches[0]), sid
|
||||
# Claude Code encodes the cwd into the project dir name by replacing every
|
||||
# non-alphanumeric char with a dash (/Users/kaan/Code/Veery -> -Users-kaan-Code-Veery).
|
||||
encoded = re.sub(r"[^A-Za-z0-9]", "-", os.getcwd())
|
||||
cand = os.path.join(PROJECTS_DIR, encoded)
|
||||
return (cand if os.path.isdir(cand) else None), sid
|
||||
|
||||
|
||||
def resolve_latest():
|
||||
"""Newest session log in the current project, excluding the caller's own
|
||||
session, so compacting right after `/clear` lands on the just-cleared one."""
|
||||
project_dir, sid = current_session()
|
||||
if project_dir:
|
||||
pool = glob.glob(os.path.join(project_dir, "*.jsonl"))
|
||||
else:
|
||||
pool = glob.glob(os.path.join(PROJECTS_DIR, "**", "*.jsonl"), recursive=True)
|
||||
if sid:
|
||||
pool = [p for p in pool if os.path.basename(p) != f"{sid}.jsonl"]
|
||||
if not pool:
|
||||
sys.exit("No previous session found to compact in this project")
|
||||
latest = max(pool, key=os.path.getmtime)
|
||||
sys.stderr.write(f"Auto-selected latest session: {latest}\n")
|
||||
return latest
|
||||
|
||||
|
||||
def resolve_file(args):
|
||||
if args.latest:
|
||||
return resolve_latest()
|
||||
|
||||
if args.file:
|
||||
return os.path.expanduser(args.file)
|
||||
|
||||
|
|
@ -131,10 +174,13 @@ def resolve_file(args):
|
|||
|
||||
def main():
|
||||
ap = argparse.ArgumentParser(description=__doc__, formatter_class=argparse.RawDescriptionHelpFormatter)
|
||||
g = ap.add_mutually_exclusive_group(required=True)
|
||||
g = ap.add_mutually_exclusive_group()
|
||||
g.add_argument("--file", help="path to a session .jsonl")
|
||||
g.add_argument("--id", help="session UUID")
|
||||
g.add_argument("--title", help="substring of the session's ai-title")
|
||||
g.add_argument("--latest", action="store_true",
|
||||
help="newest session in the current project, excluding the caller's own "
|
||||
"(the default when no selector is given)")
|
||||
ap.add_argument("--first", type=int, default=5, help="how many opening exchanges")
|
||||
ap.add_argument("--last", type=int, default=10, help="how many closing exchanges")
|
||||
ap.add_argument("--middle", type=int, default=5, help="how many exchanges randomly sampled from the middle")
|
||||
|
|
@ -142,6 +188,8 @@ def main():
|
|||
ap.add_argument("--maxlen", type=int, default=800, help="max chars per quoted message")
|
||||
ap.add_argument("--seed", type=int, default=1, help="seed for the middle random sample")
|
||||
args = ap.parse_args()
|
||||
if not (args.file or args.id or args.title):
|
||||
args.latest = True
|
||||
|
||||
path = resolve_file(args)
|
||||
|
||||
|
|
|
|||
Loading…
Reference in a new issue