@lattices/cli 0.3.0 → 0.4.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 (95) hide show
  1. package/README.md +85 -9
  2. package/app/Package.swift +8 -1
  3. package/app/Sources/AdvisorLearningStore.swift +90 -0
  4. package/app/Sources/AgentSession.swift +377 -0
  5. package/app/Sources/AppDelegate.swift +44 -12
  6. package/app/Sources/AppShellView.swift +81 -8
  7. package/app/Sources/AudioProvider.swift +386 -0
  8. package/app/Sources/CheatSheetHUD.swift +261 -19
  9. package/app/Sources/DaemonProtocol.swift +13 -0
  10. package/app/Sources/DaemonServer.swift +8 -0
  11. package/app/Sources/DesktopModel.swift +164 -5
  12. package/app/Sources/DesktopModelTypes.swift +2 -0
  13. package/app/Sources/DiagnosticLog.swift +104 -2
  14. package/app/Sources/EventBus.swift +1 -0
  15. package/app/Sources/HUDBottomBar.swift +279 -0
  16. package/app/Sources/HUDController.swift +1158 -0
  17. package/app/Sources/HUDLeftBar.swift +849 -0
  18. package/app/Sources/HUDMinimap.swift +179 -0
  19. package/app/Sources/HUDRightBar.swift +774 -0
  20. package/app/Sources/HUDState.swift +367 -0
  21. package/app/Sources/HUDTopBar.swift +243 -0
  22. package/app/Sources/HandsOffSession.swift +733 -0
  23. package/app/Sources/HomeDashboardView.swift +125 -0
  24. package/app/Sources/HotkeyManager.swift +2 -0
  25. package/app/Sources/HotkeyStore.swift +45 -9
  26. package/app/Sources/IntentEngine.swift +925 -0
  27. package/app/Sources/Intents/CreateLayerIntent.swift +54 -0
  28. package/app/Sources/Intents/DistributeIntent.swift +56 -0
  29. package/app/Sources/Intents/FocusIntent.swift +69 -0
  30. package/app/Sources/Intents/HelpIntent.swift +41 -0
  31. package/app/Sources/Intents/KillIntent.swift +47 -0
  32. package/app/Sources/Intents/LatticeIntent.swift +78 -0
  33. package/app/Sources/Intents/LaunchIntent.swift +67 -0
  34. package/app/Sources/Intents/ListSessionsIntent.swift +32 -0
  35. package/app/Sources/Intents/ListWindowsIntent.swift +30 -0
  36. package/app/Sources/Intents/ScanIntent.swift +52 -0
  37. package/app/Sources/Intents/SearchIntent.swift +190 -0
  38. package/app/Sources/Intents/SwitchLayerIntent.swift +50 -0
  39. package/app/Sources/Intents/TileIntent.swift +61 -0
  40. package/app/Sources/LatticesApi.swift +1235 -30
  41. package/app/Sources/LauncherHUD.swift +348 -0
  42. package/app/Sources/MainView.swift +147 -44
  43. package/app/Sources/OcrModel.swift +34 -1
  44. package/app/Sources/OmniSearchState.swift +99 -102
  45. package/app/Sources/OnboardingView.swift +457 -0
  46. package/app/Sources/PermissionChecker.swift +2 -12
  47. package/app/Sources/PiChatDock.swift +454 -0
  48. package/app/Sources/PiChatSession.swift +815 -0
  49. package/app/Sources/PiWorkspaceView.swift +364 -0
  50. package/app/Sources/PlacementSpec.swift +195 -0
  51. package/app/Sources/Preferences.swift +59 -0
  52. package/app/Sources/ProjectScanner.swift +1 -1
  53. package/app/Sources/ScreenMapState.swift +701 -55
  54. package/app/Sources/ScreenMapView.swift +843 -103
  55. package/app/Sources/ScreenMapWindowController.swift +22 -0
  56. package/app/Sources/SessionLayerStore.swift +285 -0
  57. package/app/Sources/SessionManager.swift +4 -1
  58. package/app/Sources/SettingsView.swift +186 -3
  59. package/app/Sources/Theme.swift +9 -8
  60. package/app/Sources/TmuxModel.swift +7 -0
  61. package/app/Sources/TmuxQuery.swift +27 -3
  62. package/app/Sources/VoiceChatView.swift +192 -0
  63. package/app/Sources/VoiceCommandWindow.swift +1594 -0
  64. package/app/Sources/VoiceIntentResolver.swift +671 -0
  65. package/app/Sources/VoxClient.swift +454 -0
  66. package/app/Sources/WindowTiler.swift +348 -87
  67. package/app/Sources/WorkspaceManager.swift +127 -18
  68. package/bin/client.ts +16 -0
  69. package/bin/{daemon-client.js → daemon-client.ts} +49 -30
  70. package/bin/handsoff-infer.ts +280 -0
  71. package/bin/handsoff-worker.ts +731 -0
  72. package/bin/{lattices-app.js → lattices-app.ts} +67 -32
  73. package/bin/lattices-dev +160 -0
  74. package/bin/{lattices.js → lattices.ts} +600 -137
  75. package/bin/project-twin.ts +645 -0
  76. package/docs/agent-execution-plan.md +562 -0
  77. package/docs/agents.md +142 -0
  78. package/docs/api.md +153 -34
  79. package/docs/app.md +29 -1
  80. package/docs/config.md +5 -1
  81. package/docs/handsoff-test-scenarios.md +84 -0
  82. package/docs/layers.md +20 -20
  83. package/docs/ocr.md +14 -5
  84. package/docs/overview.md +5 -1
  85. package/docs/presentation-execution-review.md +491 -0
  86. package/docs/prompts/hands-off-system.md +374 -0
  87. package/docs/prompts/hands-off-turn.md +30 -0
  88. package/docs/prompts/voice-advisor.md +31 -0
  89. package/docs/prompts/voice-fallback.md +23 -0
  90. package/docs/tiling-reference.md +167 -0
  91. package/docs/twins.md +138 -0
  92. package/docs/voice-command-protocol.md +278 -0
  93. package/docs/voice.md +219 -0
  94. package/package.json +21 -10
  95. package/bin/client.js +0 -4
