native macOS codings agent orchestrator
6
fork

Configure Feed

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

Merge pull request #93 from onevcat/onevpaw/issue-68-cli-key-output-contract

docs(cli): define JSON contract for prowl key

authored by

Wei Wang and committed by
GitHub
203c5ed4 e5ed26f5

+287
+287
doc-onevcat/contracts/cli/key.md
··· 1 + # CLI Contract: `prowl key` 2 + 3 + Status: draft truth source for `#68`. 4 + 5 + This file defines the **input/output contract** for: 6 + 7 + - `prowl key ... --json` 8 + 9 + ## What changed in this revision 10 + 11 + Compared with the initial draft, this version makes `key` safer and more script-friendly: 12 + 13 + - define a strict input grammar with aliases (`return` -> `enter`, `escape` -> `esc`, `pgup` -> `pageup`, ...) 14 + - add `--repeat <n>` as first-class input instead of forcing shell loops 15 + - split `requested` vs `normalized` in output so callers can debug token normalization 16 + - add `delivery` counters for machine verification (`attempted` / `delivered`) 17 + - require `key` to target an existing pane context (no implicit tab creation) 18 + 19 + ## Contract goals 20 + 21 + - `key` should be deterministic for agent loops and TUI automation. 22 + - output must clearly answer: 23 + - what was requested 24 + - what token was actually accepted 25 + - where it was delivered 26 + - how many key events were delivered 27 + - keep key semantics separate from `send` (text input) and avoid hidden side effects. 28 + 29 + ## Recommended usage 30 + 31 + - Prefer `--pane <id>` for deterministic scripting. 32 + - Use `--tab` / `--worktree` for human-friendly calls when exact pane ID is not needed. 33 + - Typical loop: 34 + 1) `prowl list --json` 35 + 2) resolve target pane 36 + 3) `prowl key --pane <pane-id> <token> [--repeat n]` 37 + 4) `prowl read --pane <pane-id> --last <n> --json` 38 + 39 + ## Supported targeting 40 + 41 + - `--worktree <id|name|path>` 42 + - `--tab <id>` 43 + - `--pane <id>` 44 + - no selector, meaning current focused pane 45 + 46 + ## Input contract (v1) 47 + 48 + ### Positional token 49 + 50 + ```bash 51 + prowl key [target selectors] <token> [--repeat <n>] [--json] 52 + ``` 53 + 54 + Rules: 55 + 56 + - exactly one positional `<token>` is required 57 + - parsing is case-insensitive 58 + - surrounding spaces are ignored 59 + - canonical token form is lowercase kebab-case 60 + 61 + ### Canonical tokens 62 + 63 + - `enter` 64 + - `esc` 65 + - `tab` 66 + - `backspace` 67 + - `up` 68 + - `down` 69 + - `left` 70 + - `right` 71 + - `pageup` 72 + - `pagedown` 73 + - `home` 74 + - `end` 75 + - `ctrl-c` 76 + - `ctrl-d` 77 + - `ctrl-l` 78 + 79 + ### Accepted aliases (normalized to canonical) 80 + 81 + - `return` -> `enter` 82 + - `escape` -> `esc` 83 + - `arrow-up` -> `up` 84 + - `arrow-down` -> `down` 85 + - `arrow-left` -> `left` 86 + - `arrow-right` -> `right` 87 + - `pgup` -> `pageup` 88 + - `pgdn` -> `pagedown` 89 + - `ctrl+c` -> `ctrl-c` 90 + - `ctrl+d` -> `ctrl-d` 91 + - `ctrl+l` -> `ctrl-l` 92 + 93 + ### `--repeat` 94 + 95 + - optional, integer, default `1` 96 + - valid range in v1: `1...100` 97 + - repeat means “deliver the same normalized key token N times” 98 + 99 + ## Success payload 100 + 101 + ```json 102 + { 103 + "ok": true, 104 + "command": "key", 105 + "schema_version": "prowl.cli.key.v1", 106 + "data": { 107 + "requested": { 108 + "token": "Ctrl+C", 109 + "repeat": 3 110 + }, 111 + "key": { 112 + "normalized": "ctrl-c", 113 + "category": "control" 114 + }, 115 + "delivery": { 116 + "attempted": 3, 117 + "delivered": 3, 118 + "mode": "keyDownUp" 119 + }, 120 + "target": { 121 + "worktree": { 122 + "id": "Prowl:/Users/onevcat/Projects/Prowl", 123 + "name": "Prowl", 124 + "path": "/Users/onevcat/Projects/Prowl", 125 + "root_path": "/Users/onevcat/Projects/Prowl", 126 + "kind": "git" 127 + }, 128 + "tab": { 129 + "id": "2FC00CF0-3974-4E1B-BEF8-7A08A8E3B7C0", 130 + "title": "Prowl 1", 131 + "selected": true 132 + }, 133 + "pane": { 134 + "id": "6E1A2A10-D99F-4E3F-920C-D93AA3C05764", 135 + "title": "Claude", 136 + "cwd": "/Users/onevcat/Projects/Prowl", 137 + "focused": true 138 + } 139 + } 140 + } 141 + } 142 + ``` 143 + 144 + ## Required top-level fields 145 + 146 + - `ok`: boolean, must be `true` on success. 147 + - `command`: string, must be `"key"`. 148 + - `schema_version`: string, currently `"prowl.cli.key.v1"`. 149 + - `data`: object. 150 + 151 + ## `data` fields 152 + 153 + ### `requested` 154 + 155 + - `token`: string 156 + - raw user token after trimming 157 + - `repeat`: integer 158 + - parsed repeat count 159 + 160 + ### `key` 161 + 162 + - `normalized`: string 163 + - canonical token used by runtime 164 + - must be one of the canonical tokens listed above 165 + - `category`: `"navigation"` | `"editing"` | `"control"` 166 + 167 + ### `delivery` 168 + 169 + - `attempted`: integer 170 + - equals `requested.repeat` 171 + - `delivered`: integer 172 + - number of successful key deliveries 173 + - `mode`: string 174 + - `"keyDownUp"` for v1 175 + 176 + ### `target` 177 + 178 + Same shape used by other CLI contracts: 179 + 180 + #### `worktree` 181 + 182 + - `id`: string 183 + - `name`: string 184 + - `path`: string, absolute path 185 + - `root_path`: string, absolute path 186 + - `kind`: `"git"` | `"plain"` 187 + 188 + #### `tab` 189 + 190 + - `id`: string, UUID text form 191 + - `title`: string 192 + - `selected`: boolean 193 + 194 + #### `pane` 195 + 196 + - `id`: string, UUID text form 197 + - `title`: string 198 + - `cwd`: string or `null` 199 + - `focused`: boolean 200 + 201 + ## Output invariants 202 + 203 + - payload must resolve to the final pane that received the key event(s) 204 + - `requested.token` may differ from `key.normalized` when an alias was used 205 + - `delivery.attempted == requested.repeat` 206 + - success requires `delivery.delivered == delivery.attempted` 207 + - unlike `send`, `key` must not create a new tab implicitly; no active pane means error 208 + - JSON should not expose low-level keycodes/modifier bitmasks in v1 209 + 210 + ## Error payload 211 + 212 + ```json 213 + { 214 + "ok": false, 215 + "command": "key", 216 + "schema_version": "prowl.cli.key.v1", 217 + "error": { 218 + "code": "UNSUPPORTED_KEY", 219 + "message": "The key token 'ctrl-z' is not supported in v1", 220 + "details": { 221 + "token": "ctrl-z" 222 + } 223 + } 224 + } 225 + ``` 226 + 227 + ## Error codes for v1 228 + 229 + - `APP_NOT_RUNNING` 230 + - `INVALID_ARGUMENT` 231 + - `INVALID_REPEAT` 232 + - `TARGET_NOT_FOUND` 233 + - `TARGET_NOT_UNIQUE` 234 + - `NO_ACTIVE_PANE` 235 + - `UNSUPPORTED_KEY` 236 + - `KEY_DELIVERY_FAILED` 237 + 238 + ## Notes 239 + 240 + - `key` is control/navigation input; normal text remains `send`. 241 + - aliases are accepted for UX, but output always returns canonical `normalized` token. 242 + - this contract intentionally allows future token aliases without breaking script logic. 243 + 244 + ## Example: alias + repeat 245 + 246 + ```json 247 + { 248 + "ok": true, 249 + "command": "key", 250 + "schema_version": "prowl.cli.key.v1", 251 + "data": { 252 + "requested": { 253 + "token": "return", 254 + "repeat": 2 255 + }, 256 + "key": { 257 + "normalized": "enter", 258 + "category": "editing" 259 + }, 260 + "delivery": { 261 + "attempted": 2, 262 + "delivered": 2, 263 + "mode": "keyDownUp" 264 + }, 265 + "target": { 266 + "worktree": { 267 + "id": "Prowl:/Users/onevcat/Projects/Prowl", 268 + "name": "Prowl", 269 + "path": "/Users/onevcat/Projects/Prowl", 270 + "root_path": "/Users/onevcat/Projects/Prowl", 271 + "kind": "git" 272 + }, 273 + "tab": { 274 + "id": "2FC00CF0-3974-4E1B-BEF8-7A08A8E3B7C0", 275 + "title": "Prowl 1", 276 + "selected": true 277 + }, 278 + "pane": { 279 + "id": "6E1A2A10-D99F-4E3F-920C-D93AA3C05764", 280 + "title": "zsh", 281 + "cwd": "/Users/onevcat/Projects/Prowl", 282 + "focused": true 283 + } 284 + } 285 + } 286 + } 287 + ```