@lattices/cli 0.4.2 → 0.4.6

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 (146) hide show
  1. package/README.md +3 -0
  2. package/app/Info.plist +2 -2
  3. package/app/Lattices.app/Contents/Info.plist +2 -2
  4. package/app/Lattices.app/Contents/MacOS/Lattices +0 -0
  5. package/app/Package.swift +6 -0
  6. package/app/Sources/AppShell/App.swift +20 -0
  7. package/app/Sources/{AppDelegate.swift → AppShell/AppDelegate.swift} +94 -34
  8. package/app/Sources/{AppShellView.swift → AppShell/AppShellView.swift} +12 -1
  9. package/app/Sources/AppShell/AppUpdater.swift +92 -0
  10. package/app/Sources/AppShell/CliActionLauncher.swift +50 -0
  11. package/app/Sources/{HomeDashboardView.swift → AppShell/HomeDashboardView.swift} +18 -10
  12. package/app/Sources/AppShell/LatticesRuntime.swift +61 -0
  13. package/app/Sources/{MainView.swift → AppShell/MainView.swift} +351 -191
  14. package/app/Sources/{OnboardingView.swift → AppShell/OnboardingView.swift} +30 -16
  15. package/app/Sources/{Preferences.swift → AppShell/Preferences.swift} +78 -0
  16. package/app/Sources/{SettingsView.swift → AppShell/SettingsView.swift} +869 -152
  17. package/app/Sources/{HotkeyStore.swift → Core/Actions/HotkeyStore.swift} +9 -5
  18. package/app/Sources/{IntentEngine.swift → Core/Actions/IntentEngine.swift} +51 -27
  19. package/app/Sources/Core/Actions/IntentSchema.swift +94 -0
  20. package/app/Sources/{Intents → Core/Actions/Intents}/LatticeIntent.swift +0 -25
  21. package/app/Sources/{PaletteCommand.swift → Core/Actions/PaletteCommand.swift} +26 -6
  22. package/app/Sources/{VoiceIntentResolver.swift → Core/Actions/VoiceIntentResolver.swift} +46 -4
  23. package/app/Sources/Core/Companion/CompanionActivityLog.swift +70 -0
  24. package/app/Sources/Core/Companion/CompanionKeyboardController.swift +141 -0
  25. package/app/Sources/Core/Companion/LatticesCompanionBridgeServer.swift +438 -0
  26. package/app/Sources/Core/Companion/LatticesCompanionCockpit.swift +555 -0
  27. package/app/Sources/Core/Companion/LatticesCompanionSecurityCoordinator.swift +594 -0
  28. package/app/Sources/Core/Companion/LatticesCompanionTrackpadController.swift +204 -0
  29. package/app/Sources/Core/Companion/LatticesDeckHost.swift +1463 -0
  30. package/app/Sources/{LatticesApi.swift → Core/Daemon/LatticesApi.swift} +125 -4
  31. package/app/Sources/{AppTypeClassifier.swift → Core/Desktop/AppTypeClassifier.swift} +36 -0
  32. package/app/Sources/{DesktopModel.swift → Core/Desktop/DesktopModel.swift} +6 -8
  33. package/app/Sources/Core/Desktop/MouseFinder.swift +527 -0
  34. package/app/Sources/Core/Desktop/SessionWindowLocator.swift +139 -0
  35. package/app/Sources/Core/Desktop/WindowDragSnapController.swift +628 -0
  36. package/app/Sources/Core/Desktop/WindowPreviewCard.swift +100 -0
  37. package/app/Sources/Core/Desktop/WindowPreviewStore.swift +113 -0
  38. package/app/Sources/Core/Desktop/WindowSelectionStore.swift +76 -0
  39. package/app/Sources/{WindowTiler.swift → Core/Desktop/WindowTiler.swift} +351 -172
  40. package/app/Sources/Core/Input/MouseGestureConfig.swift +364 -0
  41. package/app/Sources/Core/Input/MouseGestureController.swift +1203 -0
  42. package/app/Sources/Core/Input/MouseInputDeviceStore.swift +98 -0
  43. package/app/Sources/Core/Input/MouseInputEventViewer.swift +272 -0
  44. package/app/Sources/Core/Input/MouseShortcutStore.swift +107 -0
  45. package/app/Sources/{CommandModeState.swift → Core/Overlays/CommandMode/CommandModeState.swift} +127 -24
  46. package/app/Sources/{CommandModeView.swift → Core/Overlays/CommandMode/CommandModeView.swift} +492 -79
  47. package/app/Sources/Core/Overlays/CommandPalette/CommandPaletteWindow.swift +67 -0
  48. package/app/Sources/{CheatSheetHUD.swift → Core/Overlays/HUD/CheatSheetHUD.swift} +1 -0
  49. package/app/Sources/{HUDRightBar.swift → Core/Overlays/HUD/HUDRightBar.swift} +23 -201
  50. package/app/Sources/{LauncherHUD.swift → Core/Overlays/HUD/LauncherHUD.swift} +12 -26
  51. package/app/Sources/{OmniSearchView.swift → Core/Overlays/OmniSearch/OmniSearchView.swift} +136 -2
  52. package/app/Sources/{OmniSearchWindow.swift → Core/Overlays/OmniSearch/OmniSearchWindow.swift} +21 -32
  53. package/app/Sources/Core/Overlays/OverlayPanelShell.swift +241 -0
  54. package/app/Sources/{ScreenMapState.swift → Core/Overlays/ScreenMap/ScreenMapState.swift} +116 -32
  55. package/app/Sources/{ScreenMapView.swift → Core/Overlays/ScreenMap/ScreenMapView.swift} +510 -524
  56. package/app/Sources/{ScreenMapWindowController.swift → Core/Overlays/ScreenMap/ScreenMapWindowController.swift} +12 -4
  57. package/app/Sources/{VoiceCommandWindow.swift → Core/Overlays/Voice/VoiceCommandWindow.swift} +46 -53
  58. package/app/Sources/Core/Pi/PiAuthNextStepCard.swift +148 -0
  59. package/app/Sources/Core/Pi/PiAuthPromptCard.swift +90 -0
  60. package/app/Sources/{PiChatDock.swift → Core/Pi/PiChatDock.swift} +137 -74
  61. package/app/Sources/{PiChatSession.swift → Core/Pi/PiChatSession.swift} +608 -108
  62. package/app/Sources/Core/Pi/PiInstallCallout.swift +86 -0
  63. package/app/Sources/Core/Pi/PiProviderSetupCallout.swift +99 -0
  64. package/app/Sources/{PiWorkspaceView.swift → Core/Pi/PiWorkspaceView.swift} +174 -77
  65. package/app/Sources/{PermissionChecker.swift → Core/System/PermissionChecker.swift} +76 -2
  66. package/app/Sources/Core/System/SystemTelemetryMonitor.swift +273 -0
  67. package/app/Sources/{HandsOffSession.swift → Core/Voice/HandsOffSession.swift} +15 -4
  68. package/app/Sources/{WorkspaceManager.swift → Core/Workspace/WorkspaceManager.swift} +288 -0
  69. package/bin/assistant-intelligence.ts +874 -0
  70. package/bin/handsoff-infer.ts +16 -209
  71. package/bin/handsoff-worker.ts +45 -258
  72. package/bin/lattices-app.ts +62 -0
  73. package/bin/lattices-dev +4 -0
  74. package/bin/lattices.ts +125 -14
  75. package/docs/agents.md +14 -0
  76. package/docs/api.md +55 -0
  77. package/docs/app.md +3 -0
  78. package/docs/companion-deck.md +180 -0
  79. package/docs/component-extraction-roadmap.md +392 -0
  80. package/docs/config.md +25 -0
  81. package/docs/tiling-reference.md +55 -0
  82. package/docs/voice-error-model.md +73 -0
  83. package/package.json +4 -1
  84. package/app/Sources/App.swift +0 -10
  85. package/app/Sources/CommandPaletteWindow.swift +0 -134
  86. package/app/Sources/MouseFinder.swift +0 -222
  87. /package/app/Sources/{KeyRecorderView.swift → AppShell/KeyRecorderView.swift} +0 -0
  88. /package/app/Sources/{MainWindow.swift → AppShell/MainWindow.swift} +0 -0
  89. /package/app/Sources/{SettingsWindow.swift → AppShell/SettingsWindow.swift} +0 -0
  90. /package/app/Sources/{HotkeyManager.swift → Core/Actions/HotkeyManager.swift} +0 -0
  91. /package/app/Sources/{Intents → Core/Actions/Intents}/CreateLayerIntent.swift +0 -0
  92. /package/app/Sources/{Intents → Core/Actions/Intents}/DistributeIntent.swift +0 -0
  93. /package/app/Sources/{Intents → Core/Actions/Intents}/FocusIntent.swift +0 -0
  94. /package/app/Sources/{Intents → Core/Actions/Intents}/HelpIntent.swift +0 -0
  95. /package/app/Sources/{Intents → Core/Actions/Intents}/KillIntent.swift +0 -0
  96. /package/app/Sources/{Intents → Core/Actions/Intents}/LaunchIntent.swift +0 -0
  97. /package/app/Sources/{Intents → Core/Actions/Intents}/ListSessionsIntent.swift +0 -0
  98. /package/app/Sources/{Intents → Core/Actions/Intents}/ListWindowsIntent.swift +0 -0
  99. /package/app/Sources/{Intents → Core/Actions/Intents}/ScanIntent.swift +0 -0
  100. /package/app/Sources/{Intents → Core/Actions/Intents}/SearchIntent.swift +0 -0
  101. /package/app/Sources/{Intents → Core/Actions/Intents}/SwitchLayerIntent.swift +0 -0
  102. /package/app/Sources/{Intents → Core/Actions/Intents}/TileIntent.swift +0 -0
  103. /package/app/Sources/{DaemonProtocol.swift → Core/Daemon/DaemonProtocol.swift} +0 -0
  104. /package/app/Sources/{DaemonServer.swift → Core/Daemon/DaemonServer.swift} +0 -0
  105. /package/app/Sources/{AccessibilityTextExtractor.swift → Core/Desktop/AccessibilityTextExtractor.swift} +0 -0
  106. /package/app/Sources/{DesktopModelTypes.swift → Core/Desktop/DesktopModelTypes.swift} +0 -0
  107. /package/app/Sources/{InventoryManager.swift → Core/Desktop/InventoryManager.swift} +0 -0
  108. /package/app/Sources/{InventoryPath.swift → Core/Desktop/InventoryPath.swift} +0 -0
  109. /package/app/Sources/{OcrModel.swift → Core/Desktop/OcrModel.swift} +0 -0
  110. /package/app/Sources/{OcrStore.swift → Core/Desktop/OcrStore.swift} +0 -0
  111. /package/app/Sources/{PlacementSpec.swift → Core/Desktop/PlacementSpec.swift} +0 -0
  112. /package/app/Sources/{TilePickerView.swift → Core/Desktop/TilePickerView.swift} +0 -0
  113. /package/app/Sources/{AppWindowShell.swift → Core/Overlays/AppWindowShell.swift} +0 -0
  114. /package/app/Sources/{CommandModeWindow.swift → Core/Overlays/CommandMode/CommandModeWindow.swift} +0 -0
  115. /package/app/Sources/{CommandPaletteView.swift → Core/Overlays/CommandPalette/CommandPaletteView.swift} +0 -0
  116. /package/app/Sources/{HUDBottomBar.swift → Core/Overlays/HUD/HUDBottomBar.swift} +0 -0
  117. /package/app/Sources/{HUDController.swift → Core/Overlays/HUD/HUDController.swift} +0 -0
  118. /package/app/Sources/{HUDLeftBar.swift → Core/Overlays/HUD/HUDLeftBar.swift} +0 -0
  119. /package/app/Sources/{HUDMinimap.swift → Core/Overlays/HUD/HUDMinimap.swift} +0 -0
  120. /package/app/Sources/{HUDState.swift → Core/Overlays/HUD/HUDState.swift} +0 -0
  121. /package/app/Sources/{HUDTopBar.swift → Core/Overlays/HUD/HUDTopBar.swift} +0 -0
  122. /package/app/Sources/{LayerBezel.swift → Core/Overlays/HUD/LayerBezel.swift} +0 -0
  123. /package/app/Sources/{OmniSearchState.swift → Core/Overlays/OmniSearch/OmniSearchState.swift} +0 -0
  124. /package/app/Sources/{DiagnosticLog.swift → Core/System/DiagnosticLog.swift} +0 -0
  125. /package/app/Sources/{EventBus.swift → Core/System/EventBus.swift} +0 -0
  126. /package/app/Sources/{ProcessModel.swift → Core/System/ProcessModel.swift} +0 -0
  127. /package/app/Sources/{ProcessQuery.swift → Core/System/ProcessQuery.swift} +0 -0
  128. /package/app/Sources/{AdvisorLearningStore.swift → Core/Voice/AdvisorLearningStore.swift} +0 -0
  129. /package/app/Sources/{AgentSession.swift → Core/Voice/AgentSession.swift} +0 -0
  130. /package/app/Sources/{AudioProvider.swift → Core/Voice/AudioProvider.swift} +0 -0
  131. /package/app/Sources/{VoiceChatView.swift → Core/Voice/VoiceChatView.swift} +0 -0
  132. /package/app/Sources/{VoxClient.swift → Core/Voice/VoxClient.swift} +0 -0
  133. /package/app/Sources/{Project.swift → Core/Workspace/Project.swift} +0 -0
  134. /package/app/Sources/{ProjectScanner.swift → Core/Workspace/ProjectScanner.swift} +0 -0
  135. /package/app/Sources/{SessionLayerStore.swift → Core/Workspace/SessionLayerStore.swift} +0 -0
  136. /package/app/Sources/{SessionManager.swift → Core/Workspace/SessionManager.swift} +0 -0
  137. /package/app/Sources/{Terminal.swift → Core/Workspace/Terminal/Terminal.swift} +0 -0
  138. /package/app/Sources/{TerminalQuery.swift → Core/Workspace/Terminal/TerminalQuery.swift} +0 -0
  139. /package/app/Sources/{TerminalSynthesizer.swift → Core/Workspace/Terminal/TerminalSynthesizer.swift} +0 -0
  140. /package/app/Sources/{TmuxModel.swift → Core/Workspace/Tmux/TmuxModel.swift} +0 -0
  141. /package/app/Sources/{TmuxQuery.swift → Core/Workspace/Tmux/TmuxQuery.swift} +0 -0
  142. /package/app/Sources/{ActionRow.swift → UI/ActionRow.swift} +0 -0
  143. /package/app/Sources/{OrphanRow.swift → UI/OrphanRow.swift} +0 -0
  144. /package/app/Sources/{ProjectRow.swift → UI/ProjectRow.swift} +0 -0
  145. /package/app/Sources/{TabGroupRow.swift → UI/TabGroupRow.swift} +0 -0
  146. /package/app/Sources/{Theme.swift → UI/Theme.swift} +0 -0
