Install
$ agentstack add skill-melodic-software-claude-code-plugins-lanes ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ✓ Network access No
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ✓ Environment & secrets No
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
Pre-computed context
claude CLI: !command -v claude >/dev/null 2>&1 && echo "present ($(claude --version 2>/dev/null))" || echo "MISSING (required)" jq: !command -v jq >/dev/null 2>&1 && echo "present" || echo "MISSING (required)" Repo root: !git rev-parse --show-toplevel 2>/dev/null || echo "unknown (pass --repo)" Lane config: !c="${CLAUDE_OPS_LANES_CONFIG:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.work/lanes.json}"; [[ -f "$c" ]] && echo "$c ($(jq -r '(.lanes//[])|length' "$c" 2>/dev/null) lanes)" || echo "absent ($c) — author one (see context/config.md)"
Variables
Arguments: $ARGUMENTS
Purpose
Running N loop lanes on a machine means a daily ritual: for each lane, cancel its loop, clear, and re-paste its canonical prompt. This skill collapses that to one command. start/restart pull the repo and refresh the plugin marketplace once, then launch each configured lane as a named background session seeded from the lane's canonical prompt file, mirroring that lane's model/effort onto the launch. status/stop read and manage those sessions through the CLI's own background-session surface.
Owns only its own lanes. stop/restart act on a session only when its name is a lane in the resolved config — a hand-started session (e.g. an interactive work window, or an unrelated PR Babysit) is never stopped by this skill.
A relaunch is the only context reset a loop lane gets
/loop re-invokes its prompt in the same session (it self-paces via ScheduleWakeup), so a lane's context carries forward across every cycle, subagent return, and operator turn — the loop never starts a fresh one. A running loop cannot reset its own context on demand: a built-in like /clear issued from a loop re-run reaches the model as plain text, not an executed command, so the model cannot /clear itself. Any "restart the loop when context passes ~N%" discipline is therefore an admonition with no in-session enforcement — the lane has no reliable way to measure its own context usage and no way to act on the threshold if it could.
The context change that does happen automatically in-session is Claude Code's auto-compaction: when the conversation nears the model's input limit, older history is summarized in place to free space. That is not a reset — the session continues on a lossy summary of what came before, not a fresh context — so a lane that runs long enough will lose earlier context to compaction well before any operator relaunch, not keep every turn until then.
restart (stop the session, relaunch from the canonical prompt) is the only fresh-session reset: the relaunched lane starts a fresh session seeded from the prompt file, carrying none of the prior conversation. That reset is operator- or launcher-initiated, not automatic — there is no per-cycle or threshold trigger that runs restart for you today. Auto-compaction can still fire between restarts, but it only summarizes; it does not clear the accumulated, increasingly degraded context. Until an automatic relaunch trigger exists, treat the periodic restart (e.g. the morning refresh) as the mechanism that keeps long-running lanes off a stale, repeatedly-compacted context, and do not let a lane prompt assume each /loop cycle begins with fresh context.
Run it
bash "${CLAUDE_PLUGIN_ROOT}/skills/lanes/scripts/lane-launcher.sh" --data-dir "${CLAUDE_PLUGIN_DATA}" $ARGUMENTS
Print the script's output verbatim — it is the deliverable. Preview any mutating run first with --dry-run (prints the exact claude/git commands, seeds nothing, kills nothing).
--data-dir is passed explicitly, not left to the script's own $CLAUDE_PLUGIN_DATA env-var fallback. Per plugins-reference, ${CLAUDE_PLUGIN_DATA} is exported as a real environment variable only to hook processes and MCP/LSP subprocesses — for skill content it instead resolves by inline text substitution anywhere the placeholder appears in the rendered skill body, exactly like ${CLAUDE_PLUGIN_ROOT} above. A script this skill shells out to via the Bash tool does not inherit CLAUDE_PLUGIN_DATA as an env var, so leaving --data-dir off here would silently fall through to lane-launcher.sh's own ~/.claude/plugins/data/claude-ops guess instead of the marketplace-qualified directory Claude Code actually resolves. $ARGUMENTS comes after --data-dir, so an explicit --data-dir the caller passes in $ARGUMENTS still wins (last flag wins in lane-launcher.sh's parser).
Action Router
Parse $ARGUMENTS for the action (first token); remaining tokens are lane names (targets for restart/stop; an unknown name is rejected).
| Action | Mutates | Description | |---|---|---| | start (default) | Yes | Pull + marketplace update, then launch every configured lane not already running | | restart [lane...] | Yes | Pull + marketplace update, then stop-and-relaunch each target lane (all, or named) | | status | No | Per-lane table: model, effort, running/stopped, and the live sessionId | | stop [lane...] | Yes | Stop each running target lane (all, or named) via claude stop |
Options: --config FILE, --repo DIR, --no-pull, --no-update, --dry-run, --agents-json FILE (read the session list from a file instead of the live CLI — offline/scripted reuse), --data-dir DIR (base dir for the per-lane launch-commit marker; default $CLAUDE_PLUGIN_DATA). Exit codes: 0 ok · 3 bad argument/config · 4 prerequisite missing or repo/config unresolved.
Lane config
Lanes are defined in a JSON config, resolved first-hit-wins: --config FILE → $CLAUDE_OPS_LANES_CONFIG → /.work/lanes.json. Each lane carries a name, a prompt file path, and optional model/effort/settings (a session-only claude --settings override — e.g. opting the lane into the autonomy plugin's lane-stop gate). The full schema, resolution rules, and the prompt-storage seam live in [context/config.md](context/config.md) — read it before authoring a config.
Prompt storage is provisional (composes with #480). Today prompt files live in a session-local .work dir (prompt_dir, default .work). Issue #480 (loop-prompt authoring skill) is slated to own durable prompt storage. When it lands, repoint prompt_dir at that home; the launcher resolves the prompt dir in exactly one place (resolve_prompt_dir in the script), which is the single seam to update.
Mid-session staleness & restart cadence
A running lane keeps the skill versions it loaded at launch: a fix merged to a plugin the lane runs does not reach that lane mid-session. This is not a missing feature to build around — it is verified Claude Code behavior (a live session keeps its launch-time plugin versions, /loop never re-reads a skill's body on later cycles, and a loop can't self-trigger /reload-plugins). Restart is the honest refresh mechanism — the same restart that clears context bloat (#496). Detect an unconsumed self-fix with a read-only git probe against the repo's default branch, then restart that lane at its next cycle boundary. The probe reads the launch commit lane-launcher.sh records per lane at start/restart (${CLAUDE_PLUGIN_DATA}/lanes//-launch-commit, #792 — the data directory is plugin-wide, so `, a digest of the repo's canonical path, keeps a conventional work` lane in two different checkouts from sharing one marker) — no manual fill-in needed. Full reasoning, the probe, and the cadence live in [context/refresh.md](context/refresh.md) — read it before answering "why is my merged fix not live in the lane?" or setting a restart frequency.
Carry this line into that probe — it is the data_dir assignment context/refresh.md deliberately leaves unresolved, because only skill content (this file) substitutes the placeholder:
data_dir="${CLAUDE_PLUGIN_DATA}"
Copy it as it renders here, already substituted to an absolute path. Writing the placeholder — or a ${CLAUDE_PLUGIN_DATA:-…} env fallback — inside context/refresh.md would not work: that file is read raw, and per plugins-reference CLAUDE_PLUGIN_DATA reaches only hook and MCP/LSP subprocesses as a real environment variable, never a script the Bash tool runs. The probe would then read the unqualified ~/.claude/plugins/data/claude-ops guess, find no marker, and skip the staleness check silently.
Verified CLI surface
The launcher shells out only to primitives confirmed on this machine's claude (--help / real invocation): claude --bg -n [--model M] [--effort E] [--settings JSON] "" (launch a named background session, return immediately; --settings accepts inline JSON and applies session-only, per the CLI reference), claude agents --json (list active sessions: pid, cwd, kind, startedAt, sessionId, name, status), claude stop (stop one session; conversation kept, resumable with claude attach), and claude plugin marketplace update. There is no claude agents stop verb — stop resolves the sessionId from agents --json and only for a configured lane name.
Gotchas
- No durable prompt home yet.
.workis session-local; a fresh machine/session
has no prompts until they are authored there (or prompt_dir is pointed at a committed dir). This is the #480 dependency, not a bug.
- Name is the identity. Lanes are matched by session
nameand `kind:
background — every lane is launched with --bg`, so an interactive window sharing a lane name is never matched or stopped. Two lanes must not share a name; a hand-started background session sharing a lane name would still be treated as that lane, so keep lane names distinct from ad-hoc background session names.
startis idempotent-ish,restartis not.startskips a lane already
running; restart always stops-and-relaunches (discarding the running lane's in-flight conversation). Use start for "bring up whatever is down".
- A missing/empty prompt file skips that lane (with an error) rather than
launching an empty session. status flags [prompt MISSING].
- The launch-commit marker is per-machine and best-effort. It lives under
${CLAUDE_PLUGIN_DATA} (a per-machine dir, not synced), so a lane restarted on a different machine has no marker there yet. A write failure only warns — it never fails an already-launched (or already-stopped-and-relaunched) lane — so a missing marker means "never started here via lane-launcher.sh", not "launcher broken". A (re)start that cannot record its commit also deletes any marker the previous launch left, so "missing" always beats a stale commit the probe would otherwise trust.
- A lane name must be a single path component. It is the marker's filename,
so config preflight exits 3 on a name containing / or \, or equal to . or .. — otherwise two distinct lanes could share one marker.
Per-cycle deterministic scripts (#538)
Two lane-cycle mechanics need no reasoning, so they are scripted here and a lane prompt references the script instead of re-deriving the work every cycle. Both follow lane-launcher.sh's conventions (jq CRLF wrapper, the --help header as the full contract, explicit exit codes). The header is the source of truth for each — the summary below is a pointer, not a copy.
scripts/machine-behavior.sh— emits the lane's MACHINE-BEHAVIOR block (gh
identity, clone path, worktree inventory, installed plugin versions) as a verbatim-printable text block. Pure environment inspection: it emits only mechanically unambiguous facts and deliberately does NOT compute "deviations from standing rules" — that stays a model judgment made by reading these facts against the prose rules. Plugin versions are the INSTALLED runtime versions, which per [context/refresh.md](context/refresh.md) can lag repo HEAD mid-session (the honest number for a running lane). --plugin (repeatable) scopes the block to the plugins a lane runs.
scripts/telemetry-upsert.sh— maintains exactly ONE marker-identified
telemetry comment on a tracking issue, editing it in place instead of posting a second (the interim home of the #502 telemetry contract). Given --issue N --marker STR --body-file PATH, it writes a machine-detectable sentinel ` as the comment's first line, finds that sentinel across ALL comments (paginated — a match on any page prevents a duplicate) and PATCHes it; failing that (first migration off a hand-authored comment) it adopts the most recent comment BY THE AUTHENTICATED USER carrying the raw marker text; else it creates one. STR is [A-Za-z0-9:.-]+ (so it can never close the HTML comment early), and one writer identity owns a given marker. Body input: prefer --body-file - (stdin) for a body generated in memory (e.g. piped from machine-behavior.sh); a real --body-file PATH must resolve under --body-dir (default $CLAUDEPLUGIN_DATA`), may not be a symlink, and is capped at 64 KiB — a prompt-driven script must not be coaxed into posting an arbitrary file (a secret, a token store) as a public comment.
Cross-references
- [context/refresh.md](context/refresh.md) — why a running lane can't hot-reload a
merged fix to its own skills, the git staleness probe, and the restart cadence (#514).
/claude-ops:plugins— the authoritative, richer plugin-fleet sync (scope
divergence, new-catalog installs). This skill's marketplace refresh is the light claude plugin marketplace update step of a launch, not a substitute.
/claude-ops:morning-brief— reads the loop-lane telemetry (per-lane
last-cycle freshness). This skill starts/stops the lanes that emit it.
- #480 (loop-prompt authoring skill) — forward dependency that will own durable
prompt storage. #496 (context economy / restart discipline) — why lanes get restarted. #502 (telemetry) — the per-lane telemetry the running lanes feed.
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: melodic-software
- Source: melodic-software/claude-code-plugins
- License: MIT
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.