@hayasaka7/haya-pet 0.1.0

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.
Files changed (131) hide show
  1. package/.gitattributes +34 -0
  2. package/.github/workflows/release.yml +61 -0
  3. package/LICENSE +21 -0
  4. package/README.md +247 -0
  5. package/apps/cli/src/haya-pet.js +395 -0
  6. package/apps/cli/test/haya-pet.test.mjs +339 -0
  7. package/apps/companion/README.md +83 -0
  8. package/apps/companion/package.json +17 -0
  9. package/apps/companion/src/main/display-manager.js +71 -0
  10. package/apps/companion/src/main/index.js +349 -0
  11. package/apps/companion/src/main/lock-file.js +52 -0
  12. package/apps/companion/src/main/panel-placement.js +45 -0
  13. package/apps/companion/src/main/pet-loader.js +2 -0
  14. package/apps/companion/src/main/position-store.js +3 -0
  15. package/apps/companion/src/main/preload.cjs +13 -0
  16. package/apps/companion/src/main/state-file.js +2 -0
  17. package/apps/companion/src/main/terminal-helper-client.js +79 -0
  18. package/apps/companion/src/main/terminal-locator.js +44 -0
  19. package/apps/companion/src/main/tray-menu.js +79 -0
  20. package/apps/companion/src/main/window-options.js +66 -0
  21. package/apps/companion/src/renderer/index.html +18 -0
  22. package/apps/companion/src/renderer/interaction-controller.js +114 -0
  23. package/apps/companion/src/renderer/pet-window.js +275 -0
  24. package/apps/companion/src/renderer/session-bubbles.js +138 -0
  25. package/apps/companion/src/renderer/styles.css +225 -0
  26. package/apps/companion/src/renderer/task-talk-window.js +141 -0
  27. package/apps/companion/test/display-manager.test.mjs +48 -0
  28. package/apps/companion/test/interaction-controller.test.mjs +107 -0
  29. package/apps/companion/test/panel-placement.test.mjs +60 -0
  30. package/apps/companion/test/position-store.test.mjs +54 -0
  31. package/apps/companion/test/state-file.test.mjs +52 -0
  32. package/apps/companion/test/terminal-helper-client.test.mjs +68 -0
  33. package/apps/companion/test/terminal-locator.test.mjs +35 -0
  34. package/apps/companion/test/tray-menu.test.mjs +45 -0
  35. package/apps/companion/test/window-options.test.mjs +62 -0
  36. package/apps/pet-preview/index.html +42 -0
  37. package/apps/pet-preview/src/preview-app.js +123 -0
  38. package/apps/pet-preview/src/preview-state.js +70 -0
  39. package/apps/pet-preview/src/preview.css +125 -0
  40. package/apps/pet-preview/test/preview-state.test.mjs +62 -0
  41. package/assets/fallback-pet/README.md +16 -0
  42. package/assets/fallback-pet/pet.json +13 -0
  43. package/docs/architecture.md +144 -0
  44. package/docs/known-issues.md +49 -0
  45. package/docs/publishing.md +48 -0
  46. package/docs/screenshots/README.md +7 -0
  47. package/docs/screenshots/folder-collapsed.png +0 -0
  48. package/docs/screenshots/hero.png +0 -0
  49. package/docs/screenshots/pet-overlay.png +0 -0
  50. package/docs/screenshots/session-bubbles.png +0 -0
  51. package/docs/screenshots/tray-menu.png +0 -0
  52. package/docs/troubleshooting.md +36 -0
  53. package/native/README.md +80 -0
  54. package/native/linux-window-helper/README.md +29 -0
  55. package/native/mac-window-helper/README.md +30 -0
  56. package/native/win-window-helper/Program.cs +312 -0
  57. package/native/win-window-helper/README.md +53 -0
  58. package/native/win-window-helper/win-window-helper.csproj +12 -0
  59. package/package.json +35 -0
  60. package/packages/adapters/src/adapter-info.js +61 -0
  61. package/packages/adapters/src/capabilities.js +39 -0
  62. package/packages/adapters/src/heuristics.js +114 -0
  63. package/packages/adapters/src/output-observer.js +164 -0
  64. package/packages/adapters/src/routing.js +86 -0
  65. package/packages/adapters/test/adapter-info.test.mjs +35 -0
  66. package/packages/adapters/test/capabilities.test.mjs +44 -0
  67. package/packages/adapters/test/heuristics.test.mjs +42 -0
  68. package/packages/adapters/test/output-observer.test.mjs +142 -0
  69. package/packages/adapters/test/routing.test.mjs +93 -0
  70. package/packages/app-state/src/state-file.js +53 -0
  71. package/packages/app-state/src/state.js +80 -0
  72. package/packages/app-state/test/state.test.mjs +36 -0
  73. package/packages/cli-core/src/companion-launcher.js +69 -0
  74. package/packages/cli-core/src/pty-runner.js +96 -0
  75. package/packages/cli-core/src/run-command.js +353 -0
  76. package/packages/cli-core/src/strip-ansi.js +16 -0
  77. package/packages/cli-core/test/companion-launcher.test.mjs +98 -0
  78. package/packages/cli-core/test/run-command.test.mjs +177 -0
  79. package/packages/cli-core/test/strip-ansi.test.mjs +27 -0
  80. package/packages/daemon-core/src/daemon-runtime.js +49 -0
  81. package/packages/daemon-core/src/ipc-server.js +180 -0
  82. package/packages/daemon-core/src/ipc-transport.js +70 -0
  83. package/packages/daemon-core/src/singleton.js +46 -0
  84. package/packages/daemon-core/test/daemon-runtime.test.mjs +65 -0
  85. package/packages/daemon-core/test/ipc-server.test.mjs +70 -0
  86. package/packages/daemon-core/test/ipc-transport.test.mjs +72 -0
  87. package/packages/daemon-core/test/singleton.test.mjs +32 -0
  88. package/packages/pet-core/src/animation-state.js +84 -0
  89. package/packages/pet-core/src/animator.js +26 -0
  90. package/packages/pet-core/src/atlas.js +81 -0
  91. package/packages/pet-core/src/discovery.js +90 -0
  92. package/packages/pet-core/src/manifest.js +112 -0
  93. package/packages/pet-core/src/validation.js +43 -0
  94. package/packages/pet-core/test/animation-state.test.mjs +47 -0
  95. package/packages/pet-core/test/animator.test.mjs +31 -0
  96. package/packages/pet-core/test/atlas.test.mjs +81 -0
  97. package/packages/pet-core/test/discovery.test.mjs +93 -0
  98. package/packages/pet-core/test/manifest.test.mjs +93 -0
  99. package/packages/pet-core/test/validation.test.mjs +69 -0
  100. package/packages/platform-core/src/capabilities.js +49 -0
  101. package/packages/platform-core/src/paths.js +75 -0
  102. package/packages/platform-core/src/platform.js +15 -0
  103. package/packages/platform-core/test/platform.test.mjs +84 -0
  104. package/packages/protocol/src/messages.js +156 -0
  105. package/packages/protocol/test/messages.test.mjs +112 -0
  106. package/packages/session-core/src/bubble-linger.js +47 -0
  107. package/packages/session-core/src/bubble-view.js +79 -0
  108. package/packages/session-core/src/pet-state.js +56 -0
  109. package/packages/session-core/src/priority.js +56 -0
  110. package/packages/session-core/src/registry.js +144 -0
  111. package/packages/session-core/src/summaries.js +54 -0
  112. package/packages/session-core/test/bubble-linger.test.mjs +96 -0
  113. package/packages/session-core/test/bubble-view.test.mjs +79 -0
  114. package/packages/session-core/test/pet-state.test.mjs +118 -0
  115. package/packages/session-core/test/priority.test.mjs +53 -0
  116. package/packages/session-core/test/registry.test.mjs +161 -0
  117. package/packages/session-core/test/summaries.test.mjs +38 -0
  118. package/packages/task-core/src/approvals.js +91 -0
  119. package/packages/task-core/src/controls.js +61 -0
  120. package/packages/task-core/src/replies.js +80 -0
  121. package/packages/task-core/src/task-events.js +101 -0
  122. package/packages/task-core/src/task-status.js +93 -0
  123. package/packages/task-core/src/task-store.js +74 -0
  124. package/packages/task-core/test/approvals.test.mjs +61 -0
  125. package/packages/task-core/test/controls.test.mjs +61 -0
  126. package/packages/task-core/test/replies.test.mjs +51 -0
  127. package/packages/task-core/test/task-events.test.mjs +67 -0
  128. package/packages/task-core/test/task-status.test.mjs +49 -0
  129. package/packages/task-core/test/task-store.test.mjs +65 -0
  130. package/test/harness.mjs +22 -0
  131. package/test/run-tests.mjs +47 -0
