@lattices/cli 0.3.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.
- package/README.md +155 -0
- package/app/Lattices.app/Contents/Info.plist +24 -0
- package/app/Package.swift +13 -0
- package/app/Sources/AccessibilityTextExtractor.swift +111 -0
- package/app/Sources/ActionRow.swift +61 -0
- package/app/Sources/App.swift +10 -0
- package/app/Sources/AppDelegate.swift +242 -0
- package/app/Sources/AppShellView.swift +62 -0
- package/app/Sources/AppTypeClassifier.swift +70 -0
- package/app/Sources/AppWindowShell.swift +63 -0
- package/app/Sources/CheatSheetHUD.swift +332 -0
- package/app/Sources/CommandModeState.swift +1362 -0
- package/app/Sources/CommandModeView.swift +1405 -0
- package/app/Sources/CommandModeWindow.swift +192 -0
- package/app/Sources/CommandPaletteView.swift +307 -0
- package/app/Sources/CommandPaletteWindow.swift +134 -0
- package/app/Sources/DaemonProtocol.swift +101 -0
- package/app/Sources/DaemonServer.swift +414 -0
- package/app/Sources/DesktopModel.swift +149 -0
- package/app/Sources/DesktopModelTypes.swift +71 -0
- package/app/Sources/DiagnosticLog.swift +271 -0
- package/app/Sources/EventBus.swift +30 -0
- package/app/Sources/HotkeyManager.swift +254 -0
- package/app/Sources/HotkeyStore.swift +338 -0
- package/app/Sources/InventoryManager.swift +35 -0
- package/app/Sources/InventoryPath.swift +43 -0
- package/app/Sources/KeyRecorderView.swift +210 -0
- package/app/Sources/LatticesApi.swift +1234 -0
- package/app/Sources/LayerBezel.swift +203 -0
- package/app/Sources/MainView.swift +479 -0
- package/app/Sources/MainWindow.swift +83 -0
- package/app/Sources/OcrModel.swift +430 -0
- package/app/Sources/OcrStore.swift +329 -0
- package/app/Sources/OmniSearchState.swift +283 -0
- package/app/Sources/OmniSearchView.swift +288 -0
- package/app/Sources/OmniSearchWindow.swift +105 -0
- package/app/Sources/OrphanRow.swift +129 -0
- package/app/Sources/PaletteCommand.swift +419 -0
- package/app/Sources/PermissionChecker.swift +125 -0
- package/app/Sources/Preferences.swift +99 -0
- package/app/Sources/ProcessModel.swift +199 -0
- package/app/Sources/ProcessQuery.swift +151 -0
- package/app/Sources/Project.swift +28 -0
- package/app/Sources/ProjectRow.swift +368 -0
- package/app/Sources/ProjectScanner.swift +128 -0
- package/app/Sources/ScreenMapState.swift +2387 -0
- package/app/Sources/ScreenMapView.swift +2820 -0
- package/app/Sources/ScreenMapWindowController.swift +89 -0
- package/app/Sources/SessionManager.swift +72 -0
- package/app/Sources/SettingsView.swift +1064 -0
- package/app/Sources/SettingsWindow.swift +20 -0
- package/app/Sources/TabGroupRow.swift +178 -0
- package/app/Sources/Terminal.swift +259 -0
- package/app/Sources/TerminalQuery.swift +156 -0
- package/app/Sources/TerminalSynthesizer.swift +200 -0
- package/app/Sources/Theme.swift +163 -0
- package/app/Sources/TilePickerView.swift +209 -0
- package/app/Sources/TmuxModel.swift +53 -0
- package/app/Sources/TmuxQuery.swift +81 -0
- package/app/Sources/WindowTiler.swift +1778 -0
- package/app/Sources/WorkspaceManager.swift +575 -0
- package/bin/client.js +4 -0
- package/bin/daemon-client.js +187 -0
- package/bin/lattices-app.js +221 -0
- package/bin/lattices.js +1551 -0
- package/docs/api.md +924 -0
- package/docs/app.md +297 -0
- package/docs/concepts.md +135 -0
- package/docs/config.md +245 -0
- package/docs/layers.md +410 -0
- package/docs/ocr.md +185 -0
- package/docs/overview.md +94 -0
- package/docs/quickstart.md +75 -0
- package/package.json +42 -0
package/docs/app.md
ADDED
|
@@ -0,0 +1,297 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Menu Bar App
|
|
3
|
+
description: Command palette, window tiling, and session management
|
|
4
|
+
order: 3
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
The lattices menu bar app sits in your menu bar and controls your
|
|
8
|
+
workspace from there.
|
|
9
|
+
|
|
10
|
+
## Installation
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
lattices app # Build (or download) and launch
|
|
14
|
+
lattices app build # Rebuild from source
|
|
15
|
+
lattices app restart # Quit, rebuild, relaunch
|
|
16
|
+
lattices app quit # Stop the app
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
The first run builds from source if Swift is available, otherwise
|
|
20
|
+
downloads a pre-built binary from GitHub releases.
|
|
21
|
+
|
|
22
|
+
## Command palette
|
|
23
|
+
|
|
24
|
+
Press **Cmd+Shift+M** from anywhere to open the command palette.
|
|
25
|
+
It's a searchable list of every action the app can perform, with
|
|
26
|
+
fuzzy matching on titles and subtitles.
|
|
27
|
+
|
|
28
|
+
### Project commands
|
|
29
|
+
|
|
30
|
+
| Command | Description |
|
|
31
|
+
|-------------------------------|------------------------------------------|
|
|
32
|
+
| Launch *project* | Create a new session and open terminal |
|
|
33
|
+
| Attach *project* | Focus or open the running session |
|
|
34
|
+
| Sync *project* | Reconcile session to declared config |
|
|
35
|
+
| Restart *pane* in *project* | Kill and re-run a specific pane's command |
|
|
36
|
+
|
|
37
|
+
### Window commands
|
|
38
|
+
|
|
39
|
+
Available for running sessions:
|
|
40
|
+
|
|
41
|
+
| Command | Description |
|
|
42
|
+
|-------------------------------|------------------------------------------|
|
|
43
|
+
| Go to *project* | Focus the terminal window (switches Spaces if needed) |
|
|
44
|
+
| Tile *project* Left | Snap window to left half |
|
|
45
|
+
| Tile *project* Right | Snap window to right half |
|
|
46
|
+
| Maximize *project* | Expand window to fill screen |
|
|
47
|
+
| Detach *project* | Disconnect clients, keep session alive |
|
|
48
|
+
| Kill *project* | Terminate the tmux session |
|
|
49
|
+
|
|
50
|
+
### Tab group commands
|
|
51
|
+
|
|
52
|
+
Available when `groups` are configured in `~/.lattices/workspace.json`
|
|
53
|
+
(see [Tab Groups](/docs/layers#tab-groups)):
|
|
54
|
+
|
|
55
|
+
| Command | Description |
|
|
56
|
+
|-----------------------------|------------------------------------------|
|
|
57
|
+
| Launch *group* | Start the group session (all tabs) |
|
|
58
|
+
| Attach *group* | Focus the running group session |
|
|
59
|
+
| *Group*: *Tab* | Switch to a specific tab within a group |
|
|
60
|
+
| Kill *group* Group | Terminate the group session |
|
|
61
|
+
|
|
62
|
+
### Layer commands
|
|
63
|
+
|
|
64
|
+
Available when `layers` are configured in `~/.lattices/workspace.json`
|
|
65
|
+
(see [Layers](/docs/layers#layers)):
|
|
66
|
+
|
|
67
|
+
| Command | Description |
|
|
68
|
+
|-----------------------------|------------------------------------------|
|
|
69
|
+
| Switch to Layer: *label* | Focus and tile the layer's project windows |
|
|
70
|
+
|
|
71
|
+
### App commands
|
|
72
|
+
|
|
73
|
+
| Command | Description |
|
|
74
|
+
|-------------------|------------------------------------------|
|
|
75
|
+
| Settings | Open preferences (terminal, scan root) |
|
|
76
|
+
| Diagnostics | View logs and debug info |
|
|
77
|
+
| Refresh Projects | Re-scan for .lattices.json configs |
|
|
78
|
+
| Quit Lattices | Exit the menu bar app |
|
|
79
|
+
|
|
80
|
+
## Project discovery
|
|
81
|
+
|
|
82
|
+
The app scans a configurable root directory (up to 3 levels deep)
|
|
83
|
+
for `.lattices.json` files. It skips `.git/` and `node_modules/`.
|
|
84
|
+
|
|
85
|
+
Auto-detection for the scan root checks these paths in order:
|
|
86
|
+
`~/dev`, `~/Developer`, `~/projects`, `~/src`.
|
|
87
|
+
|
|
88
|
+
For each project found, the app reads:
|
|
89
|
+
- Pane names and commands from `.lattices.json`
|
|
90
|
+
- Dev command and package manager from `package.json`
|
|
91
|
+
- Running status by checking `tmux has-session`
|
|
92
|
+
|
|
93
|
+
## Session management
|
|
94
|
+
|
|
95
|
+
The app calls the lattices CLI for session operations. Launch runs
|
|
96
|
+
`lattices` in the project directory, Sync runs `lattices sync` to
|
|
97
|
+
reconcile panes, and Restart runs `lattices restart <pane>` to kill
|
|
98
|
+
and re-run a pane's process. Detach and Kill call `tmux detach-client`
|
|
99
|
+
and `tmux kill-session` directly.
|
|
100
|
+
|
|
101
|
+
## Window tiling
|
|
102
|
+
|
|
103
|
+
The app can tile terminal windows to preset screen positions via
|
|
104
|
+
the command palette. It finds windows by their `[lattices:session-name]`
|
|
105
|
+
title tag.
|
|
106
|
+
|
|
107
|
+
For Terminal.app and iTerm2, tiling uses AppleScript to set window
|
|
108
|
+
bounds by matching the title tag. For other terminals, it tiles the
|
|
109
|
+
frontmost window.
|
|
110
|
+
|
|
111
|
+
### Tile positions (app)
|
|
112
|
+
|
|
113
|
+
| Position | Area |
|
|
114
|
+
|--------------|---------------------------------|
|
|
115
|
+
| Left | Left half |
|
|
116
|
+
| Right | Right half |
|
|
117
|
+
| Top Left | Top-left quarter |
|
|
118
|
+
| Top Right | Top-right quarter |
|
|
119
|
+
| Bottom Left | Bottom-left quarter |
|
|
120
|
+
| Bottom Right | Bottom-right quarter |
|
|
121
|
+
| Maximize | Full visible screen |
|
|
122
|
+
| Left Third | Left third |
|
|
123
|
+
| Center Third | Center third |
|
|
124
|
+
| Right Third | Right third |
|
|
125
|
+
| Center | 70% width, 80% height, centered |
|
|
126
|
+
|
|
127
|
+
## Space navigation
|
|
128
|
+
|
|
129
|
+
"Go to" commands can switch macOS Spaces to reach a window on a
|
|
130
|
+
different desktop. The app uses a three-path fallback:
|
|
131
|
+
|
|
132
|
+
1. **CGWindowList** (needs Screen Recording) — looks up the window
|
|
133
|
+
by title tag, finds its Space via SkyLight, switches to it, then
|
|
134
|
+
raises the window
|
|
135
|
+
2. **Accessibility API** (needs Accessibility) — finds the window
|
|
136
|
+
via AXUIElement, raises it, and activates the app
|
|
137
|
+
3. **AppleScript** — iterates windows by name for Terminal/iTerm2,
|
|
138
|
+
or bare-activates for other terminals
|
|
139
|
+
|
|
140
|
+
When a window is found and focused, the app flashes a green border
|
|
141
|
+
highlight around it for ~1 second so you can spot it immediately.
|
|
142
|
+
|
|
143
|
+
Grant Screen Recording and Accessibility permissions in System
|
|
144
|
+
Settings > Privacy & Security for all three paths to work.
|
|
145
|
+
|
|
146
|
+
## Settings
|
|
147
|
+
|
|
148
|
+
Open via the command palette or the gear icon in the main view.
|
|
149
|
+
The settings window has three tabs:
|
|
150
|
+
|
|
151
|
+
### General
|
|
152
|
+
|
|
153
|
+
| Setting | Description |
|
|
154
|
+
|------------|------------------------------------------------------|
|
|
155
|
+
| Terminal | Which terminal to use (auto-detected from installed) |
|
|
156
|
+
| Mode | `learning` or `auto` (see below) |
|
|
157
|
+
| Scan Root | Directory to scan for .lattices.json configs (type a path or click Browse) |
|
|
158
|
+
|
|
159
|
+
**Mode** controls how the app handles session interaction:
|
|
160
|
+
|
|
161
|
+
- **Learning** — shows tmux keybinding hints when you detach
|
|
162
|
+
(helpful while getting used to tmux)
|
|
163
|
+
- **Auto** — detaches sessions automatically (fewer prompts)
|
|
164
|
+
|
|
165
|
+
### Shortcuts
|
|
166
|
+
|
|
167
|
+
Shows keyboard shortcut reference:
|
|
168
|
+
|
|
169
|
+
| Shortcut | Action |
|
|
170
|
+
|-------------------|----------------------|
|
|
171
|
+
| Cmd+Shift+M | Open command palette |
|
|
172
|
+
| Cmd+Option+1/2/3 | Switch workspace layer |
|
|
173
|
+
| Ctrl+B D | Detach from session |
|
|
174
|
+
| Ctrl+B X | Kill current pane |
|
|
175
|
+
| Ctrl+B Left/Right| Move between panes |
|
|
176
|
+
| Ctrl+B Z | Zoom pane (toggle) |
|
|
177
|
+
| Ctrl+B [ | Scroll mode |
|
|
178
|
+
|
|
179
|
+
### Docs
|
|
180
|
+
|
|
181
|
+
Embedded quick reference with glossary, "how it works" steps, and
|
|
182
|
+
links to open the full `config.md` and `concepts.md` docs.
|
|
183
|
+
|
|
184
|
+
## Supported terminals
|
|
185
|
+
|
|
186
|
+
| Terminal | Launch | Focus/Attach | Tile by tag |
|
|
187
|
+
|--------------|--------|--------------|-------------|
|
|
188
|
+
| Terminal.app | yes | yes | yes |
|
|
189
|
+
| iTerm2 | yes | yes | yes |
|
|
190
|
+
| Warp | yes | activate | frontmost |
|
|
191
|
+
| Ghostty | yes | activate | frontmost |
|
|
192
|
+
| Kitty | yes | activate | frontmost |
|
|
193
|
+
| Alacritty | yes | activate | frontmost |
|
|
194
|
+
|
|
195
|
+
"yes" means full AppleScript-based window matching by title tag.
|
|
196
|
+
"activate" means the app is brought to front but a specific window
|
|
197
|
+
can't be targeted. "frontmost" means tiling applies to whatever
|
|
198
|
+
window is in front.
|
|
199
|
+
|
|
200
|
+
## Screen OCR
|
|
201
|
+
|
|
202
|
+
> See [Screen OCR](/docs/ocr) for full details on configuration, scanning,
|
|
203
|
+
> search, and agent usage.
|
|
204
|
+
|
|
205
|
+
The app reads text from visible windows using Apple's Vision framework
|
|
206
|
+
and stores results in a local SQLite database with FTS5 full-text search.
|
|
207
|
+
Agents can use this to "see" what's on screen.
|
|
208
|
+
|
|
209
|
+
### How it works
|
|
210
|
+
|
|
211
|
+
1. Every 60 seconds, the app captures the top visible windows as images
|
|
212
|
+
2. A SHA256 hash detects whether the window content has changed
|
|
213
|
+
3. Changed windows are processed through `VNRecognizeTextRequest` (fast mode)
|
|
214
|
+
4. Results are stored in `~/.lattices/ocr.db` with full-text indexing
|
|
215
|
+
5. Entries older than 3 days are automatically purged
|
|
216
|
+
|
|
217
|
+
### Desktop Inventory integration
|
|
218
|
+
|
|
219
|
+
The Desktop Inventory view (Hyper+4) uses OCR to make windows searchable
|
|
220
|
+
by their content — not just by title or app name. When you type a search
|
|
221
|
+
query, windows matching by OCR content show contextual snippets.
|
|
222
|
+
|
|
223
|
+
### API access
|
|
224
|
+
|
|
225
|
+
Agents can query OCR data through four API methods:
|
|
226
|
+
|
|
227
|
+
| Method | Description |
|
|
228
|
+
|----------------|------------------------------------------------|
|
|
229
|
+
| `ocr.snapshot` | Current OCR results for all visible windows |
|
|
230
|
+
| `ocr.search` | Full-text search across history (FTS5 syntax) |
|
|
231
|
+
| `ocr.history` | Timeline of OCR results for a specific window |
|
|
232
|
+
| `ocr.scan` | Trigger an immediate scan (bypasses timer) |
|
|
233
|
+
|
|
234
|
+
```js
|
|
235
|
+
import { daemonCall } from '@lattices/cli'
|
|
236
|
+
|
|
237
|
+
// Find windows showing error messages
|
|
238
|
+
const errors = await daemonCall('ocr.search', { query: 'error OR failed' })
|
|
239
|
+
|
|
240
|
+
// Read what's currently on screen
|
|
241
|
+
const snapshot = await daemonCall('ocr.snapshot')
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
More in the [Agent API reference](/docs/api#ocrsnapshot).
|
|
245
|
+
|
|
246
|
+
### Requirements
|
|
247
|
+
|
|
248
|
+
- **Screen Recording** permission — required to capture window images
|
|
249
|
+
- Granted via System Settings > Privacy & Security > Screen Recording
|
|
250
|
+
|
|
251
|
+
## Agent API server
|
|
252
|
+
|
|
253
|
+
The menu bar app runs a WebSocket server on `ws://127.0.0.1:9399`.
|
|
254
|
+
It starts automatically when the app launches and stops when the app
|
|
255
|
+
quits.
|
|
256
|
+
|
|
257
|
+
### Checking status
|
|
258
|
+
|
|
259
|
+
```bash
|
|
260
|
+
lattices daemon status
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
Or programmatically:
|
|
264
|
+
|
|
265
|
+
```js
|
|
266
|
+
import { isDaemonRunning, daemonCall } from '@lattices/cli'
|
|
267
|
+
|
|
268
|
+
if (await isDaemonRunning()) {
|
|
269
|
+
const status = await daemonCall('daemon.status')
|
|
270
|
+
console.log(status) // { uptime, clientCount, version, windowCount, tmuxSessionCount }
|
|
271
|
+
}
|
|
272
|
+
```
|
|
273
|
+
|
|
274
|
+
### What it provides
|
|
275
|
+
|
|
276
|
+
- 35+ RPC methods for reading windows, sessions, projects, spaces, layers,
|
|
277
|
+
processes, terminals, and OCR. Also launching/killing sessions, tiling
|
|
278
|
+
windows, switching layers, and managing tab groups.
|
|
279
|
+
- 5 real-time events (`windows.changed`, `tmux.changed`, `processes.changed`,
|
|
280
|
+
`layer.switched`, `ocr.scanComplete`) broadcast to all connected clients.
|
|
281
|
+
- Window tracking that monitors the desktop window list and correlates
|
|
282
|
+
windows to lattices sessions via title tags.
|
|
283
|
+
- Space awareness, so it knows which macOS Space each window is on.
|
|
284
|
+
|
|
285
|
+
### Security
|
|
286
|
+
|
|
287
|
+
The server binds to localhost only (`127.0.0.1:9399`). Not accessible
|
|
288
|
+
from the network. No authentication, so any local process can connect.
|
|
289
|
+
This is intentional — it's for local automation, not remote access.
|
|
290
|
+
|
|
291
|
+
Full method list in the [Agent API reference](/docs/api).
|
|
292
|
+
|
|
293
|
+
## Diagnostics
|
|
294
|
+
|
|
295
|
+
The diagnostics panel shows a timestamped log of window navigation
|
|
296
|
+
attempts, including which path succeeded or failed. Useful for
|
|
297
|
+
debugging Screen Recording / Accessibility permission issues.
|
package/docs/concepts.md
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Concepts
|
|
3
|
+
description: Core ideas, glossary, and architecture of lattices
|
|
4
|
+
order: 6
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Glossary
|
|
8
|
+
|
|
9
|
+
| Term | Definition |
|
|
10
|
+
|------|------------|
|
|
11
|
+
| **Command Palette** | The menu bar app's primary interface (**Cmd+Shift+M**). Searchable list of actions: launch, tile, sync, restart, settings. |
|
|
12
|
+
| **Window Tiling** | Snap terminal windows to preset screen positions (halves, quarters, thirds, maximize, center). Works from the CLI (`lattices tile`) or the command palette. |
|
|
13
|
+
| **Agent API** | WebSocket server (`ws://127.0.0.1:9399`) inside the menu bar app. Exposes 35+ RPC methods and 5 real-time events for programmatic control. See the [API reference](/docs/api). |
|
|
14
|
+
| **Agent** | Any program that calls the agent API autonomously — an AI coding agent, a shell script, a CI pipeline, or a custom tool. |
|
|
15
|
+
| **Session** | A persistent tmux workspace that lives in the background. Survives terminal crashes, disconnects, and closing your laptop. One session per project. Requires tmux. |
|
|
16
|
+
| **Pane** | A single terminal view inside a session. A typical setup has two panes side by side — Claude Code on the left, dev server on the right. Requires tmux. |
|
|
17
|
+
| **Attach / Detach** | Attaching connects your terminal to an existing session. Detaching disconnects but keeps the session alive — your dev server keeps running, Claude keeps thinking. Requires tmux. |
|
|
18
|
+
| **Sync / Reconcile** | `lattices sync` brings a running session back in line with its declared config — recreates missing panes, re-applies layout, restores labels, re-runs commands in idle panes. Requires tmux. |
|
|
19
|
+
| **Ensure / Prefill** | Two modes for restoring exited commands on reattach. **Ensure** auto-reruns the command. **Prefill** types it but waits for you to press Enter. Set via `.lattices.json`. Requires tmux. |
|
|
20
|
+
| **tmux** | Terminal multiplexer (optional). Provides persistent sessions, pane layouts, and command restoration. Install with `brew install tmux` if you want session management. |
|
|
21
|
+
|
|
22
|
+
## How it works
|
|
23
|
+
|
|
24
|
+
1. You create a `.lattices.json` file in your project root (or run `lattices init`)
|
|
25
|
+
2. The menu bar app discovers the project and adds it to the command palette
|
|
26
|
+
3. You can tile windows, switch layers, search via OCR, and use the agent API
|
|
27
|
+
4. With tmux installed, `lattices` also creates persistent terminal sessions:
|
|
28
|
+
- Each pane gets its command (claude, dev server, tests, etc.)
|
|
29
|
+
- The session persists in the background until you kill it
|
|
30
|
+
- You can attach/detach from any terminal at any time
|
|
31
|
+
- If `ensure` is enabled, exited commands auto-restart on reattach
|
|
32
|
+
|
|
33
|
+
## Architecture
|
|
34
|
+
|
|
35
|
+
<img src="/architecture.svg" alt="lattices architecture diagram" style="margin: 2rem 0; max-width: 100%;" />
|
|
36
|
+
|
|
37
|
+
- The menu bar app is the core. It provides the command palette,
|
|
38
|
+
window tiling, OCR, project discovery, and the agent API (a WebSocket
|
|
39
|
+
server on `ws://127.0.0.1:9399`). It works with or without tmux.
|
|
40
|
+
- The CLI handles tiling, OCR queries, and (when tmux is installed)
|
|
41
|
+
session management via `tmux` shell commands.
|
|
42
|
+
- Agents and scripts connect to the agent API over WebSocket. They can
|
|
43
|
+
do everything the app and CLI can do: discover projects, tile windows,
|
|
44
|
+
switch layers, read on-screen text, and subscribe to real-time events.
|
|
45
|
+
See the [Agent API reference](/docs/api).
|
|
46
|
+
- When tmux is installed, the app and CLI can also launch, sync, and
|
|
47
|
+
manage persistent terminal sessions. All layers share the same session
|
|
48
|
+
naming convention so they always agree on which session belongs to
|
|
49
|
+
which project.
|
|
50
|
+
|
|
51
|
+
### Session naming
|
|
52
|
+
|
|
53
|
+
A session name is `<basename>-<hash>`, where:
|
|
54
|
+
|
|
55
|
+
- `basename` is the project directory name (non-alphanumeric chars replaced with `-`)
|
|
56
|
+
- `hash` is the first 6 hex chars of the SHA-256 of the full absolute path
|
|
57
|
+
|
|
58
|
+
This guarantees unique session names even if two projects share the
|
|
59
|
+
same directory name (e.g. `~/work/app` and `~/personal/app`).
|
|
60
|
+
|
|
61
|
+
Both the CLI (Node.js `crypto.createHash`) and the app (Swift
|
|
62
|
+
`CryptoKit.SHA256`) produce identical hashes.
|
|
63
|
+
|
|
64
|
+
### Window discovery via title tags
|
|
65
|
+
|
|
66
|
+
When lattices creates a tmux session, it sets the tmux option:
|
|
67
|
+
|
|
68
|
+
```
|
|
69
|
+
set-titles-string "[lattices:<session-name>] #{pane_title}"
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
This embeds a `[lattices:name]` tag in the terminal window title. The
|
|
73
|
+
menu bar app uses this tag to find windows via three fallback paths:
|
|
74
|
+
|
|
75
|
+
1. **CGWindowList** (needs Screen Recording permission) — fastest,
|
|
76
|
+
reads window names from the window server
|
|
77
|
+
2. **Accessibility API** (needs Accessibility permission) — queries
|
|
78
|
+
AXUIElement window titles for the terminal app
|
|
79
|
+
3. **AppleScript** — iterates Terminal.app or iTerm2 windows by name
|
|
80
|
+
|
|
81
|
+
### Space switching via SkyLight
|
|
82
|
+
|
|
83
|
+
The menu bar app can switch macOS Spaces to reach a session's window.
|
|
84
|
+
It uses private SkyLight framework APIs loaded at runtime via `dlopen`:
|
|
85
|
+
|
|
86
|
+
- `CGSMainConnectionID` — get the connection to the window server
|
|
87
|
+
- `CGSGetActiveSpace` — current Space ID
|
|
88
|
+
- `CGSCopyManagedDisplaySpaces` — enumerate all Spaces per display
|
|
89
|
+
- `SLSCopySpacesForWindows` — find which Space a window is on
|
|
90
|
+
- `SLSManagedDisplaySetCurrentSpace` — switch a display to a Space
|
|
91
|
+
|
|
92
|
+
This is the same approach used by [Loop](https://github.com/MrKai77/Loop)
|
|
93
|
+
and other macOS window managers.
|
|
94
|
+
|
|
95
|
+
### Ensure/prefill restoration (requires tmux)
|
|
96
|
+
|
|
97
|
+
When you run `lattices` (no arguments) and a session already exists:
|
|
98
|
+
|
|
99
|
+
1. lattices checks the `ensure` / `prefill` flag in `.lattices.json`
|
|
100
|
+
2. For each pane, it queries `#{pane_current_command}` via tmux
|
|
101
|
+
3. If the pane is running a shell (bash, zsh, fish, sh, dash) — meaning
|
|
102
|
+
the original command has exited — it either:
|
|
103
|
+
- **ensure**: sends the command + Enter (auto-restart)
|
|
104
|
+
- **prefill**: sends the command without Enter (manual restart)
|
|
105
|
+
4. Then it attaches to the session as normal
|
|
106
|
+
|
|
107
|
+
## Agent control
|
|
108
|
+
|
|
109
|
+
The agent API gives agents the same control as a human using the
|
|
110
|
+
menu bar app. An agent can list projects and windows, launch sessions,
|
|
111
|
+
tile windows to screen positions, subscribe to real-time events
|
|
112
|
+
(`windows.changed`, `tmux.changed`, `layer.switched`), and sync
|
|
113
|
+
sessions back to their declared config.
|
|
114
|
+
|
|
115
|
+
A typical orchestrator sets up a multi-project workspace in a few
|
|
116
|
+
`daemonCall()` invocations. See the [Agent API reference](/docs/api)
|
|
117
|
+
for the full method list and code examples.
|
|
118
|
+
|
|
119
|
+
## Key shortcuts (inside tmux)
|
|
120
|
+
|
|
121
|
+
These work when you're inside a tmux session:
|
|
122
|
+
|
|
123
|
+
| Shortcut | Action |
|
|
124
|
+
|----------------|-----------------------|
|
|
125
|
+
| Ctrl+B D | Detach from session |
|
|
126
|
+
| Ctrl+B X | Kill current pane |
|
|
127
|
+
| Ctrl+B Left | Move to left pane |
|
|
128
|
+
| Ctrl+B Right | Move to right pane |
|
|
129
|
+
| Ctrl+B Up | Move to pane above |
|
|
130
|
+
| Ctrl+B Down | Move to pane below |
|
|
131
|
+
| Ctrl+B Z | Zoom pane (toggle) |
|
|
132
|
+
| Ctrl+B [ | Scroll mode (q exits) |
|
|
133
|
+
|
|
134
|
+
The prefix `Ctrl+B` means: hold Control, press B, release both,
|
|
135
|
+
then press the next key.
|
package/docs/config.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Configuration
|
|
3
|
+
description: CLI commands, .lattices.json format, and tile positions
|
|
4
|
+
order: 2
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## .lattices.json
|
|
8
|
+
|
|
9
|
+
Place a `.lattices.json` file in your project root to define your
|
|
10
|
+
workspace layout. lattices reads this file when creating a session.
|
|
11
|
+
|
|
12
|
+
### Minimal example
|
|
13
|
+
|
|
14
|
+
```json
|
|
15
|
+
{
|
|
16
|
+
"panes": [
|
|
17
|
+
{ "name": "claude", "cmd": "claude" },
|
|
18
|
+
{ "name": "server", "cmd": "pnpm dev" }
|
|
19
|
+
]
|
|
20
|
+
}
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### Full example
|
|
24
|
+
|
|
25
|
+
```json
|
|
26
|
+
{
|
|
27
|
+
"ensure": true,
|
|
28
|
+
"panes": [
|
|
29
|
+
{ "name": "claude", "cmd": "claude", "size": 60 },
|
|
30
|
+
{ "name": "server", "cmd": "pnpm dev" },
|
|
31
|
+
{ "name": "tests", "cmd": "pnpm test --watch" }
|
|
32
|
+
]
|
|
33
|
+
}
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
## Config fields
|
|
37
|
+
|
|
38
|
+
| Field | Type | Required | Description |
|
|
39
|
+
|----------|---------|----------|------------------------------------------------------|
|
|
40
|
+
| panes | array | no | List of pane definitions (see below) |
|
|
41
|
+
| ensure | boolean | no | Auto-restart exited commands on reattach |
|
|
42
|
+
| prefill | boolean | no | Type exited commands into idle panes on reattach (you hit Enter) |
|
|
43
|
+
|
|
44
|
+
`ensure` and `prefill` are mutually exclusive. If both are set,
|
|
45
|
+
`ensure` takes priority.
|
|
46
|
+
|
|
47
|
+
- **ensure** — when you reattach to an existing session, lattices checks
|
|
48
|
+
each pane. If a pane's process has exited and the shell is idle, lattices
|
|
49
|
+
automatically re-runs its declared command.
|
|
50
|
+
- **prefill** — same check, but the command is typed into the pane
|
|
51
|
+
without pressing Enter. You review and hit Enter yourself.
|
|
52
|
+
|
|
53
|
+
## Pane fields
|
|
54
|
+
|
|
55
|
+
| Field | Type | Required | Description |
|
|
56
|
+
|--------|--------|----------|-------------------------------------|
|
|
57
|
+
| name | string | no | Label for the pane (shown in app) |
|
|
58
|
+
| cmd | string | no | Command to run when pane opens |
|
|
59
|
+
| size | number | no | Width % for the first pane (1-99) |
|
|
60
|
+
|
|
61
|
+
- `size` only applies to the **first pane**. It sets the width of the
|
|
62
|
+
main pane as a percentage. Default is 60.
|
|
63
|
+
- `cmd` can be any shell command. If omitted, the pane opens a shell.
|
|
64
|
+
- `name` is used in the lattices app to show a summary of your layout,
|
|
65
|
+
and as a target for `lattices restart <name>`.
|
|
66
|
+
|
|
67
|
+
## Layouts
|
|
68
|
+
|
|
69
|
+
lattices picks a layout based on how many panes you define:
|
|
70
|
+
|
|
71
|
+
### 2 panes — side by side
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
┌──────────┬─────────┐
|
|
75
|
+
│ claude │ server │
|
|
76
|
+
│ (60%) │ (40%) │
|
|
77
|
+
└──────────┴─────────┘
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Horizontal split. First pane on the left, second on the right.
|
|
81
|
+
|
|
82
|
+
### 3+ panes — main-vertical
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
┌──────────┬─────────┐
|
|
86
|
+
│ claude │ server │
|
|
87
|
+
│ (60%) ├─────────┤
|
|
88
|
+
│ │ tests │
|
|
89
|
+
└──────────┴─────────┘
|
|
90
|
+
```
|
|
91
|
+
|
|
92
|
+
First pane takes the left side. Remaining panes stack vertically
|
|
93
|
+
on the right.
|
|
94
|
+
|
|
95
|
+
### 4 panes
|
|
96
|
+
|
|
97
|
+
```
|
|
98
|
+
┌──────────┬─────────┐
|
|
99
|
+
│ claude │ server │
|
|
100
|
+
│ (60%) ├─────────┤
|
|
101
|
+
│ │ tests │
|
|
102
|
+
│ ├─────────┤
|
|
103
|
+
│ │ logs │
|
|
104
|
+
└──────────┴─────────┘
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## Auto-detection (no config)
|
|
108
|
+
|
|
109
|
+
If there's no `.lattices.json`, lattices still works. It will:
|
|
110
|
+
|
|
111
|
+
1. Create a 2-pane layout (60/40 split)
|
|
112
|
+
2. Run `claude` in the left pane
|
|
113
|
+
3. Auto-detect your dev command from package.json scripts:
|
|
114
|
+
- Looks for: `dev`, `start`, `serve`, `watch` (in that order)
|
|
115
|
+
- Detects package manager: pnpm > bun > yarn > npm
|
|
116
|
+
|
|
117
|
+
## Creating a config
|
|
118
|
+
|
|
119
|
+
Run `lattices init` in your project directory to generate a starter
|
|
120
|
+
`.lattices.json` based on your project. The generated config includes
|
|
121
|
+
`"ensure": true` by default.
|
|
122
|
+
|
|
123
|
+
## CLI commands
|
|
124
|
+
|
|
125
|
+
| Command | Description |
|
|
126
|
+
|----------------------------|--------------------------------------------------|
|
|
127
|
+
| `lattices` | Create or attach to session for current project |
|
|
128
|
+
| `lattices init` | Generate .lattices.json config for this project |
|
|
129
|
+
| `lattices ls` | List active sessions (requires tmux) |
|
|
130
|
+
| `lattices kill [name]` | Kill a session (defaults to current project) |
|
|
131
|
+
| `lattices sync` | Reconcile session to match declared config |
|
|
132
|
+
| `lattices restart [pane]` | Restart a pane's process (by name or index) |
|
|
133
|
+
| `lattices tile <position>` | Tile the frontmost window to a screen position |
|
|
134
|
+
| `lattices group [id]` | List tab groups or launch/attach a group |
|
|
135
|
+
| `lattices groups` | List all tab groups with status |
|
|
136
|
+
| `lattices tab <group> [tab]` | Switch tab within a group (by label or index) |
|
|
137
|
+
| `lattices app` | Launch the menu bar companion app |
|
|
138
|
+
| `lattices app build` | Rebuild the menu bar app from source |
|
|
139
|
+
| `lattices app restart` | Rebuild and relaunch the menu bar app |
|
|
140
|
+
| `lattices layer [name\|index]` | Switch to a workspace layer by name or index |
|
|
141
|
+
| `lattices windows [--json]` | List all visible windows |
|
|
142
|
+
| `lattices window assign <wid> <layer>` | Tag a window to a layer |
|
|
143
|
+
| `lattices window map [--json]` | Show all window→layer assignments |
|
|
144
|
+
| `lattices scan search <query>` | Search indexed screen text |
|
|
145
|
+
| `lattices diag [limit]` | Show recent diagnostic entries |
|
|
146
|
+
| `lattices focus <session>` | Focus a session's window and switch Spaces |
|
|
147
|
+
| `lattices app` | Launch the menu bar companion app |
|
|
148
|
+
| `lattices app build` | Rebuild the menu bar app from source |
|
|
149
|
+
| `lattices app restart` | Rebuild and relaunch the menu bar app |
|
|
150
|
+
| `lattices app quit` | Stop the menu bar app |
|
|
151
|
+
| `lattices help` | Show help |
|
|
152
|
+
|
|
153
|
+
Aliases: `ls`/`list`, `kill`/`rm`, `sync`/`reconcile`,
|
|
154
|
+
`restart`/`respawn`, `tile`/`t`.
|
|
155
|
+
|
|
156
|
+
## Machine-readable output
|
|
157
|
+
|
|
158
|
+
### `--json` flag
|
|
159
|
+
|
|
160
|
+
The `lattices windows` command supports a `--json` flag for structured
|
|
161
|
+
output:
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
lattices windows --json
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Returns a JSON array of window objects to stdout, useful for piping
|
|
168
|
+
into `jq` or consuming from scripts.
|
|
169
|
+
|
|
170
|
+
### Daemon responses
|
|
171
|
+
|
|
172
|
+
All agent API calls return JSON natively. If you need structured data
|
|
173
|
+
from lattices, the daemon is easier than parsing stdout. See the
|
|
174
|
+
[API reference](/docs/api).
|
|
175
|
+
|
|
176
|
+
### Exit codes
|
|
177
|
+
|
|
178
|
+
| Code | Meaning |
|
|
179
|
+
|------|---------------------------------------------|
|
|
180
|
+
| `0` | Success |
|
|
181
|
+
| `1` | General error (missing args, bad config) |
|
|
182
|
+
| `2` | Session not found |
|
|
183
|
+
|
|
184
|
+
## Recovery
|
|
185
|
+
|
|
186
|
+
### sync
|
|
187
|
+
|
|
188
|
+
```
|
|
189
|
+
lattices sync
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
Reconciles a running session to match the declared config:
|
|
193
|
+
|
|
194
|
+
1. Counts actual panes vs declared panes
|
|
195
|
+
2. Recreates any missing panes
|
|
196
|
+
3. Re-applies the layout (main-vertical with correct width)
|
|
197
|
+
4. Restores pane labels
|
|
198
|
+
5. Re-runs declared commands in any idle panes
|
|
199
|
+
|
|
200
|
+
Use when a pane was killed and you want to get back to the declared
|
|
201
|
+
state without killing the whole session.
|
|
202
|
+
|
|
203
|
+
### restart
|
|
204
|
+
|
|
205
|
+
```
|
|
206
|
+
lattices restart [target]
|
|
207
|
+
```
|
|
208
|
+
|
|
209
|
+
Kills the process in a specific pane and re-runs its declared command.
|
|
210
|
+
The target can be:
|
|
211
|
+
|
|
212
|
+
- A **pane name** (case-insensitive): `lattices restart server`
|
|
213
|
+
- A **0-based index**: `lattices restart 1`
|
|
214
|
+
- **Omitted** (defaults to pane 0): `lattices restart`
|
|
215
|
+
|
|
216
|
+
The restart sequence: send Ctrl-C, wait 0.5s, check if the process
|
|
217
|
+
stopped. If it's still running, escalate to SIGKILL on child
|
|
218
|
+
processes. Then send the declared command.
|
|
219
|
+
|
|
220
|
+
## Tile positions
|
|
221
|
+
|
|
222
|
+
The `lattices tile` command moves the frontmost window to a preset
|
|
223
|
+
screen position. Available positions:
|
|
224
|
+
|
|
225
|
+
| Position | Area |
|
|
226
|
+
|----------------|-----------------------------|
|
|
227
|
+
| `left` | Left half |
|
|
228
|
+
| `right` | Right half |
|
|
229
|
+
| `top` | Top half |
|
|
230
|
+
| `bottom` | Bottom half |
|
|
231
|
+
| `top-left` | Top-left quarter |
|
|
232
|
+
| `top-right` | Top-right quarter |
|
|
233
|
+
| `bottom-left` | Bottom-left quarter |
|
|
234
|
+
| `bottom-right` | Bottom-right quarter |
|
|
235
|
+
| `maximize` | Full screen (visible area) |
|
|
236
|
+
| `left-third` | Left third |
|
|
237
|
+
| `center-third` | Center third |
|
|
238
|
+
| `right-third` | Right third |
|
|
239
|
+
| `center` | 70% width, 80% height, centered |
|
|
240
|
+
|
|
241
|
+
Aliases: `left-half`/`left`, `right-half`/`right`, `top-half`/`top`,
|
|
242
|
+
`bottom-half`/`bottom`, `max`/`maximize`.
|
|
243
|
+
|
|
244
|
+
Tiling respects the menu bar and dock. It uses the visible desktop
|
|
245
|
+
area, not the full screen.
|