package/docs/api.md CHANGED
@@ -112,7 +112,10 @@ const win = await daemonCall('windows.get', { wid: 1234 })
112
112
 
113
113
  // Mutations
114
114
  await daemonCall('session.launch', { path: '/Users/you/dev/myapp' })
115
- await daemonCall('window.tile', { session: 'myapp-a1b2c3', position: 'left' })
115
+ await daemonCall('window.place', {
116
+ session: 'myapp-a1b2c3',
117
+ placement: { kind: 'tile', value: 'left' }
118
+ })
116
119
 
117
120
  // Custom timeout (default: 3000ms)
118
121
  await daemonCall('projects.scan', null, 10000)
@@ -190,6 +193,12 @@ Useful for agent self-discovery.
190
193
 
191
194
  **Params**: none
192
195
 
196
+ CLI shortcut:
197
+
198
+ ```bash
199
+ lattices call api.schema
200
+ ```
201
+
193
202
  #### `diagnostics.list`
194
203
 
195
204
  Return recent diagnostic log entries from the daemon.
@@ -210,13 +219,15 @@ Return recent diagnostic log entries from the daemon.
210
219
  | `windows.get` | read | Single window by ID |
211
220
  | `windows.search` | read | Search windows by query |
212
221
  | `spaces.list` | read | macOS display spaces |
213
- | `window.tile` | write | Tile a window to a position |
222
+ | `window.place` | write | Place a window or session using a typed placement spec |
223
+ | `window.tile` | write | Compatibility wrapper for session tiling |
214
224
  | `window.focus` | write | Focus a window / switch Spaces |
215
225
  | `window.move` | write | Move a window to another Space |
216
226
  | `window.assignLayer` | write | Tag a window to a layer |
217
227
  | `window.removeLayer` | write | Remove a window's layer tag |
218
228
  | `window.layerMap` | read | All window→layer assignments |
219
- | `layout.distribute` | write | Distribute windows evenly |
229
+ | `space.optimize` | write | Optimize a set of windows using an explicit scope and strategy |
230
+ | `layout.distribute` | write | Compatibility wrapper for visible-window balancing |
220
231
 
221
232
  #### `windows.list`
222
233
 
@@ -263,17 +274,50 @@ Get a single window by its CGWindowID.
263
274
 
264
275
  #### `windows.search`
265
276
 
266
- Search windows by text query, including OCR content.
277
+ Search windows by text query across title, app name, session tags, and OCR content. Returns results with `matchSource` indicating how the match was found, and `ocrSnippet` for OCR matches.
267
278
 
268
279
  **Params**:
269
280
 
270
281
  | Field | Type | Required | Description |
271
282
  |---------|--------|----------|--------------------------------|
