@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.
Files changed (74) hide show
  1. package/README.md +155 -0
  2. package/app/Lattices.app/Contents/Info.plist +24 -0
  3. package/app/Package.swift +13 -0
  4. package/app/Sources/AccessibilityTextExtractor.swift +111 -0
  5. package/app/Sources/ActionRow.swift +61 -0
  6. package/app/Sources/App.swift +10 -0
  7. package/app/Sources/AppDelegate.swift +242 -0
  8. package/app/Sources/AppShellView.swift +62 -0
  9. package/app/Sources/AppTypeClassifier.swift +70 -0
  10. package/app/Sources/AppWindowShell.swift +63 -0
  11. package/app/Sources/CheatSheetHUD.swift +332 -0
  12. package/app/Sources/CommandModeState.swift +1362 -0
  13. package/app/Sources/CommandModeView.swift +1405 -0
  14. package/app/Sources/CommandModeWindow.swift +192 -0
  15. package/app/Sources/CommandPaletteView.swift +307 -0
  16. package/app/Sources/CommandPaletteWindow.swift +134 -0
  17. package/app/Sources/DaemonProtocol.swift +101 -0
  18. package/app/Sources/DaemonServer.swift +414 -0
  19. package/app/Sources/DesktopModel.swift +149 -0
  20. package/app/Sources/DesktopModelTypes.swift +71 -0
  21. package/app/Sources/DiagnosticLog.swift +271 -0
  22. package/app/Sources/EventBus.swift +30 -0
  23. package/app/Sources/HotkeyManager.swift +254 -0
  24. package/app/Sources/HotkeyStore.swift +338 -0
  25. package/app/Sources/InventoryManager.swift +35 -0
  26. package/app/Sources/InventoryPath.swift +43 -0
  27. package/app/Sources/KeyRecorderView.swift +210 -0
  28. package/app/Sources/LatticesApi.swift +1234 -0
  29. package/app/Sources/LayerBezel.swift +203 -0
  30. package/app/Sources/MainView.swift +479 -0
  31. package/app/Sources/MainWindow.swift +83 -0
  32. package/app/Sources/OcrModel.swift +430 -0
  33. package/app/Sources/OcrStore.swift +329 -0
  34. package/app/Sources/OmniSearchState.swift +283 -0
  35. package/app/Sources/OmniSearchView.swift +288 -0
  36. package/app/Sources/OmniSearchWindow.swift +105 -0
  37. package/app/Sources/OrphanRow.swift +129 -0
  38. package/app/Sources/PaletteCommand.swift +419 -0
  39. package/app/Sources/PermissionChecker.swift +125 -0
  40. package/app/Sources/Preferences.swift +99 -0
  41. package/app/Sources/ProcessModel.swift +199 -0
  42. package/app/Sources/ProcessQuery.swift +151 -0
  43. package/app/Sources/Project.swift +28 -0
  44. package/app/Sources/ProjectRow.swift +368 -0
  45. package/app/Sources/ProjectScanner.swift +128 -0
  46. package/app/Sources/ScreenMapState.swift +2387 -0
  47. package/app/Sources/ScreenMapView.swift +2820 -0
  48. package/app/Sources/ScreenMapWindowController.swift +89 -0
  49. package/app/Sources/SessionManager.swift +72 -0
  50. package/app/Sources/SettingsView.swift +1064 -0
  51. package/app/Sources/SettingsWindow.swift +20 -0
  52. package/app/Sources/TabGroupRow.swift +178 -0
  53. package/app/Sources/Terminal.swift +259 -0
  54. package/app/Sources/TerminalQuery.swift +156 -0
  55. package/app/Sources/TerminalSynthesizer.swift +200 -0
  56. package/app/Sources/Theme.swift +163 -0
  57. package/app/Sources/TilePickerView.swift +209 -0
  58. package/app/Sources/TmuxModel.swift +53 -0
  59. package/app/Sources/TmuxQuery.swift +81 -0
  60. package/app/Sources/WindowTiler.swift +1778 -0
  61. package/app/Sources/WorkspaceManager.swift +575 -0
  62. package/bin/client.js +4 -0
  63. package/bin/daemon-client.js +187 -0
  64. package/bin/lattices-app.js +221 -0
  65. package/bin/lattices.js +1551 -0
  66. package/docs/api.md +924 -0
  67. package/docs/app.md +297 -0
  68. package/docs/concepts.md +135 -0
  69. package/docs/config.md +245 -0
  70. package/docs/layers.md +410 -0
  71. package/docs/ocr.md +185 -0
  72. package/docs/overview.md +94 -0
  73. package/docs/quickstart.md +75 -0
  74. package/package.json +42 -0
