Skip to main content

cop

Delegate coding tasks to a GitHub Copilot CLI agent running in a Herdr pane, and collect the result later. cop drives herdr directly (it's a plain subprocess call) — it doesn't need the /herdr skill or any other intermediary to submit a task or collect its result; it's just as happy called from a script or by hand as from a Claude session that happens to be using the /herdr skill for other things.

The pattern is deliberately two steps, not one blocking call:

cop start "Add unit tests for src/foo.py" --dir ~/r/myrepo
# -> prints a job id immediately; the Copilot agent keeps working in its own pane
# (task can also come from stdin for a quick throwaway job, e.g.
#  echo "fix the typo in README" | cop start --dir ~/r/myrepo)

# ... do other things, or poll ...

cop collect <job-id> --wait
# -> blocks until the agent settles, then prints/stores its response

Requirements

  • Must run inside a Herdr-managed pane (HERDR_ENV=1) — it drives the herdr CLI directly over the local socket API.
  • herdr and copilot (GitHub Copilot CLI) on PATH.

Install

uv tool install cop-pilot

or with pip:

pip install cop-pilot

Straight from GitHub:

uv tool install git+https://github.com/vivainio/cop-pilot

For local development:

uv sync

Commands

  • cop start [<task>] --dir <path> [--name <mnemonic>] — opens a new tab in a dedicated cop-tasks workspace (created on first use; unfocused, so it never steals the caller's view), starts a copilot agent there, sends the task, and returns a job id as soon as the agent confirms it started working (it does not wait for the task to finish). The agent is named cop-<hint>, where <hint> is --name if given, else the directory's name (e.g. --name unit-testscop-unit-tests) — readable in herdr agent list / Herdr's agents panel instead of a bare hex id. The job id is only appended (cop-unit-tests-<job-id>) if that plain name is already in use by another live agent. Trust and permission handling (see below) are unconditional. All delegated jobs land in that one workspace, one tab per job, so they never clutter whatever window/tab the caller is in. Herdr workspaces are flat (no parent/child relationship, confirmed via herdr api schema), so this can't be nested "under" the caller's own workspace the way a git worktree nests under its parent repo — a separate workspace is the closest available primitive.
  • cop collect <job-id> [--wait] [--timeout MS] [--raw] — without --wait, does a non-blocking status check; with --wait, blocks until the agent reaches idle, done, or blocked, then reads back its terminal output as the result. By default the pane read is stripped down to just the last turn (see below); pass --raw for the full dump.
  • cop respond <job-id> "<text>" [--raw] — send a follow-up into a job's agent, e.g. to answer a permission/approval dialog that left it blocked.
  • cop show <job-id> [--refresh] — full detail for one job.
  • cop list — table of all known jobs, cached status (no herdr calls, works offline).
  • cop status — table of all known jobs, refreshing every non-terminal job's live status from herdr first.

Every command accepts --json for machine-readable output (useful when another agent is the caller).

Jobs are flat JSON files under ~/.cop/jobs (override with COP_HOME).

Why start always does three things

Herdr's agent-lifecycle detection reports a Copilot pane as idle/ready-for-input based on the screen looking settled — it has no idea what's actually on screen. Three of Copilot's own startup/runtime screens look exactly like that to herdr, but aren't a normal chat prompt, so a task sent into one of them silently goes nowhere (no error, no state change, the job just never starts). start unconditionally works around all three:

  1. Tool/approval prompts — launches copilot with --allow-all-tools --no-ask-user so it never pauses for a permission or clarifying-question prompt. (Copilot's AI-judged --assisted-approval mode can't be used here — it's documented as prompt-mode (-p) only and refuses to start in the persistent interactive session herdr needs.)
  2. Folder trust — a never-before-seen --dir shows an interactive "Confirm folder trust" dialog at startup, before anything else. No CLI flag suppresses it (checked --add-dir, --allow-all-tools, --allow-all) — the only way to skip it is to already be listed in Copilot's own trustedFolders config, so start pre-seeds --dir into ~/.copilot/config.json (textually, leaving the rest of that file — including a live GitHub token — untouched).
  3. Session restore — if a prior Copilot session in the same --dir was left "Interrupted" (e.g. its pane got closed mid-task), the next launch shows a "restore interrupted sessions" picker instead of a normal chat prompt. A fresh --session-id per job skips it.

start also confirms the prompt actually landed (waits for the agent to reach working, retrying a couple of times on agent_prompt_stalled) instead of firing blind — agent_start reporting ready can race a beat ahead of the TUI actually accepting input.

Result extraction and the full session log

start gives each job its own --session-id. Copilot writes a full structured event log for it to ~/.copilot/session-state/<session-id>/events.jsonl (one JSON object per line: session.start, user.message, model.response, tool calls, ...) — shown as session_file in every job. By default, collect/respond read the answer straight from there: the last assistant.message event with phase: "final_answer" has a plain content string, no chrome to parse around.

If the session file is missing, unparseable, or has no final answer yet (e.g. blocked mid-turn), they fall back to scraping the pane — a full-screen dump (startup banner, nav bar, tips, the echoed prompt in its own box, the turn, a status bar, an empty input box) stripped down to just the last turn's content. Pass --raw to get that raw pane dump directly, bypassing both.

session_file itself is worth keeping around regardless — it's the entire session, queryable, for anything a single extracted answer can't show.

Development

uv sync                    # install with dev dependencies
uv run pytest tests/ -v    # run tests
uv run ruff format .       # format
uv run ruff check .        # lint
uv run ty check            # type check

hookmaster init . installs a pre-commit hook (githooks.toml) that runs ruff format --check ..

Known limitations

  • agent read only sees what's on Copilot's alternate screen / Herdr's scrollback of it; a very long response can be truncated. If that happens, ask the task (in a follow-up via respond) to write its full answer to a file and reply with just the path, then read the file directly.
  • One job = one fresh pane + agent. There's no session reuse/continuation across jobs yet (each start begins a brand new Copilot session).

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

cop_pilot-0.1.0.tar.gz (21.6 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

cop_pilot-0.1.0-py3-none-any.whl (17.1 kB view details)

Uploaded Python 3

File details

Details for the file cop_pilot-0.1.0.tar.gz.

File metadata

  • Download URL: cop_pilot-0.1.0.tar.gz
  • Upload date:
  • Size: 21.6 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cop_pilot-0.1.0.tar.gz
Algorithm Hash digest
SHA256 669fd3a683c623fad6f03ca00ff2e340ac6725dc66422540e58db475811faec9
MD5 0da9947716fdae09e6105f4f705aa35a
BLAKE2b-256 0c6b0f6449994d9152f86096a1cd1257c486faf8991b5c416d59671b4cd0cd97

See more details on using hashes here.

Provenance

The following attestation bundles were made for cop_pilot-0.1.0.tar.gz:

Publisher: publish.yml on vivainio/cop-pilot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

File details

Details for the file cop_pilot-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: cop_pilot-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 17.1 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: twine/7.0.0 CPython/3.13.14

File hashes

Hashes for cop_pilot-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 d43895b80a389c7de163e8eeecfa75f22c84b82b8726af6dbc5b06c512f31a11
MD5 bb7aa05f99b24af6e66397ba6f358e5c
BLAKE2b-256 f70b14488a4124e8c6d9ef6698cc61cda944bc269c6d0e34b614338647d4f9e7

See more details on using hashes here.

Provenance

The following attestation bundles were made for cop_pilot-0.1.0-py3-none-any.whl:

Publisher: publish.yml on vivainio/cop-pilot

Attestations: Values shown here reflect the state when the release was signed and may no longer be current.

Release history Release notifications | RSS feed

0.2.0

2 files

This release

0.1.0 This release

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page