Skip to content

Repository files navigation

scopey

CI License: MIT

Scopey — the Scope Guy

It looks like you're writing code.
Would you like help staying in scope?

Keep Claude Code, Codex, Grok, Pi, and OpenCode sessions aligned with your current intent.

Scopey is a lightweight Rust CLI that turns your prompts into a current scope, checks coding-agent activity against it, and surfaces sessions that need your attention. It can inject a correction into the active session and notify you when work drifts off scope.

Scopey is pre-1.0 software. Config and session formats are designed to remain compatible, but may still evolve before the first stable release.

Install

Install the prebuilt binary with Homebrew:

brew install ArchAstro/tools/scopey
scopey setup
scopey doctor

For installation from source, prerequisites are stable Rust and at least one supported coding-agent CLI for live scope extraction. Tests do not require an agent CLI.

Install from the GitHub source repository:

cargo install --git https://github.com/ArchAstro/scopey.git
scopey setup
scopey doctor

Or clone the repository and use the Makefile:

make                 # debug build
make build-release
make install         # cargo install --path . --force
make setup           # release build + hooks + config
make doctor
make verify-models   # probe claude/codex fast defaults
make test            # unit + CLI integration tests
make lint
make release-check

Tests cover recursion guards, session storage, JSONL logs, judgement parsing, hook detection, config loading, model resolution, and hook CLI paths under isolated SCOPEY_HOME.

Or without make:

cargo install --path .
scopey setup
scopey doctor
scopey models --verify

Harnesses

Flag Installs
--claude / --no-claude ~/.claude/settings.json
--codex / --no-codex ~/.codex/hooks.json (trust via /hooks)
--grok / --no-grok ~/.grok/hooks/scopey.json
--pi / --no-pi ~/.pi/agent/extensions/scopey.ts (restart Pi)
--opencode / --no-opencode ~/.config/opencode/plugins/scopey.js
scopey setup --force
scopey setup --no-claude --no-codex --grok --no-pi --no-opencode   # grok only

Subagents

Scopey tracks the conversation between you and the top-level agent and stays out of subagent/child-agent sessions entirely (ignore_subagents = true, default): their prompts come from the orchestrating agent, not from you, and injecting scope reminders into delegated work derails it. Suppressed events do not count tools, create sessions, schedule judges, or inject anything; each is logged at debug level as hook.subagent.

Detection per harness:

Harness Signal
Claude Code agent_id in the hook input — present on every hook fired inside a Task subagent, and only there. Bare agent_type (a claude --agent <name> top-level session) keeps full scopey behavior. There is no settings.json matcher or env var for this; filtering must parse hook stdin.
Codex Same two fields. Codex multi-agent (collaborationspawn_agent) runs children whose PostToolUse carries agent_id + agent_type while session_id stays the parent's — verified live. The orchestrator's own spawn call still counts; collaborationwait_agent is bookkeeping noise.
Grok No subagent payload documented; generic markers apply.
Pi The extension skips events whose event/context carries a subagent or parent-session marker before invoking scopey.
OpenCode The plugin tracks child sessions (session objects with a parent id) from session.created and never calls scopey for them; a parentID reaching the binary is dropped there too.

Generic markers the binary honors from any harness or adapter: a truthy subagent/is_subagent field, a non-empty parent_session_id (any common spelling), a transcript path under a subagents/ folder, or SCOPEY_SUBAGENT=1 in the hook environment. Set ignore_subagents = false to restore the old behavior.

Model selection

Summarize/judge use a cheap/fast model on the same harness as the agent session when possible.

Config Default Meaning
model_runner auto Use the session harness when available, or pin claude, codex, grok, pi, or opencode
model auto auto → product shipped fast tier
claude_fast_model haiku Claude Code alias for current fast Haiku
codex_fast_model gpt-5.6-terra Codex mini-like / lower-cost GPT-5.6 tier

Claude invoke: OAuth-compatible claude -p --model <fast> first, with --bare as an API-key/provider fallback. Codex invoke: codex exec --ephemeral -m <fast> --output-last-message …

scopey models              # print resolution table
scopey models --verify     # live one-word probes