272
- | `query` | string | yes | Search query (matches title, app, OCR text) |
283
+ | `query` | string | yes | Search query (matches title, app, session, OCR text) |
273
284
  | `ocr` | boolean| no | Include OCR text in search (default true) |
274
- | `limit` | number | no | Max results (default 20) |
285
+ | `limit` | number | no | Max results (default 50) |
286
+
287
+ **Returns**: array of window objects with additional search fields:
288
+
289
+ ```json
290
+ [
291
+ {
292
+ "wid": 265,
293
+ "app": "iTerm2",
294
+ "title": "✳ Claude Code",
295
+ "matchSource": "ocr",
296
+ "ocrSnippet": "…~/dev/vox StatusBarIconFolder…",
297
+ "frame": { "x": 688, "y": 3, "w": 1720, "h": 720 },
298
+ "isOnScreen": true
299
+ }
300
+ ]
301
+ ```
275
302
 
276
- **Returns**: array of window objects matching the query.
303
+ `matchSource` values: `"title"`, `"app"`, `"session"`, `"ocr"`.
304
+
305
+ **CLI usage**:
306
+
307
+ ```bash
308
+ # Basic search (uses windows.search)
309
+ lattices search vox
310
+
311
+ # Deep search — adds terminal tab/process inspection for ranking
312
+ lattices search vox --deep
313
+
314
+ # Pipeable output
315
+ lattices search vox --wid
316
+ lattices search vox --json
317
+
318
+ # Search + focus + tile in one step
319
+ lattices place vox right
320
+ ```
277
321
 
278
322
  #### `spaces.list`
279
323
 
@@ -297,20 +341,53 @@ List macOS display spaces (virtual desktops).
297
341
  ]
