@arach/lattices 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 (64) hide show
  1. package/README.md +157 -0
  2. package/app/Lattices.app/Contents/Info.plist +24 -0
  3. package/app/Package.swift +13 -0
  4. package/app/Sources/App.swift +49 -0
  5. package/app/Sources/AppDelegate.swift +104 -0
  6. package/app/Sources/AppShellView.swift +62 -0
  7. package/app/Sources/AppTypeClassifier.swift +70 -0
  8. package/app/Sources/AppWindowShell.swift +63 -0
  9. package/app/Sources/CheatSheetHUD.swift +331 -0
  10. package/app/Sources/CommandModeState.swift +1341 -0
  11. package/app/Sources/CommandModeView.swift +1380 -0
  12. package/app/Sources/CommandModeWindow.swift +192 -0
  13. package/app/Sources/CommandPaletteView.swift +307 -0
  14. package/app/Sources/CommandPaletteWindow.swift +134 -0
  15. package/app/Sources/DaemonProtocol.swift +101 -0
  16. package/app/Sources/DaemonServer.swift +406 -0
  17. package/app/Sources/DesktopModel.swift +121 -0
  18. package/app/Sources/DesktopModelTypes.swift +71 -0
  19. package/app/Sources/DiagnosticLog.swift +253 -0
  20. package/app/Sources/EventBus.swift +29 -0
  21. package/app/Sources/HotkeyManager.swift +249 -0
  22. package/app/Sources/HotkeyStore.swift +330 -0
  23. package/app/Sources/InventoryManager.swift +35 -0
  24. package/app/Sources/InventoryPath.swift +43 -0
  25. package/app/Sources/KeyRecorderView.swift +210 -0
  26. package/app/Sources/LatticesApi.swift +915 -0
  27. package/app/Sources/MainView.swift +507 -0
  28. package/app/Sources/MainWindow.swift +70 -0
  29. package/app/Sources/OrphanRow.swift +129 -0
  30. package/app/Sources/PaletteCommand.swift +409 -0
  31. package/app/Sources/PermissionChecker.swift +115 -0
  32. package/app/Sources/Preferences.swift +48 -0
  33. package/app/Sources/ProcessModel.swift +199 -0
  34. package/app/Sources/ProcessQuery.swift +151 -0
  35. package/app/Sources/Project.swift +28 -0
  36. package/app/Sources/ProjectRow.swift +368 -0
  37. package/app/Sources/ProjectScanner.swift +121 -0
  38. package/app/Sources/ScreenMapState.swift +2397 -0
  39. package/app/Sources/ScreenMapView.swift +2817 -0
  40. package/app/Sources/ScreenMapWindowController.swift +89 -0
  41. package/app/Sources/SessionManager.swift +72 -0
  42. package/app/Sources/SettingsView.swift +641 -0
  43. package/app/Sources/SettingsWindow.swift +20 -0
  44. package/app/Sources/TabGroupRow.swift +178 -0
  45. package/app/Sources/Terminal.swift +259 -0
  46. package/app/Sources/TerminalQuery.swift +156 -0
  47. package/app/Sources/TerminalSynthesizer.swift +200 -0
  48. package/app/Sources/Theme.swift +124 -0
  49. package/app/Sources/TilePickerView.swift +209 -0
  50. package/app/Sources/TmuxModel.swift +53 -0
  51. package/app/Sources/TmuxQuery.swift +81 -0
  52. package/app/Sources/WindowTiler.swift +1752 -0
  53. package/app/Sources/WorkspaceManager.swift +434 -0
  54. package/bin/daemon-client.js +187 -0
  55. package/bin/lattices-app.js +205 -0
  56. package/bin/lattices.js +1295 -0
  57. package/docs/api.md +707 -0
  58. package/docs/app.md +250 -0
  59. package/docs/concepts.md +225 -0
  60. package/docs/config.md +234 -0
  61. package/docs/layers.md +317 -0
  62. package/docs/overview.md +74 -0
  63. package/docs/quickstart.md +82 -0
  64. package/package.json +38 -0
