@treeport/pi 0.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Noice Tech
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,79 @@
1
+ # `@treeport/pi`
2
+
3
+ Give Pi compact Treeport context and access to the Treeport CLI.
4
+
5
+ ## Requirements
6
+
7
+ Install these applications first:
8
+
9
+ - Treeport `0.5.0` or later;
10
+ - Pi `0.84.3` or later;
11
+ - Node.js 24 or later;
12
+ - macOS or Linux.
13
+
14
+ Make sure that the `treeport` command is on `PATH`.
15
+
16
+ ## Install
17
+
18
+ Install the package globally in Pi:
19
+
20
+ ```sh
21
+ pi install npm:@treeport/pi
22
+ ```
23
+
24
+ The package contains a Pi extension and the detailed Treeport skill.
25
+
26
+ The extension starts only in a terminal that Treeport manages.
27
+
28
+ A managed Pi session shows this footer status:
29
+
30
+ ```text
31
+ treeport · <tree-name>
32
+ ```
33
+
34
+ Outside Treeport, the extension adds no guidance, notification, or footer status.
35
+
36
+ ## Use natural requests
37
+
38
+ Ask Pi to do the work. You do not have to name Treeport.
39
+
40
+ Examples:
41
+
42
+ - “Start the development server.”
43
+ - “Stop the development server.”
44
+ - “Open the app and check the settings page.”
45
+ - “Do this side quest in a separate tree.”
46
+
47
+ In a managed session, the extension briefly defines Treeport projects and trees.
48
+
49
+ It includes the current project and tree names. It does not include IDs, paths, or the daemon URL.
50
+
51
+ The guidance tells Pi to use the `treeport` CLI through its standard Bash tool.
52
+
53
+ Pi uses a persistent terminal for a long-running process. Pi uses Bash directly for a finite command.
54
+
55
+ When you ask Pi to stop a process, Pi can delete its persistent terminal. Pi must not delete its own terminal.
56
+
57
+ For a side quest, Pi can use another terminal in the current tree. It can use `treeport spawn` for another tree.
58
+
59
+ Pi can control visible browser tabs when the Treeport CLI and daemon support browser commands.
60
+
61
+ The browser tabs stay open so you can inspect them.
62
+
63
+ The guidance requires your approval before a Chromium installation.
64
+
65
+ ## Use the skill for detailed workflows
66
+
67
+ The bundled skill contains detailed Treeport CLI procedures.
68
+
69
+ Pi does not need the skill for routine terminal or browser operations.
70
+
71
+ Use the skill for lifecycle operations, remote access, updates, recovery, or complex child-tree work.
72
+
73
+ ## Remove
74
+
75
+ Remove the package from Pi:
76
+
77
+ ```sh
78
+ pi remove npm:@treeport/pi
79
+ ```
@@ -0,0 +1,231 @@
1
+ import type { ExtensionAPI } from '@earendil-works/pi-coding-agent'
2
+ import { Type, type Static } from 'typebox'
3
+ import { runTreeportJson } from './treeport-cli.ts'
4
+
5
+ const CONTEXT_TIMEOUT_MS = 5_000
6
+
7
+ interface ManagedContext {
8
+ project: {
9
+ id: string
10
+ name: string
11
+ }
12
+ worktree: {
13
+ id: string
14
+ projectId: string
15
+ name: string
16
+ }
17
+ terminal: {
18
+ id: string
19
+ worktreeId: string
20
+ }
21
+ }
22
+
23
+ const BrowserStatusSchema = Type.Object(
24
+ {
25
+ installed: Type.Boolean(),
26
+ launchReady: Type.Boolean()
27
+ },
28
+ { additionalProperties: true }
29
+ )
30
+ const ContextSchema = Type.Union([
31
+ Type.Object(
32
+ {
33
+ managed: Type.Literal(false),
34
+ reason: Type.String()
35
+ },
36
+ { additionalProperties: true }
37
+ ),
38
+ Type.Object(
39
+ {
40
+ managed: Type.Literal(true),
41
+ apiUrl: Type.String({ minLength: 1 }),
42
+ daemonLifecycle: Type.Union([
43
+ Type.Literal('treeport'),
44
+ Type.Literal('service'),
45
+ Type.Literal('external')
46
+ ]),
47
+ project: Type.Object(
48
+ {
49
+ id: Type.String({ minLength: 1 }),
50
+ name: Type.String({ minLength: 1 }),
51
+ kind: Type.Union([Type.Literal('repository'), Type.Literal('folder')])
52
+ },
53
+ { additionalProperties: true }
54
+ ),
55
+ worktree: Type.Object(
56
+ {
57
+ id: Type.String({ minLength: 1 }),
58
+ projectId: Type.String({ minLength: 1 }),
59
+ name: Type.String({ minLength: 1 }),
60
+ path: Type.String({ minLength: 1 })
61
+ },
62
+ { additionalProperties: true }
63
+ ),
64
+ terminal: Type.Object(
65
+ {
66
+ id: Type.String({ minLength: 1 }),
67
+ worktreeId: Type.String({ minLength: 1 }),
68
+ name: Type.String({ minLength: 1 })
69
+ },
70
+ { additionalProperties: true }
71
+ )
72
+ },
73
+ { additionalProperties: true }
74
+ )
75
+ ])
76
+
77
+ type ContextOutput = Static<typeof ContextSchema>
78
+
79
+ function managedContext(value: ContextOutput): ManagedContext | null {
80
+ if (value.managed === false) {
81
+ return null
82
+ }
83
+
84
+ if (
85
+ value.worktree.projectId !== value.project.id ||
86
+ value.terminal.worktreeId !== value.worktree.id
87
+ ) {
88
+ return null
89
+ }
90
+
91
+ let parsedApiUrl: URL
92
+ try {
93
+ parsedApiUrl = new URL(value.apiUrl)
94
+ } catch {
95
+ return null
96
+ }
97
+ if (!['http:', 'https:'].includes(parsedApiUrl.protocol)) {
98
+ return null
99
+ }
100
+
101
+ return {
102
+ project: value.project,
103
+ worktree: value.worktree,
104
+ terminal: value.terminal
105
+ }
106
+ }
107
+
108
+ export default function treeportExtension(pi: ExtensionAPI): void {
109
+ let guidance: string | null = null
110
+ let badgeVisible = false
111
+
112
+ pi.on('session_start', async (_event, sessionContext) => {
113
+ guidance = null
114
+ let detectedValue: ContextOutput
115
+ try {
116
+ detectedValue = await runTreeportJson(pi, ['context'], ContextSchema, {
117
+ cwd: sessionContext.cwd,
118
+ signal: undefined,
119
+ timeout: CONTEXT_TIMEOUT_MS
120
+ })
121
+ } catch {
122
+ const injectedIds = [
123
+ process.env.TREEPORT_PROJECT_ID,
124
+ process.env.TREEPORT_WORKTREE_ID,
125
+ process.env.TREEPORT_TERMINAL_ID
126
+ ].some((value) => Boolean(value?.trim()))
127
+ if (injectedIds && sessionContext.hasUI) {
128
+ sessionContext.ui.notify(
129
+ 'Treeport context is unavailable. The Treeport integration is inactive.',
130
+ 'warning'
131
+ )
132
+ }
133
+
134
+ return
135
+ }
136
+
137
+ if (detectedValue.managed === false) {
138
+ return
139
+ }
140
+
141
+ const detected = managedContext(detectedValue)
142
+ if (!detected) {
143
+ const injectedIds = [
144
+ process.env.TREEPORT_PROJECT_ID,
145
+ process.env.TREEPORT_WORKTREE_ID,
146
+ process.env.TREEPORT_TERMINAL_ID
147
+ ].some((value) => Boolean(value?.trim()))
148
+ if (injectedIds && sessionContext.hasUI) {
149
+ sessionContext.ui.notify(
150
+ 'Treeport context is invalid. The Treeport integration is inactive.',
151
+ 'warning'
152
+ )
153
+ }
154
+
155
+ return
156
+ }
157
+
158
+ let browserCommandsAvailable = true
159
+ try {
160
+ await runTreeportJson(pi, ['browser', 'status'], BrowserStatusSchema, {
161
+ cwd: sessionContext.cwd,
162
+ signal: undefined,
163
+ timeout: CONTEXT_TIMEOUT_MS
164
+ })
165
+ } catch {
166
+ browserCommandsAvailable = false
167
+ }
168
+
169
+ const guidanceLines = [
170
+ 'Treeport context:',
171
+ browserCommandsAvailable
172
+ ? 'Treeport is a worktree-first workspace for projects, trees, persistent terminals, and browser tabs.'
173
+ : 'Treeport is a worktree-first workspace for projects, trees, and persistent terminals.',
174
+ 'A project is a registered repository or folder. A tree is its main checkout or a linked Git worktree.',
175
+ `This session runs in project ${JSON.stringify(
176
+ detected.project.name
177
+ )} and tree ${JSON.stringify(detected.worktree.name)}.`,
178
+ 'Use the `treeport` CLI through bash for Treeport operations. Use `--json` when you must parse a result.',
179
+ 'Use bash directly for finite commands that Pi must await.',
180
+ 'For a persistent process, run `treeport terminal create --worktree . --name <name> -- <program> <arg> ...`.',
181
+ 'Pass the child program and its arguments after `--`. Do not use an implicit shell command string.',
182
+ 'Observe persistent terminals with `treeport terminal inspect`, `treeport terminal capture`, or `treeport terminal wait`.',
183
+ 'Do not poll through repeated model calls. Sleep and capture in one bash call, such as `sleep 5; treeport terminal capture <id>`.',
184
+ '`treeport terminal wait --until idle` observes OSC progress. It is not a readiness check and can return immediately.',
185
+ 'Delete a terminal only when the user asks to stop or close its process. Never delete this Pi session terminal.',
186
+ 'A side quest is independent work in another persistent terminal. Use `treeport terminal create` here or `treeport spawn` for another tree.',
187
+ ...(browserCommandsAvailable
188
+ ? [
189
+ 'Use `treeport browser` commands for visible browser tabs. Take a new snapshot after navigation or a runtime change.',
190
+ 'Leave browser tabs open for user inspection. Do not install Chromium without user approval.',
191
+ 'Do not put secrets in browser URLs or command arguments.'
192
+ ]
193
+ : []),
194
+ 'Use `treeport <area> <command> --help` for exact syntax. Do not load the Treeport skill for these routine operations.'
195
+ ]
196
+ guidance = guidanceLines.join('\n')
197
+
198
+ if (!browserCommandsAvailable && sessionContext.hasUI) {
199
+ sessionContext.ui.notify(
200
+ 'Treeport browser commands are unavailable in this session.',
201
+ 'warning'
202
+ )
203
+ }
204
+
205
+ if (sessionContext.hasUI) {
206
+ sessionContext.ui.setStatus(
207
+ 'treeport',
208
+ sessionContext.ui.theme.fg(
209
+ 'accent',
210
+ `treeport · ${detected.worktree.name}`
211
+ )
212
+ )
213
+ badgeVisible = true
214
+ }
215
+ })
216
+
217
+ pi.on('before_agent_start', (event) => {
218
+ if (!guidance) {
219
+ return
220
+ }
221
+
222
+ return { systemPrompt: `${event.systemPrompt}\n\n${guidance}` }
223
+ })
224
+
225
+ pi.on('session_shutdown', (_event, sessionContext) => {
226
+ if (badgeVisible) {
227
+ sessionContext.ui.setStatus('treeport', undefined)
228
+ badgeVisible = false
229
+ }
230
+ })
231
+ }
@@ -0,0 +1,146 @@
1
+ import { constants as fsConstants } from 'node:fs'
2
+ import { access } from 'node:fs/promises'
3
+ import { basename, dirname, join } from 'node:path'
4
+ import type { ExecOptions, ExtensionAPI } from '@earendil-works/pi-coding-agent'
5
+ import { Type, type Static, type TSchema } from 'typebox'
6
+ import { Value } from 'typebox/value'
7
+
8
+ const TreeportCliErrorBodySchema = Type.Object(
9
+ {
10
+ error: Type.Object(
11
+ {
12
+ code: Type.String(),
13
+ message: Type.String()
14
+ },
15
+ { additionalProperties: true }
16
+ )
17
+ },
18
+ { additionalProperties: true }
19
+ )
20
+
21
+ type TreeportCliErrorBody = Static<typeof TreeportCliErrorBodySchema>
22
+
23
+ class TreeportCliError extends Error {
24
+ constructor(
25
+ readonly code: string,
26
+ message: string
27
+ ) {
28
+ super(`[${code}] ${message}`)
29
+ this.name = 'TreeportCliError'
30
+ }
31
+ }
32
+
33
+ function parseJson<T extends TSchema>(
34
+ value: string,
35
+ schema: T
36
+ ): Static<T> | null {
37
+ try {
38
+ return Value.Parse(schema, JSON.parse(value))
39
+ } catch {
40
+ return null
41
+ }
42
+ }
43
+
44
+ function conciseError(value: string): string {
45
+ return (value.split(/\n\n(?:AI agents:|Usage:)/, 1)[0] ?? value).trim()
46
+ }
47
+
48
+ async function treeportExecutable(): Promise<string> {
49
+ const configured = process.env.TREEPORT_CLI_ENTRYPOINT?.trim()
50
+ if (
51
+ configured &&
52
+ (await access(configured, fsConstants.X_OK).then(
53
+ () => true,
54
+ () => false
55
+ ))
56
+ ) {
57
+ return configured
58
+ }
59
+
60
+ const daemonRecord = process.env.TREEPORT_DAEMON_RECORD?.trim()
61
+ if (daemonRecord) {
62
+ const developmentRuntime = dirname(dirname(daemonRecord))
63
+ if (basename(developmentRuntime) === '.treeport-dev') {
64
+ const developmentCli = join(
65
+ dirname(developmentRuntime),
66
+ '.treeport-dev-dist/node/cli/index.js'
67
+ )
68
+ if (
69
+ await access(developmentCli, fsConstants.X_OK).then(
70
+ () => true,
71
+ () => false
72
+ )
73
+ ) {
74
+ return developmentCli
75
+ }
76
+ }
77
+ }
78
+
79
+ return 'treeport'
80
+ }
81
+
82
+ export async function runTreeportJson<T extends TSchema>(
83
+ pi: Pick<ExtensionAPI, 'exec'>,
84
+ args: readonly string[],
85
+ schema: T,
86
+ options: {
87
+ cwd: string
88
+ signal: AbortSignal | undefined
89
+ timeout: number
90
+ }
91
+ ): Promise<Static<T>> {
92
+ const jsonArgs = [...args]
93
+ const separator = jsonArgs.indexOf('--')
94
+ jsonArgs.splice(separator === -1 ? jsonArgs.length : separator, 0, '--json')
95
+
96
+ const execOptions: ExecOptions = {
97
+ cwd: options.cwd,
98
+ timeout: options.timeout
99
+ }
100
+ if (options.signal) {
101
+ execOptions.signal = options.signal
102
+ }
103
+
104
+ const result = await pi.exec(
105
+ await treeportExecutable(),
106
+ jsonArgs,
107
+ execOptions
108
+ )
109
+
110
+ if (result.code !== 0) {
111
+ const parsed: TreeportCliErrorBody | null = parseJson(
112
+ result.stderr.trim(),
113
+ TreeportCliErrorBodySchema
114
+ )
115
+ const code = parsed
116
+ ? parsed.error.code
117
+ : result.killed
118
+ ? options.signal?.aborted
119
+ ? 'TREEPORT_CANCELLED'
120
+ : 'TREEPORT_EXECUTION_TIMEOUT'
121
+ : 'TREEPORT_EXECUTION_FAILED'
122
+ const message = parsed
123
+ ? conciseError(parsed.error.message)
124
+ : result.killed
125
+ ? options.signal?.aborted
126
+ ? 'The Treeport command was cancelled.'
127
+ : 'The Treeport command reached its execution time limit.'
128
+ : result.stderr.trim()
129
+ ? `${conciseError(result.stderr)} (Treeport exit code ${result.code}).`
130
+ : `Treeport exited with code ${result.code}.`
131
+ throw new TreeportCliError(code, message)
132
+ }
133
+
134
+ const output = result.stdout.trim()
135
+ const parsed: Static<T> | null = parseJson(output, schema)
136
+ if (parsed === null) {
137
+ throw new TreeportCliError(
138
+ 'TREEPORT_INVALID_JSON',
139
+ output
140
+ ? 'Treeport returned invalid JSON.'
141
+ : 'Treeport returned no JSON output.'
142
+ )
143
+ }
144
+
145
+ return parsed
146
+ }
package/package.json ADDED
@@ -0,0 +1,58 @@
1
+ {
2
+ "name": "@treeport/pi",
3
+ "version": "0.6.1",
4
+ "description": "Treeport context and CLI guidance for Pi.",
5
+ "type": "module",
6
+ "files": [
7
+ "extensions/treeport/index.ts",
8
+ "extensions/treeport/treeport-cli.ts",
9
+ "skills/treeport/SKILL.md",
10
+ "LICENSE",
11
+ "README.md"
12
+ ],
13
+ "keywords": [
14
+ "pi-package",
15
+ "treeport",
16
+ "coding-agent"
17
+ ],
18
+ "pi": {
19
+ "extensions": [
20
+ "./extensions/treeport/index.ts"
21
+ ],
22
+ "skills": [
23
+ "./skills/treeport"
24
+ ]
25
+ },
26
+ "engines": {
27
+ "node": ">=24"
28
+ },
29
+ "os": [
30
+ "darwin",
31
+ "linux"
32
+ ],
33
+ "license": "Apache-2.0",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "git+https://github.com/noice-tech/treeport.git",
37
+ "directory": "packages/pi"
38
+ },
39
+ "bugs": {
40
+ "url": "https://github.com/noice-tech/treeport/issues"
41
+ },
42
+ "homepage": "https://treeport.app",
43
+ "publishConfig": {
44
+ "access": "public"
45
+ },
46
+ "peerDependencies": {
47
+ "@earendil-works/pi-coding-agent": "*",
48
+ "typebox": "*"
49
+ },
50
+ "devDependencies": {
51
+ "@earendil-works/pi-coding-agent": "^0.84.3",
52
+ "typebox": "^1.3.7"
53
+ },
54
+ "scripts": {
55
+ "typecheck": "tsc --noEmit -p tsconfig.json --pretty",
56
+ "test:package": "node scripts/check-package.mjs"
57
+ }
58
+ }
@@ -0,0 +1,219 @@
1
+ ---
2
+ name: treeport
3
+ description: Detailed Treeport CLI guidance for child-tree orchestration, lifecycle, services, remote access, updates, and recovery. Use when compact Pi guidance or command help is not sufficient. Do not load for routine background processes or browser tabs in a managed Pi session.
4
+ compatibility: Requires the treeport CLI on PATH and a reachable Treeport daemon. Creation commands also require the requested child executable to be installed.
5
+ ---
6
+
7
+ # Treeport
8
+
9
+ In a managed Pi session, use the injected CLI guidance for routine terminal and browser operations. Load this skill only when you need more detail.
10
+
11
+ Treeport is a generic terminal and tree layer. Its terminals are persistent tmux sessions that appear in the Treeport UI. A user can open a created terminal, take control of the normal application TUI, and continue working in the same session.
12
+
13
+ Treeport does not define task sources, planning or approval states, agent tool policies, or provider-specific workflows. The caller owns names, commands, prompts, and higher-level orchestration.
14
+
15
+ ## Operating rules
16
+
17
+ - Use normal CLI output when you are reading the result. Use `--json` only for programmatic extraction or branching.
18
+ - Treat command arguments after `--` as an argv array. Do not turn them into a shell command string.
19
+ - Do not use `eval` or an implicit `sh -lc`. Launch a shell explicitly only when the caller intentionally requests shell semantics.
20
+ - Do not place untrusted titles, prompts, or other external text into interpolated shell fragments. Pass each value as one argument or use a caller-managed file when the child supports file arguments.
21
+ - Never delete a terminal or remove a tree unless the user explicitly asks.
22
+ - Obey the daemon lifecycle reported by `treeport context`.
23
+ - When the lifecycle is `external`, never run `treeport start`, `treeport stop`, or `treeport remote enable`.
24
+ - In the external lifecycle, the parent process owns startup, shutdown, and remote exposure.
25
+ - When the lifecycle is `service`, normal `start` and `stop` delegate to the OS manager.
26
+ - Use bare `treeport update` to update a supported local npm installation. It preserves tmux terminals and an enabled service, and restarts only a daemon that was running.
27
+ - Never run bare `treeport update` for an external or selected remote daemon.
28
+ - Never invoke `sudo` for a normal service or update operation.
29
+ - Normal macOS service mode is a user/login LaunchAgent and does not need an administrator.
30
+ - Advanced headless mode is explicit.
31
+ - If an existing headless service prints an administrator command, report the command and wait for the user.
32
+ - Do not restrict a launched agent's normal tools or make it ephemeral unless the caller explicitly asks. The persistent interactive session is intended to remain useful when the user takes over.
33
+
34
+ ## Understand the current context
35
+
36
+ Run:
37
+
38
+ ```sh
39
+ treeport context
40
+ ```
41
+
42
+ Inside a managed terminal, this reports the current project, tree, terminal, paths, statuses, IDs, tree context values, and daemon URL. User-supplied context can contain an issue identifier, a task description, or other information for the tree. Use these values as task input when the caller asks you to work from the tree context. It reports whether Treeport, the OS service, or an external process manages the daemon lifecycle. It resolves the injected IDs strictly. It does not guess identity from the current path.
43
+
44
+ Outside Treeport it reports that the terminal is not managed and exits successfully. `TREEPORT_API_URL` may be configured outside a managed terminal; if any context ID is present, however, all injected values are required. Partial IDs or IDs that no longer belong together fail instead of falling back to path inference.
45
+
46
+ Use the exact IDs from this command when an operation targets a different project or tree. `.` is a convenient shorthand for the current project or tree.
47
+
48
+ When the current folder is in the target project, omit `--project` from `worktree create` and `spawn`. Treeport detects the registered project from the current folder.
49
+
50
+ ## Create a terminal in the current tree
51
+
52
+ Create a persistent login shell:
53
+
54
+ ```sh
55
+ treeport terminal create --worktree <tree-id> --name <terminal-name>
56
+ ```
57
+
58
+ Launch a program directly:
59
+
60
+ ```sh
61
+ treeport terminal create --worktree <tree-id> --name <terminal-name> -- <program> <arg> ...
62
+ ```
63
+
64
+ The command returns after Treeport creates the tmux session. The program continues independently of the browser and of the caller that created it.
65
+
66
+ ## Create a child tree and terminal
67
+
68
+ Create a linked tree and its first persistent terminal together:
69
+
70
+ ```sh
71
+ treeport spawn \
72
+ --worktree-name <tree-name> \
73
+ --name <terminal-name> \
74
+ -- <program> <arg> ...
75
+ ```
76
+
77
+ The child program and its arguments are entirely caller-owned. Treeport preserves them but does not add prompts, modes, capability restrictions, or lifecycle policy.
78
+
79
+ By default, Treeport bases the tree on the fetched remote default branch. Add `--from-current` only for the current tree's committed `HEAD`. Uncommitted changes are not copied.
80
+
81
+ Treeport serializes tree mutations per project. If a caller needs several child trees, create them one at a time. Their terminal programs can run concurrently after creation.
82
+
83
+ ## Interpret creation results
84
+
85
+ A successful `terminal create` means the tmux session was created. The requested program can still exit later.
86
+
87
+ `spawn` is intentionally non-atomic after Git creates the worktree:
88
+
89
+ - A terminal ID means the persistent session was created.
90
+ - `terminalError` means the tree remains but its initial terminal could not be created.
91
+ - `setupError` means tree setup could not be prepared. A retained terminal may display that error and exit without launching the requested program.
92
+ - Setup tasks can also fail after the create response. Their output and failure remain visible in the retained terminal.
93
+
94
+ Report partial creation with the returned tree and terminal IDs. Do not blindly rerun `spawn`: the tree may already exist. Do not remove retained resources automatically.
95
+
96
+ Inspect terminal inventory later with:
97
+
98
+ ```sh
99
+ treeport terminal list --worktree <tree-id>
100
+ ```
101
+
102
+ Inspect one terminal's refreshed process status and volatile runtime metadata with:
103
+
104
+ ```sh
105
+ treeport terminal inspect <terminal-id>
106
+ treeport terminal inspect <terminal-id> --json
107
+ ```
108
+
109
+ Runtime metadata includes the title, current OSC `9;4` progress, last progress start and clear timestamps, and latest daemon-observed real BEL. BEL metadata also reports daemon-lifetime unread attention shared by every browser; inspection and waits never acknowledge it, while viewing the terminal acknowledges the exact observed BEL sequence. `.` resolves to the exact `TREEPORT_TERMINAL_ID` inside a managed terminal; it is not a name or path lookup.
110
+
111
+ Read recent terminal contents with:
112
+
113
+ ```sh
114
+ treeport terminal capture <terminal-id>
115
+ treeport terminal capture <terminal-id> --lines 500
116
+ treeport terminal capture <terminal-id> --json
117
+ ```
118
+
119
+ Capture returns up to 200 pane rows by default. Plain output is the terminal text; JSON output includes the terminal ID, capture time, line limit, and content. `.` can be used for the current managed terminal.
120
+
121
+ Wait for raw terminal conditions without polling or scraping output:
122
+
123
+ ```sh
124
+ treeport terminal wait <terminal-id> --until working
125
+ treeport terminal wait <terminal-id> --until idle --timeout 30m
126
+ treeport terminal wait <terminal-id> --until bell
127
+ treeport terminal wait <terminal-id> --until exit
128
+ ```
129
+
130
+ - `idle` means no daemon-owned OSC progress is currently observed and can return immediately.
131
+ - `working` means daemon-owned OSC progress is currently present and can return immediately. Every valid active progress frame renews a five-minute inactivity lease; an explicit clear or terminal/observer shutdown clears immediately.
132
+ - `bell` means the next real BEL after the event subscription is established.
133
+ - `exit` means the retained terminal process has exited.
134
+ - Waits have no default timeout. Use a positive `ms`, `s`, `m`, or `h` duration when a deadline is required; Ctrl+C cancels.
135
+
136
+ Treeport does not infer agent settlement. An orchestrator can inspect first, wait for `working` if no progress cycle has been observed, and then wait for `idle`. Progress depends on the child application emitting OSC `9;4`; for Pi, `terminal.showTerminalProgress` must be enabled. Applications should clear progress or refresh active progress more frequently than the five-minute lease. A null progress value is not proof that every application supports progress reporting.
137
+
138
+ ## Inspect the worktree application in a browser tab
139
+
140
+ The Browser primitive has one live runtime for each browser tab.
141
+
142
+ Electron owns the visible `<webview>` page for a local desktop connection.
143
+
144
+ Playwright controls managed Chromium for a web or remote desktop connection.
145
+
146
+ Never attach Treeport to a personal browser profile.
147
+
148
+ First, inspect the available browser tabs:
149
+
150
+ ```sh
151
+ treeport browser list
152
+ ```
153
+
154
+ If no browser tab is open, open one in the current tree:
155
+
156
+ ```sh
157
+ treeport browser open --worktree .
158
+ ```
159
+
160
+ Use these commands on the current live page:
161
+
162
+ ```sh
163
+ treeport browser snapshot
164
+ treeport browser click e12
165
+ treeport browser fill e14 "value"
166
+ treeport browser press Enter
167
+ treeport browser console
168
+ treeport browser network
169
+ treeport browser screenshot
170
+ ```
171
+
172
+ The commands select the only browser tab in the current tree.
173
+
174
+ When more than one browser tab exists, add `--panel <panel-id>`.
175
+
176
+ Snapshot references belong to one runtime generation.
177
+
178
+ Take a new snapshot after navigation or a runtime change.
179
+
180
+ A local desktop owner does not need managed Chromium.
181
+
182
+ If no local owner exists, check managed Chromium before daemon automation:
183
+
184
+ ```sh
185
+ treeport browser status
186
+ ```
187
+
188
+ Ask the user before you run `treeport browser install`.
189
+
190
+ Leave the browser tab open so the user can inspect the result.
191
+
192
+ ## Automation and integrations
193
+
194
+ Extensions and scripts should add `--json` before the `--` command separator:
195
+
196
+ ```sh
197
+ treeport context --json
198
+
199
+ treeport terminal create \
200
+ --worktree <tree-id> \
201
+ --name <terminal-name> \
202
+ --json -- <program> <arg> ...
203
+
204
+ treeport spawn \
205
+ --worktree-name <tree-name> \
206
+ --name <terminal-name> \
207
+ --json -- <program> <arg> ...
208
+ ```
209
+
210
+ JSON success output is written to stdout. JSON errors use `{ "error": { "code", "message", "details"? } }` on stderr. Relevant exit codes are:
211
+
212
+ - `0`: command completed; for `spawn`, still inspect `terminal`, `terminalError`, and `setupError`.
213
+ - `2`: invalid CLI usage.
214
+ - `3`: the daemon could not be reached or its event stream failed.
215
+ - `4`: a terminal wait timed out.
216
+ - `5`: API, domain, or invalid-context refusal.
217
+ - `130`: a terminal wait was interrupted with Ctrl+C.
218
+
219
+ Treeport trusts the local OS boundary for loopback access and delegates supported remote authentication to Tailscale Serve. Do not expose the daemon through a direct network listener, invent credentials, or put secrets in command arguments or URLs.