Session insights

scopey insights turns stored judgement history into a cross-session drift report. Off-track sessions sort first, and each result includes its scope, evaluated tool windows, attention rate, and the judge's explanation.

scopey insights                              # recent overview
scopey insights --off-scope --since 2026-07-01
scopey insights --session 019fb598 --details # exact id or unique prefix
scopey insights --date 2026-07-30 --harness codex
scopey insights --cwd . --verdict warning
scopey insights --since 2026-07-01T09:00:00-07:00 --json
scopey insights --include-empty                 # audit raw zero-tool stores

Date-only filters use the machine's local timezone. --date selects one calendar day; --since and --until accept either YYYY-MM-DD or RFC3339. Verdict filters select sessions containing that signal while retaining all their evaluated windows, so the reported attention rate still has useful context.

Zero-tool stores are excluded from default reports because they contain no agent trajectory to assess; the report states how many it excluded. Exact --session lookups include an empty match automatically, and --include-empty restores the raw cross-session view. Reports also show session evaluation coverage, judge failure rate, and contaminated scope records. Historical or new warning/off-track results that explicitly say the transcript or tool evidence is missing are counted as insufficient-evidence, not agent drift.

--json exposes the same totals, data-quality counts, per-session scope quality, summaries, details, tool windows, and timestamps for scripts.

Session logs

Hooks and background jobs append structured JSONL for debugging:

~/.scopey/logs/<session_id>.jsonl
scopey logs                              # list recent session log files
scopey logs --session <id>               # pretty print
scopey logs --session <id> --tail 50
scopey logs --session <id> --level warn
scopey logs --session <id> --event guard
scopey logs --session <id> --follow
scopey logs --session <id> --raw
scopey logs --session <id> --path

Runtime safety

Scopey keeps hooks responsive by doing model calls in bounded background jobs. It prevents its own model subprocesses from triggering Scopey again, limits machine-wide concurrency, and serializes work per session. If a session is busy, its hook returns without delaying the coding agent and queued analysis is picked up later.

Use scopey doctor to check an installation. scopey purge stops stale background jobs, while scopey setup --force refreshes installed hooks. scopey uninstall removes hooks but keeps local data; add --purge-data to remove Scopey's config, sessions, logs, and locks as well.

Herdr awareness

Herdr is an agent multiplexer with its own notification + state API.

When Claude/Codex run inside a Herdr pane, scopey can:

  1. Notify via Herdrherdr notification show … --sound request
    (Herdr routes to in-app toast, outer terminal, or OS depending on [ui.toast] delivery)
  2. Report pane stateherdr pane report-agent … --state blocked so the sidebar shows needs-attention
scopey config Default Meaning
notify_backend auto auto → Herdr if available, else OS; or pin herdr / os / command
herdr_report_state true Also mark the pane blocked on off-track/warning
herdr_notify_sound (auto) none | done | request
notify_fallback_os_if_herdr_disabled true If Herdr returns shown=false, use OS notify
scopey herdr           # detection status
scopey herdr --probe   # test toast path

Enable toasts in Herdr if probes say shown=false:

# ~/.config/herdr/config.toml
[ui.toast]
delivery = "system"   # or "herdr" / "terminal"

How it works

UserPromptSubmit  →  scopey hook user-prompt
                     · append user_prompt to session JSON
                     · background: scopey summarize → scope_requirements

PostToolUse / PostToolBatch  →  scopey hook post-tool
                     · tool_call_count += batch size
                     · if ready off_track/warning judgement → inject correction
                     · else every M tools → inject scope reminder
                     · every N tools → trajectory_mark + background scopey judge
                       (judgement becomes injectable at the *next* N boundary ≈ 2N lag)

Stop  →  scopey hook stop
                     · inject any pending correction

Scope extraction is intentionally current-state, not an ever-growing union of the conversation. Each new prompt is an authoritative mutation that may add, subtract, modify, or replace requirements. Additions and modifications preserve unaffected active requirements; explicit removals, contradictions, and replacements win. Questions add a read-only obligation without becoming new implementation authorization. Machine-generated task notifications are treated as context, not new user goals. Each transition is written to the structured session log as job.summarize.transition, including the operation and before/after scope hashes. If the summarizer model is unavailable, the fallback preserves only the latest request instead of replaying the full prompt history.

