[{"content":" For agents Install from claude-ops-plugins. Run claude-context-pct to check context window usage (0-100%). See the README for statusline setup.\nTL;DR claude-context-pct returns context window usage as an integer 0-100. Agents check it between tasks to decide whether to keep working or hand off.\nClaude Code agents have no built-in way to check how much of their context window is consumed. Depending on the workflow, this can lead to conservative behavior: asking permission to continue, wrapping up early, or writing premature handoff summaries. This isn\u0026rsquo;t universal. Strictly defined workflows or heavy use of compaction may make context visibility unnecessary. But for open-ended sessions where the agent self-paces across multiple tasks, having a signal helps.\ncontext-tracker is a Claude Code plugin that exposes a single command, claude-context-pct, returning an integer 0-100. The agent calls it, sees \u0026ldquo;23%\u0026rdquo;, and keeps working. Or sees \u0026ldquo;74%\u0026rdquo; and starts wrapping up (depending on your instructions).\nHow it works The plugin has two parts: a producer that writes context data to a temp file on each statusline render, and a consumer script that reads it.\nThe consumer is the entire bin/claude-context-pct:\n#!/bin/bash _sid=\u0026#34;${1:-$CLAUDE_CODE_SESSION_ID}\u0026#34; _file=\u0026#34;/tmp/claude-$(id -u)/context/${_sid}.json\u0026#34; _pct=$(jq -r \u0026#39;.used_pct\u0026#39; \u0026#34;$_file\u0026#34; 2\u0026gt;/dev/null) \u0026amp;\u0026amp; echo \u0026#34;${_pct}%\u0026#34; || echo \u0026#34;-1\u0026#34; The producer is a block you add to your statusline script. Claude Code passes a JSON blob to statusline scripts on every render that includes context_window.used_percentage. The producer extracts that value and writes it to /tmp/claude-\u0026lt;uid\u0026gt;/context/\u0026lt;session_id\u0026gt;.json:\n# --- BEGIN context-tracker (claude-ops-plugins) --- _ctx_dir=\u0026#34;/tmp/claude-$(id -u)/context\u0026#34; { read -r _ctx_session_id; read -r _ctx_pct; read -r _ctx_cwd; } \u0026lt;\u0026lt;\u0026lt; \u0026#34;$(echo \u0026#34;$input\u0026#34; | jq -r \u0026#39; (.session_id // \u0026#34;\u0026#34;), (.context_window.used_percentage // 0 | floor), .workspace.current_dir \u0026#39;)\u0026#34; if [ -n \u0026#34;$_ctx_pct\u0026#34; ] \u0026amp;\u0026amp; [ -n \u0026#34;$_ctx_session_id\u0026#34; ]; then mkdir -p \u0026#34;$_ctx_dir\u0026#34; 2\u0026gt;/dev/null chmod 700 \u0026#34;$_ctx_dir\u0026#34; 2\u0026gt;/dev/null _ctx_tmp=$(mktemp \u0026#34;$_ctx_dir/.ctx.XXXXXX\u0026#34;) printf \u0026#39;{\u0026#34;session_id\u0026#34;:\u0026#34;%s\u0026#34;,\u0026#34;used_pct\u0026#34;:%s,\u0026#34;cwd\u0026#34;:\u0026#34;%s\u0026#34;,\u0026#34;updated_at\u0026#34;:\u0026#34;%s\u0026#34;}\\n\u0026#39; \\ \u0026#34;$_ctx_session_id\u0026#34; \u0026#34;${_ctx_pct:-0}\u0026#34; \u0026#34;$_ctx_cwd\u0026#34; \u0026#34;$(date -u +%Y-%m-%dT%H:%M:%SZ)\u0026#34; \\ \u0026gt; \u0026#34;$_ctx_tmp\u0026#34; mv -f \u0026#34;$_ctx_tmp\u0026#34; \u0026#34;$_ctx_dir/${_ctx_session_id}.json\u0026#34; fi # --- END context-tracker (claude-ops-plugins) --- The atomic write (write to temp, then mv) avoids partial reads if the agent checks while the statusline is updating.\nLimitations Scope This only reports the main agent's context window. Claude Code doesn't expose context usage to subagents, background agents, or team members. A subagent calling claude-context-pct will get the parent's value, not its own. The skill is designed for the top-level agent only.\nWhat changes Instead of asking the user \u0026ldquo;should I keep going?\u0026rdquo; or preemptively writing a handoff summary, the agent checks its usage after finishing a task and decides for itself. The user stops being a context-window oracle.\nTwo things worth noting from early use:\nDon\u0026rsquo;t check often. Checking mid-task adds noise to the conversation. The useful pattern is checking after completing a unit of work, not during. The skill description intentionally doesn\u0026rsquo;t prescribe check frequency. Project-level instructions (CLAUDE.md) are the right place to define thresholds and actions.\nUsage isn\u0026rsquo;t linear. Early tasks consume more context because project files are loaded for the first time. Once cached, subsequent tasks are cheaper. A task that costs 10% early in a session might cost 3% later. Early readings will overestimate future consumption. The value can also decrease after auto-compaction, so it\u0026rsquo;s not monotonically increasing.\nSetup Install the plugin and configure thresholds in your project\u0026rsquo;s CLAUDE.md. The producer snippet above is bash, but the statusline script path and language are configurable. If yours is Python, TypeScript, or something else, Claude can adapt the logic. The only requirement is writing the JSON file to the expected path. The plugin gives agents visibility but deliberately does not define what to do with it. Some patterns that work:\nPeriodic check-ins: \u0026ldquo;Run claude-context-pct after completing each major task to decide whether to continue or hand off.\u0026rdquo; Handoff threshold: \u0026ldquo;When context usage reaches N%, wrap up the current task, commit progress, and prepare a handoff summary.\u0026rdquo; Subagent delegation: \u0026ldquo;If context exceeds N%, prefer delegating work to subagents. Their intermediate work stays in their own context window; only the result summary returns to the parent.\u0026rdquo; The plugin is part of claude-ops-plugins and works on macOS and Linux. Full setup instructions are in the README.\n","permalink":"https://j4c0bs.github.io/posts/context-tracker/","summary":"A plugin that exposes context window usage to Claude Code agents so they can self-pace instead of guessing.","title":"Making the Context Window Visible"},{"content":" For agents This post has a companion gist with copy-paste building blocks and setup instructions: SessionStart Hook Guide.\nClaude Code runs shell hooks on specific events. The SessionStart hook fires once at the beginning of every conversation. Its output is injected into the system context before you type anything, so the agent starts the session already knowing things you\u0026rsquo;d otherwise have to tell it.\nThis matters because the single biggest source of friction in multi-session AI work is stale assumptions. The agent doesn\u0026rsquo;t know what day it is, what changed since last time, or whether the project is in a broken state. A session start hook fixes that by surfacing state that silently invalidates assumptions between sessions.\nThe most useful section here might be scheduled notes: passive, date-keyed markdown files that surface context when you start a session, not on a timer. They fill a gap that cron and memory systems don\u0026rsquo;t cover.\nWhat to surface The sections below are ordered by criticality. Things that gate all other work come first.\nCurrent date and time echo \u0026#34;$(date \u0026#39;+%A %Y-%m-%d %H:%M:%S %Z\u0026#39;) ($(date -u \u0026#39;+%Y-%m-%dT%H:%M:%SZ\u0026#39;))\u0026#34; Output: Sunday 2026-04-26 07:12:20 PDT (2026-04-26T14:12:20Z)\nBoth local and UTC. The agent has no clock. Without this, it will guess the date from its training data or the last message timestamp, and it will be wrong. If your project has any time-sensitive logic (scheduled jobs, market hours, deployment windows, cron), this is load-bearing.\nGit status GIT_STATUS=$(git status -s 2\u0026gt;/dev/null) if [ -n \u0026#34;$GIT_STATUS\u0026#34; ]; then echo \u0026#34;Uncommitted changes:\u0026#34; echo \u0026#34;$GIT_STATUS\u0026#34; | head -10 | sed \u0026#39;s/^/ /\u0026#39; fi Prevents the agent from stepping on uncommitted work from a prior session. It also catches the \u0026ldquo;forgot to commit during handoff\u0026rdquo; case. If the hook shows staged changes, the agent knows to address that before starting new work.\nRecent commits echo \u0026#34;Recent commits (last 8):\u0026#34; git log -8 --pretty=format:\u0026#39; %h %s\u0026#39; | cut -c1-100 Establishes continuity. The agent sees what happened recently and can orient itself without reading session notes. The short hash + subject line is enough. If it needs more detail, it can git show.\nChangelog If your project maintains a changelog, surfacing the latest entry anchors the agent to the current version and the last notable change.\nif [ -f CHANGELOG.md ]; then HEADER=$(grep -m1 \u0026#34;^## \\[\u0026#34; CHANGELOG.md) DESC=$(awk \u0026#39;/^## \\[/{f++; next} f==1 \u0026amp;\u0026amp; NF{print; exit}\u0026#39; CHANGELOG.md | cut -c1-140) echo \u0026#34;Latest changelog: $HEADER\u0026#34; [ -n \u0026#34;$DESC\u0026#34; ] \u0026amp;\u0026amp; echo \u0026#34; $DESC\u0026#34; fi Output: Latest changelog: ## [1.10.0] — 2026-04-25 followed by a one-line summary.\nThis works best when the changelog follows a consistent format. We use Keep a Changelog with semver. Every entry includes a rationale and before/after metrics. The hook only grabs the headline. The full entry is there if the agent needs to read it.\nThe changelog lives at CHANGELOG.md in the repo root. Entries are added as part of the versioning workflow, not retroactively. The agent knows the format and can write entries that match.\nScheduled notes Claude Code has a native schedule feature that runs agents at specific times via cron. Scheduled notes are different. They\u0026rsquo;re passive, date-keyed markdown files that surface context on the first session that matches the date. They don\u0026rsquo;t execute anything.\nThe distinction matters. A cron schedule fires whether or not you have a session. Scheduled notes fire when you show up. They\u0026rsquo;re for things like \u0026ldquo;check if the deploy from Thursday caused issues\u0026rdquo; or \u0026ldquo;re-evaluate this decision after a week of data.\u0026rdquo; Tasks that need a human-in-the-loop session, not an autonomous run.\nImplementation:\nnotes/scheduled/\n├── 2026-04-26.md \u0026nbsp;\u0026nbsp;\u0026nbsp; # surfaces today or any day after\n├── 2026-05-01.md \u0026nbsp;\u0026nbsp;\u0026nbsp; # surfaces starting May 1\n├── 2026-10-01.md \u0026nbsp;\u0026nbsp;\u0026nbsp; # long-dated reminder\n└── README.md The hook iterates over files and shows any with a date \u0026lt;= today:\nTODAY=$(date \u0026#39;+%Y-%m-%d\u0026#39;) SCHED_DIR=\u0026#34;notes/scheduled\u0026#34; if [ -d \u0026#34;$SCHED_DIR\u0026#34; ]; then for f in \u0026#34;$SCHED_DIR\u0026#34;/2*.md; do [ -f \u0026#34;$f\u0026#34; ] || continue FILE_DATE=$(basename \u0026#34;$f\u0026#34; .md) if [[ ! \u0026#34;$FILE_DATE\u0026#34; \u0026gt; \u0026#34;$TODAY\u0026#34; ]]; then echo \u0026#34;[scheduled] $(basename \u0026#34;$f\u0026#34;):\u0026#34; sed \u0026#39;s/^/ /\u0026#39; \u0026#34;$f\u0026#34; fi done fi The default retention strategy is delete-after-addressing: once all items in a file are complete, the file is removed and deferred items get moved to a later date file. This keeps the directory clean and the hook output focused.\nIf you prefer to retain addressed notes (for audit trail, retrospectives, etc.), the bash logic needs one change. Match only today\u0026rsquo;s exact date instead of \u0026lt;=:\nif [[ \u0026#34;$FILE_DATE\u0026#34; == \u0026#34;$TODAY\u0026#34; ]]; then With retention, older addressed notes should be moved to an archive directory (e.g. notes/scheduled/archive/) to avoid accumulating stale output in the hook. Either way, decide the retention strategy upfront and document it where the agent will see it.\nPutting it together The full hook is a single bash script registered in .claude/settings.json:\n{ \u0026#34;hooks\u0026#34;: { \u0026#34;SessionStart\u0026#34;: [ { \u0026#34;hooks\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;command\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;.claude/hooks/session-start.sh\u0026#34; } ] } ] } } The script itself follows one rule: output only what changes assumptions. The agent\u0026rsquo;s context window is finite. Every line of hook output competes with the actual conversation. Keep each section to 1-5 lines of output under normal conditions, with verbose output reserved for error states.\nDesign principle A good session start hook is invisible when everything is fine and loud when something is wrong.\nWhat else could go here The sections above are general-purpose. Depending on your project, you might add:\nCI status — gh run list --limit 1 to surface a failing build before the agent starts writing code against a broken branch Environment health — ping a service, check a lockfile, verify a daemon is running Data freshness — if your project depends on external data, check when it was last updated Blocker detection — check for lock files, circuit breaker flags, or other hard stops that should halt work before it starts The pattern is the same: if the agent would otherwise make a wrong assumption, surface the truth in the hook.\n","permalink":"https://j4c0bs.github.io/posts/session-start-hook/","summary":"Using Claude Code\u0026rsquo;s SessionStart hook to surface project state and kill stale assumptions between sessions.","title":"The Session Start Hook"},{"content":" I'm Jeremy Jacobs, a Staff Data Engineer at Runpod. Writing Data engineering, quantitative finance, and building with LLMs. Posts are co-authored with Claude.\nElsewhere GitHub · LinkedIn\n","permalink":"https://j4c0bs.github.io/about/","summary":"About this blog and its author.","title":"About"}]