···11+# CLI Contract: `prowl open`
22+33+Status: draft truth source for `#64`.
44+55+This file defines the **JSON output contract** for the path-opening entry points:
66+77+- `prowl`
88+- `prowl <cwd>`
99+- `prowl open <cwd>`
1010+- any of the above with `--json`
1111+1212+The goal is to give later implementation tasks a stable machine-facing contract.
1313+1414+## Contract goals
1515+1616+- Bare `prowl <cwd>` and explicit `prowl open <cwd>` must share the same JSON shape.
1717+- Success output must tell an agent **what Prowl actually focused or opened**.
1818+- Path fields must be normalized to **absolute paths** after CLI parsing.
1919+- Success output must be stable enough for scripts; human-oriented prose belongs to non-JSON mode.
2020+2121+## Success payload
2222+2323+```json
2424+{
2525+ "ok": true,
2626+ "command": "open",
2727+ "schema_version": "prowl.cli.open.v1",
2828+ "data": {
2929+ "invocation": "implicit-open",
3030+ "requested_path": "/Users/onevcat/Projects/Prowl/supacode",
3131+ "resolved_path": "/Users/onevcat/Projects/Prowl/supacode",
3232+ "resolution": "inside-root",
3333+ "app_launched": false,
3434+ "brought_to_front": true,
3535+ "created_tab": true,
3636+ "target": {
3737+ "worktree": {
3838+ "id": "Prowl:/Users/onevcat/Projects/Prowl",
3939+ "name": "Prowl",
4040+ "path": "/Users/onevcat/Projects/Prowl",
4141+ "root_path": "/Users/onevcat/Projects/Prowl",
4242+ "kind": "git"
4343+ },
4444+ "tab": {
4545+ "id": "0E2A7C03-9C01-4BC1-9327-6C1C7B629A52",
4646+ "title": "supacode",
4747+ "cwd": "/Users/onevcat/Projects/Prowl/supacode"
4848+ },
4949+ "pane": {
5050+ "id": "0FB4DDB4-A797-4315-A00E-8AAFB32BFC95",
5151+ "title": "supacode",
5252+ "cwd": "/Users/onevcat/Projects/Prowl/supacode"
5353+ }
5454+ }
5555+ }
5656+}
5757+```
5858+5959+## Required top-level fields
6060+6161+- `ok`: boolean, must be `true` on success.
6262+- `command`: string, must be `"open"` even when the user invoked bare `prowl <cwd>`.
6363+- `schema_version`: string, currently `"prowl.cli.open.v1"`.
6464+- `data`: object.
6565+6666+## `data` fields
6767+6868+- `invocation`: string.
6969+ - `"bare"` for `prowl`
7070+ - `"implicit-open"` for `prowl <cwd>`
7171+ - `"open-subcommand"` for `prowl open <cwd>`
7272+- `requested_path`: string or `null`.
7373+ - `null` only for bare `prowl` with no explicit path.
7474+ - otherwise the absolute path after resolving `.` / `..` / `~` / `file://`.
7575+- `resolved_path`: string or `null`.
7676+ - `null` only when `requested_path` is `null`.
7777+ - must be the path Prowl actually targeted after normalization.
7878+- `resolution`: string enum.
7979+ - `"no-argument"`: bare `prowl`
8080+ - `"exact-root"`: requested path matched an already-open root exactly
8181+ - `"inside-root"`: requested path was inside an already-open root, so Prowl focused that root and opened a tab at the exact subpath
8282+ - `"new-root"`: requested path was not yet managed and Prowl opened it as a new root
8383+- `app_launched`: boolean.
8484+ - `true` only when the command had to start Prowl.
8585+- `brought_to_front`: boolean.
8686+ - must be `true` on success.
8787+- `created_tab`: boolean.
8888+ - `true` when the operation created a new tab as part of satisfying the request.
8989+ - `false` when Prowl only focused an existing target.
9090+- `target`: object describing the final focused target.
9191+9292+## `target` shape
9393+9494+### `target.worktree`
9595+9696+- `id`: string
9797+- `name`: string
9898+- `path`: string, absolute worktree/plain-folder path
9999+- `root_path`: string, absolute repository root or plain-folder root
100100+- `kind`: `"git"` | `"plain"`
101101+102102+### `target.tab`
103103+104104+- `id`: string, UUID text form
105105+- `title`: string
106106+- `cwd`: string or `null`
107107+108108+### `target.pane`
109109+110110+- `id`: string, UUID text form
111111+- `title`: string
112112+- `cwd`: string or `null`
113113+114114+## Error payload
115115+116116+```json
117117+{
118118+ "ok": false,
119119+ "command": "open",
120120+ "schema_version": "prowl.cli.open.v1",
121121+ "error": {
122122+ "code": "PATH_NOT_FOUND",
123123+ "message": "No directory exists at '/Users/onevcat/Projects/Missing'",
124124+ "details": {
125125+ "requested_path": "/Users/onevcat/Projects/Missing"
126126+ }
127127+ }
128128+}
129129+```
130130+131131+## Required error fields
132132+133133+- `ok`: boolean, must be `false`.
134134+- `command`: string, must be `"open"`.
135135+- `schema_version`: string, must be `"prowl.cli.open.v1"`.
136136+- `error.code`: stable machine-readable string.
137137+- `error.message`: human-readable string.
138138+- `error.details`: optional object with structured context.
139139+140140+## Error codes for v1
141141+142142+- `INVALID_ARGUMENT`
143143+- `PATH_NOT_FOUND`
144144+- `PATH_NOT_DIRECTORY`
145145+- `PATH_NOT_ALLOWED`
146146+- `LAUNCH_FAILED`
147147+- `OPEN_FAILED`
148148+149149+## Notes
150150+151151+- Success JSON should report the **resolved target**, not only the input path.
152152+- `target.tab.cwd` and `target.pane.cwd` should be the exact directory the user lands in when available.
153153+- `created_tab` may be `false` for `exact-root` and `true` for `inside-root`; `new-root` may do either depending on implementation, so callers must trust the boolean instead of inferring it.
154154+- `prowl` without arguments still returns `command: "open"`; it is the app-entry form of the same capability.
155155+156156+## Example: exact-root focus
157157+158158+```json
159159+{
160160+ "ok": true,
161161+ "command": "open",
162162+ "schema_version": "prowl.cli.open.v1",
163163+ "data": {
164164+ "invocation": "open-subcommand",
165165+ "requested_path": "/Users/onevcat/Projects/Prowl",
166166+ "resolved_path": "/Users/onevcat/Projects/Prowl",
167167+ "resolution": "exact-root",
168168+ "app_launched": false,
169169+ "brought_to_front": true,
170170+ "created_tab": false,
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": "95A6DF8D-4E7E-4A67-895B-0EAF7DB6D7A8",
181181+ "title": "Prowl 1",
182182+ "cwd": "/Users/onevcat/Projects/Prowl"
183183+ },
184184+ "pane": {
185185+ "id": "7C38206E-1C9D-4740-A6B0-675C3BC93B47",
186186+ "title": "Prowl",
187187+ "cwd": "/Users/onevcat/Projects/Prowl"
188188+ }
189189+ }
190190+ }
191191+}
192192+```