@@ -0,0 +1,392 @@
1
+ # Component Extraction Roadmap
2
+
3
+ This note turns a codebase review into an incremental component plan for `lattices`.
4
+
5
+ The goal is not to rewrite the app around a new architecture in one move. The goal is to extract a few reusable primitives that make future features cheaper:
6
+
7
+ - a cmdcmd-style visual window or session switcher
8
+ - one shared action model across hotkeys, palette, voice, daemon, and companion
9
+ - less duplicated window lookup, space lookup, and preview logic
10
+
11
+ ## Why this exists
12
+
13
+ Three pressure points showed up repeatedly:
14
+
15
+ 1. `WindowTiler.swift` acts like several libraries at once.
16
+ 2. action definitions exist in multiple parallel forms.
17
+ 3. overlay and panel shells are repeatedly rebuilt per surface.
18
+
19
+ The result is that new UX surfaces often have to re-solve the same problems:
20
+
21
+ - how to find a target window
22
+ - how to map a user intent to a canonical action
23
+ - how to show a floating interactive surface
24
+ - how to capture and render previews
25
+
26
+ ## Current duplication seams
27
+
28
+ ### 1. Window and session lookup
29
+
30
+ The same session-tagged window matching idea appears in multiple places:
31
+
32
+ - `DesktopModel.windowForSession(...)`
33
+ - tag parsing during desktop polling
34
+ - CG window lookup paths in `WindowTiler`
35
+ - AX window lookup paths in `WindowTiler`
36
+
37
+ This is the strongest candidate for a single reusable locator.
38
+
39
+ ### 2. Space topology and window membership
40
+
41
+ Display-space maps, current-space discovery, and window-space membership are rebuilt in several flows instead of being queried from one read model.
42
+
43
+ This makes space-aware features harder than they need to be:
44
+
45
+ - move window to space
46
+ - present window on the current space
47
+ - show where a session already lives
48
+ - build a visual desktop map
49
+
50
+ ### 3. Window presentation and motion
51
+
52
+ `tile`, `present`, `batchMoveAndRaiseWindows`, and related paths all contain their own versions of:
53
+
54
+ - resolve target
55
+ - move or resize
56
+ - raise
57
+ - activate app
58
+ - mark interaction
59
+
60
+ That sequencing should be owned by one operation layer.
61
+
62
+ ### 4. Preview capture and preview rendering
63
+
64
+ The codebase already has useful preview pieces, but they live in separate pockets:
65
+
66
+ - `WindowPreviewStore` in `HUDRightBar.swift`
67
+ - preview placeholder and preview card variants in HUD
68
+ - separate preview capture in `ScreenMapState.swift`
69
+
70
+ This is a strong signal that preview should become its own reusable subsystem.
71
+
72
+ ### 5. Action definitions
73
+
74
+ Action and intent metadata currently live in several places:
75
+
76
+ - `HotkeyStore.swift`
77
+ - `PaletteCommand.swift`
78
+ - `IntentEngine.swift`
79
+ - `Intents/LatticeIntent.swift`
80
+ - `LatticesApi.swift`
81
+
82
+ The sharpest duplication is that `IntentEngine.swift` and `Intents/LatticeIntent.swift` each define their own intent schema.
83
+
84
+ ### 6. Overlay and panel shells
85
+
86
+ There is already a useful shared primitive for normal app windows in `AppWindowShell.swift`, but overlay surfaces still rebuild similar shell code:
87
+
88
+ - `CommandPaletteWindow.swift`
89
+ - `OmniSearchWindow.swift`
90
+ - `VoiceCommandWindow.swift`
91
+ - `LauncherHUD.swift`
92
+
93
+ The repeated shell concerns are:
94
+
95
+ - `NSPanel` setup
96
+ - blur and rounded-mask container setup
97
+ - screen placement
98
+ - activation and dismissal behavior
99
+ - event monitor lifecycle
100
+
101
+ ## Proposed reusable components
102
+
103
+ This is the target component map.
104
+
105
+ ### Desktop substrate
106
+
107
+ #### `SessionWindowLocator`
108
+
109
+ Responsibility:
110
+
111
+ - resolve a lattices session, title tag, app target, or explicit window id into a canonical window target
112
+ - try fast cache lookup first
113
+ - fall back through CG and AX in one place
114
+
115
+ Why:
116
+
117
+ - removes repeated session-tag matching logic
118
+ - gives palette, daemon, voice, HUD, and future switchers the same targeting rules
119
+
120
+ #### `SpaceTopologySnapshot`
121
+
122
+ Responsibility:
123
+
124
+ - expose a single read model for displays, spaces, current space, and window-to-space membership
125
+
126
+ Why:
127
+
128
+ - prevents repeated recomputation of display-space facts
129
+ - makes space-aware UIs easier to build
130
+
131
+ #### `WindowPresenter`
132
+
133
+ Responsibility:
134
+
135
+ - own the canonical move, resize, raise, activate, and interaction-marking flow
136
+ - support both single-window and batched operations
137
+
138
+ Why:
139
+
140
+ - centralizes the side-effect sequence
141
+ - makes future planners and higher-level actions less fragile
142
+
143
+ #### `WindowPreviewProvider`
144
+
145
+ Responsibility:
146
+
147
+ - capture, cache, and serve still previews or live previews for windows
148
+ - separate capture policy from UI rendering
149
+
150
+ Why:
151
+
152
+ - avoids HUD and Screen Map each inventing preview behavior
153
+ - directly supports a visual selector or session fan-out
154
+
155
+ ### Action substrate
156
+
157
+ #### `ActionRegistry`
158
+
159
+ Responsibility:
160
+
161
+ - define canonical verbs once
162
+ - own parameter metadata, user-facing labels, phrase templates, and execution hooks
163
+
164
+ Minimal shape:
165
+
166
+ ```swift
167
+ enum ActionID: String {
168
+ case openPalette
169
+ case openSearch
170
+ case focusWindow
171
+ case placeWindow
172
+ case launchProject
173
+ case switchLayer
174
+ case killSession
175
+ case refreshProjects
176
+ }
177
+
178
+ struct ActionParam {
179
+ let name: String
180
+ let type: ActionParamType
181
+ let required: Bool
182
+ let values: [String]?
183
+ }
184
+
185
+ struct ActionDef {
186
+ let id: ActionID
187
+ let title: String
188
+ let params: [ActionParam]
189
+ let hotkey: HotkeyMeta?
190
+ let palette: PaletteMeta?
191
+ let phrases: [String]
192
+ let run: (ActionContext) throws -> JSON
193
+ }
194
+ ```
195
+
196
+ Why:
197
+
198
+ - one action identity across hotkeys, palette, voice, daemon, and companion
199
+ - palette rows become runtime bindings of a verb to a target, not bespoke actions
200
+
201
+ #### `ActionContext`
202
+
203
+ Responsibility:
204
+
205
+ - carry structured arguments plus source information like `hotkey`, `palette`, `voice-local`, `daemon`, or `companion`
206
+
207
+ Why:
208
+
209
+ - makes execution and logging more consistent
210
+
211
+ ### Overlay substrate
212
+
213
+ #### `OverlayPanelShell`
214
+
215
+ Responsibility:
216
+
217
+ - build a reusable floating `NSPanel` shell from configuration
218
+ - own blur or plain background, corner radius, window level, collection behavior, and hosting setup
219
+
220
+ Why:
221
+
222
+ - extracts the shared Spotlight-style panel construction path
223
+
224
+ #### `OverlayPlacement`
225
+
226
+ Responsibility:
227
+
228
+ - centralize placement policies like centered, spotlight offset, top-center, or mouse-screen placement
229
+
230
+ Why:
231
+
232
+ - removes repeated `visibleFrame` math
233
+
234
+ #### `OverlayLifecycleController`
235
+
236
+ Responsibility:
237
+
238
+ - own local event monitors, Escape dismissal, deactivate behavior, and cleanup
239
+
240
+ Why:
241
+
242
+ - reduces panel-specific lifecycle glue
243
+
244
+ ### UI primitives
245
+
246
+ #### `WindowPreviewCard`
247
+
248
+ Responsibility:
249
+
250
+ - render a window preview, loading state, and unavailable state consistently
251
+
252
+ Why:
253
+
254
+ - low-risk first UI extraction
255
+ - immediately reduces duplicated HUD preview rendering
256
+
257
+ ## Recommended extraction order
258
+
259
+ The sequence below favors leverage without taking unnecessary risk.
260
+
261
+ ### Slice 1: `WindowPreviewCard`
262
+
263
+ Extract the repeated preview body and placeholder logic from HUD into a shared SwiftUI component.
264
+
265
+ Why first:
266
+
267
+ - UI-only
268
+ - already duplicated
269
+ - does not disturb CGS, AX, or window mutation paths
270
+
271
+ ### Slice 2: `OverlayPanelShell`
272
+
273
+ Extract the shared panel-construction path from `CommandPaletteWindow` and `OmniSearchWindow`.
274
+
275
+ Why second:
276
+
277
+ - those two surfaces are the cleanest near-duplicates
278
+ - builds a reusable shell for a future visual selector
279
+
280
+ ### Slice 3: unify intent schema
281
+
282
+ Remove the parallel intent-definition structures by expanding or reusing the types in `Intents/LatticeIntent.swift` and pointing them at existing execution handlers.
283
+
284
+ Why third:
285
+
286
+ - high leverage
287
+ - removes one entire duplicate definition system
288
+ - proves the registry shape before migrating hotkeys or palette
289
+
290
+ ### Slice 4: `SessionWindowLocator`
291
+
292
+ Centralize session-tagged lookup across DesktopModel and WindowTiler.
293
+
294
+ Why fourth:
295
+
296
+ - strongest desktop duplication seam
297
+ - unlocks cleaner action execution and better future switcher targeting
298
+
299
+ ### Slice 5: `SpaceTopologySnapshot`
300
+
301
+ Create one query layer for display and space topology.
302
+
303
+ Why fifth:
304
+
305
+ - stabilizes space-aware features before touching more motion logic
306
+
307
+ ### Slice 6: `WindowPresenter`
308
+
309
+ Unify move, resize, raise, activate, and interaction-marking flows.
310
+
311
+ Why sixth:
312
+
313
+ - this is higher risk because it sits directly on side effects
314
+ - it is safer after lookup and topology are centralized
315
+
316
+ ### Slice 7: `WindowPreviewProvider`
317
+
318
+ Lift preview capture and caching out of HUD-specific code and reconcile it with Screen Map preview capture.
319
+
320
+ Why seventh:
321
+
322
+ - more useful after overlay shell and preview card exist
323
+ - becomes the substrate for a visual window or session chooser
324
+
325
+ ## Features this should unlock
326
+
327
+ Once the components above exist, the app can add new surfaces with much less bespoke code.
328
+
329
+ ### cmdcmd-style visual switcher
330
+
331
+ Use:
332
+
333
+ - `OverlayPanelShell`
334
+ - `OverlayPlacement`
335
+ - `SessionWindowLocator`
336
+ - `WindowPreviewProvider`
337
+ - `WindowPresenter`
338
+
339
+ Possible behavior:
340
+
341
+ - fan out lattices sessions or all windows
342
+ - show live or cached previews
343
+ - focus, tile, move to space, or close from one surface
344
+
345
+ ### Shared action surfaces
346
+
347
+ Use:
348
+
349
+ - `ActionRegistry`
350
+ - `ActionContext`
351
+
352
+ Possible behavior:
353
+
354
+ - define `placeWindow` once
355
+ - trigger it from voice, hotkey, palette, daemon, or companion
356
+ - keep labels and phrases aligned across surfaces
357
+
358
+ ### Stronger planning and preview
359
+
360
+ Use:
361
+
362
+ - `ActionRegistry`
363
+ - `WindowPresenter`
364
+ - `SpaceTopologySnapshot`
365
+
366
+ Possible behavior:
367
+
368
+ - preview a multi-window action before applying it
369
+ - build transactional-feeling UI around batched movement
370
+
371
+ ## Things not to do yet
372
+
373
+ - do not rewrite `WindowTiler.swift` in one shot
374
+ - do not migrate every overlay surface onto one abstraction immediately
375
+ - do not force the palette to become purely registry-generated before the action model is proven
376
+
377
+ The safer path is:
378
+
379
+ 1. extract small reusable pieces
380
+ 2. move one production surface onto them
381
+ 3. verify behavior
382
+ 4. repeat
383
+
384
+ ## Summary
385
+
386
+ The strongest architectural opportunity here is not one big framework. It is three small substrates:
387
+
388
+ - desktop targeting and motion
389
+ - action definition and routing
390
+ - overlay panel construction
391
+
392
+ If those become reusable, `lattices` gets a much cleaner path to new features without making every new surface solve the same desktop and execution problems again.
package/docs/config.md CHANGED
@@ -131,10 +131,13 @@ Run `lattices init` in your project directory to generate a starter
131
131
  | `lattices sync` | Reconcile session to match declared config |