298
342
  ```
299
343
 
300
- #### `window.tile`
344
+ #### `window.place`
301
345
 
302
- Tile a session's terminal window to a screen position.
346
+ Canonical window placement mutation. Use this when an agent needs a
347
+ single, typed placement contract across voice, CLI, and daemon clients.
303
348
 
304
349
  **Params**:
305
350
 
306
- | Field | Type | Required | Description |
307
- |------------|--------|----------|-------------------------------------|
308
- | `session` | string | yes | Session name |
309
- | `position` | string | yes | Tile position (see below) |
351
+ | Field | Type | Required | Description |
352
+ |-------------|-----------------|----------|------------------------------------------|
353
+ | `wid` | number | no | Target window ID |
354
+ | `session` | string | no | Target lattices session |
355
+ | `app` | string | no | Target app name |
356
+ | `title` | string | no | Optional title substring for app matching |
357
+ | `display` | number | no | Target display index |
358
+ | `placement` | string \| object | yes | Placement shorthand or typed object |
359
+
360
+ Target resolution priority is `wid` → `session` → `app/title` → frontmost window.
310
361
 
311
- **Positions**: `left`, `right`, `top`, `bottom`, `top-left`, `top-right`,
362
+ **Placement strings**: `left`, `right`, `top`, `bottom`, `top-left`, `top-right`,
312
363
  `bottom-left`, `bottom-right`, `left-third`, `center-third`, `right-third`,
313
- `maximize`, `center`
364
+ `top-third`, `middle-third`, `bottom-third`, `left-quarter`, `right-quarter`,
365
+ `top-quarter`, `bottom-quarter`, `maximize`, `center`, or `grid:CxR:C,R`.
366
+
367
+ **Typed placement examples**:
368
+
369
+ ```json
370
+ { "kind": "tile", "value": "top-right" }
371
+ { "kind": "grid", "columns": 3, "rows": 2, "column": 2, "row": 0 }
372
+ { "kind": "fractions", "x": 0.5, "y": 0, "w": 0.5, "h": 1 }
373
+ ```
374
+
375
+ **Returns**: execution receipt including resolved target, placement, and trace.
376
+
377
+ #### `window.tile`
378
+
379
+ Compatibility wrapper for `window.place` when the target is a lattices
380
+ session window.
381
+
382
+ **Params**:
383
+
384
+ | Field | Type | Required | Description |
385
+ |------------|--------|----------|-----------------------------------------|
386
+ | `session` | string | yes | Session name |
387
+ | `position` | string | yes | Placement shorthand or grid syntax |
388
+
389
+ This method exists for compatibility. New integrations should prefer
390
+ `window.place`.
314
391
 
315
392
  #### `window.focus`
316
393
 
@@ -375,9 +452,31 @@ Return all current window→layer assignments.
375
452
 
376
453
  Keys are CGWindowIDs (as strings), values are layer IDs.
377
454
 
455
+ #### `space.optimize`
456
+
457
+ Canonical space-balancing mutation. Use this when the goal is to make
458
+ the current workspace coherent rather than placing one specific window.
459
+
460
+ **Params**:
461
+
462
+ | Field | Type | Required | Description |
463
+ |-------------|----------|----------|-----------------------------------------------------|
464
+ | `scope` | string | no | `visible`, `active-app`, `app`, or `selection` |
465
+ | `strategy` | string | no | `balanced` or `mosaic` |
466
+ | `app` | string | no | App name for `app` scope |
467
+ | `title` | string | no | Optional title substring for app matching |
468
+ | `windowIds` | number[] | no | Explicit window IDs for `selection` scope |
469
+
470
+ If `windowIds` is provided, scope is inferred as `selection`.
471
+ If `app` is provided and `scope` is omitted, scope is inferred as `app`.
472
+
473
+ **Returns**: execution receipt including resolved scope, strategy,
474
+ affected window IDs, and trace.
475
+
378
476
  #### `layout.distribute`
379
477
 
380
- Distribute all visible lattices windows evenly across the screen.
478
+ Compatibility wrapper for `space.optimize` with `scope=visible` and
479
+ `strategy=balanced`.
381
480
 
382
481
  **Params**: none
383
482
 
@@ -512,7 +611,8 @@ Restart a specific pane's process within a session.
512
611
  | `projects.list` | read | Discovered projects |
513
612
  | `projects.scan` | write | Re-scan project directory |
514
613
  | `layers.list` | read | Workspace layers and active index |
515
- | `layer.switch` | write | Switch workspace layer |
614
+ | `layer.activate` | write | Activate a workspace layer using an explicit mode |
615
+ | `layer.switch` | write | Compatibility wrapper for launch-style layer activation |
516
616
  | `group.launch` | write | Launch a tab group |
517
617
  | `group.kill` | write | Kill a tab group |
518
618
 
@@ -569,20 +669,33 @@ List configured workspace layers and the active index.
569
669
 
570
670
  Returns empty `layers` array if no workspace config is loaded.
571
671
 
572
- #### `layer.switch`
672
+ #### `layer.activate`
573
673
 
574
- Switch the active workspace layer. Focuses and tiles all windows in the
575
- target layer, launches any projects that aren't running yet, and posts
576
- a `layer.switched` event.
674
+ Canonical layer mutation. Use this when an agent wants an explicit
675
+ activation mode instead of implicit "switch" behavior.
577
676
 
578
677
  **Params**:
579
678
 
580
- | Field | Type | Required | Description |
581
- |---------|--------|----------|--------------------------------|
582
- | `index` | number | no | Layer index (0-based) |
583
- | `name` | string | no | Layer ID or label |
679
+ | Field | Type | Required | Description |
680
+ |---------|--------|----------|---------------------------------------------|
681
+ | `index` | number | no | Layer index (0-based) |
682
+ | `name` | string | no | Layer ID or label |
683
+ | `mode` | string | no | `launch`, `focus`, or `retile` |
684
+
685
+ Provide either `index` or `name`.
584
686
 
585
- Provide either `index` or `name`. If both are given, `name` takes priority.
687
+ **Modes**:
688
+
689
+ - `launch` — bring up the layer, launching missing projects and retiling
690
+ - `focus` — raise the layer's windows in place
691
+ - `retile` — re-apply the layer layout without launch semantics
692
+
693
+ **Returns**: execution receipt including resolved layer, mode, and trace.
694
+
695
+ #### `layer.switch`
696
+
697
+ Compatibility wrapper for `layer.activate` with `mode=launch`.
698
+ It keeps the old semantics and still posts a `layer.switched` event.
586
699
 
587
700
  #### `group.launch`
588
701
 
@@ -839,13 +952,19 @@ project knows how to control the workspace:
839
952
  This project uses lattices for workspace management. The daemon API
840
953
  is available at ws://127.0.0.1:9399.
841
954
 
842
- ### Available commands
843
- - List windows: `daemonCall('windows.list')`
844
- - List sessions: `daemonCall('tmux.sessions')`
955
+ ### Search (find windows)
956
+ - Search by content: `daemonCall('windows.search', { query: 'myproject' })`
957
+ Returns windows with `matchSource` ("title", "app", "session", "ocr") and `ocrSnippet`
958
+ - Search terminals: `daemonCall('terminals.search', {})` — tabs, cwds, processes
959
+ - CLI: `lattices search myproject` or `lattices search myproject --deep`
960
+
961
+ ### Actions
962
+ - Focus a window: `daemonCall('window.focus', { wid: 1234 })`
963
+ - Place a window: `daemonCall('window.place', { session: 'name', placement: 'left' })`
845
964
  - Launch a project: `daemonCall('session.launch', { path: '/absolute/path' })`
846
- - Tile a window: `daemonCall('window.tile', { session: 'name', position: 'left' })`
847
- - Switch layer: `daemonCall('layer.switch', { index: 0 })`
848
- - Switch layer by name: `daemonCall('layer.switch', { name: 'web' })`
965
+ - Activate a layer: `daemonCall('layer.activate', { name: 'web', mode: 'launch' })`
966
+ - Optimize the workspace: `daemonCall('space.optimize', { scope: 'visible', strategy: 'balanced' })`
967
+ - CLI: `lattices place myproject left` (search + focus + tile in one step)
849
968
 
850
969
  ### Import
851
970
  \```js
@@ -872,8 +991,8 @@ const sessions = await daemonCall('tmux.sessions')
872
991
  const fe = sessions.find(s => s.name.startsWith('frontend'))
873
992
  const api = sessions.find(s => s.name.startsWith('api'))
874
993
 
875
- await daemonCall('window.tile', { session: fe.name, position: 'left' })
876
- await daemonCall('window.tile', { session: api.name, position: 'right' })
994
+ await daemonCall('window.place', { session: fe.name, placement: 'left' })
995
+ await daemonCall('window.place', { session: api.name, placement: 'right' })
877
996
  ```
