AGENT SKILL

operate-prowl

ALWAYS invoke this skill when a workflow needs a public Prowl operation or a correlated delegation handback between Prowl coding agents. NEVER run Prowl command help or construct the public CLI command directly when this capability is available.

License 许可
MIT
所属仓库
outcomeeng/plugins
最近更新
2026-11-11
Stars
★ 1
Forks
1

01 前言 Front Matter

name
operate-prowl
description
ALWAYS invoke this skill when a workflow needs a public Prowl operation or a correlated delegation handback between Prowl coding agents. NEVER run Prowl command help or construct the public CLI command directly when this capability is available.
allowed-tools
Write, Bash(python3 "${CLAUDE_SKILL_DIR}/scripts/prowl_environment.py":*), {{! tool('ask_user') !}}
argument-hint
<operation, delegation, or JSON request>

02 资源文件 1

03 SKILL.md 正文

<objective> A versioned JSON Prowl operation result or one correlated terminal delegation handback, with complete public identities and checked command status preserved verbatim. </objective>

<operation_surface>

The source-owned operation names are:

Operation Arguments Mutation authorization
list none no
agents none no
read one selector; optional last, waitStable, stability bounds no
send one selector, text; optional noEnter, noWait, capture, timeout with the constraints below no
key one selector, key; optional repeat required
focus one selector required
tab-create optional selector and path required
tab-close one selector; optional force required
pane-close one selector; optional force required
open optional path required

A selector is exactly one of target, worktree, tab, or pane. Preserve its complete public value. For send, capture cannot combine with noEnter or noWait, and timeout cannot combine with noWait. The adapter always requests public JSON and owns every Prowl command token and flag.

</operation_surface>

<operator_target_resolution>

An operator names a target by where the work lives — an absolute worktree path, repository directory, or working directory. Resolve that path through one resolve-target invocation before operating. Write {"schemaVersion":1,"path":"<absolute-operator-supplied-path>"} to a request file per <invocation_forms> and submit it:

python3 "${CLAUDE_SKILL_DIR}/scripts/prowl_environment.py" resolve-target < <request-file>

The resolver runs the public agents operation once and returns its complete checked result under inventory, every complete participant under participants, the complete caller selected from PROWL_PANE_ID or the exact PROWL_WORKTREE_PATH fallback, and non-caller path matches under candidates. When both caller values exist, both must identify the same participant. Each candidate carries its complete participant metadata and a sendRequestTemplate with that pane already selected, noWait: true, and text: null. Fill text with the semantic payload; never repair the JSON through shell substitution.

Use the one candidate directly when status is succeeded. On identity-ambiguous with a complete caller, use {{! tool('ask_user') !}} for one single-select question. Number candidates in resolver order; show each candidate's complete pane, worktree, branch, and repository; and map the answer back to that exact captured candidate, including its sendRequestTemplate. When the runtime's option cap is below the candidate count, include the complete numbered inventory in the question and accept an exact candidate number through its free-form response; never omit a candidate. On identity-ambiguous with caller: null, report the exact detail as an unresolved caller-identity conflict and stop; no candidate choice can resolve it. On identity-unavailable, report the supplied path and the returned participant worktrees. The resolver performs no send in every result state.

A target that is not a coding-agent pane is outside what agents returns, so no path match is available for it. Say that the operator's target is not among the agent panes and name the ones that are, rather than falling back to an inventory that carries no worktree to match.

resolve-target resolves active pane targets from agents. When the supplied path matches no non-caller participant, return identity-unavailable with the complete pane inventory. A sidebar worktree that has never been entered has no pane UUID and cannot receive a message yet; an explicitly authorized open request can activate an operator-known path, after which resolve-target can run again. The resolver never probes with open or claims whether an unmatched path is known to Prowl.

Report the target back to the operator as the supplied path while using the selected template's pane internally. Never ask the operator for a pane UUID or guess by focus, position, or title.

</operator_target_resolution>

<workflow>

  1. Interpret $ARGUMENTS as one target resolution, low-level operation, handback plan, delegation request, or terminal handback. When it is empty, require a concrete operation before running the adapter. Resolve an absolute worktree, repository, or working-directory target per <operator_target_resolution> before building its operation request.
  2. For list, agents, read, or send, build this source-owned request shape and set only arguments the operation accepts:
{
  "schemaVersion": 1,
  "operation": "agents",
  "arguments": {}
}