132
132
  | `lattices restart [pane]` | Restart a pane's process (by name or index) |
133
133
  | `lattices tile <position>` | Tile the frontmost window to a screen position |
134
+ | `lattices tile family [app] [region]` | Smart-grid the frontmost app family, or a named app |
135
+ | `lattices distribute [app] [region]` | Smart-grid visible windows or just one app |
134
136
  | `lattices group [id]` | List tab groups or launch/attach a group |
135
137
  | `lattices groups` | List all tab groups with status |
136
138
  | `lattices tab <group> [tab]` | Switch tab within a group (by label or index) |
137
139
  | `lattices app` | Launch the menu bar companion app |
140
+ | `lattices app update` | Download the latest menu bar app and relaunch |
138
141
  | `lattices app build` | Rebuild the menu bar app from source |
139
142
  | `lattices app restart` | Rebuild and relaunch the menu bar app |
140
143
  | `lattices layer [name\|index]` | Switch to a workspace layer by name or index |
@@ -149,6 +152,7 @@ Run `lattices init` in your project directory to generate a starter
149
152
  | `lattices scan search <query>` | Search indexed screen text |
150
153
  | `lattices diag [limit]` | Show recent diagnostic entries |
151
154
  | `lattices app` | Launch the menu bar companion app |
155
+ | `lattices app update` | Download the latest menu bar app and relaunch |
152
156
  | `lattices app build` | Rebuild the menu bar app from source |