Development checks

Contributors should run make install-pre-commit once after installing pre-commit. The checked-in hook runs cargo fmt --all -- --check before each commit. CI enforces the same formatting check, Clippy, rustdoc warnings, clean-runner tests, and package assembly. See CONTRIBUTING.md for the complete workflow. CI also checks the locked dependency graph against the RustSec advisory database.

Session store path

Sessions are keyed by session_id only (not cwd). One agent session stays one file even when the agent cds into subdirectories.

~/.scopey/work/by-id/<session_id>.json

SessionData.cwd still tracks the latest working directory. On first open, legacy files at work/<escaped-cwd>/<session_id>.json are migrated into by-id/.

Config (~/.scopey/config.toml)

Key Default Meaning
n_tool_calls 10 Journal + start background judge every N tools
m_reminder 20 Inject scope reminder every M tools
model_runner auto Session harness, or pin any supported runner
model auto Shipped fast tier for that runner
claude_fast_model haiku Claude fast alias
codex_fast_model gpt-5.6-terra Codex fast/mini-like tier
notify_on_off_track true Desktop alert on off-track judgement
ignore_subagents true No-op for subagent/child-agent hook events (see Subagents)
log_raw_events false Debug-log each hook's raw stdin payload (contains prompts)

Project overlay: <cwd>/.scopey/config.toml wins when present.

Commands (models: read each --help)

scopey --help
scopey setup --help
scopey doctor
scopey config
scopey config --init
scopey status --session-id <id>
scopey sessions
scopey insights --off-scope --since 2026-07-01
scopey path escape --cwd .
scopey path session-file --cwd . --session-id <id>
scopey hook user-prompt     # stdin: harness JSON
scopey hook session-start
scopey hook post-tool
scopey hook stop
scopey summarize --session-id <id> --cwd .
scopey judge --session-id <id> --cwd . --from-count 0 --to-count 10
scopey notify --title scopey --body "test"

Hook contract for harness authors

  • stdin: normalized harness event JSON (session_id, cwd, prompt / tools, transcript_path, …).
  • stdout: harness-compatible injection JSON when steering, else empty.
  • stderr: diagnostics.
  • Hooks must stay fast; model work is detached (~/.scopey/logs/).

Injection shape:

{
  "hookSpecificOutput": {
    "hookEventName": "PostToolUse",
    "additionalContext": ""
  }
}

Manual smoke test

export PATH="$PWD/target/debug:$PATH"
scopey setup --force --no-codex

# simulate user prompt
echo '{"session_id":"demo1","cwd":"'"$PWD"'","prompt":"Only refactor pathutil tests; do not touch main.rs"}' \
  | scopey hook user-prompt

scopey sessions
scopey status --session-id demo1 --cwd .

# simulate tools (will schedule judge every N)
for i in $(seq 1 10); do
  echo '{"session_id":"demo1","cwd":"'"$PWD"'","hook_event_name":"PostToolUse","tool_name":"Bash"}' \
    | scopey hook post-tool
done

scopey status --session-id demo1 --cwd . --raw | head

Privacy

Session files and logs under ~/.scopey/ contain user prompts and trajectory excerpts. Treat that directory like other agent transcripts. Do not commit it.

Scopey invokes locally installed third-party agent CLIs, which may send prompts to their configured providers under those providers' terms. Review your harness configuration before using Scopey with sensitive material. See SECURITY.md for the complete security model and private reporting process.

License

MIT. See CONTRIBUTING.md and CODE_OF_CONDUCT.md before participating. Maintainers should follow RELEASING.md before changing repository visibility or publishing a version.

Scopey is an independent project and is not affiliated with or endorsed by the vendors of Claude Code, Codex, Grok, Pi, or OpenCode. Product names are used only to describe compatibility.

About

Keep Claude/Codex/Grok/Pi/OpenCode sessions on scope

Resources

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Releases

Packages

Used by

Contributors

Languages