Summary
Add an optional, default-OFF hook to the CCY launch path that lets an
in-container wrapper command be prepended to the claude invocation. When the
hook is unset (the default), CCY behaves exactly as it does today — zero behaviour
change. When enabled, it allows claude to be run under a supervisor process
(a PTY wrapper) in the same namespace as claude.
This is the CCY-side integration seam for a capability being built in the
claude-code-hooks-daemon
repo (its Plan 00135): a thin PTY supervisor that can, opt-in, auto-issue a
whitelisted slash command (flagship: /compact at a custom, lower context-usage
threshold) into a live session. The daemon side owns the supervisor and all its
safety rails; this issue is only the launch-path seam so CCY can run it.
Why this shape (and not something bespoke)
- Generic, unopinionated seam. CCY should not know what the wrapper is — it
just prepends an operator-provided command. That keeps this repo decoupled from
the daemon's release cadence and avoids baking in supervisor-specific logic.
- In-container, in-namespace. The wrap must happen at
entrypoint.sh's final
exec (inside the container), NOT on the host around podman run. A host-side
tmux/wrapper would sit across the PID-namespace boundary, so the wrapper could
only see podman, not claude — it could not observe Claude's I/O or reap it.
In-container exec claude-supervise -- claude … gives the supervisor Claude's
own PTY and a direct waitpid liveness signal.
- Default-off = fail-safe. Unset env var → today's
exec "$@". No dormant
behaviour, nothing to regress.
The change (two files)
1. files/var/local/claude-yolo/entrypoint.sh — the seam (line 266)
Today:
# Execute the command
exec "$@"
Proposed:
# Execute the command.
# Optional supervisor wrap (default OFF): if CCY_CLAUDE_WRAPPER is set, prepend it
# to the claude invocation. Unset => unchanged behaviour. The wrapper command must
# exist on PATH inside the image (see supervisor delivery below).
if [[ -n "${CCY_CLAUDE_WRAPPER:-}" ]]; then
# Word-split the operator-provided wrapper into argv (e.g. "claude-supervise --")
read -ra _ccy_wrapper <<< "$CCY_CLAUDE_WRAPPER"
exec "${_ccy_wrapper[@]}" "$@"
fi
exec "$@"
Shellcheck-clean (array expansion, no unquoted word-split of "$@"). No error
hiding. Fail-fast preserved — if the wrapper command is missing, exec fails
loudly rather than silently falling back.
2. files/var/local/claude-yolo/claude-yolo — opt-in flag + env forward
-
Add a --supervise flag, mirroring the existing --headless handling
(declare SUPERVISE_MODE=false near line 421; parse near line 492).
-
When --supervise is given (or a config var is set), export
CCY_CLAUDE_WRAPPER (default e.g. claude-supervise --, overridable) and add
it to the podman run -e list (near lines 2745–2766) so it reaches the
container:
-e "CCY_CLAUDE_WRAPPER=${CCY_CLAUDE_WRAPPER:-}" \
With --supervise absent, CCY_CLAUDE_WRAPPER is empty and the entrypoint
branch above is a no-op.
-
Document --supervise in the usage/help block (near line 155).
-
CCY_VERSION bump required (minor — new backward-compatible feature), per
CLAUDE/ContainerRules.md. The pre-commit hook enforces this.
Supervisor delivery (dependency — NOT part of the first mergeable unit)
The wrapper command (claude-supervise) is being built in the hooks-daemon repo
(Plan 00135, ARCH-B). It must be present on PATH inside the CCY image before
--supervise does anything useful. Options to decide when it lands:
- Vendor into the image —
Dockerfile COPYs a self-contained
claude-supervise (Python, stdlib-only) to a known path. Fully self-contained,
no runtime cross-repo coupling; costs a periodic sync of the vendored copy.
- Install from a daemon release —
Dockerfile fetches it during build.
Single source of truth; adds a build-time network dependency.
- Resolve at runtime from the project's daemon venv — entrypoint locates the
installed daemon's console script. Most decoupled, but fiddly PATH resolution.
Recommendation: (1) vendor for the first cut (matches this repo's
self-contained image philosophy), revisit (2) later. Either way this is a separate
change from the seam above and gated behind the same default-off flag.
Constraints respected
- Optional & default-off — unset env var = today's behaviour, verifiably.
- IaC only — all via
entrypoint.sh + claude-yolo + Dockerfile, deployed
by image rebuild on the host. No manual container edits.
- Fail-fast — no
|| true, no error hiding; missing wrapper fails loudly.
- Version-bumped — CCY_VERSION minor bump;
REQUIRED_CONTAINER_VERSION
bump only when the vendored supervisor lands (forces the rebuild).
- QA —
./scripts/qa-all.bash (bash -n + shellcheck + semgrep) before commit.
Staged plan
- Seam only (this issue, mergeable alone): entrypoint wrapper branch +
--supervise flag + env forward + version bump + help/docs. Ships dormant
(default-off); no supervisor yet, so --supervise is a documented no-op-until-
supervisor-present.
- Supervisor delivery: vendor
claude-supervise into the image once the
daemon side (Plan 00135 Slice 2) is ready; bump REQUIRED_CONTAINER_VERSION.
- Enable & dogfood:
ccy --supervise, verify auto-/compact at threshold in
a real session, with the supervisor in --dry-run first, then armed.
Open questions to confirm
- Flag name + trigger:
ccy --supervise (recommended), a host_vars/config var,
or both?
- Supervisor delivery: vendor (1), fetch-on-build (2), or venv-resolve (3)?
- Should step 1 (the dormant seam) merge on its own now, or wait and land the
seam + supervisor together to avoid a merged-but-inert --supervise flag
(YAGNI)?
Source design & audit: hooks-daemon Plan 00135 (plan-audit-fable-1.md,
Decision G — ARCH-B PTY supervisor, CCY-first).
Summary
Add an optional, default-OFF hook to the CCY launch path that lets an
in-container wrapper command be prepended to the
claudeinvocation. When thehook is unset (the default), CCY behaves exactly as it does today — zero behaviour
change. When enabled, it allows
claudeto be run under a supervisor process(a PTY wrapper) in the same namespace as
claude.This is the CCY-side integration seam for a capability being built in the
claude-code-hooks-daemon
repo (its Plan 00135): a thin PTY supervisor that can, opt-in, auto-issue a
whitelisted slash command (flagship:
/compactat a custom, lower context-usagethreshold) into a live session. The daemon side owns the supervisor and all its
safety rails; this issue is only the launch-path seam so CCY can run it.
Why this shape (and not something bespoke)
just prepends an operator-provided command. That keeps this repo decoupled from
the daemon's release cadence and avoids baking in supervisor-specific logic.
entrypoint.sh's finalexec(inside the container), NOT on the host aroundpodman run. A host-sidetmux/wrapper would sit across the PID-namespace boundary, so the wrapper could
only see
podman, notclaude— it could not observe Claude's I/O or reap it.In-container
exec claude-supervise -- claude …gives the supervisor Claude'sown PTY and a direct
waitpidliveness signal.exec "$@". No dormantbehaviour, nothing to regress.
The change (two files)
1.
files/var/local/claude-yolo/entrypoint.sh— the seam (line 266)Today:
Proposed:
Shellcheck-clean (array expansion, no unquoted word-split of
"$@"). No errorhiding. Fail-fast preserved — if the wrapper command is missing,
execfailsloudly rather than silently falling back.
2.
files/var/local/claude-yolo/claude-yolo— opt-in flag + env forwardAdd a
--superviseflag, mirroring the existing--headlesshandling(declare
SUPERVISE_MODE=falsenear line 421; parse near line 492).When
--superviseis given (or a config var is set), exportCCY_CLAUDE_WRAPPER(default e.g.claude-supervise --, overridable) and addit to the
podman run-elist (near lines 2745–2766) so it reaches thecontainer:
-e "CCY_CLAUDE_WRAPPER=${CCY_CLAUDE_WRAPPER:-}" \With
--superviseabsent,CCY_CLAUDE_WRAPPERis empty and the entrypointbranch above is a no-op.
Document
--supervisein the usage/help block (near line 155).CCY_VERSION bump required (minor — new backward-compatible feature), per
CLAUDE/ContainerRules.md. The pre-commit hook enforces this.Supervisor delivery (dependency — NOT part of the first mergeable unit)
The wrapper command (
claude-supervise) is being built in the hooks-daemon repo(Plan 00135, ARCH-B). It must be present on PATH inside the CCY image before
--supervisedoes anything useful. Options to decide when it lands:DockerfileCOPYs a self-containedclaude-supervise(Python, stdlib-only) to a known path. Fully self-contained,no runtime cross-repo coupling; costs a periodic sync of the vendored copy.
Dockerfilefetches it during build.Single source of truth; adds a build-time network dependency.
installed daemon's console script. Most decoupled, but fiddly PATH resolution.
Recommendation: (1) vendor for the first cut (matches this repo's
self-contained image philosophy), revisit (2) later. Either way this is a separate
change from the seam above and gated behind the same default-off flag.
Constraints respected
entrypoint.sh+claude-yolo+Dockerfile, deployedby image rebuild on the host. No manual container edits.
|| true, no error hiding; missing wrapper fails loudly.REQUIRED_CONTAINER_VERSIONbump only when the vendored supervisor lands (forces the rebuild).
./scripts/qa-all.bash(bash-n+ shellcheck + semgrep) before commit.Staged plan
--superviseflag + env forward + version bump + help/docs. Ships dormant(default-off); no supervisor yet, so
--superviseis a documented no-op-until-supervisor-present.
claude-superviseinto the image once thedaemon side (Plan 00135 Slice 2) is ready; bump
REQUIRED_CONTAINER_VERSION.ccy --supervise, verify auto-/compactat threshold ina real session, with the supervisor in
--dry-runfirst, then armed.Open questions to confirm
ccy --supervise(recommended), ahost_vars/config var,or both?
seam + supervisor together to avoid a merged-but-inert
--superviseflag(YAGNI)?
Source design & audit: hooks-daemon Plan 00135 (
plan-audit-fable-1.md,Decision G — ARCH-B PTY supervisor, CCY-first).