878
997
 
879
998
  ### Reactive event pattern
package/docs/app.md CHANGED
@@ -143,10 +143,21 @@ highlight around it for ~1 second so you can spot it immediately.
143
143
  Grant Screen Recording and Accessibility permissions in System
144
144
  Settings > Privacy & Security for all three paths to work.
145
145
 
146
+ ## Voice commands
147
+
148
+ > See [Voice Commands](/docs/voice) for the full guide.
149
+
150
+ Press **Hyper+3** to open the voice command window. Hold **Option** to
151
+ speak, release to stop. Lattices transcribes via Vox, matches to an
152
+ intent, and executes. Built-in commands: find, show, open, tile, kill, scan.
153
+
154
+ A Claude Haiku advisor runs in parallel, offering follow-up suggestions
155
+ in the AI corner. Configure the model and budget in Settings > AI.
156
+
146
157
  ## Settings
147
158
 
148
159
  Open via the command palette or the gear icon in the main view.
149
- The settings window has three tabs:
160
+ The settings window has four tabs:
150
161
 
151
162
  ### General
152
163
 
@@ -162,6 +173,17 @@ The settings window has three tabs:
162
173
  (helpful while getting used to tmux)
163
174
  - **Auto** — detaches sessions automatically (fewer prompts)
164
175
 
176
+ ### AI
177
+
178
+ | Setting | Default | Description |
179
+ |----------------------|----------------|------------------------------------------|
180
+ | Claude CLI path | Auto-detected | Path to `claude` binary |
181
+ | Advisor model | Haiku | `haiku` (fast) or `sonnet` (smarter) |
182
+ | Budget per session | $0.50 | Max spend per Claude CLI invocation |
183
+
184
+ Shows live session stats: context usage %, session cost, and learned
185
+ pattern count.
186
+
165
187
  ### Shortcuts
166
188
 
167
189
  Shows keyboard shortcut reference:
@@ -169,6 +191,12 @@ Shows keyboard shortcut reference:
169
191
  | Shortcut | Action |
170
192
  |-------------------|----------------------|
171
193
  | Cmd+Shift+M | Open command palette |
194
+ | Hyper+1 | Screen map |
195
+ | Hyper+2 | Window bezel |
196
+ | Hyper+3 | Voice commands |
197
+ | Hyper+4 | Desktop inventory |
198
+ | Hyper+5 | Omni search |
199
+ | Hyper+6 | Cheat sheet |
172
200
  | Cmd+Option+1/2/3 | Switch workspace layer |
173
201
  | Ctrl+B D | Detach from session |
174
202
  | Ctrl+B X | Kill current pane |