package/docs/layers.md ADDED
@@ -0,0 +1,410 @@
1
+ ---
2
+ title: Workspace Layers & Tab Groups
3
+ description: Group projects into switchable layers and tabbed groups
4
+ order: 4
5
+ ---
6
+
7
+ Two ways to organize related projects in `~/.lattices/workspace.json`:
8
+
9
+ - **Layers** — switchable contexts that focus and tile windows
10
+ - **Tab groups** — related projects as tabs within a single terminal window
11
+
12
+ Both features are configured in the same workspace config and
13
+ work together.
14
+
15
+ ## Tab Groups
16
+
17
+ Tab groups let you bundle related projects as tabs within a single
18
+ terminal session (requires tmux). This is useful when you have a family
19
+ of projects — like an iOS app, macOS app, website, and API — that
20
+ you think of as one logical unit.
21
+
22
+ ### Configuration
23
+
24
+ Add `groups` to `~/.lattices/workspace.json`:
25
+
26
+ ```json
27
+ {
28
+ "name": "my-setup",
29
+ "groups": [
30
+ {
31
+ "id": "talkie",
32
+ "label": "Talkie",
33
+ "tabs": [
34
+ { "path": "/Users/you/dev/talkie-ios", "label": "iOS" },
35
+ { "path": "/Users/you/dev/talkie-macos", "label": "macOS" },
36
+ { "path": "/Users/you/dev/talkie-web", "label": "Website" },
37
+ { "path": "/Users/you/dev/talkie-api", "label": "API" }
38
+ ]
39
+ }
40
+ ]
41
+ }
42
+ ```
43
+
44
+ Each tab's pane layout comes from its own `.lattices.json` — no changes
45
+ to per-project configs.
46
+
47
+ ### How it works
48
+
49
+ - Session name follows the pattern `lattices-group-<id>` (e.g. `lattices-group-talkie`)
50
+ - 1 group = 1 tmux session. Each tab is a tmux window, and each window
51
+ gets its own panes from that project's `.lattices.json`
52
+ - You can still launch projects independently: `cd talkie-ios && lattices`
53
+ creates its own standalone session as before
54
+
55
+ ### Tab group fields
56
+
57
+ | Field | Type | Description |
58
+ |----------------|----------|--------------------------------------|
59
+ | `id` | string | Unique identifier for the group |
60
+ | `label` | string | Display name shown in the UI |
61
+ | `tabs` | array | List of tab definitions |
62
+ | `tabs[].path` | string | Absolute path to project directory |
63
+ | `tabs[].label` | string? | Tab name (defaults to directory name) |
64
+
65
+ ### CLI commands
66
+
67
+ ```bash
68
+ lattices groups # List all groups with status
69
+ lattices group <id> # Launch or attach to a group
70
+ lattices tab <group> [tab] # Switch tab by label or index
71
+ ```
72
+
73
+ Examples:
74
+
75
+ ```bash
76
+ lattices group talkie # Launch all Talkie tabs
77
+ lattices tab talkie iOS # Switch to the iOS tab
78
+ lattices tab talkie 0 # Switch to first tab (by index)
79
+ ```
80
+
81
+ ### Menu bar app
82
+
83
+ Tab groups appear above the project list in the menu bar panel.
84
+ Each group row shows:
85
+
86
+ - Status indicator (running/stopped)
87
+ - Tab count badge
88
+ - Expand/collapse to see individual tabs
89
+ - Launch/Attach and Kill buttons
90
+ - Per-tab "Go" buttons to switch and focus a specific tab
91
+
92
+ The command palette also includes group commands:
93
+
94
+ | Command | Description |
95
+ |----------------------------|----------------------------------------|
96
+ | Launch *group* | Start the group session |
97
+ | Attach *group* | Focus the running group session |
98
+ | *Group*: *Tab* | Switch to a specific tab in a group |
99
+ | Kill *group* Group | Terminate the group session |
100
+
101
+ ## Layers
102
+
103
+ Layers let you group projects into switchable contexts. Define two or
104
+ three layers and switch between them. The target layer's windows come
105
+ to the front and tile into position; the previous layer's windows fall
106
+ behind.
107
+
108
+ All tmux sessions stay alive across switches. Nothing is detached or
109
+ killed. Layers only control which windows are focused.
110
+
111
+ ### Configuration
112
+
113
+ Add `layers` to `~/.lattices/workspace.json`:
114
+
115
+ ```json
116
+ {
117
+ "name": "my-setup",
118
+ "layers": [
119
+ {
120
+ "id": "web",
121
+ "label": "Web",
122
+ "projects": [
123
+ { "path": "/Users/you/dev/frontend", "tile": "left" },
124
+ { "path": "/Users/you/dev/api", "tile": "right" }
125
+ ]
126
+ },
127
+ {
128
+ "id": "mobile",
129
+ "label": "Mobile",
130
+ "projects": [
131
+ { "path": "/Users/you/dev/ios-app", "tile": "left" },
132
+ { "path": "/Users/you/dev/backend", "tile": "right" }
133
+ ]
134
+ }
135
+ ]
136
+ }
137
+ ```
138
+
139
+ ### App windows in layers
140
+
141
+ Layers aren't limited to terminal sessions. You can include any
142
+ application window by using the `app`, `title`, `url`, and `launch`
143
+ fields instead of `path`:
144
+
145
+ ```json
146
+ {
147
+ "name": "hudson",
148
+ "layers": [
149
+ {
150
+ "id": "main",
151
+ "label": "Main",
152
+ "projects": [
153
+ { "app": "Google Chrome", "title": "GitHub", "tile": "left" },
154
+ { "app": "Talkie", "tile": "top-right", "launch": "open -a Talkie" },
155
+ { "path": "/Users/you/dev/frontend", "tile": "bottom-right" }
156
+ ]
157
+ },
158
+ {
159
+ "id": "docs",
160
+ "label": "Docs",
161
+ "projects": [
162
+ { "app": "Google Chrome", "url": "https://docs.example.com", "tile": "left" },
163
+ { "app": "Notes", "title": "Sprint Notes", "tile": "right" }
164
+ ]
165
+ }
166
+ ]
167
+ }
168
+ ```
169
+
170
+ When switching to a layer, lattices matches windows by `app` name and
171
+ optionally filters by `title` substring or `url` prefix. If `launch`
172
+ is provided and no matching window is found, the command is executed
173
+ to open the app.
174
+
175
+ ### Using groups in layers
176
+
177
+ Layer projects can reference a tab group instead of a single path.
178
+ This lets you tile a whole group into a screen position:
179
+
180
+ ```json
181
+ {
182
+ "name": "my-setup",
183
+ "groups": [
184
+ {
185
+ "id": "talkie",
186
+ "label": "Talkie",
187
+ "tabs": [
188
+ { "path": "/Users/you/dev/talkie-ios", "label": "iOS" },
189
+ { "path": "/Users/you/dev/talkie-web", "label": "Website" }
190
+ ]
191
+ }
192
+ ],
193
+ "layers": [
194
+ {
195
+ "id": "main",
196
+ "label": "Main",
197
+ "projects": [
198
+ { "group": "talkie", "tile": "top-left" },
199
+ { "path": "/Users/you/dev/design-system", "tile": "right" }
200
+ ]
201
+ }
202
+ ]
203
+ }
204
+ ```
205
+
206
+ When switching to this layer, lattices launches (or focuses) the
207
+ "talkie" group session and tiles it to the top-left quarter, alongside
208
+ the design-system project on the right.
209
+
210
+ ### Layer fields
211
+
212
+ | Field | Type | Description |
213
+ |-------------------|----------|------------------------------------------|
214
+ | `name` | string | Workspace name (for your reference) |
215
+ | `layers` | array | List of layer definitions |
216
+ | `layers[].id` | string | Unique identifier (e.g. `"web"`) |
217
+ | `layers[].label` | string | Display name shown in the UI |
218
+ | `layers[].projects` | array | Projects in this layer |
219
+ | `projects[].path` | string? | Absolute path to project directory |
220
+ | `projects[].group`| string? | Group ID (alternative to `path`) |
221
+ | `projects[].app` | string? | Application name (for non-terminal windows) |
222
+ | `projects[].title`| string? | Window title substring to match |
223
+ | `projects[].url` | string? | URL prefix to match (browser windows) |
224
+ | `projects[].launch`| string? | Shell command to launch the app if not found |
225
+ | `projects[].tile` | string? | Tile position (optional, see below) |
226
+
227
+ Each project entry must have either `path`, `group`, or `app` — pick one.
228
+
229
+ ### Tile values
230
+
231
+ Any tile position from the [config reference](/docs/config#tile-positions)
232
+ works: `left`, `right`, `top`, `bottom`, `top-left`, `top-right`,
233
+ `bottom-left`, `bottom-right`, `left-third`, `center-third`,
234
+ `right-third`, `maximize`, `center`.
235
+
236
+ ### Switching layers
237
+
238
+ Four ways to switch:
239
+
240
+ | Method | How |
241
+ |----------------------|------------------------------------------|
242
+ | **Hotkey** | Cmd+Option+1, Cmd+Option+2, Cmd+Option+3... |
243
+ | **Layer bar** | Click a layer pill in the menu bar panel |
244
+ | **Command palette** | Search "Switch to Layer" in Cmd+Shift+M |
245
+ | **CLI** | `lattices layer <name\|index>` |
246
+
247
+ When you switch to a layer:
248
+
249
+ 1. Each project's window is **raised and focused**
250
+ 2. App windows are matched by `app` / `title` / `url`
251
+ 3. If a project isn't running yet, it gets **launched** automatically
252
+ 4. Windows with a `tile` value are **tiled** to that position
253
+ 5. The previous layer's windows stay open behind the new ones
254
+
255
+ The app remembers which layer was last active across restarts.
256
+
257
+ ### Named layer switching
258
+
259
+ You can switch layers by name from the CLI:
260
+
261
+ ```bash
262
+ lattices layer hudson # Switch to the layer named "hudson"
263
+ lattices layer 0 # Switch to the first layer (by index)
264
+ ```
265
+
266
+ This is useful for scripting — you don't need to know the index,
267
+ just the layer's `id` or `label`.
268
+
269
+ ### Window tagging
270
+
271
+ You can manually assign any window to a layer, even if it's not
272
+ declared in `workspace.json`. This is useful for ad-hoc windows
273
+ that you want to move with a layer:
274
+
275
+ ```bash
276
+ lattices window assign <wid> <layer> # Tag a window to a layer
277
+ lattices window map # Show all window→layer assignments
278
+ ```
279
+
280
+ Tagged windows behave like declared ones — they're raised and tiled
281
+ when their layer activates. Remove a tag by reassigning or with:
282
+
283
+ ```bash
284
+ # Via the agent API
285
+ await daemonCall('window.removeLayer', { wid: 1234 })
286
+ ```
287
+
288
+ ### Layer bezel
289
+
290
+ When you switch layers via hotkey, a translucent HUD pill appears
291
+ briefly at the top of the screen showing the new layer's name.
292
+ This provides instant visual feedback without interrupting your flow.
293
+
294
+ ### Programmatic switching
295
+
296
+ Agents and scripts can switch layers via the agent API:
297
+
298
+ ```js
299
+ import { daemonCall } from '@lattices/cli'
300
+
301
+ // List available layers
302
+ const { layers, active } = await daemonCall('layers.list')
303
+ console.log(`Active: ${layers[active].label}`)
304
+
305
+ // Switch to a layer by index
306
+ await daemonCall('layer.switch', { index: 0 })
307
+
308
+ // Switch to a layer by name
309
+ await daemonCall('layer.switch', { name: 'hudson' })
310
+ ```
311
+
312
+ The `layer.switch` call focuses and tiles all windows in the target
313
+ layer, just like the hotkey or command palette. A `layer.switched`
314
+ event is broadcast to all connected clients.
315
+
316
+ More methods in the [Agent API reference](/docs/api).
317
+
318
+ ### Layer bar
319
+
320
+ When a workspace config is loaded, a layer bar appears between the
321
+ header and search field in the menu bar panel:
322
+
323
+ ```
324
+ lattices 2 sessions [↔] [⟳]
325
+ ┌────────────────────────────────────────┐
326
+ │ ● Web ○ Mobile │
327
+ │ ⌥1 ⌥2 │
328
+ └────────────────────────────────────────┘
329
+ Search projects...
330
+ ```
331
+
332
+ - Active layer: filled green dot
333
+ - Inactive layers: dim outline dot
334
+ - Hotkey hints shown below each label
335
+
336
+ ## Layout examples
337
+
338
+ ### Single project
339
+
340
+ ```json
341
+ {
342
+ "projects": [
343
+ { "path": "/Users/you/dev/talkie" }
344
+ ]
345
+ }
346
+ ```
347
+
348
+ No `tile` — just focuses the window wherever it is.
349
+
350
+ ### Two-project split
351
+
352
+ ```json
353
+ {
354
+ "projects": [
355
+ { "path": "/Users/you/dev/app", "tile": "left" },
356
+ { "path": "/Users/you/dev/api", "tile": "right" }
357
+ ]
358
+ }
359
+ ```
360
+
361
+ ### Mixed: apps + terminals
362
+
363
+ ```json
364
+ {
365
+ "projects": [
366
+ { "app": "Google Chrome", "title": "GitHub", "tile": "left" },
367
+ { "path": "/Users/you/dev/api", "tile": "right" }
368
+ ]
369
+ }
370
+ ```
371
+
372
+ ### Group + project
373
+
374
+ ```json
375
+ {
376
+ "projects": [
377
+ { "group": "talkie", "tile": "left" },
378
+ { "path": "/Users/you/dev/api", "tile": "right" }
379
+ ]
380
+ }
381
+ ```
382
+
383
+ ### Four quadrants
384
+
385
+ ```json
386
+ {
387
+ "projects": [
388
+ { "path": "/Users/you/dev/frontend", "tile": "top-left" },
389
+ { "path": "/Users/you/dev/backend", "tile": "top-right" },
390
+ { "path": "/Users/you/dev/mobile", "tile": "bottom-left" },
391
+ { "path": "/Users/you/dev/infra", "tile": "bottom-right" }
392
+ ]
393
+ }
394
+ ```
395
+
396
+ ## Tips
397
+
398
+ - Projects don't need a `.lattices.json` config to be in a layer — any
399
+ directory path works. If the project has a config, lattices uses it; if
400
+ not, it opens a plain terminal in that directory.
401
+ - App windows don't need any config at all — just specify `app` and
402
+ optionally `title` or `url` to match the right window.
403
+ - You can have up to 9 layers (Cmd+Option+1 through Cmd+Option+9).
404
+ - Edit `workspace.json` by hand — the app re-reads it on launch. Use
405
+ the Refresh Projects button or restart the app to pick up changes.
406
+ - The `tile` field is optional. Omit it if you just want the window
407
+ focused without repositioning.
408
+ - Tab groups and standalone projects can coexist in the same workspace.
409
+ Use groups for related project families, standalone paths for
410
+ individual projects.
package/docs/ocr.md ADDED
@@ -0,0 +1,185 @@
1
+ ---
2
+ title: Screen OCR & Search
3
+ description: Vision-powered screen reading with full-text search for agents
4
+ order: 3.5
5
+ ---
6
+
7
+ The menu bar app reads text from visible windows using Apple's Vision
8
+ framework and stores results in a local SQLite database with FTS5
9
+ full-text search. Agents can use this to "see" what's on screen.
10
+
11
+ ## Enabling OCR
12
+
13
+ Open **Settings** (via command palette or gear icon) and toggle
14
+ **Search & OCR** on. OCR is disabled by default.
15
+
16
+ ### Accuracy modes
17
+
18
+ | Mode | Description |
19
+ |------|-------------|
20
+ | **Accurate** (default) | Higher quality recognition, slower processing |
21
+ | **Fast** | Lower latency, reduced accuracy |
22
+
23
+ Both modes use `VNRecognizeTextRequest` with language correction enabled.
24
+
25
+ ## How scanning works
26
+
27
+ The app runs two scan schedules:
28
+
29
+ | Schedule | Interval | Window limit | Purpose |
30
+ |----------|----------|-------------|---------|
31
+ | **Quick scan** | 60 seconds | Top 5 windows | Keep recent content fresh |
32
+ | **Deep scan** | 2 hours | Up to 15 windows | Catch less-active windows |
33
+
34
+ Both intervals and limits are configurable in Settings.
35
+
36
+ ### Change detection
37
+
38
+ Before running OCR on a window, the app captures the window image and
39
+ computes a SHA-256 hash of the pixel data. If the hash matches the
40
+ previous scan, the cached result is reused. No Vision processing needed.
41
+
42
+ This keeps CPU usage low when windows haven't changed. A 100ms throttle
43
+ between windows further limits processing bursts.
44
+
45
+ ## Browsing results
46
+
47
+ The **Recent Captures** section in Settings shows OCR results grouped by
48
+ app. Each entry displays the window title, recognized text preview, and
49
+ timestamp.
50
+
51
+ ## Searching
52
+
53
+ ### From the command palette
54
+
55
+ The OmniSearch bar (Cmd+Shift+M) searches OCR content alongside windows,
56
+ projects, and sessions. Matches show as "Screen Text" results with
57
+ contextual snippets.
58
+
59
+ ### From the CLI
60
+
61
+ ```bash
62
+ # View current OCR snapshot
63
+ lattices ocr
64
+
65
+ # Search OCR history
66
+ lattices ocr search "error OR failed"
67
+
68
+ # Trigger an immediate deep scan
69
+ lattices ocr scan
70
+
71
+ # View OCR history for a specific window ID
72
+ lattices ocr history 12345
73
+ ```
74
+
75
+ ### From the agent API
76
+
77
+ Agents can query OCR data through four API methods:
78
+
79
+ | Method | Description |
80
+ |--------|-------------|
81
+ | `ocr.snapshot` | Current OCR results for all visible windows |
82
+ | `ocr.search` | Full-text search across history (FTS5 syntax) |
83
+ | `ocr.history` | Timeline of OCR results for a specific window |
84
+ | `ocr.scan` | Trigger an immediate deep scan |
85
+
86
+ #### `ocr.snapshot`
87
+
88
+ Returns the latest OCR results for all on-screen windows.
89
+
90
+ ```js
91
+ import { daemonCall } from '@lattices/cli'
92
+
93
+ const snapshot = await daemonCall('ocr.snapshot')
94
+ // [{ wid, app, title, frame, fullText, blocks, timestamp }, ...]
95
+ ```
96
+
97
+ Each result includes:
98
+ - `wid` — window ID
99
+ - `app` — application name
100
+ - `title` — window title
101
+ - `frame` — `{ x, y, w, h }` screen position
102
+ - `fullText` — all recognized text concatenated
103
+ - `blocks` — individual text blocks with `{ text, confidence, x, y, w, h }`
104
+ - `timestamp` — Unix timestamp of the scan
105
+
106
+ #### `ocr.search`
107
+
108
+ Full-text search across OCR history using FTS5 query syntax.
109
+
110
+ ```js
111
+ const results = await daemonCall('ocr.search', {
112
+ query: 'error OR failed', // FTS5 query (required)
113
+ app: 'Terminal', // filter by app name (optional)
114
+ limit: 50, // max results (optional, default 50)
115
+ live: false // search in-memory snapshot instead of history (optional)
116
+ })
117
+ // [{ id, wid, app, title, frame, fullText, snippet, timestamp }, ...]
118
+ ```
119
+
120
+ The `snippet` field contains FTS5-highlighted text with `«` and `»`
121
+ delimiters around matched terms.
122
+
123
+ #### `ocr.history`
124
+
125
+ Get the OCR content timeline for a specific window.
126
+
127
+ ```js
128
+ const history = await daemonCall('ocr.history', {
129
+ wid: 12345, // window ID (required)
130
+ limit: 50 // max results (optional, default 50)
131
+ })
132
+ ```
133
+
134
+ #### `ocr.scan`
135
+
136
+ Trigger an immediate deep scan (all visible windows up to the deep limit).
137
+
138
+ ```js
139
+ await daemonCall('ocr.scan')
140
+ // { ok: true }
141
+ ```
142
+
143
+ ## Storage
144
+
145
+ OCR data is stored in `~/.lattices/ocr.db`, a SQLite database in WAL
146
+ (Write-Ahead Logging) mode for safe concurrent reads.
147
+
148
+ The schema uses two tables:
149
+ - `ocr_entry` — stores window ID, app, title, frame, full text, and timestamp
150
+ - `ocr_fts` — FTS5 virtual table indexing `full_text`, `app`, and `title`
151
+
152
+ Triggers keep the FTS index in sync with inserts, updates, and deletes.
153
+
154
+ Entries older than 3 days are automatically deleted.
155
+
156
+ ## Agent usage
157
+
158
+ A typical agent workflow: trigger a scan, then search for relevant content.
159
+
160
+ ```js
161
+ import { daemonCall } from '@lattices/cli'
162
+
163
+ // Trigger a fresh scan
164
+ await daemonCall('ocr.scan')
165
+
166
+ // Search for compilation errors across all windows
167
+ const errors = await daemonCall('ocr.search', { query: 'error OR warning' })
168
+
169
+ for (const result of errors) {
170
+ console.log(`[${result.app}] ${result.title}`)
171
+ console.log(result.snippet)
172
+ }
173
+
174
+ // Read everything currently visible
175
+ const snapshot = await daemonCall('ocr.snapshot')
176
+ for (const win of snapshot) {
177
+ console.log(`${win.app}: ${win.fullText.slice(0, 200)}`)
178
+ }
179
+ ```
180
+
181
+ ## Requirements
182
+
183
+ - **Screen Recording** permission — required to capture window images
184
+ - Grant via System Settings > Privacy & Security > Screen Recording
185
+ - Add the lattices menu bar app to the allowed list
@@ -0,0 +1,94 @@
1
+ ---
2
+ title: Overview
3
+ description: What lattices is and who it's for
4
+ order: 0
5
+ ---
6
+
7
+ lattices is an agentic window manager for macOS. It provides a programmable
8
+ workspace, smart layout management, and managed tmux sessions — all
9
+ controllable from the CLI or a 35+-method agent API.
10
+
11
+ ## The problem
12
+
13
+ I kept losing track of windows. Terminal here, dev server there, browser
14
+ somewhere on the second monitor, three other projects buried under it all.
15
+ Every morning I'd rebuild the same layout. Half my context-switching was
16
+ just hunting for the right window.
17
+
18
+ AI agents have it worse. They're stuck inside a single shell with no way
19
+ to see what's on screen, arrange windows, or jump between projects.
20
+
21
+ ## The solution
22
+
23
+ lattices addresses both sides with three layers:
24
+
25
+ ### Programmable workspace
26
+
27
+ The CLI and agent API expose everything: query what's on screen, search
28
+ window text, tile layouts, manage sessions. Claude Code skills, MCP servers,
29
+ or your own scripts can drive your desktop the same way you do. Your
30
+ workspace becomes infrastructure you can observe and control programmatically.
31
+
32
+ ### Smart layout manager
33
+
34
+ A native menu bar app tracks every window across all your monitors. Tile
35
+ with hotkeys, organize into switchable layers, snap to grids. It reads
36
+ your windows too — extracting text from UI elements every 60 seconds and
37
+ running Vision OCR on background windows every 2 hours. Everything is
38
+ searchable.
39
+
40
+ ### Managed tmux sessions
41
+
42
+ We make tmux easy. Declare your dev environment in a `.lattices.json`:
43
+ which panes, which commands, what layout. lattices builds it, runs it,
44
+ and keeps it alive. Use your own terminal — sessions survive reboots
45
+ and you can reattach anytime.
46
+
47
+ ## What's included
48
+
49
+ | Component | Description |
50
+ |-----------|-------------|
51
+ | **CLI** | The `lattices` command. Tile windows, manage sessions, scan screen text, control the workspace from your terminal |
52
+ | **Menu bar app** | Native macOS companion. Command palette, window tiling, project discovery, screen text indexing |
53
+ | **Agent API** | WebSocket server on `ws://127.0.0.1:9399`. 35+ methods, 5 real-time events |
54
+ | **Screen scanner** | Reads text from visible windows using Accessibility API (60s) and Apple Vision OCR (2h), indexes with FTS5 |
55
+ | **Node.js client** | Zero-dependency `daemonCall()` helper for scripting |
56
+
57
+ ## Example
58
+
59
+ ```bash
60
+ cd ~/my-project && lattices
61
+ ```
62
+
63
+ Agents get the same control programmatically:
64
+
65
+ ```js
66
+ import { daemonCall } from '@lattices/cli'
67
+ await daemonCall('session.launch', { path: '/Users/you/dev/frontend' })
68
+ await daemonCall('window.tile', { session: 'frontend-a1b2c3', position: 'left' })
69
+ ```
70
+
71
+ ## Who it's for
72
+
73
+ - Developers who juggle multiple projects and want faster window management
74
+ - People building AI agents that need to observe and control the desktop
75
+ - Power users who work across multiple macOS Spaces
76
+ - Anyone who wants persistent, auto-configured terminal sessions
77
+
78
+ ## Requirements
79
+
80
+ - macOS 13.0+
81
+ - Node.js 18+
82
+
83
+ ### Dev dependencies
84
+
85
+ - Swift 5.9+ — only to build the menu bar app from source
86
+ - tmux — needed for persistent terminal sessions (`brew install tmux`)
87
+
88
+ ## Next steps
89
+
90
+ - [Quickstart](/docs/quickstart): install and run your first session in 2 minutes
91
+ - [Configuration](/docs/config): `.lattices.json` format and CLI commands
92
+ - [Screen scanning](/docs/ocr): AX text extraction, Vision OCR, and full-text search
93
+ - [Concepts](/docs/concepts): architecture, glossary, and how it all works
94
+ - [Agent API](/docs/api): RPC method reference for agents and scripts