@@ -0,0 +1,144 @@
1
+ # Architecture
2
+
3
+ How Haya Pet is put together. For installing and using it, see the
4
+ [README](../README.md); this doc is for contributors and the curious.
5
+
6
+ ## Pipeline
7
+
8
+ ```text
9
+ AI terminal clients
10
+ → client adapters (normalize behavior into a common event model)
11
+ → haya-petd daemon (sessions, priority, pet state, IPC)
12
+ → shared pet runtime (assets, animation, interaction)
13
+ → desktop overlay (global pet + session bubbles)
14
+ ```
15
+
16
+ You launch any AI CLI through the `haya-pet` wrapper. The wrapper registers a
17
+ session and reports lifecycle/activity events to the daemon over local IPC (a
18
+ named pipe on Windows, a unix socket elsewhere). The first `haya-pet run`
19
+ **auto-starts the daemon/overlay**, so users only ever type `haya-pet run …`.
20
+
21
+ | Component | Responsibility |
22
+ |---|---|
23
+ | `haya-pet` (CLI) | Wrapper: launches clients, registers sessions, reports events, auto-starts the companion. |
24
+ | `haya-petd` (companion) | Global daemon + overlay: owns sessions, pet state, windows, IPC. |
25
+ | adapters | Translate client-specific behavior into the common state model. |
26
+ | pet-core | Loads pet assets, computes frames, drives animation state. |
27
+ | session-core | Tracks sessions, priority, summaries, bubble view models, linger. |
28
+ | task-core | Task status, events, approvals, replies, control gating (reply/approval UI is parked). |
29
+ | platform-core | Per-OS paths, capabilities, and fallback tiers. |
30
+
31
+ ## Normalized state model
32
+
33
+ Every client maps to a shared state vocabulary that drives the pet animation and
34
+ the bubble status icons:
35
+
36
+ `idle`, `thinking`, `running_tool`, `editing_files`, `waiting_user`,
37
+ `waiting_approval`, `reviewing`, `compacting`, `failed`, `success`, `stale`,
38
+ `exited`.
39
+
40
+ Bubbles collapse these into four status kinds: **working** (spinner), **done**
41
+ (check), **attention** (yellow), **failed** (red cross).
42
+
43
+ ## Adapter support tiers
44
+
45
+ The daemon never bakes in client-specific logic; adapters provide as much fidelity
46
+ as each client allows:
47
+
48
+ | Tier | Source | Fidelity |
49
+ |---|---|---|
50
+ | L1 | Process wrapper (lifecycle only) | session exists / exit code |
51
+ | L2 | PTY output observation (`--observe`, default) | activity-based working/idle |
52
+ | L3 | Client logs / state files | client-specific (future) |
53
+ | L4 | Official plugin/hooks | richest (future) |
54
+
55
+ L2 is **activity-based**: any visible output → *working*; a short quiet window →
56
+ *idle*; success/failure come from the real exit code, never from scraping output
57
+ text. Keyword heuristics exist but are opt-in (unreliable on rich TUIs). See
58
+ [known-issues.md](known-issues.md) for the current L2/PTY tradeoffs.
59
+
60
+ ## Overlay model
61
+
62
+ The overlay is a transparent, always-on-top window spanning the work area, kept
63
+ click-through except over the pet and bubble chips (via `setIgnoreMouseEvents`
64
+ with mouse-move forwarding). The pet is positioned inside the window and dragged
65
+ via CSS; the bubble panel is placed on whichever side of the pet has room so it
66
+ stays fully on-screen. The pet currently lives on a single display's work area.
67
+
68
+ ## Distribution & runtime dependencies
69
+
70
+ - `electron` is a **runtime dependency** (not just a dev tool), because
71
+ `haya-pet run` launches the overlay by spawning `electron <companion>`.
72
+ - `node-pty` is **optional**: live observation (L2) uses it, and degrades to L1
73
+ lifecycle tracking when it's absent.
74
+ - Auto-start can be disabled with `HAYA_PET_NO_AUTOSTART=1`; `haya-pet start` /
75
+ `haya-pet stop` control the overlay explicitly.
76
+
77
+ See [publishing.md](publishing.md) for the npm release process.
78
+
79
+ ## Project structure
80
+
81
+ ```text
82
+ packages/
83
+ protocol/ IPC message types + validation
84
+ pet-core/ atlas, manifest, validation, animator, animation-state
85
+ session-core/ registry, priority, summaries, bubble views, linger, pet-state
86
+ task-core/ task status, events, store, approvals, replies, controls
87
+ adapters/ client info, heuristics, capabilities, output observer, routing
88
+ daemon-core/ IPC server/transport, runtime bridge, singleton
89
+ platform-core/ platform, paths, capabilities
90
+ apps/
91
+ cli/ haya-pet entrypoint + parser (run / start / stop / pets)
92
+ companion/ Electron overlay app (main + renderer)
93
+ pet-preview/ static preview scaffold
94
+ native/
95
+ win-window-helper/ Windows terminal-window helper (.NET, implemented)
96
+ mac-window-helper/ macOS helper (contract documented)
97
+ linux-window-helper/ Linux X11/Wayland helper (contract documented)
98
+ assets/
99
+ fallback-pet/ bundled fallback pet manifest
100
+ ```
101
+
102
+ There are no per-package `package.json` files — packages import each other by
103
+ relative path, which is also why the whole tree ships as one npm package.
104
+
105
+ ## Platform support
106
+
107
+ | Feature | Windows | macOS | Linux X11 | Linux Wayland |
108
+ |---|---|---|---|---|
109
+ | Protocol / session core | ✅ | ✅ | ✅ | ✅ |
110
+ | Generic CLI wrapper | ✅ | ✅ | ✅ | ✅ |
111
+ | Local daemon IPC | named pipe | unix socket | unix socket | unix socket |
112
+ | Transparent overlay | ✅ | ✅ | ✅ | best-effort |
113
+ | Terminal attachment | ✅ helper | 🔜 | 🔜 | fallback |
114
+
115
+ See [cross-os-qa.md](cross-os-qa.md) for the full test matrix.
116
+
117
+ ## Native window helpers
118
+
119
+ Optional per-OS helpers locate terminal windows so bubbles can attach near them.
120
+
121
+ ```powershell
122
+ # Windows (.NET SDK 10, net10.0-windows):
123
+ cd native\win-window-helper
124
+ dotnet build -c Release
125
+ # -> bin/Release/net10.0-windows/haya-pet-win-window-helper.exe
126
+ ```
127
+
128
+ macOS (Swift/AppKit) and Linux (X11/Wayland) helpers have documented contracts
129
+ but are not yet implemented.
130
+
131
+ ## Status & roadmap
132
+
133
+ Implemented and tested: shared core, CLI wrapper (run/start/stop/pets),
134
+ auto-start, daemon IPC + shutdown, adapters, PTY-based live observation,
135
+ Codex-style session bubbles, the Electron overlay, and the Windows terminal
136
+ helper. In progress:
137
+
138
+ - Bidirectional IPC so the daemon routes replies/approvals back to the wrapper
139
+ (re-enabling the parked reply/approval UI).
140
+ - macOS (Swift/AppKit) and Linux X11 terminal helpers.
141
+ - Faithful PTY passthrough (see [known-issues.md](known-issues.md)).
142
+ - Production overlay/IPC validation across all platforms.
143
+
144
+ See [`../PROGRESS.md`](../PROGRESS.md) for the detailed log.
@@ -0,0 +1,49 @@
1
+ # Known Issues (deferred)
2
+
3
+ Issues found in live use that are **recorded for later** — not yet fixed. The
4
+ decision on *how* to fix is pending.
5
+
6
+ ## 1. Terminal scrolling breaks when running a CLI through `haya-pet run` (observe/PTY mode)
7
+
8
+ - **Symptom:** While a CLI is running under `haya-pet run` (default `--observe`), the
9
+ terminal window can no longer scroll normally.
10
+ - **Trigger:** Only in observe (PTY) mode. The plain `--no-observe` path
11
+ (`stdio: "inherit"`) does not have this problem.
12
+ - **Diagnosis:** Observe mode runs the CLI inside a `node-pty`/ConPTY pseudo-terminal
13
+ with a fixed `cols × rows` and tees its output to our `stdout`
14
+ (`packages/cli-core/src/pty-runner.js`). Full-screen TUIs (Claude Code, Codex)
15
+ render into that fixed grid, so the outer terminal's scrollback only ever sees
16
+ redraws of the grid rather than a growing transcript — there is effectively
17
+ nothing meaningful to scroll. On Windows there is an extra layer: shims are run
18
+ as `cmd /d /s /c "<shim> ..."` *inside* the PTY, so `cmd.exe` is the PTY's
19
+ foreground process and the TUI is its child.
20
+ - **Notes for a future fix:** Tools like `script`/`asciinema` interpose a PTY
21
+ without breaking the terminal, so a faithful passthrough is achievable. Avenues:
22
+ resolve the shim and spawn the real executable directly in the PTY (drop the
23
+ `cmd /c` layer); verify `cols/rows` always match the host; revisit whether
24
+ observe should be the default for interactive TUIs vs. native passthrough.
25
+
26
+ ## 2. Backspace deletes a whole word instead of one character (observe/PTY mode)
27
+
28
+ - **Symptom:** While a CLI is running under `haya-pet run`, pressing Backspace
29
+ deletes an entire word rather than a single character.
30
+ - **Trigger:** Only in observe (PTY) mode. Native (`--no-observe`) is unaffected.
31
+ - **Diagnosis:** stdin is put in raw mode and forwarded byte-for-byte into the PTY
32
+ (`pty-runner.js` `forwardInput` → `child.write(chunk.toString("utf8"))`). The
33
+ whole-word deletion points to a key-encoding/line-discipline mismatch between the
34
+ host terminal (Windows Terminal/conhost) and the nested ConPTY (and possibly the
35
+ intermediate `cmd /c`) — e.g. Backspace `0x7f`/`0x08` being interpreted by the
36
+ inner app as a word-delete (Ctrl+W `0x17` / Alt+Backspace `ESC 0x7f`), or input
37
+ bytes being re-segmented across `data` events.
38
+ - **Notes for a future fix:** Forward stdin as raw bytes without a UTF-8 round-trip;
39
+ audit the exact bytes the host sends for Backspace vs. what the inner app receives
40
+ (a small PTY echo harness); consider removing the `cmd /c` layer; re-evaluate
41
+ raw-mode handling. Hard to fully verify without interactive testing.
42
+
43
+ ## Shared root cause & the open decision
44
+
45
+ Both issues stem from observe mode interposing a pseudo-terminal on an interactive
46
+ session. The plain wrapper path avoids them entirely but cannot report fine-grained
47
+ "thinking / running tools" status. The open product decision: **native terminal by
48
+ default (perfect fidelity, coarser status) vs. keep PTY observation default and
49
+ invest in faithful passthrough.** Deferred until the maintainer decides.
@@ -0,0 +1,48 @@
1
+ # Publishing
2
+
3
+ How Haya Pet is released to npm. Most users never need this — see the
4
+ [README](../README.md) to install and use it.
5
+
6
+ ## Release flow
7
+
8
+ Releases are automated by [`.github/workflows/release.yml`](../.github/workflows/release.yml):
9
+ pushing a `v*` tag triggers a workflow that installs, runs the tests, checks the
10
+ tag matches `package.json`, and publishes to npm.
11
+
12
+ ```bash
13
+ npm version patch # bumps package.json + creates tag vX.Y.Z
14
+ git push --follow-tags # pushes the tag → triggers the release workflow
15
+ ```
16
+
17
+ The workflow installs with `ELECTRON_SKIP_BINARY_DOWNLOAD=1` (the ~150 MB binary
18
+ isn't needed to test or publish) and publishes with
19
+ `npm publish --provenance --access public` using the `NPM_TOKEN` secret.
20
+
21
+ ## One-time setup
22
+
23
+ 1. **`NPM_TOKEN` secret** — create an npm *automation* (or granular publish) token
24
+ at npmjs.com and add it under GitHub → repo Settings → Secrets and variables →
25
+ Actions → `NPM_TOKEN`.
26
+ 2. **Package name** — confirm the name in `package.json` is available
27
+ (`npm view <name>`); rename or use a scope (`@you/haya-pet`) if taken. The
28
+ workflow already passes `--access public` for scoped packages.
29
+ 3. **`private` removed** — `npm publish` refuses private packages; the root
30
+ `package.json` must not have `"private": true` (already removed).
31
+ 4. **Provenance / public repo** — `--provenance` requires a public repo and the
32
+ `id-token: write` permission (set in the workflow). Drop `--provenance` if the
33
+ repo is private.
34
+
35
+ ## Notes
36
+
37
+ - **Local registry mirror.** If your local npm points at a mirror (e.g.
38
+ `registry.npmmirror.com`), don't `npm publish` locally — the workflow targets
39
+ `registry.npmjs.org` explicitly, which is what you want.
40
+ - **Lockfile sync.** After changing dependencies in `package.json`, refresh the
41
+ lockfile (`npm install --package-lock-only`) and commit it, or CI's `npm ci`
42
+ will fail on the mismatch.
43
+ - **Tarball contents.** The package ships the whole tree (`apps/` + `packages/`)
44
+ because modules import each other by relative path. Test files are currently
45
+ included; add a `"files"` allowlist to `package.json` to trim the tarball to
46
+ runtime code only.
47
+ - **Runtime deps.** `electron` is a dependency (the CLI launches it); `node-pty`
48
+ is optional (native build; live observation degrades gracefully without it).
@@ -0,0 +1,7 @@
1
+ Place screenshot PNGs referenced by the root README here (~800px wide):
2
+
3
+ - `hero.png` — wide shot: the pet on the desktop with a couple of session bubbles
4
+ - `pet-overlay.png` — the pet reacting to the highest-priority session
5
+ - `session-bubbles.png` — bubbles expanded, showing per-session status icons
6
+ - `folder-collapsed.png` — bubbles folded away beside the pet
7
+ - `tray-menu.png` — the tray menu (show/hide, pets, reset position, Quit)
Binary file
Binary file
Binary file
@@ -0,0 +1,36 @@
1
+ # Troubleshooting
2
+
3
+ Quick fixes for common issues. See also [known-issues.md](known-issues.md) for
4
+ deferred problems with known root causes.
5
+
6
+ | Symptom | Fix |
7
+ |---|---|
8
+ | `haya-pet: command not found` | Install globally (`npm i -g …`), or in a source checkout run `npm link` in the repo root, or call the file directly: `node <repo>/apps/cli/src/haya-pet.js`. |
9
+ | Running a CLI starts the pet but not the command | Fixed — update to the latest version. (Was caused by the auto-start poll exiting early.) |
10
+ | Pet doesn't react to a session | Launch the CLI via `haya-pet run …`. If the overlay didn't auto-start, run `haya-pet start`, or check `HAYA_PET_NO_AUTOSTART` isn't set. |
11
+ | Pet shows a blue placeholder box | No spritesheet found — add a pet (see the README); behaviour is otherwise correct. |
12
+ | Pet is off-screen / can't find it | Tray icon → **Reset Position**. |
13
+ | Can't exit the pet | `haya-pet stop`, or right-click the tray icon → **Quit**. |
14
+ | `haya-pet pets` shows "No pets found" | Add a pet folder with **both** `pet.json` and a spritesheet to a search path. |
15
+ | Terminal scroll / backspace odd while a CLI runs under `haya-pet run` | Known PTY-observation tradeoff — see [known-issues.md](known-issues.md). Workaround: `haya-pet run --no-observe …`. |
16
+ | `ENOENT … electron\path.txt` | Electron's install extraction was interrupted — see below. |
17
+
18
+ ## Fixing a broken Electron install
19
+
20
+ If launching the overlay fails with `ENOENT … node_modules\electron\path.txt`,
21
+ the Electron binary downloaded but extraction left `dist/` empty. The cached zip
22
+ is fine, so re-extract it without re-downloading (PowerShell, from the repo root):
23
+
24
+ ```powershell
25
+ $cache = Get-ChildItem "$env:LOCALAPPDATA\electron\Cache" -Recurse -Filter "electron-*.zip" |
26
+ Sort-Object LastWriteTime -Descending | Select-Object -First 1
27
+ $dist = "node_modules\electron\dist"
28
+ Remove-Item -Recurse -Force $dist -ErrorAction SilentlyContinue
29
+ New-Item -ItemType Directory $dist | Out-Null
30
+ Expand-Archive -Path $cache.FullName -DestinationPath $dist -Force
31
+ Set-Content "node_modules\electron\path.txt" "electron.exe" -NoNewline
32
+ node_modules\.bin\electron --version # should print the version
33
+ ```
34
+
35
+ If `electron.exe` is missing even after a clean `Expand-Archive`, your antivirus
36
+ likely quarantined it — allow `node_modules\electron\dist\electron.exe` and retry.
@@ -0,0 +1,80 @@
1
+ # Native Window Helpers
2
+
3
+ Terminal-window discovery is platform-specific and is isolated behind small,
4
+ optional native helper processes. The JavaScript runtime never links native
5
+ code directly; it spawns a helper and talks to it over a line-delimited JSON
6
+ protocol on stdin/stdout. This keeps the daemon portable and lets the helper be
7
+ written in the most appropriate language per OS (C#/Win32, Swift/AppKit, C/Xlib).
8
+
9
+ The `apps/companion/src/main/terminal-locator.js` facade decides *which* helper
10
+ strategy applies per platform (`win32-window-helper`, `macos-accessibility-helper`,
11
+ `x11-window-helper`, or `manual-fallback`). When the strategy is implemented, the
12
+ main process spawns the matching helper and uses this contract.
13
+
14
+ ## Transport
15
+
16
+ - One JSON object per line (`\n`-delimited), UTF-8, on both stdin and stdout.
17
+ - The helper is long-lived: it reads requests until stdin closes.
18
+ - The helper must never write anything but protocol JSON to stdout. Diagnostics
19
+ go to stderr.
20
+
21
+ ## Requests
22
+
23
+ ```json
24
+ { "id": "req_1", "op": "capabilities" }
25
+ ```
26
+
27
+ ```json
28
+ { "id": "req_2", "op": "locate", "pid": 12345, "terminalPid": 5678 }
29
+ ```
30
+
31
+ | Field | Meaning |
32
+ |---|---|
33
+ | `id` | Opaque correlation id echoed back in the response. |
34
+ | `op` | `"capabilities"` or `"locate"`. |
35
+ | `pid` | AI client process id (walk its parent tree to the terminal). |
36
+ | `terminalPid` | Optional known terminal pid hint. |
37
+
38
+ ## Responses
39
+
40
+ Capabilities:
41
+
42
+ ```json
43
+ { "id": "req_1", "ok": true, "capabilities": { "locate": true, "follow": false, "permission": "granted" } }
44
+ ```
45
+
46
+ Locate (found):
47
+
48
+ ```json
49
+ {
50
+ "id": "req_2",
51
+ "ok": true,
52
+ "window": {
53
+ "x": 100,
54
+ "y": 200,
55
+ "width": 1200,
56
+ "height": 760,
57
+ "displayId": "primary",
58
+ "title": "pwsh — project",
59
+ "confidence": 0.8
60
+ }
61
+ }
62
+ ```
63
+
64
+ Locate (not found / unsupported / permission denied):
65
+
66
+ ```json
67
+ { "id": "req_2", "ok": false, "error": "not_found" }
68
+ ```
69
+
70
+ `error` is one of: `not_found`, `unsupported`, `permission_denied`, `invalid_request`.
71
+
72
+ ## Rules
73
+
74
+ - Coordinates are in OS virtual-screen pixels; the runtime converts to the pet's
75
+ display/DPI space using `display-manager.js`.
76
+ - A helper that cannot resolve a window must return `ok:false`, never guess.
77
+ - `permission` in capabilities lets the UI explain why attachment is unavailable
78
+ (e.g. macOS Accessibility not granted) and fall back to manual/cluster bubbles.
79
+ - Helpers are best-effort. The runtime always supports manual bubble positioning
80
+ when a helper reports `unsupported` or `permission_denied`.
@@ -0,0 +1,29 @@
1
+ # Linux Window Helper
2
+
3
+ Strategy ids: `x11-window-helper` (X11, best-effort) and `manual-fallback` (Wayland).
4
+
5
+ Implements the shared helper protocol in [`../README.md`](../README.md).
6
+
7
+ ## Responsibility
8
+
9
+ - On X11: discover terminal windows through Xlib (`_NET_WM_PID`, `XGetWindowProperty`)
10
+ or a `wmctrl`/`xdotool`-style approach, match the tracked process tree, and
11
+ return window bounds.
12
+ - On Wayland: report limited capability instead of assuming global window
13
+ positioning works.
14
+
15
+ ## Implementation notes
16
+
17
+ - Suggested language: C with Xlib/XCB, or a thin wrapper over `wmctrl`/`xdotool`.
18
+ - X11: match `_NET_WM_PID` against the AI client's parent terminal pid; read
19
+ geometry via `XGetGeometry` + `XTranslateCoordinates` for absolute coordinates.
20
+ - Wayland: most compositors block global window enumeration/positioning. The
21
+ helper should answer `capabilities` with `{ "locate": false }` and return
22
+ `{ "ok": false, "error": "unsupported" }` for `locate`, so the runtime uses
23
+ global pet + cluster/manual bubbles.
24
+
25
+ ## Protocol mapping
26
+
27
+ - X11 `op: "capabilities"` → `{ "locate": true, "follow": false, "permission": "granted" }`.
28
+ - Wayland `op: "capabilities"` → `{ "locate": false, "follow": false, "permission": "granted" }`.
29
+ - `op: "locate"` → `window` rect on X11 when found, otherwise `not_found` / `unsupported`.
@@ -0,0 +1,30 @@
1
+ # macOS Window Helper
2
+
3
+ Strategy id: `macos-accessibility-helper` (best-effort).
4
+
5
+ Implements the shared helper protocol in [`../README.md`](../README.md).
6
+
7
+ ## Responsibility
8
+
9
+ - Locate Terminal.app, iTerm2, VS Code, or other terminal windows for tracked
10
+ process trees.
11
+ - Use the Accessibility API (`AXUIElement`) or `CGWindowListCopyWindowInfo`
12
+ where permission is available.
13
+ - Return window bounds and permission status for bubble attachment.
14
+
15
+ ## Implementation notes
16
+
17
+ - Suggested language: Swift with AppKit/ApplicationServices.
18
+ - Accessibility requires the user to grant permission in System Settings →
19
+ Privacy & Security → Accessibility. When not granted, return
20
+ `{ "ok": false, "error": "permission_denied" }` and report
21
+ `"permission": "denied"` from `capabilities` so the UI can prompt the user.
22
+ - Handle Retina scale: report points and include the backing scale so the
23
+ runtime can map to device pixels.
24
+ - Spaces/full-screen windows may be undiscoverable; return `not_found` rather
25
+ than guessing.
26
+
27
+ ## Protocol mapping
28
+
29
+ - `op: "capabilities"` → `{ "locate": true, "follow": false, "permission": "granted" | "denied" }`.
30
+ - `op: "locate"` → `window` rect on success, or `permission_denied` / `not_found`.