package/docs/config.md CHANGED
@@ -141,9 +141,13 @@ Run `lattices init` in your project directory to generate a starter
141
141
  | `lattices windows [--json]` | List all visible windows |
142
142
  | `lattices window assign <wid> <layer>` | Tag a window to a layer |
143
143
  | `lattices window map [--json]` | Show all window→layer assignments |
144
+ | `lattices search <query>` | Search windows by title, app, session, OCR |
145
+ | `lattices search <q> --deep` | Deep search: index + live terminal inspection |
146
+ | `lattices search <q> --wid` | Print matching window IDs only (pipeable) |
147
+ | `lattices place <query> [pos]` | Deep search + focus + tile (default: bottom-right)|
148
+ | `lattices focus <session>` | Focus a session's window and switch Spaces |
144
149
  | `lattices scan search <query>` | Search indexed screen text |
145
150
  | `lattices diag [limit]` | Show recent diagnostic entries |
146
- | `lattices focus <session>` | Focus a session's window and switch Spaces |
147
151
  | `lattices app` | Launch the menu bar companion app |
148
152
  | `lattices app build` | Rebuild the menu bar app from source |
149
153
  | `lattices app restart` | Rebuild and relaunch the menu bar app |
@@ -0,0 +1,84 @@
1
+ # Hands-Off Mode — Test Scenarios
2
+
3
+ Press Ctrl+Cmd+M, say the phrase, press Ctrl+Cmd+M again. Check the result.
4
+
5
+ ## 1. Basic awareness
6
+
7
+ | # | Say | Expected | Check |
8
+ |---|-----|----------|-------|
9
+ | 1.1 | "What's the frontmost window?" | Names the app + project/title, no wid | Correct window? No numbers? |
10
+ | 1.2 | "How many monitors do I have?" | Correct count + sizes | Matches reality? |
11
+ | 1.3 | "What terminals do I have open?" | Lists terminals with projects/cwds | Does it know the projects? |
12
+ | 1.4 | "Which ones are running Claude Code?" | Lists only Claude terminals + their projects | Correct? Uses hasClaude? |
13
+ | 1.5 | "What's on my second monitor?" | Describes windows on the non-main display | Correct display? |
14
+
15
+ ## 2. Simple tiling
16
+
17
+ | # | Say | Expected | Check |
18
+ |---|-----|----------|-------|
19
+ | 2.1 | "Tile Chrome left" | Chrome moves to left half | Did it move? Spoken confirmation? |
20
+ | 2.2 | "Put iTerm on the right" | iTerm moves to right half | Correct window? |
21
+ | 2.3 | "Maximize this window" | Frontmost window maximizes | Right window? |
22
+ | 2.4 | "Center the Finder window" | Finder centers | Correct? |
23
+
24
+ ## 3. Multi-window layouts
25
+
26
+ | # | Say | Expected | Check |
27
+ |---|-----|----------|-------|
28
+ | 3.1 | "Split Chrome and iTerm" | Chrome left, iTerm right | Both move? Spoken narration? |
29
+ | 3.2 | "Put everything in a grid" | distribute intent fires | Windows arrange? |
30
+ | 3.3 | "Thirds with Chrome, iTerm, and Finder" | Three apps in left/center/right thirds | Correct apps + positions? |
31
+ | 3.4 | "Quadrants" | Four windows in corners | Reasonable choices? |
32
+
33
+ ## 4. Focus + switching
34
+
35
+ | # | Say | Expected | Check |
36
+ |---|-----|----------|-------|
37
+ | 4.1 | "Focus on Slack" | Slack comes to front | Did it switch? |
38
+ | 4.2 | "Switch to Chrome" | Chrome comes to front | Correct? |
39
+ | 4.3 | "Go to the lattices terminal" | Focuses the iTerm in ~/dev/lattices | Right terminal? |
40
+ | 4.4 | "Show me the Hudson Claude Code" | Focuses iTerm with hasClaude + cwd hudson | Correct? |
41
+
42
+ ## 5. Conversational context
43
+
44
+ | # | Say | Expected | Check |
45
+ |---|-----|----------|-------|
46
+ | 5.1 | "Tile Chrome left" then "Now put iTerm on the right" | Two separate turns, both work | Context from turn 1 used? |
47
+ | 5.2 | "Tile Chrome left" then "Swap them" | Chrome right, iTerm left | Understood "them"? |
48
+ | 5.3 | "What terminals do I have?" then "Organize those" | Lists, then distributes the terminals | Connected the turns? |
49
+ | 5.4 | "Put it back" (after any tiling) | Reverses last action | Worked? (may not be supported yet) |
50
+
51
+ ## 6. Intelligence
52
+
53
+ | # | Say | Expected | Check |
54
+ |---|-----|----------|-------|
55
+ | 6.1 | "Set up for coding" | Intelligent layout based on visible apps | Reasonable? Explained reasoning? |
56
+ | 6.2 | "I'm going to do a code review" | Suggests/applies review layout | Smart choice? |
57
+ | 6.3 | "Clean up my desktop" | Distributes or suggests organization | Actionable? |
58
+ | 6.4 | "Too many windows, simplify" | Suggests hiding some, focuses key ones | Reasonable? |
59
+
60
+ ## 7. Error handling
61
+
62
+ | # | Say | Expected | Check |
63
+ |---|-----|----------|-------|
64
+ | 7.1 | "Focus on Firefox" | "I don't see Firefox. You have Chrome and Safari." | Honest? Names real apps? |
65
+ | 7.2 | "Tile the Photoshop window" | "Photoshop isn't open right now." | No hallucination? |
66
+ | 7.3 | (mumble something unclear) | "I didn't catch that. Can you say it again?" | Graceful? |
67
+
68
+ ## 8. Actions actually execute
69
+
70
+ | # | Say | Expected | Check |
71
+ |---|-----|----------|-------|
72
+ | 8.1 | "Distribute my windows" | spoken + distribute action | Actions array not empty? |
73
+ | 8.2 | "Organize my terminals" | spoken + distribute or tile actions | Actions actually fire? |
74
+ | 8.3 | Any action command | Hear narration BEFORE windows move | Sequence correct? |
75
+
76
+ ## Scoring
77
+
78
+ For each test:
79
+ - ✅ Works correctly
80
+ - ⚠️ Partially works (note what's wrong)
81
+ - ❌ Broken (note error)
82
+ - 🔇 No audio feedback
83
+
84
+ Track in a copy of this file or in the terminal.
package/docs/layers.md CHANGED
@@ -28,13 +28,13 @@ Add `groups` to `~/.lattices/workspace.json`:
28
28
  "name": "my-setup",
29
29
  "groups": [
30
30
  {
31
- "id": "talkie",
32
- "label": "Talkie",
31
+ "id": "vox",
32
+ "label": "Vox",
33
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" }
34
+ { "path": "/Users/you/dev/vox-ios", "label": "iOS" },
35
+ { "path": "/Users/you/dev/vox-macos", "label": "macOS" },
36
+ { "path": "/Users/you/dev/vox-web", "label": "Website" },
37
+ { "path": "/Users/you/dev/vox-api", "label": "API" }
38
38
  ]
39
39
  }
40
40
  ]