package/docs/app.md ADDED
@@ -0,0 +1,250 @@
1
+ ---
2
+ title: Menu Bar App
3
+ description: Command palette, window tiling, and session management
4
+ order: 3
5
+ ---
6
+
7
+ # Menu Bar App
8
+
9
+ The lattices menu bar app is a native macOS companion that lives in your
10
+ menu bar and gives you quick access to all your lattices sessions.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ lattices app # Build (or download) and launch
16
+ lattices app build # Rebuild from source
17
+ lattices app restart # Quit, rebuild, relaunch
18
+ lattices app quit # Stop the app
19
+ ```
20
+
21
+ The first run builds from source if Swift is available, otherwise
22
+ downloads a pre-built binary from GitHub releases.
23
+
24
+ ## Command palette
25
+
26
+ Press **Cmd+Shift+M** from anywhere to open the command palette.
27
+ It's a searchable list of every action the app can perform, with
28
+ fuzzy matching on titles and subtitles.
29
+
30
+ ### Project commands
31
+
32
+ | Command | Description |
33
+ |-------------------------------|------------------------------------------|
34
+ | Launch *project* | Create a new session and open terminal |
35
+ | Attach *project* | Focus or open the running session |
36
+ | Sync *project* | Reconcile session to declared config |
37
+ | Restart *pane* in *project* | Kill and re-run a specific pane's command |
38
+
39
+ ### Window commands
40
+
41
+ Available for running sessions:
42
+
43
+ | Command | Description |
44
+ |-------------------------------|------------------------------------------|
45
+ | Go to *project* | Focus the terminal window (switches Spaces if needed) |
46
+ | Tile *project* Left | Snap window to left half |
47
+ | Tile *project* Right | Snap window to right half |
48
+ | Maximize *project* | Expand window to fill screen |
49
+ | Detach *project* | Disconnect clients, keep session alive |
50
+ | Kill *project* | Terminate the tmux session |
51
+
52
+ ### Tab group commands
53
+
54
+ Available when `groups` are configured in `~/.lattices/workspace.json`
55
+ (see [Tab Groups](/docs/layers#tab-groups)):
56
+
57
+ | Command | Description |
58
+ |-----------------------------|------------------------------------------|
59
+ | Launch *group* | Start the group session (all tabs) |
60
+ | Attach *group* | Focus the running group session |
61
+ | *Group*: *Tab* | Switch to a specific tab within a group |
62
+ | Kill *group* Group | Terminate the group session |
63
+
64
+ ### Layer commands
65
+
66
+ Available when `layers` are configured in `~/.lattices/workspace.json`
67
+ (see [Layers](/docs/layers#layers)):
68
+
69
+ | Command | Description |
70
+ |-----------------------------|------------------------------------------|
71
+ | Switch to Layer: *label* | Focus and tile the layer's project windows |
72
+
73
+ ### App commands
74
+
75
+ | Command | Description |
76
+ |-------------------|------------------------------------------|
77
+ | Settings | Open preferences (terminal, scan root) |
78
+ | Diagnostics | View logs and debug info |
79
+ | Refresh Projects | Re-scan for .lattices.json configs |
80
+ | Quit Lattices | Exit the menu bar app |
81
+
82
+ ## Project discovery
83
+
84
+ The app scans a configurable root directory (up to 3 levels deep)
85
+ for `.lattices.json` files. It skips `.git/` and `node_modules/`.
86
+
87
+ Auto-detection for the scan root checks these paths in order:
88
+ `~/dev`, `~/Developer`, `~/projects`, `~/src`.
89
+
90
+ For each project found, the app reads:
91
+ - Pane names and commands from `.lattices.json`
92
+ - Dev command and package manager from `package.json`
93
+ - Running status by checking `tmux has-session`
94
+
95
+ ## Session management
96
+
97
+ The app calls the lattices CLI for session operations:
98
+
99
+ - **Launch** — runs `lattices` in the project directory, which creates
100
+ or reattaches to the session
101
+ - **Sync** — runs `lattices sync` to reconcile panes to the config
102
+ - **Restart** — runs `lattices restart <pane>` to kill and re-run a
103
+ specific pane's process
104
+ - **Detach** — calls `tmux detach-client` directly
105
+ - **Kill** — calls `tmux kill-session` directly
106
+
107
+ ## Window tiling
108
+
109
+ The app can tile terminal windows to preset screen positions via
110
+ the command palette. It finds windows by their `[lattices:session-name]`
111
+ title tag.
112
+
113
+ For Terminal.app and iTerm2, tiling uses AppleScript to set window
114
+ bounds by matching the title tag. For other terminals, it tiles the
115
+ frontmost window.
116
+
117
+ ### Tile positions (app)
118
+
119
+ | Position | Area |
120
+ |--------------|---------------------------------|
121
+ | Left | Left half |
122
+ | Right | Right half |
123
+ | Top Left | Top-left quarter |
124
+ | Top Right | Top-right quarter |
125
+ | Bottom Left | Bottom-left quarter |
126
+ | Bottom Right | Bottom-right quarter |
127
+ | Maximize | Full visible screen |
128
+ | Center | 70% width, 80% height, centered |
129
+
130
+ ## Space navigation
131
+
132
+ "Go to" commands can switch macOS Spaces to reach a window on a
133
+ different desktop. The app uses a three-path fallback:
134
+
135
+ 1. **CGWindowList** (needs Screen Recording) — looks up the window
136
+ by title tag, finds its Space via SkyLight, switches to it, then
137
+ raises the window
138
+ 2. **Accessibility API** (needs Accessibility) — finds the window
139
+ via AXUIElement, raises it, and activates the app
140
+ 3. **AppleScript** — iterates windows by name for Terminal/iTerm2,
141
+ or bare-activates for other terminals
142
+
143
+ When a window is found and focused, the app flashes a green border
144
+ highlight around it for ~1 second so you can spot it immediately.
145
+
146
+ Grant Screen Recording and Accessibility permissions in System
147
+ Settings > Privacy & Security for the best experience.
148
+
149
+ ## Settings
150
+
151
+ Open via the command palette or the gear icon in the main view.
152
+ The settings window has three tabs:
153
+
154
+ ### General
155
+
156
+ | Setting | Description |
157
+ |------------|------------------------------------------------------|
158
+ | Terminal | Which terminal to use (auto-detected from installed) |
159
+ | Mode | `learning` or `auto` (see below) |
160
+ | Scan Root | Directory to scan for .lattices.json configs (type a path or click Browse) |
161
+
162
+ **Mode** controls how the app handles session interaction:
163
+
164
+ - **Learning** — shows tmux keybinding hints when you detach
165
+ (helpful while getting used to tmux)
166
+ - **Auto** — detaches sessions automatically (fewer prompts)
167
+
168
+ ### Shortcuts
169
+
170
+ Shows keyboard shortcut reference:
171
+
172
+ | Shortcut | Action |
173
+ |-------------------|----------------------|
174
+ | Cmd+Shift+M | Open command palette |
175
+ | Cmd+Option+1/2/3 | Switch workspace layer |
176
+ | Ctrl+B D | Detach from session |
177
+ | Ctrl+B X | Kill current pane |
178
+ | Ctrl+B Left/Right| Move between panes |
179
+ | Ctrl+B Z | Zoom pane (toggle) |
180
+ | Ctrl+B [ | Scroll mode |
181
+
182
+ ### Docs
183
+
184
+ Embedded quick reference with glossary, "how it works" steps, and
185
+ links to open the full `config.md` and `concepts.md` docs.
186
+
187
+ ## Supported terminals
188
+
189
+ | Terminal | Launch | Focus/Attach | Tile by tag |
190
+ |--------------|--------|--------------|-------------|
191
+ | Terminal.app | yes | yes | yes |
192
+ | iTerm2 | yes | yes | yes |
193
+ | Warp | yes | activate | frontmost |
194
+ | Ghostty | yes | activate | frontmost |
195
+ | Kitty | yes | activate | frontmost |
196
+ | Alacritty | yes | activate | frontmost |
197
+
198
+ "yes" means full AppleScript-based window matching by title tag.
199
+ "activate" means the app is brought to front but a specific window
200
+ can't be targeted. "frontmost" means tiling applies to whatever
201
+ window is in front.
202
+
203
+ ## Daemon
204
+
205
+ The menu bar app runs a WebSocket daemon on `ws://127.0.0.1:9399`.
206
+ It starts automatically when the app launches and stops when the app
207
+ quits.
208
+
209
+ ### Checking status
210
+
211
+ ```bash
212
+ lattices daemon status
213
+ ```
214
+
215
+ Or programmatically:
216
+
217
+ ```js
218
+ import { isDaemonRunning, daemonCall } from 'lattices/daemon-client'
219
+
220
+ if (await isDaemonRunning()) {
221
+ const status = await daemonCall('daemon.status')
222
+ console.log(status) // { uptime, clientCount, version, windowCount, tmuxSessionCount }
223
+ }
224
+ ```
225
+
226
+ ### What it provides
227
+
228
+ - **20 RPC methods** — read windows, sessions, projects, spaces, layers;
229
+ launch/kill/sync sessions; tile/focus/move windows; switch layers;
230
+ manage tab groups
231
+ - **3 real-time events** — `windows.changed`, `tmux.changed`,
232
+ `layer.switched` — broadcast to all connected clients
233
+ - **Window tracking** — the daemon monitors the desktop window list
234
+ and correlates windows to lattices sessions via title tags
235
+ - **Space awareness** — knows which macOS Space each window is on
236
+
237
+ ### Security
238
+
239
+ The daemon binds to **localhost only** (`127.0.0.1:9399`). It is not
240
+ accessible from the network. There is no authentication — any process
241
+ on the same machine can connect. This is intentional: the daemon is
242
+ designed for local automation, not remote access.
243
+
244
+ See the [Daemon API reference](/docs/api) for the full method list.
245
+
246
+ ## Diagnostics
247
+
248
+ The diagnostics panel shows a timestamped log of window navigation
249
+ attempts, including which path succeeded or failed. Useful for
250
+ debugging Screen Recording / Accessibility permission issues.
@@ -0,0 +1,225 @@
1
+ ---
2
+ title: Concepts
3
+ description: Core ideas, glossary, and architecture of lattices
4
+ order: 1
5
+ ---
6
+
7
+ # Concepts
8
+
9
+ ## What is lattices?
10
+
11
+ lattices is a developer workspace launcher. It creates pre-configured
12
+ terminal layouts for your projects using tmux, so you can go from
13
+ "I want to work on X" to a full development environment in one click.
14
+
15
+ It has two parts:
16
+
17
+ 1. **CLI** (`lattices`) — creates and manages tmux sessions from the terminal
18
+ 2. **Menu bar app** — a native macOS companion for launching, tiling,
19
+ and navigating sessions with a command palette
20
+
21
+ ## Glossary
22
+
23
+ ### Daemon
24
+ The lattices daemon is a WebSocket server (`ws://127.0.0.1:9399`) that
25
+ runs inside the menu bar app. It exposes 20 RPC methods and 3 real-time
26
+ events, giving scripts and AI agents full programmatic control over
27
+ sessions, windows, layers, and projects. See the
28
+ [API reference](/docs/api).
29
+
30
+ ### Agent
31
+ Any program that calls the daemon API to control the workspace
32
+ autonomously — an AI coding agent, a shell script, a CI pipeline,
33
+ or a custom tool. Agents can discover projects, launch sessions, tile
34
+ windows, switch layers, and react to real-time events without human
35
+ interaction.
36
+
37
+ ### Session
38
+ A tmux session is a persistent workspace that lives in the background.
39
+ It survives terminal crashes, disconnects, and even closing your laptop.
40
+ Think of it as a virtual desktop for a single project.
41
+
42
+ ### Pane
43
+ A pane is a single terminal view inside a session. A typical lattices
44
+ setup has two panes side by side — one running Claude Code and one
45
+ running your dev server. You can have up to four or more.
46
+
47
+ ### Attach / Detach
48
+ Attaching connects your terminal window to an existing session.
49
+ Detaching disconnects your terminal but keeps the session alive.
50
+ Your dev server keeps running, Claude keeps thinking — nothing is lost.
51
+
52
+ ### tmux
53
+ tmux (terminal multiplexer) is the engine behind lattices. It manages
54
+ sessions, panes, and layouts. lattices configures tmux for you so you
55
+ don't need to learn tmux commands — but knowing a few shortcuts helps.
56
+
57
+ ### Multiplexer
58
+ A program that lets you run multiple terminal sessions inside a single
59
+ window and switch between them. tmux is the most popular one.
60
+
61
+ ### Sync / Reconcile
62
+ Sync (`lattices sync`) brings a running session back in line with its
63
+ declared config. It recreates missing panes, re-applies the layout,
64
+ restores labels, and re-runs commands in idle panes. Useful when a pane
65
+ was accidentally killed but you don't want to restart the whole session.
66
+
67
+ ### Ensure / Prefill
68
+ Two modes for restoring exited commands when you reattach to a session:
69
+
70
+ - **Ensure** — automatically re-runs the command (hands-free recovery)
71
+ - **Prefill** — types the command into the pane but waits for you to
72
+ press Enter (manual confirmation)
73
+
74
+ Set via `"ensure": true` or `"prefill": true` in `.lattices.json`.
75
+
76
+ ### Command Palette
77
+ The menu bar app's primary interface, opened with **Cmd+Shift+M**.
78
+ A searchable list of actions: launch/attach projects, tile windows,
79
+ sync sessions, restart panes, open settings.
80
+
81
+ ### Window Tiling
82
+ Both the CLI (`lattices tile`) and the menu bar app can snap terminal
83
+ windows to preset screen positions (halves, quarters, maximize, center).
84
+ Tiling uses AppleScript bounds and respects the menu bar and dock.
85
+
86
+ ## How it works
87
+
88
+ 1. You create a `.lattices.json` file in your project root (or run `lattices init`)
89
+ 2. lattices reads the config and creates a tmux session with your layout
90
+ 3. Each pane gets its command (claude, dev server, tests, etc.)
91
+ 4. The session persists in the background until you kill it
92
+ 5. You can attach/detach from any terminal at any time
93
+ 6. If `ensure` is enabled, exited commands auto-restart on reattach
94
+
95
+ ## Architecture
96
+
97
+ ### Four-layer stack
98
+
99
+ ```
100
+ ┌─────────────────────────────┐
101
+ │ AI Agents / Scripts │ ← daemon API: 20 RPC methods, real-time events
102
+ ├─────────────────────────────┤
103
+ │ Menu bar app (Swift/AppKit)│ ← GUI: command palette, tiling, project list
104
+ ├─────────────────────────────┤
105
+ │ CLI (Node.js) │ ← lattices, lattices sync, lattices restart ...
106
+ ├─────────────────────────────┤
107
+ │ tmux │ ← session/pane lifecycle, layout, persistence
108
+ └─────────────────────────────┘
109
+ ```
110
+
111
+ - The **CLI** talks to tmux directly via `tmux` shell commands.
112
+ - The **menu bar app** calls the CLI binary for session operations
113
+ (launch, sync, restart) and uses tmux directly for status checks
114
+ (has-session, list-panes). It also runs the **daemon** — a WebSocket
115
+ server on `ws://127.0.0.1:9399`.
116
+ - **Agents and scripts** connect to the daemon over WebSocket and can
117
+ do everything the app and CLI can do: discover projects, launch
118
+ sessions, tile windows, switch layers, and subscribe to real-time
119
+ events.
120
+ - All layers share the same session naming convention so they always
121
+ agree on which session belongs to which project.
122
+
123
+ ### Session naming
124
+
125
+ A session name is `<basename>-<hash>`, where:
126
+
127
+ - `basename` is the project directory name (non-alphanumeric chars replaced with `-`)
128
+ - `hash` is the first 6 hex chars of the SHA-256 of the full absolute path
129
+
130
+ This guarantees unique session names even if two projects share the
131
+ same directory name (e.g. `~/work/app` and `~/personal/app`).
132
+
133
+ Both the CLI (Node.js `crypto.createHash`) and the app (Swift
134
+ `CryptoKit.SHA256`) produce identical hashes.
135
+
136
+ ### Window discovery via title tags
137
+
138
+ When lattices creates a session, it sets the tmux option:
139
+
140
+ ```
141
+ set-titles-string "[lattices:<session-name>] #{pane_title}"
142
+ ```
143
+
144
+ This embeds a `[lattices:name]` tag in the terminal window title. The
145
+ menu bar app uses this tag to find windows via three fallback paths:
146
+
147
+ 1. **CGWindowList** (needs Screen Recording permission) — fastest,
148
+ reads window names from the window server
149
+ 2. **Accessibility API** (needs Accessibility permission) — queries
150
+ AXUIElement window titles for the terminal app
151
+ 3. **AppleScript** — iterates Terminal.app or iTerm2 windows by name
152
+
153
+ ### Space switching via SkyLight
154
+
155
+ The menu bar app can switch macOS Spaces to reach a session's window.
156
+ It uses private SkyLight framework APIs loaded at runtime via `dlopen`:
157
+
158
+ - `CGSMainConnectionID` — get the connection to the window server
159
+ - `CGSGetActiveSpace` — current Space ID
160
+ - `CGSCopyManagedDisplaySpaces` — enumerate all Spaces per display
161
+ - `SLSCopySpacesForWindows` — find which Space a window is on
162
+ - `SLSManagedDisplaySetCurrentSpace` — switch a display to a Space
163
+
164
+ This is the same approach used by [Loop](https://github.com/MrKai77/Loop)
165
+ and other macOS window managers.
166
+
167
+ ### Ensure/prefill restoration
168
+
169
+ When you run `lattices` (no arguments) and a session already exists:
170
+
171
+ 1. lattices checks the `ensure` / `prefill` flag in `.lattices.json`
172
+ 2. For each pane, it queries `#{pane_current_command}` via tmux
173
+ 3. If the pane is running a shell (bash, zsh, fish, sh, dash) — meaning
174
+ the original command has exited — it either:
175
+ - **ensure**: sends the command + Enter (auto-restart)
176
+ - **prefill**: sends the command without Enter (manual restart)
177
+ 4. Then it attaches to the session as normal
178
+
179
+ ## Agentic architecture
180
+
181
+ lattices is designed for programmatic control. The daemon API gives
182
+ agents the same capabilities as a human using the menu bar app:
183
+
184
+ - **Discover** — list projects, sessions, windows, and Spaces
185
+ - **Launch** — start sessions for any scanned project
186
+ - **Arrange** — tile windows to screen positions, move between Spaces
187
+ - **Monitor** — subscribe to `windows.changed`, `tmux.changed`, and
188
+ `layer.switched` events for real-time workspace awareness
189
+ - **Recover** — sync sessions back to their declared config, restart
190
+ failed panes
191
+
192
+ An orchestrator agent can set up an entire multi-project workspace in
193
+ a few calls:
194
+
195
+ ```js
196
+ import { daemonCall } from 'lattices/daemon-client'
197
+
198
+ await daemonCall('session.launch', { path: '/Users/you/dev/frontend' })
199
+ await daemonCall('session.launch', { path: '/Users/you/dev/api' })
200
+
201
+ const sessions = await daemonCall('tmux.sessions')
202
+ await daemonCall('window.tile', { session: sessions[0].name, position: 'left' })
203
+ await daemonCall('window.tile', { session: sessions[1].name, position: 'right' })
204
+ ```
205
+
206
+ See the [Daemon API reference](/docs/api) for the full method list,
207
+ event shapes, and integration patterns.
208
+
209
+ ## Key shortcuts (inside tmux)
210
+
211
+ These work when you're inside a tmux session:
212
+
213
+ | Shortcut | Action |
214
+ |----------------|-----------------------|
215
+ | Ctrl+B D | Detach from session |
216
+ | Ctrl+B X | Kill current pane |
217
+ | Ctrl+B Left | Move to left pane |
218
+ | Ctrl+B Right | Move to right pane |
219
+ | Ctrl+B Up | Move to pane above |
220
+ | Ctrl+B Down | Move to pane below |
221
+ | Ctrl+B Z | Zoom pane (toggle) |
222
+ | Ctrl+B [ | Scroll mode (q exits) |
223
+
224
+ The prefix `Ctrl+B` means: hold Control, press B, release both,
225
+ then press the next key.