native macOS codings agent orchestrator
6
fork

Configure Feed

Select the types of activity you want to include in your feed.

Merge pull request #92 from onevcat/onevpaw/issue-67-cli-send-output-contract

docs(cli): define JSON contract for prowl send

authored by

Wei Wang and committed by
GitHub
e5ed26f5 79fae3f6

+200
+200
doc-onevcat/contracts/cli/send.md
··· 1 + # CLI Contract: `prowl send` 2 + 3 + Status: draft truth source for `#67`. 4 + 5 + This file defines the **JSON output contract** for: 6 + 7 + - `prowl send ... --json` 8 + 9 + ## Contract goals 10 + 11 + - `send` must report **where** text was delivered and **how** it was delivered. 12 + - JSON output must never echo the full text payload back by default; scripts often send secrets, prompts, or long commands. 13 + - The success payload must describe whether Enter was sent and whether the command had to create a tab first. 14 + 15 + ## Supported targeting 16 + 17 + - `--worktree <id|name|path>` 18 + - `--tab <id>` 19 + - `--pane <id>` 20 + - no selector, meaning current focused pane 21 + 22 + ## Supported input forms 23 + 24 + - positional text argument 25 + - stdin text 26 + - `--no-enter` 27 + 28 + ### Input source semantics 29 + 30 + `data.input.source` is **inferred from where the accepted payload came from**. It is not a separate `--source` flag. 31 + 32 + - `"argv"` 33 + - means the payload came from the positional text argument of `prowl send` 34 + - typical trigger: `prowl send "echo hello"` 35 + - planned use: short, explicit, human-authored inline sends 36 + - `"stdin"` 37 + - means the payload came from process stdin, usually via a pipe or redirection 38 + - typical trigger: `printf 'echo hello\n' | prowl send` 39 + - planned use: multiline text, generated text, file/pipe input, or payloads you do not want to expose inline in shell history 40 + 41 + ## Success payload 42 + 43 + ```json 44 + { 45 + "ok": true, 46 + "command": "send", 47 + "schema_version": "prowl.cli.send.v1", 48 + "data": { 49 + "target": { 50 + "worktree": { 51 + "id": "Prowl:/Users/onevcat/Projects/Prowl", 52 + "name": "Prowl", 53 + "path": "/Users/onevcat/Projects/Prowl", 54 + "root_path": "/Users/onevcat/Projects/Prowl", 55 + "kind": "git" 56 + }, 57 + "tab": { 58 + "id": "2FC00CF0-3974-4E1B-BEF8-7A08A8E3B7C0", 59 + "title": "Prowl 1", 60 + "selected": true 61 + }, 62 + "pane": { 63 + "id": "6E1A2A10-D99F-4E3F-920C-D93AA3C05764", 64 + "title": "zsh", 65 + "cwd": "/Users/onevcat/Projects/Prowl", 66 + "focused": true 67 + } 68 + }, 69 + "input": { 70 + "source": "argv", 71 + "characters": 10, 72 + "bytes": 10, 73 + "trailing_enter_sent": true 74 + }, 75 + "created_tab": false 76 + } 77 + } 78 + ``` 79 + 80 + ## Required top-level fields 81 + 82 + - `ok`: boolean, must be `true` on success. 83 + - `command`: string, must be `"send"`. 84 + - `schema_version`: string, currently `"prowl.cli.send.v1"`. 85 + - `data`: object. 86 + 87 + ## `data.target` shape 88 + 89 + ### `worktree` 90 + 91 + - `id`: string 92 + - `name`: string 93 + - `path`: string, absolute path 94 + - `root_path`: string, absolute path 95 + - `kind`: `"git"` | `"plain"` 96 + 97 + ### `tab` 98 + 99 + - `id`: string, UUID text form 100 + - `title`: string 101 + - `selected`: boolean, should be `true` when the target tab is also the selected tab 102 + 103 + ### `pane` 104 + 105 + - `id`: string, UUID text form 106 + - `title`: string 107 + - `cwd`: string or `null` 108 + - `focused`: boolean 109 + 110 + ## `data.input` fields 111 + 112 + - `source`: `"argv"` | `"stdin"` 113 + - `"argv"`: accepted payload came from the positional command argument 114 + - `"stdin"`: accepted payload came from stdin 115 + - the reported value must match the actual accepted payload source 116 + - `characters`: integer, count of Unicode scalar content accepted for delivery 117 + - `bytes`: integer, UTF-8 byte count of content accepted for delivery 118 + - `trailing_enter_sent`: boolean 119 + - `true` for default behavior 120 + - `false` when `--no-enter` is used 121 + 122 + ## `data.created_tab` 123 + 124 + - boolean 125 + - `true` only when targeting a worktree that had no current tab and Prowl had to create one before sending. 126 + 127 + ## Output invariants 128 + 129 + - The payload must describe the **resolved pane** that received the text. 130 + - The payload must **not** include the original text body. 131 + - If stdin is empty, the command should fail instead of pretending that an empty payload was delivered. 132 + - `characters` and `bytes` refer only to the text payload, not the synthetic Enter keypress. 133 + 134 + ## Error payload 135 + 136 + ```json 137 + { 138 + "ok": false, 139 + "command": "send", 140 + "schema_version": "prowl.cli.send.v1", 141 + "error": { 142 + "code": "EMPTY_INPUT", 143 + "message": "No text payload was provided" 144 + } 145 + } 146 + ``` 147 + 148 + ## Error codes for v1 149 + 150 + - `APP_NOT_RUNNING` 151 + - `INVALID_ARGUMENT` 152 + - `TARGET_NOT_FOUND` 153 + - `TARGET_NOT_UNIQUE` 154 + - `EMPTY_INPUT` 155 + - `SEND_FAILED` 156 + 157 + ## Notes 158 + 159 + - `send` is text delivery, not general key simulation. Control inputs such as `ctrl-c` belong to `key`. 160 + - Returning byte and character counts gives scripts enough confirmation without leaking payload contents into logs. 161 + - A future implementation may add optional debug echo flags, but v1 default JSON must stay redaction-friendly. 162 + 163 + ## Example: stdin + `--no-enter` 164 + 165 + ```json 166 + { 167 + "ok": true, 168 + "command": "send", 169 + "schema_version": "prowl.cli.send.v1", 170 + "data": { 171 + "target": { 172 + "worktree": { 173 + "id": "Prowl:/Users/onevcat/Projects/Prowl", 174 + "name": "Prowl", 175 + "path": "/Users/onevcat/Projects/Prowl", 176 + "root_path": "/Users/onevcat/Projects/Prowl", 177 + "kind": "git" 178 + }, 179 + "tab": { 180 + "id": "2FC00CF0-3974-4E1B-BEF8-7A08A8E3B7C0", 181 + "title": "Prowl 1", 182 + "selected": true 183 + }, 184 + "pane": { 185 + "id": "6E1A2A10-D99F-4E3F-920C-D93AA3C05764", 186 + "title": "Claude", 187 + "cwd": "/Users/onevcat/Projects/Prowl", 188 + "focused": true 189 + } 190 + }, 191 + "input": { 192 + "source": "stdin", 193 + "characters": 24, 194 + "bytes": 24, 195 + "trailing_enter_sent": false 196 + }, 197 + "created_tab": false 198 + } 199 + } 200 + ```