153
157
  | `lattices app restart` | Rebuild and relaunch the menu bar app |
154
158
  | `lattices app quit` | Stop the menu bar app |
@@ -247,3 +251,24 @@ Aliases: `left-half`/`left`, `right-half`/`right`, `top-half`/`top`,
247
251
 
248
252
  Tiling respects the menu bar and dock. It uses the visible desktop
249
253
  area, not the full screen.
254
+
255
+ ### Smart app tiling
256
+
257
+ Use `lattices tile family` when you want lattices to arrange a whole
258
+ window family instead of just moving the frontmost window.
259
+
260
+ Examples:
261
+
262
+ ```bash
263
+ lattices tile family
264
+ lattices tile family right
265
+ lattices tile family iTerm2
266
+ lattices tile family "Google Chrome" left
267
+ ```
268
+
269
+ - With no app name, `family` means the **frontmost app**. If iTerm is
270
+ frontmost, lattices grids your visible iTerm windows.
271
+ - If you pass a region (`left`, `right`, `top`, `bottom`, etc.), the
272
+ smart grid is constrained to that part of the screen.
273
+ - `lattices distribute` uses the same smart grid engine, but defaults to
274
+ **all visible windows** instead of the current app family.
@@ -81,6 +81,56 @@ typed placement model internally:
81
81
 