list inventories instantiated terminal panes only. A worktree visible in Prowl's sidebar but never entered in the current app process can be absent from list because it has no pane UUID yet.

  1. For key, focus, tab-create, tab-close, pane-close, or open, require an explicit user instruction authorizing that exact external mutation in the same turn. open can visibly switch focus and create a first terminal tab, so it is never read-only. When authorization is absent, use {{! tool('ask_user') !}} with the exact operation and complete target identity; do not run the adapter. After authorization, add "mutationAuthorized": true inside arguments. For open, set arguments to {"mutationAuthorized":true} or {"path":"<complete-source-supplied-path>","mutationAuthorized":true}.
  2. Write the request to a request file per <invocation_forms> and submit it:
python3 "${CLAUDE_SKILL_DIR}/scripts/prowl_environment.py" run < <request-file>
  1. Accept only status: "succeeded" for a submitted operation. Preserve the complete versioned result, commandExitCode, and public response values. For open, preserve response.data.resolution, created_tab, and the complete target; exact-root with created_tab: true is the lazy equivalent of clicking a known sidebar worktree that has no terminal pane. new-root means Prowl added a previously unknown root and never proves a prepared recovery target existed. For submitted send, require commandExitCode: 0 and preserve response.data.input.trailing_enter_sent; success with that field false or absent does not prove the turn left the editor. Once the checked send reports trailing_enter_sent: true, the turn is queued and delivery is complete; never send it again because the entry box becomes free. Stop with the exact status and detail on command-failed, invalid-schema, prowl-unavailable, mutation-unauthorized, or operation-unavailable.
  2. For bounded delegation between two identities returned by agents, supply semantic completion text and submit this shape to delegate:
{
  "sender": {
    "agent": "<complete-agent-id>",
    "pane": "<complete-pane-id>",
    "worktree": "<absolute-worktree-path>",
    "branch": "<complete-branch>",
    "repository": "<absolute-repository-root>",
    "run": "<complete-run-id-when-present>"
  },
  "recipient": {
    "agent": "<complete-agent-id>",
    "pane": "<complete-pane-id>",
    "worktree": "<absolute-worktree-path>",
    "branch": "<complete-branch>",
    "repository": "<absolute-repository-root>",
    "run": "<complete-run-id-when-present>"
  },
  "subject": "<bounded subject>",
  "instruction": "<complete bounded request, with no shell command or return-path fields>",
  "completionText": "<one-line completion signal for the sender>",
  "correlation": null
}

The adapter maps completionText and the two identities to a versioned handback block. The block contains completionText, the absolute adapterPath, an exact one-line command ending at run, checked successCriteria, retryPolicy: "never-after-trailing-enter", socket: "default", and expectedPanes in sender-recipient order. A caller never supplies handback, command, handbackCommand, returnPane, or adapterPath.

The result carries the complete source-owned schema-version-2 delegation envelope and generated handback block. Preserve it for the terminal handback; transport success is not acceptance or completion.

A direct delegation submission writes that object to a request file per <invocation_forms> and submits it:

python3 "${CLAUDE_SKILL_DIR}/scripts/prowl_environment.py" delegate < <request-file>

To generate the block without sending a delegation, write exactly sender, recipient, and completionText to a request file, submit it to plan-handback, and preserve the returned handback object unchanged:

python3 "${CLAUDE_SKILL_DIR}/scripts/prowl_environment.py" plan-handback < <request-file>
  1. The recipient writes any durable result first, executes the generated handback.command exactly, and submits exactly one terminal result to handback, carrying the complete original delegation, one kind, and one supported result form:
  • delegation-completed
  • delegation-failed
  • delegation-rejected
  • delegation-unavailable

A complete inline result uses inlineResult. A durable result uses a scheme-bearing resultReference plus a bounded projection; use file:///absolute/path for a local file. Both forms may appear together. The adapter rejects a missing result, a reference without a URI scheme or projection, and a conflicting terminal handback.

The handback request file holds {"delegation":<complete-returned-delegation>,"kind":"delegation-completed","inlineResult":"<complete-result>"}. Replace the angle-bracket value with the returned delegation object itself, never a quoted summary or reconstructed envelope.

  1. Return the complete terminal result to the delegating workflow. Do not poll the recipient, add acceptance or progress phases, or infer completion from pane output.