@@ -46,10 +46,10 @@ to per-project configs.
46
46
 
47
47
  ### How it works
48
48
 
49
- - Session name follows the pattern `lattices-group-<id>` (e.g. `lattices-group-talkie`)
49
+ - Session name follows the pattern `lattices-group-<id>` (e.g. `lattices-group-vox`)
50
50
  - 1 group = 1 tmux session. Each tab is a tmux window, and each window
51
51
  gets its own panes from that project's `.lattices.json`
52
- - You can still launch projects independently: `cd talkie-ios && lattices`
52
+ - You can still launch projects independently: `cd vox-ios && lattices`
53
53
  creates its own standalone session as before
54
54
 
55
55
  ### Tab group fields
@@ -73,9 +73,9 @@ lattices tab <group> [tab] # Switch tab by label or index
73
73
  Examples:
74
74
 
75
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)
76
+ lattices group vox # Launch all Vox tabs
77
+ lattices tab vox iOS # Switch to the iOS tab
78
+ lattices tab vox 0 # Switch to first tab (by index)
79
79
  ```
80
80
 
81
81
  ### Menu bar app
@@ -151,7 +151,7 @@ fields instead of `path`:
151
151
  "label": "Main",
152
152
  "projects": [
153
153
  { "app": "Google Chrome", "title": "GitHub", "tile": "left" },
154
- { "app": "Talkie", "tile": "top-right", "launch": "open -a Talkie" },
154
+ { "app": "Vox", "tile": "top-right", "launch": "open -a Vox" },
155
155
  { "path": "/Users/you/dev/frontend", "tile": "bottom-right" }
156
156
  ]
157
157
  },
@@ -182,11 +182,11 @@ This lets you tile a whole group into a screen position:
182
182
  "name": "my-setup",
183
183
  "groups": [
184
184
  {
185
- "id": "talkie",
186
- "label": "Talkie",
185
+ "id": "vox",
186
+ "label": "Vox",
187
187
  "tabs": [
188
- { "path": "/Users/you/dev/talkie-ios", "label": "iOS" },
189
- { "path": "/Users/you/dev/talkie-web", "label": "Website" }
188
+ { "path": "/Users/you/dev/vox-ios", "label": "iOS" },
189
+ { "path": "/Users/you/dev/vox-web", "label": "Website" }
190
190
  ]
191
191
  }
192
192
  ],
@@ -195,7 +195,7 @@ This lets you tile a whole group into a screen position:
195
195
  "id": "main",
196
196
  "label": "Main",
197
197
  "projects": [
198
- { "group": "talkie", "tile": "top-left" },
198
+ { "group": "vox", "tile": "top-left" },
199
199
  { "path": "/Users/you/dev/design-system", "tile": "right" }
200
200
  ]
201
201
  }
@@ -204,7 +204,7 @@ This lets you tile a whole group into a screen position:
204
204
  ```