82
82
  That is what keeps CLI, daemon, voice, and hands-off execution aligned.
83
83
 
84
+ ## Drag Snap Zones
85
+
86
+ The menu bar app can also use placement specs as drag-to-snap targets.
87
+ When you drag a window, Lattices shows faint landing zones plus a live
88
+ preview of the resulting frame. Releasing over a zone tiles the dragged
89
+ window to that placement. Hold `Command` while dragging to reveal snap
90
+ mode, and release `Command` to drop back to a normal free drag without
91
+ ending the gesture.
92
+
93
+ The recommended agent-owned config lives in `~/.lattices/snap-zones.json`:
94
+
95
+ ```json
96
+ {
97
+ "enabled": true,
98
+ "modifier": "command",
99
+ "zoneOpacity": 0.08,
100
+ "highlightOpacity": 0.18,
101
+ "previewOpacity": 0.14,
102
+ "rules": [
103
+ {
104
+ "id": "left-edge",
105
+ "label": "Left",
106
+ "placement": "left",
107
+ "trigger": { "x": 0.0, "y": 0.18, "w": 0.12, "h": 0.64 },
108
+ "priority": 10
109
+ },
110
+ {
111
+ "id": "notes-rail",
112
+ "label": "Notes",
113
+ "placement": { "x": 0.68, "y": 0.0, "w": 0.32, "h": 1.0 },
114
+ "trigger": { "x": 0.88, "y": 0.18, "w": 0.12, "h": 0.64 },
115
+ "priority": 30
116
+ }
117
+ ]
118
+ }
119
+ ```
120
+
121
+ Notes:
122
+
123
+ - `rules` is the preferred list key. `zones` is still accepted for backward compatibility.
124
+ - `modifier` accepts `command`, `option`, `control`, or `shift`.
125
+ - `placement` can be a named placement/preset string or raw fractions.
126
+ - `trigger` uses normalized `(x, y, w, h)` fractions of the screen's
127
+ visible area, with `y = 0` at the top.
128
+ - `priority` breaks ties when trigger regions overlap.
129
+ - `trigger` can also be a named placement or preset string if you want
130
+ the trigger region itself to reuse an existing tile definition.
131
+ - The older `~/.lattices/grid.json` `snapZones` section still works, but
132
+ `~/.lattices/snap-zones.json` is the cleaner file for agents to edit.
133
+
84
134
  ## Execution Paths