</workflow>

<invocation_forms>

Every subcommand (run, resolve-target, delegate, plan-handback, handback) takes its JSON request from stdin. Write the request with the file-write tool to a file in the scratch directory the harness names for this session, under a file name unique to the agent and the request, and submit it through the input redirect < <request-file> shown with each command. Where the harness names no scratch directory, use the one the environment designates, such as $RUNNER_TEMP on a hosted runner. A request carries prompt text, quotes, and JSON that the dangerous-command guard refuses in a heredoc, a pipe, or an inline argument.

</invocation_forms>

<handback_delivery>

Completion travels by push, never by pull. The sender's environment blocks polling loops by design, so a sender that "checks later" has no later to check in — it reads once, sees nothing, and moves on while the finished result sits on disk. The recipient closes the loop or nobody does.

A durable result separates payload from signal. The file carries the payload; one line sent into the sender's pane carries the signal. Write the file first, then send. resultReference names the complete scheme-bearing reference — file:///absolute/path for a local file — and projection carries the bounded summary, so the sender knows what arrived without opening it.

The recipient delivers the handback by sending one line into the return address's pane, using the send operation with normal trailing-Enter behavior. That send lands as a turn in the sender's session, which is what makes it a signal rather than a message the sender must go looking for. A noEnter send prefills the sender's editor and signals nothing.

The adapter generates the handback at delegation time. The caller supplies only semantic completion text. The generated block binds that text to sender.pane, the bundled adapter path, checked submission criteria, the default socket, both expected panes, and the no-retry policy. This separation prevents a caller from changing CLI grammar while describing the requested work.

</handback_delivery>

<environment_traps>

Two environment conditions silently break a handback. The generated block names both instead of leaving the recipient to discover them.

The CLI may not be on PATH. Every Prowl command runs the bare prowl name, so a shell that cannot resolve it returns prowl-unavailable with its detail. That result means the CLI is missing from the environment, not that the recipient is absent; report the detail and stop.

A non-default socket may belong to a different instance. When the socket is overridden, the CLI talks to whichever instance owns that socket — which can be another agent's verification harness holding no real panes rather than the operator's live application. An empty or unrecognizable pane inventory is that condition, not an absent recipient. Confirm the inventory contains the expected panes before concluding a target is gone, and use the same socket value for every command in the exchange.

</environment_traps>

<constraints>

  • ALWAYS preserve complete source-supplied agent, pane, worktree, branch, repository, run, correlation, status, conclusion, exit-code, and result-reference values.
  • ALWAYS execute the bundled script through ${CLAUDE_SKILL_DIR}; never import it from another filesystem location or manufacture a path outside this skill directory.
  • ALWAYS generate executable handback data from semantic completion text through delegate or plan-handback.
  • NEVER accept caller-authored handback, command, handbackCommand, returnPane, or adapterPath fields.
  • NEVER pass a request through a heredoc, a pipe, or an inline argument, and NEVER remove the request file; the harness clears its scratch directory.
  • NEVER invoke raw Prowl commands, Prowl command help, or an external environment-control skill.
  • NEVER mutate focus, keys, tabs, panes, or open-path selection without explicit authorization for the exact operation and target in the same turn.
  • NEVER equate list with the sidebar worktree inventory or enumerate filesystem worktrees to compensate for an uninstantiated pane.
  • NEVER scan transcripts, parse terminal presentation as identity, or poll for delegation completion.
  • NEVER make retry, checkpoint, persistence, result-interpretation, or continuation decisions for another workflow.

</constraints>

<testing>

Before release, import the bundled module with controlled CommandRunner implementations under the interaction-protocol and failure-simulation exceptions. Run the documented run form with an agents payload and require status: "succeeded", commandExitCode: 0, and a public response; run resolve-target with pane-only, worktree-only, and combined caller evidence and require one inventory call, caller exclusion, and zero sends; fill one returned send template and require response.data.input.trailing_enter_sent: true; run plan-handback, delegate, and handback with the documented shapes and require the command to end exactly at run, the initiating correlation to survive, and every caller-authored executable handback field to fail. Cover every operation mapping, public JSON failure, mutation rejection before command construction, URI-bearing delegation result forms, repeated terminals, conflicting terminals, malformed input, missing Prowl, and CLI stdin dispatch.