205
205
 
206
206
  When switching to this layer, lattices launches (or focuses) the
207
- "talkie" group session and tiles it to the top-left quarter, alongside
207
+ "vox" group session and tiles it to the top-left quarter, alongside
208
208
  the design-system project on the right.
209
209
 
210
210
  ### Layer fields
@@ -340,7 +340,7 @@ header and search field in the menu bar panel:
340
340
  ```json
341
341
  {
342
342
  "projects": [
343
- { "path": "/Users/you/dev/talkie" }
343
+ { "path": "/Users/you/dev/vox" }
344
344
  ]
345
345
  }
346
346
  ```
@@ -374,7 +374,7 @@ No `tile` — just focuses the window wherever it is.
374
374
  ```json
375
375
  {
376
376
  "projects": [
377
- { "group": "talkie", "tile": "left" },
377
+ { "group": "vox", "tile": "left" },
378
378
  { "path": "/Users/you/dev/api", "tile": "right" }
379
379
  ]
380
380
  }
package/docs/ocr.md CHANGED
@@ -59,17 +59,26 @@ contextual snippets.
59
59
  ### From the CLI
60
60
 
61
61
  ```bash
62
+ # Search windows by title, app, session, and OCR content
63
+ lattices search "error"
64
+
65
+ # Deep search — also inspects terminal tabs and processes
66
+ lattices search "myproject" --deep
67
+
68
+ # Search + focus + tile the top result
69
+ lattices place "myproject" left
70
+
62
71
  # View current OCR snapshot
63
- lattices ocr
72
+ lattices scan
64
73
 
65
- # Search OCR history
66
- lattices ocr search "error OR failed"
74
+ # Search OCR history directly (FTS5 syntax)
75
+ lattices scan search "error OR failed"
67
76
 
68
77
  # Trigger an immediate deep scan
69
- lattices ocr scan
78
+ lattices scan deep
70
79
 
71
80
  # View OCR history for a specific window ID
72
- lattices ocr history 12345
81
+ lattices scan history 12345
73
82
  ```
74
83
 
75
84
  ### From the agent API
package/docs/overview.md CHANGED
@@ -65,7 +65,10 @@ Agents get the same control programmatically:
65
65
  ```js
66
66
  import { daemonCall } from '@lattices/cli'
67
67
  await daemonCall('session.launch', { path: '/Users/you/dev/frontend' })
68
- await daemonCall('window.tile', { session: 'frontend-a1b2c3', position: 'left' })
68
+ await daemonCall('window.place', {
69
+ session: 'frontend-a1b2c3',
70
+ placement: { kind: 'tile', value: 'left' }
71
+ })
69
72
  ```
70
73
 
71
74
  ## Who it's for
@@ -91,4 +94,5 @@ await daemonCall('window.tile', { session: 'frontend-a1b2c3', position: 'left' }
91
94
  - [Configuration](/docs/config): `.lattices.json` format and CLI commands
92
95
  - [Screen scanning](/docs/ocr): AX text extraction, Vision OCR, and full-text search
93
96
  - [Concepts](/docs/concepts): architecture, glossary, and how it all works
97
+ - [Agent Guide](/docs/agents): canonical action contracts for voice, CLI, and daemon clients
94
98
  - [Agent API](/docs/api): RPC method reference for agents and scripts