···11+# CLI Contract: `prowl send`
22+33+Status: draft truth source for `#67`.
44+55+This file defines the **JSON output contract** for:
66+77+- `prowl send ... --json`
88+99+## Contract goals
1010+1111+- `send` must report **where** text was delivered and **how** it was delivered.
1212+- JSON output must never echo the full text payload back by default; scripts often send secrets, prompts, or long commands.
1313+- The success payload must describe whether Enter was sent and whether the command had to create a tab first.
1414+1515+## Supported targeting
1616+1717+- `--worktree <id|name|path>`
1818+- `--tab <id>`
1919+- `--pane <id>`
2020+- no selector, meaning current focused pane
2121+2222+## Supported input forms
2323+2424+- positional text argument
2525+- stdin text
2626+- `--no-enter`
2727+2828+### Input source semantics
2929+3030+`data.input.source` is **inferred from where the accepted payload came from**. It is not a separate `--source` flag.
3131+3232+- `"argv"`
3333+ - means the payload came from the positional text argument of `prowl send`
3434+ - typical trigger: `prowl send "echo hello"`
3535+ - planned use: short, explicit, human-authored inline sends
3636+- `"stdin"`
3737+ - means the payload came from process stdin, usually via a pipe or redirection
3838+ - typical trigger: `printf 'echo hello\n' | prowl send`
3939+ - planned use: multiline text, generated text, file/pipe input, or payloads you do not want to expose inline in shell history
4040+4141+## Success payload
4242+4343+```json
4444+{
4545+ "ok": true,
4646+ "command": "send",
4747+ "schema_version": "prowl.cli.send.v1",
4848+ "data": {
4949+ "target": {
5050+ "worktree": {
5151+ "id": "Prowl:/Users/onevcat/Projects/Prowl",
5252+ "name": "Prowl",
5353+ "path": "/Users/onevcat/Projects/Prowl",
5454+ "root_path": "/Users/onevcat/Projects/Prowl",
5555+ "kind": "git"
5656+ },
5757+ "tab": {
5858+ "id": "2FC00CF0-3974-4E1B-BEF8-7A08A8E3B7C0",
5959+ "title": "Prowl 1",
6060+ "selected": true
6161+ },
6262+ "pane": {
6363+ "id": "6E1A2A10-D99F-4E3F-920C-D93AA3C05764",
6464+ "title": "zsh",
6565+ "cwd": "/Users/onevcat/Projects/Prowl",
6666+ "focused": true
6767+ }
6868+ },
6969+ "input": {
7070+ "source": "argv",
7171+ "characters": 10,
7272+ "bytes": 10,
7373+ "trailing_enter_sent": true
7474+ },
7575+ "created_tab": false
7676+ }
7777+}
7878+```
7979+8080+## Required top-level fields
8181+8282+- `ok`: boolean, must be `true` on success.
8383+- `command`: string, must be `"send"`.
8484+- `schema_version`: string, currently `"prowl.cli.send.v1"`.
8585+- `data`: object.
8686+8787+## `data.target` shape
8888+8989+### `worktree`
9090+9191+- `id`: string
9292+- `name`: string
9393+- `path`: string, absolute path
9494+- `root_path`: string, absolute path
9595+- `kind`: `"git"` | `"plain"`
9696+9797+### `tab`
9898+9999+- `id`: string, UUID text form
100100+- `title`: string
101101+- `selected`: boolean, should be `true` when the target tab is also the selected tab
102102+103103+### `pane`
104104+105105+- `id`: string, UUID text form
106106+- `title`: string
107107+- `cwd`: string or `null`
108108+- `focused`: boolean
109109+110110+## `data.input` fields
111111+112112+- `source`: `"argv"` | `"stdin"`
113113+ - `"argv"`: accepted payload came from the positional command argument
114114+ - `"stdin"`: accepted payload came from stdin
115115+ - the reported value must match the actual accepted payload source
116116+- `characters`: integer, count of Unicode scalar content accepted for delivery
117117+- `bytes`: integer, UTF-8 byte count of content accepted for delivery
118118+- `trailing_enter_sent`: boolean
119119+ - `true` for default behavior
120120+ - `false` when `--no-enter` is used
121121+122122+## `data.created_tab`
123123+124124+- boolean
125125+- `true` only when targeting a worktree that had no current tab and Prowl had to create one before sending.
126126+127127+## Output invariants
128128+129129+- The payload must describe the **resolved pane** that received the text.
130130+- The payload must **not** include the original text body.
131131+- If stdin is empty, the command should fail instead of pretending that an empty payload was delivered.
132132+- `characters` and `bytes` refer only to the text payload, not the synthetic Enter keypress.
133133+134134+## Error payload
135135+136136+```json
137137+{
138138+ "ok": false,
139139+ "command": "send",
140140+ "schema_version": "prowl.cli.send.v1",
141141+ "error": {
142142+ "code": "EMPTY_INPUT",
143143+ "message": "No text payload was provided"
144144+ }
145145+}
146146+```
147147+148148+## Error codes for v1
149149+150150+- `APP_NOT_RUNNING`
151151+- `INVALID_ARGUMENT`
152152+- `TARGET_NOT_FOUND`
153153+- `TARGET_NOT_UNIQUE`
154154+- `EMPTY_INPUT`
155155+- `SEND_FAILED`
156156+157157+## Notes
158158+159159+- `send` is text delivery, not general key simulation. Control inputs such as `ctrl-c` belong to `key`.
160160+- Returning byte and character counts gives scripts enough confirmation without leaking payload contents into logs.
161161+- A future implementation may add optional debug echo flags, but v1 default JSON must stay redaction-friendly.
162162+163163+## Example: stdin + `--no-enter`
164164+165165+```json
166166+{
167167+ "ok": true,
168168+ "command": "send",
169169+ "schema_version": "prowl.cli.send.v1",
170170+ "data": {
171171+ "target": {
172172+ "worktree": {
173173+ "id": "Prowl:/Users/onevcat/Projects/Prowl",
174174+ "name": "Prowl",
175175+ "path": "/Users/onevcat/Projects/Prowl",
176176+ "root_path": "/Users/onevcat/Projects/Prowl",
177177+ "kind": "git"
178178+ },
179179+ "tab": {
180180+ "id": "2FC00CF0-3974-4E1B-BEF8-7A08A8E3B7C0",
181181+ "title": "Prowl 1",
182182+ "selected": true
183183+ },
184184+ "pane": {
185185+ "id": "6E1A2A10-D99F-4E3F-920C-D93AA3C05764",
186186+ "title": "Claude",
187187+ "cwd": "/Users/onevcat/Projects/Prowl",
188188+ "focused": true
189189+ }
190190+ },
191191+ "input": {
192192+ "source": "stdin",
193193+ "characters": 24,
194194+ "bytes": 24,
195195+ "trailing_enter_sent": false
196196+ },
197197+ "created_tab": false
198198+ }
199199+}
200200+```