Recorded exercised payload/results:

  • {"schemaVersion":1,"operation":"agents","arguments":{}} with a successful public agents response → status: "succeeded", commandExitCode: 0, and the response preserved.
  • resolve-target with an active non-caller worktree path → one candidate and no send; an unmatched absolute path → identity-unavailable, an empty candidate array, and no open probe.
  • A filled returned send template with trailing_enter_sent: true in the public response → one succeeded send result; the same submission evidence then supports one delivered message envelope.

</testing>

<failure_modes>

A selectorless tab creation was rejected. Claude applied the shared selector requirement to tab-create, even though the public operation accepts both selectorless and selected forms. The shared request builder hid the operation-specific shape. Build tab-create from its declared optional-selector contract and preserve optional path independently.

An advertised operation had no construction branch. Claude listed open in the public surface but grouped only list, agents, read, and send into the non-mutating workflow. Claude then had to infer the request shape. Keep every advertised operation in an explicit construction branch; open accepts empty arguments or one source-supplied path, always with explicit mutation authorization.

A visible worktree was absent from list. Claude inferred missing Prowl topology because a sidebar row had no listed pane. The row was known but not entered; open returned resolution: exact-root, created_tab: true, and the first pane UUID. Treat list as the terminal inventory and use authorized open for visible lazy activation.

A delegation had no return path, so the operator became the message bus. Claude asked a recipient to write a file, then had no signal that it had. Polling loops are blocked in the environment, so Claude read the pane once, saw nothing, and moved on while a complete result sat on disk. Generate the structured handback from the sender, recipient, and completion text, then require the recipient to send one line on completion per <handback_delivery>.

A caller added a trailing argument to the return command. Claude copied a hand-written run . command into a delegation. argparse rejected the extra positional argument before any send occurred. Generate the command from completionText; the adapter renders the final token as run and rejects caller-authored executable fields.

A single read was mistaken for a terminal answer. Claude treated one empty pane read as evidence the recipient had produced nothing, when it proved only that nothing was on screen at that instant. A read establishes the pane's state at the moment it ran and never establishes that a delegation is incomplete. Completion arrives as the recipient's handback; its absence is an open delegation, not a negative result.

An overridden socket was read as an empty environment. Claude pointed the CLI at a non-default socket, saw an inventory with none of the expected panes, and concluded the recipient was gone. The socket belonged to a different instance — a verification harness, not the operator's live application. Confirm the inventory contains the expected panes before concluding a target is absent, per <environment_traps>.

Target resolution was rebuilt around scratch files. Claude wrote the agents result and discovery result through dynamic redirects under $SP. The dangerous-command guard blocked the command because the redirect carried a shell variable and the shell would open an unproved path with truncation. Claude then rewrote the same operation as a Python script instead of rerunning its parts with literal strings. Invoke resolve-target through the input redirect from its request file and keep its returned JSON as the tool result; an output redirect carrying a shell variable is no part of the workflow. Rewriting the blocked operation as another program is forbidden.

</failure_modes>

<success_criteria>

  • A successful public operation is mechanically established only when the bundled script exits zero and emits schemaVersion: 1, status: "succeeded", commandExitCode: 0, and a public response object without exposing Prowl command grammar.
  • Every positively identified Prowl participant retains complete public identities verbatim.
  • Every delegation preserves its initiating correlation through exactly one completed, failed, rejected, or unavailable terminal handback.
  • Every delegation carries one source-generated handback block whose command ends exactly at run and whose conditions identify checked submission, no retry after submission, the default socket, and both expected panes.
  • A durable handback writes its file before sending, and the notification reaches the sender's pane as a submitted turn rather than editor prefill.
  • One resolve-target invocation returns the checked inventory, complete caller and participants, non-caller path matches, and candidate-specific immediate-return send templates without sending.
  • An operator-named target is reported as the supplied worktree or directory, never as a pane UUID the operator must verify.
  • Terminal results carry complete inline content or an exact durable reference with a bounded projection.
  • Unauthorized focus, key, creation, closure, and open requests fail before Prowl runs.
  • Lazy activation is established by an authorized open result carrying resolution: exact-root, created_tab: true, and the complete returned pane identity.
  • No workflow polls, invokes help, or depends on a separate environment-control skill.

</success_criteria>

04 同仓库更多 Skills