85
135
 
86
136
  The old split-brain tiling logic has been collapsed toward a shared path.
@@ -146,6 +196,11 @@ These are composed from multiple `tile_window` actions:
146
196
  | Eight-up (4×2) | All eight `*-*-fourth` positions |
147
197
  | Distribute | Single `distribute` intent (auto-grid) |
148
198
 
199
+ CLI shortcuts compile into the same distributor:
200
+
201
+ - `lattices tile family` → smart-grid the frontmost app's visible windows
202
+ - `lattices distribute iTerm2 right` → smart-grid visible iTerm windows inside the right half
203
+
149
204
  ## HandsOff Smart Distribution
150
205
 
151
206
  When the LLM sends multiple `tile_window` actions targeting the **same position**, `HandsOffSession.distributeTileActions()` subdivides:
@@ -0,0 +1,73 @@
1
+ # Voice Error Model
2
+
3
+ ## Goal + anchors
4
+
5
+ Use one error vocabulary for Mac voice capture/execution and iPad relay/status. The canonical protocol says Lattices borrows Vox for capture and never owns the mic directly (`docs/voice-command-protocol.md:5-7`), but the shared runtime already has a cross-platform `DeckVoiceState` slot (`swift/Sources/DeckKit/DeckRuntimeSnapshot.swift:49-68`). Current Mac code exposes local strings (`VoxError`, `executionResult`) instead of structured errors (`app/Sources/VoxClient.swift:43-59`, `app/Sources/AudioProvider.swift:343-349`); iPad has only a generic `errorMessage` (`iOS/LatticesCompanion/Sources/DeckStore.swift:18`). Normalize at DeckKit, then let each surface render the same object.
6
+
7
+ ## Error structure
8
+
9
+ Prefer `DeckVoiceError` now; if later reused for trackpad/deck actions, lift the same shape to `LatsError`.
10
+
11
+ ```swift
12
+ public struct DeckVoiceError: Codable, Equatable, Identifiable, Sendable {
13
+ public var id: String // uuid or request id
14
+ public var code: DeckVoiceErrorCode
15
+ public var severity: DeckErrorSeverity
16
+ public var recoverable: Bool
17
+ public var retry: DeckRetryHint?
18
+ public var source: DeckErrorSource // mac, ipad, vox, daemon, intent, bridge
19
+ public var owner: String? // e.g. "Vox", "Lattices", "Bridge"
20
+ public var message: String // cockpit copy, already human-readable
21
+ public var remediation: DeckRemediationAction?
22
+ public var occurredAt: Date
23
+ public var detail: String? // diagnostic-only
24
+ }
25
+
26
+ public enum DeckVoiceErrorCode: String, Codable, Sendable { case vox_unreachable, daemon_unreachable, network, connection_lost, mic_denied, accessibility_denied, mic_busy, no_active_target, vox_not_running, vox_loading, intent_unresolved, action_failed, transcription_failed, empty_transcript, language_unsupported }
27
+ public enum DeckErrorSeverity: String, Codable, Sendable { case info, warning, error, blocked }
28
+ public enum DeckRetryHint: String, Codable, Sendable { case silent, immediate, afterLaunch, userAction }
29
+ public enum DeckErrorSource: String, Codable, Sendable { case mac, ipad, vox, daemon, intent, bridge }
30
+ public enum DeckRemediationAction: Codable, Equatable, Sendable {
31
+ case openVox, openSystemSettings(kind: String), retryVoice, openDiagnostics, chooseTarget
32
+ }
33
+ ```
34
+
35
+ Add `var error: DeckVoiceError?` and optionally `var lastError: DeckVoiceError?` to `DeckVoiceState`, preserving existing `phase`, transcript, and provider fields (`swift/Sources/DeckKit/DeckRuntimeSnapshot.swift:49-68`). Codes should stay stable string raw values for JSON logs and iPad bridge snapshots.
36
+
37
+ ## Categories
38
+
39
+ | Category | Codes | Recovery rule |
40
+ |---|---|---|
41
+ | Connection | `vox_unreachable`, `daemon_unreachable`, `network`, `connection_lost` | Recoverable unless active capture was lost. Silent reconnect when idle; visible banner during `listening`/`transcribing`. |
42
+ | Permission | `mic_denied`, `accessibility_denied` | Needs user action. `mic_denied` is Mac/Vox-owned; iPad only relays it. `accessibility_denied` blocks execution/navigation. |
43
+ | State | `mic_busy { owner }`, `no_active_target`, `vox_not_running`, `vox_loading` | Usually recoverable. `mic_busy` waits for owner; `vox_not_running` supports launch-on-demand. |
44
+ | Execution | `intent_unresolved`, `action_failed`, `transcription_failed` | Recoverable by retry or edited command; escalate to log if repeated. |
45
+ | Validation | `empty_transcript`, `language_unsupported` | Recoverable; no scary chrome. Treat as a missed command, not a crash. |
46
+
47
+ Copy examples: `Mic in use by Vox — finish memo first`, `No target window`, `Vox offline — starting`, `Connection lost — press again`, `Intent not found`.
48
+
49
+ ## Presentation patterns
50
+
51
+ **Mac VoiceCommandWindow.** Keep the three-column cockpit. The top mic bar already owns live state (`connecting...`, `processing...`; `app/Sources/VoiceCommandWindow.swift:692-719`); render the active error as a compact red/amber status chip there. The center column uses `commandSection` cards (`app/Sources/VoiceCommandWindow.swift:1287-1304`): show a single `blocked`/`needs action` card only when the user can do something. The footer already has key chips (`app/Sources/VoiceCommandWindow.swift:1308-1348`); replace the generic command list with contextual remediation: `⌥ Retry`, `Return Open Vox`, `⌘, Permissions`. Logs stay in the right rail, using existing level colors (`app/Sources/VoiceCommandWindow.swift:1112-1150`).
52
+
53
+ **Mac HUD.** `HUDTopBar.voiceStatus` already has dot, label, transcript, response (`app/Sources/HUDTopBar.swift:134-198`). Add severity tint: green idle/listening, amber connecting/recoverable, red blocked. For active voice errors, HUD shows a one-line banner in the top bar; no sheet.
54
+
55
+ **iPad Home.** Add `HomeVoiceOverlay` as the full voice modal for active relay: title row `VOICE`, phase, transcript, Mac owner, and one remediation button. The bottom bar already has dense status slots and `hold·space` (`iOS/LatticesCompanion/Sources/Home/HomeBottomBar.swift:58-68`, `iOS/LatticesCompanion/Sources/Home/HomeBottomBar.swift:129-148`); render idle/recoverable errors inline there (`voice · reconnecting`, `voice · Vox offline`). Use a deck overlay banner only when an issued iPad action failed. Use sheets only for permissions/pairing because they need human action. This follows the chrome rule: do not remove noisy UI; replace it with state that answers “what am I controlling, who is listening, what failed?” (`/Users/arach/.claude/projects/-Users-arach-dev-lattices/memory/feedback_chrome_design.md:11-13`).
56
+
57
+ ## Unhappy-path prescriptions
58
+
59
+ **Launch Vox on demand.** Spec flow is detect installed/not running, open Vox, show `Starting Vox...`, wait up to 10s, retry `startDictation`, then fail with manual-open copy (`docs/voice-command-protocol.md:73-89`). Current Mac waits 2s after `connect()` (`app/Sources/VoiceCommandWindow.swift:290-313`); design target is `vox_not_running` → `vox_loading` → retry → either clear error or `vox_unreachable` with `openVox`.
60
+
61
+ **Mic busy.** Preserve owner attribution from protocol (`docs/voice-command-protocol.md:127-135`). `mic_busy(owner: "Vox")` is warning, recoverable, retry hint `userAction`; message: `Mic in use by Vox — finish memo first`. If owner is unknown: `Mic busy — wait for current recording`.
62
+
63
+ **Connection recovery.** If idle, reconnect silently and write log only. If active, show red `Connection lost`; do not auto-retry captured audio because Vox cancels dropped sockets (`docs/voice-command-protocol.md:174-188`). iPad shows `Mac voice link lost` if bridge lost, not `network` unless the iPad transport failed.
64
+
65
+ **JSONL.** Add `~/.lattices/voice.jsonl` beside `lattices.log` (current log path is `~/.lattices/lattices.log`; `app/Sources/DiagnosticLog.swift:40-59`). Shape:
66
+
67
+ ```json
68
+ {"ts":"2026-04-27T14:03:11.120Z","platform":"mac","sessionId":"...","phase":"listening","event":"error","error":{"code":"mic_busy","severity":"warning","recoverable":true,"source":"vox","owner":"Vox","message":"Mic in use by Vox — finish memo first"},"transcript":null,"intent":null,"durationMs":820}
69
+ ```
70
+
71
+ ## Cross-platform conventions
72
+
73
+ Tone: terse cockpit, no apologies. Prefer noun-state-action: `Vox offline — starting`, `No target — pick window`, `Access denied — enable Accessibility`. Tint maps to existing palettes: Mac `Palette.detach` amber and `Palette.kill` red (`app/Sources/Theme.swift:19-23`); iPad `LatsPalette.amber/red` (`iOS/LatticesCompanion/Sources/LatsDeckScreen.swift:19-25`). Icons: `mic.fill` live, `mic.slash` denied, `waveform.badge.exclamationmark` transcription, `bolt.trianglebadge.exclamationmark` execution, `wifi.exclamationmark` connection, `scope` target. Ownership: Mac owns Vox, mic, Accessibility, intent execution, and JSONL. iPad owns relay/bridge/network presentation and never claims direct mic capture.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lattices/cli",
3
- "version": "0.4.2",
3
+ "version": "0.4.6",
4
4
  "description": "Agentic window manager for macOS — programmable workspace, smart layouts, managed tmux sessions, and a 35+-method agent API",
5
5
  "bin": {
6
6
  "lattices": "./bin/lattices.ts",
@@ -22,11 +22,14 @@
22
22
  "license": "MIT",
23
23
  "exports": {
24
24
  ".": "./bin/client.ts",
25
+ "./assistant-intelligence": "./bin/assistant-intelligence.ts",
25
26
  "./daemon-client": "./bin/daemon-client.ts",
26
27
  "./project-twin": "./bin/project-twin.ts"
27
28
  },
28
29
  "scripts": {
29
30
  "dev": "bun --cwd docs-site dev",
31
+ "test:e2e": "node --experimental-strip-types --test test/e2e-daemon.test.mjs",
32
+ "test:e2e:voice": "node --experimental-strip-types test/eval-voice.js",
30
33
  "typecheck": "tsc --noEmit",
31
34
  "build:app-bundle": "bash ./bin/lattices-dev build",
32
35
  "prepack": "bash ./bin/lattices-dev build"
@@ -1,10 +0,0 @@
1
- import SwiftUI
2
-
3
- @main
4
- struct LatticesApp: App {
5
- @NSApplicationDelegateAdaptor(AppDelegate.self) var delegate
6
-
7
- var body: some Scene {
8
- Settings { EmptyView() }
9
- }
10
- }