@lattices/cli 0.3.0 → 0.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +85 -9
- package/app/Info.plist +30 -0
- package/app/Lattices.app/Contents/Info.plist +8 -2
- package/app/Lattices.app/Contents/MacOS/Lattices +0 -0
- package/app/Lattices.app/Contents/Resources/AppIcon.icns +0 -0
- package/app/Lattices.app/Contents/Resources/tap.wav +0 -0
- package/app/Lattices.app/Contents/_CodeSignature/CodeResources +139 -0
- package/app/Lattices.entitlements +15 -0
- package/app/Package.swift +8 -1
- package/app/Resources/tap.wav +0 -0
- package/app/Sources/AdvisorLearningStore.swift +90 -0
- package/app/Sources/AgentSession.swift +377 -0
- package/app/Sources/AppDelegate.swift +45 -12
- package/app/Sources/AppShellView.swift +81 -8
- package/app/Sources/AudioProvider.swift +386 -0
- package/app/Sources/CheatSheetHUD.swift +261 -19
- package/app/Sources/DaemonProtocol.swift +13 -0
- package/app/Sources/DaemonServer.swift +8 -0
- package/app/Sources/DesktopModel.swift +189 -6
- package/app/Sources/DesktopModelTypes.swift +2 -0
- package/app/Sources/DiagnosticLog.swift +104 -2
- package/app/Sources/EventBus.swift +1 -0
- package/app/Sources/HUDBottomBar.swift +279 -0
- package/app/Sources/HUDController.swift +1158 -0
- package/app/Sources/HUDLeftBar.swift +849 -0
- package/app/Sources/HUDMinimap.swift +179 -0
- package/app/Sources/HUDRightBar.swift +774 -0
- package/app/Sources/HUDState.swift +367 -0
- package/app/Sources/HUDTopBar.swift +243 -0
- package/app/Sources/HandsOffSession.swift +802 -0
- package/app/Sources/HomeDashboardView.swift +125 -0
- package/app/Sources/HotkeyManager.swift +2 -0
- package/app/Sources/HotkeyStore.swift +49 -9
- package/app/Sources/IntentEngine.swift +962 -0
- package/app/Sources/Intents/CreateLayerIntent.swift +54 -0
- package/app/Sources/Intents/DistributeIntent.swift +56 -0
- package/app/Sources/Intents/FocusIntent.swift +69 -0
- package/app/Sources/Intents/HelpIntent.swift +41 -0
- package/app/Sources/Intents/KillIntent.swift +47 -0
- package/app/Sources/Intents/LatticeIntent.swift +78 -0
- package/app/Sources/Intents/LaunchIntent.swift +67 -0
- package/app/Sources/Intents/ListSessionsIntent.swift +32 -0
- package/app/Sources/Intents/ListWindowsIntent.swift +30 -0
- package/app/Sources/Intents/ScanIntent.swift +52 -0
- package/app/Sources/Intents/SearchIntent.swift +190 -0
- package/app/Sources/Intents/SwitchLayerIntent.swift +50 -0
- package/app/Sources/Intents/TileIntent.swift +61 -0
- package/app/Sources/LatticesApi.swift +1275 -30
- package/app/Sources/LauncherHUD.swift +348 -0
- package/app/Sources/MainView.swift +147 -44
- package/app/Sources/MouseFinder.swift +222 -0
- package/app/Sources/OcrModel.swift +34 -1
- package/app/Sources/OmniSearchState.swift +99 -102
- package/app/Sources/OnboardingView.swift +457 -0
- package/app/Sources/PermissionChecker.swift +2 -12
- package/app/Sources/PiChatDock.swift +454 -0
- package/app/Sources/PiChatSession.swift +815 -0
- package/app/Sources/PiWorkspaceView.swift +364 -0
- package/app/Sources/PlacementSpec.swift +195 -0
- package/app/Sources/Preferences.swift +59 -0
- package/app/Sources/ProjectScanner.swift +58 -45
- package/app/Sources/ScreenMapState.swift +701 -55
- package/app/Sources/ScreenMapView.swift +843 -103
- package/app/Sources/ScreenMapWindowController.swift +22 -0
- package/app/Sources/SessionLayerStore.swift +285 -0
- package/app/Sources/SessionManager.swift +4 -1
- package/app/Sources/SettingsView.swift +186 -3
- package/app/Sources/Theme.swift +9 -8
- package/app/Sources/TmuxModel.swift +7 -0
- package/app/Sources/TmuxQuery.swift +27 -3
- package/app/Sources/VoiceChatView.swift +192 -0
- package/app/Sources/VoiceCommandWindow.swift +1594 -0
- package/app/Sources/VoiceIntentResolver.swift +671 -0
- package/app/Sources/VoxClient.swift +454 -0
- package/app/Sources/WindowTiler.swift +348 -87
- package/app/Sources/WorkspaceManager.swift +127 -18
- package/app/Tests/StageDragTests.swift +333 -0
- package/app/Tests/StageJoinTests.swift +313 -0
- package/app/Tests/StageManagerTests.swift +280 -0
- package/app/Tests/StageTileTests.swift +353 -0
- package/assets/AppIcon.icns +0 -0
- package/bin/client.ts +16 -0
- package/bin/{daemon-client.js → daemon-client.ts} +49 -30
- package/bin/handsoff-infer.ts +280 -0
- package/bin/handsoff-worker.ts +740 -0
- package/bin/lattices-app.ts +338 -0
- package/bin/lattices-dev +208 -0
- package/bin/{lattices.js → lattices.ts} +777 -140
- package/bin/project-twin.ts +645 -0
- package/docs/agent-execution-plan.md +562 -0
- package/docs/agent-layer-guide.md +207 -0
- package/docs/agents.md +142 -0
- package/docs/api.md +153 -34
- package/docs/app.md +29 -1
- package/docs/config.md +5 -1
- package/docs/handsoff-test-scenarios.md +84 -0
- package/docs/layers.md +20 -20
- package/docs/ocr.md +14 -5
- package/docs/overview.md +5 -1
- package/docs/presentation-execution-review.md +491 -0
- package/docs/prompts/hands-off-system.md +374 -0
- package/docs/prompts/hands-off-turn.md +30 -0
- package/docs/prompts/voice-advisor.md +31 -0
- package/docs/prompts/voice-fallback.md +23 -0
- package/docs/tiling-reference.md +167 -0
- package/docs/twins.md +138 -0
- package/docs/voice-command-protocol.md +278 -0
- package/docs/voice.md +219 -0
- package/package.json +29 -11
- package/bin/client.js +0 -4
- package/bin/lattices-app.js +0 -221
package/README.md
CHANGED
|
@@ -4,16 +4,62 @@
|
|
|
4
4
|
|
|
5
5
|
# lattices
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
7
|
+
The agentic window manager for macOS.
|
|
8
|
+
|
|
9
|
+
Tile windows with hotkeys, manage persistent tmux sessions, index screen
|
|
10
|
+
text with OCR, and give AI agents a 35-method desktop API — all from a
|
|
11
|
+
native menu bar app and CLI.
|
|
12
|
+
|
|
13
|
+
**[lattices.dev](https://lattices.dev)** · [Docs](https://lattices.dev/docs/overview) · [Download](https://github.com/arach/lattices/releases/latest)
|
|
10
14
|
|
|
11
15
|
## Install
|
|
12
16
|
|
|
17
|
+
### Download the app
|
|
18
|
+
|
|
19
|
+
Grab the signed DMG from the [latest release](https://github.com/arach/lattices/releases/latest):
|
|
20
|
+
|
|
21
|
+
```sh
|
|
22
|
+
# Or direct download:
|
|
23
|
+
curl -LO https://github.com/arach/lattices/releases/latest/download/Lattices.dmg
|
|
24
|
+
open Lattices.dmg
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Drag **Lattices.app** into Applications. On first launch, a setup wizard
|
|
28
|
+
walks you through granting Accessibility, Screen Recording, and choosing
|
|
29
|
+
your project directory.
|
|
30
|
+
|
|
31
|
+
### Install the CLI
|
|
32
|
+
|
|
13
33
|
```sh
|
|
14
34
|
npm install -g @lattices/cli
|
|
15
35
|
```
|
|
16
36
|
|
|
37
|
+
The CLI and app work independently — use either or both.
|
|
38
|
+
|
|
39
|
+
### Build from source
|
|
40
|
+
|
|
41
|
+
```sh
|
|
42
|
+
git clone https://github.com/arach/lattices.git
|
|
43
|
+
cd lattices
|
|
44
|
+
|
|
45
|
+
# Build the menu bar app (requires Swift 5.9+ / Xcode 15+)
|
|
46
|
+
cd app && swift build -c release && cd ..
|
|
47
|
+
|
|
48
|
+
# Install CLI dependencies
|
|
49
|
+
npm install
|
|
50
|
+
|
|
51
|
+
# Launch
|
|
52
|
+
node bin/lattices-app.js build # bundle the .app
|
|
53
|
+
node bin/lattices-app.js # launch it
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
To build a signed, notarized DMG for distribution:
|
|
57
|
+
|
|
58
|
+
```sh
|
|
59
|
+
# Requires a Developer ID certificate and notarytool keychain profile
|
|
60
|
+
./scripts/build-dmg.sh
|
|
61
|
+
```
|
|
62
|
+
|
|
17
63
|
## Quick start
|
|
18
64
|
|
|
19
65
|
```sh
|
|
@@ -77,8 +123,8 @@ Bundle related repos as tabs in one session. Each tab gets its own
|
|
|
77
123
|
pane layout from its `.lattices.json`.
|
|
78
124
|
|
|
79
125
|
```sh
|
|
80
|
-
lattices group
|
|
81
|
-
lattices tab
|
|
126
|
+
lattices group vox # Launch iOS, macOS, Web, API as tabs
|
|
127
|
+
lattices tab vox iOS # Switch to the iOS tab
|
|
82
128
|
```
|
|
83
129
|
|
|
84
130
|
## Window tiling and awareness
|
|
@@ -97,16 +143,31 @@ lattices scan recent # Browse scan history
|
|
|
97
143
|
lattices scan deep # Trigger a Vision OCR scan now
|
|
98
144
|
```
|
|
99
145
|
|
|
146
|
+
## Voice commands (beta)
|
|
147
|
+
|
|
148
|
+
Speak to control your workspace — tile windows, search, focus apps,
|
|
149
|
+
and launch projects with natural language. Powered by
|
|
150
|
+
[Vox](https://github.com/arach/vox) for transcription and
|
|
151
|
+
local NLEmbedding for intent matching, with Claude fallback for
|
|
152
|
+
ambiguous commands.
|
|
153
|
+
|
|
154
|
+
Trigger with `Hyper+3` (configurable). Press Space to speak, Space to
|
|
155
|
+
stop. The panel shows what was heard, the matched intent, extracted
|
|
156
|
+
parameters, and execution results.
|
|
157
|
+
|
|
100
158
|
## A programmable desktop
|
|
101
159
|
|
|
102
|
-
The menu bar app runs a daemon with
|
|
160
|
+
The menu bar app runs a daemon with 35 RPC methods and 5 real-time
|
|
103
161
|
events over WebSocket. Anything you can do from the app, an agent or
|
|
104
162
|
script can do over the API.
|
|
105
163
|
|
|
106
164
|
```js
|
|
107
165
|
import { daemonCall } from '@lattices/cli/daemon-client'
|
|
108
166
|
|
|
109
|
-
|
|
167
|
+
// Search windows by content — title, app, session tags, OCR
|
|
168
|
+
const results = await daemonCall('windows.search', { query: 'myproject' })
|
|
169
|
+
|
|
170
|
+
// Launch and tile
|
|
110
171
|
await daemonCall('session.launch', { path: '/Users/you/dev/frontend' })
|
|
111
172
|
await daemonCall('window.tile', { session: 'frontend-a1b2c3', position: 'left' })
|
|
112
173
|
|
|
@@ -115,6 +176,14 @@ await daemonCall('ocr.scan')
|
|
|
115
176
|
const errors = await daemonCall('ocr.search', { query: 'error OR failed' })
|
|
116
177
|
```
|
|
117
178
|
|
|
179
|
+
Or from the CLI:
|
|
180
|
+
|
|
181
|
+
```sh
|
|
182
|
+
lattices search myproject # Find windows by content
|
|
183
|
+
lattices search myproject --deep # Include terminal tab/process data
|
|
184
|
+
lattices place myproject left # Search + focus + tile in one step
|
|
185
|
+
```
|
|
186
|
+
|
|
118
187
|
Claude Code skills, MCP servers, or your own scripts can drive your
|
|
119
188
|
desktop the same way you do.
|
|
120
189
|
|
|
@@ -125,12 +194,15 @@ lattices Create or reattach to session
|
|
|
125
194
|
lattices init Generate .lattices.json
|
|
126
195
|
lattices ls List active sessions
|
|
127
196
|
lattices kill [name] Kill a session
|
|
197
|
+
lattices search <query> Search windows by title, app, session, OCR
|
|
198
|
+
lattices search <q> --deep Deep search: index + terminal inspection
|
|
199
|
+
lattices place <query> [pos] Deep search + focus + tile
|
|
200
|
+
lattices focus <session> Raise a session's window
|
|
128
201
|
lattices tile <position> Tile frontmost window
|
|
129
202
|
lattices group [id] Launch or attach a tab group
|
|
130
203
|
lattices tab <group> [tab] Switch tab within a group
|
|
131
204
|
lattices scan View current screen text
|
|
132
205
|
lattices scan search <q> Search indexed text
|
|
133
|
-
lattices scan recent [n] Browse scan history
|
|
134
206
|
lattices scan deep Trigger Vision OCR now
|
|
135
207
|
lattices app Launch the menu bar app
|
|
136
208
|
lattices help Show help
|
|
@@ -148,7 +220,11 @@ lattices help Show help
|
|
|
148
220
|
|
|
149
221
|
## Docs
|
|
150
222
|
|
|
151
|
-
[lattices.dev/docs](https://lattices.dev/docs/overview)
|
|
223
|
+
Full documentation at [lattices.dev/docs](https://lattices.dev/docs/overview), including:
|
|
224
|
+
|
|
225
|
+
- [API reference](https://lattices.dev/docs/api) — all 35 daemon methods
|
|
226
|
+
- [Layers](https://lattices.dev/docs/layers) — workspace layers and tab groups
|
|
227
|
+
- [Voice commands](https://lattices.dev/docs/voice) — Vox integration
|
|
152
228
|
|
|
153
229
|
## License
|
|
154
230
|
|
package/app/Info.plist
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>CFBundleIdentifier</key>
|
|
6
|
+
<string>com.arach.lattices</string>
|
|
7
|
+
<key>CFBundleName</key>
|
|
8
|
+
<string>Lattices</string>
|
|
9
|
+
<key>CFBundleDisplayName</key>
|
|
10
|
+
<string>Lattices</string>
|
|
11
|
+
<key>CFBundleExecutable</key>
|
|
12
|
+
<string>Lattices</string>
|
|
13
|
+
<key>CFBundleIconFile</key>
|
|
14
|
+
<string>AppIcon</string>
|
|
15
|
+
<key>CFBundlePackageType</key>
|
|
16
|
+
<string>APPL</string>
|
|
17
|
+
<key>CFBundleVersion</key>
|
|
18
|
+
<string>0.4.1</string>
|
|
19
|
+
<key>CFBundleShortVersionString</key>
|
|
20
|
+
<string>0.4.1</string>
|
|
21
|
+
<key>LSMinimumSystemVersion</key>
|
|
22
|
+
<string>13.0</string>
|
|
23
|
+
<key>LSUIElement</key>
|
|
24
|
+
<true/>
|
|
25
|
+
<key>NSHighResolutionCapable</key>
|
|
26
|
+
<true/>
|
|
27
|
+
<key>NSSupportsAutomaticTermination</key>
|
|
28
|
+
<true/>
|
|
29
|
+
</dict>
|
|
30
|
+
</plist>
|
|
@@ -6,6 +6,8 @@
|
|
|
6
6
|
<string>com.arach.lattices</string>
|
|
7
7
|
<key>CFBundleName</key>
|
|
8
8
|
<string>Lattices</string>
|
|
9
|
+
<key>CFBundleDisplayName</key>
|
|
10
|
+
<string>Lattices</string>
|
|
9
11
|
<key>CFBundleExecutable</key>
|
|
10
12
|
<string>Lattices</string>
|
|
11
13
|
<key>CFBundleIconFile</key>
|
|
@@ -13,11 +15,15 @@
|
|
|
13
15
|
<key>CFBundlePackageType</key>
|
|
14
16
|
<string>APPL</string>
|
|
15
17
|
<key>CFBundleVersion</key>
|
|
16
|
-
<string>1</string>
|
|
18
|
+
<string>0.4.1</string>
|
|
17
19
|
<key>CFBundleShortVersionString</key>
|
|
18
|
-
<string>0.1
|
|
20
|
+
<string>0.4.1</string>
|
|
21
|
+
<key>LSMinimumSystemVersion</key>
|
|
22
|
+
<string>13.0</string>
|
|
19
23
|
<key>LSUIElement</key>
|
|
20
24
|
<true/>
|
|
25
|
+
<key>NSHighResolutionCapable</key>
|
|
26
|
+
<true/>
|
|
21
27
|
<key>NSSupportsAutomaticTermination</key>
|
|
22
28
|
<true/>
|
|
23
29
|
</dict>
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<key>files</key>
|
|
6
|
+
<dict>
|
|
7
|
+
<key>Resources/AppIcon.icns</key>
|
|
8
|
+
<data>
|
|
9
|
+
3sIZmtGJHMo2S/XvIMl46SOHcFY=
|
|
10
|
+
</data>
|
|
11
|
+
<key>Resources/tap.wav</key>
|
|
12
|
+
<data>
|
|
13
|
+
eOpp5td/ovQGMumXPpwy4Vyt/uc=
|
|
14
|
+
</data>
|
|
15
|
+
</dict>
|
|
16
|
+
<key>files2</key>
|
|
17
|
+
<dict>
|
|
18
|
+
<key>Resources/AppIcon.icns</key>
|
|
19
|
+
<dict>
|
|
20
|
+
<key>hash2</key>
|
|
21
|
+
<data>
|
|
22
|
+
LZsztS/9I1hmuQmDOk+anfxOpqVryB3y4a1kwSaUK4s=
|
|
23
|
+
</data>
|
|
24
|
+
</dict>
|
|
25
|
+
<key>Resources/tap.wav</key>
|
|
26
|
+
<dict>
|
|
27
|
+
<key>hash2</key>
|
|
28
|
+
<data>
|
|
29
|
+
K4QV08FuKEJR29hhgUbEG7Em3J6zHYpGKmGWdnZopzs=
|
|
30
|
+
</data>
|
|
31
|
+
</dict>
|
|
32
|
+
</dict>
|
|
33
|
+
<key>rules</key>
|
|
34
|
+
<dict>
|
|
35
|
+
<key>^Resources/</key>
|
|
36
|
+
<true/>
|
|
37
|
+
<key>^Resources/.*\.lproj/</key>
|
|
38
|
+
<dict>
|
|
39
|
+
<key>optional</key>
|
|
40
|
+
<true/>
|
|
41
|
+
<key>weight</key>
|
|
42
|
+
<real>1000</real>
|
|
43
|
+
</dict>
|
|
44
|
+
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
|
45
|
+
<dict>
|
|
46
|
+
<key>omit</key>
|
|
47
|
+
<true/>
|
|
48
|
+
<key>weight</key>
|
|
49
|
+
<real>1100</real>
|
|
50
|
+
</dict>
|
|
51
|
+
<key>^Resources/Base\.lproj/</key>
|
|
52
|
+
<dict>
|
|
53
|
+
<key>weight</key>
|
|
54
|
+
<real>1010</real>
|
|
55
|
+
</dict>
|
|
56
|
+
<key>^version.plist$</key>
|
|
57
|
+
<true/>
|
|
58
|
+
</dict>
|
|
59
|
+
<key>rules2</key>
|
|
60
|
+
<dict>
|
|
61
|
+
<key>.*\.dSYM($|/)</key>
|
|
62
|
+
<dict>
|
|
63
|
+
<key>weight</key>
|
|
64
|
+
<real>11</real>
|
|
65
|
+
</dict>
|
|
66
|
+
<key>^(.*/)?\.DS_Store$</key>
|
|
67
|
+
<dict>
|
|
68
|
+
<key>omit</key>
|
|
69
|
+
<true/>
|
|
70
|
+
<key>weight</key>
|
|
71
|
+
<real>2000</real>
|
|
72
|
+
</dict>
|
|
73
|
+
<key>^(Frameworks|SharedFrameworks|PlugIns|Plug-ins|XPCServices|Helpers|MacOS|Library/(Automator|Spotlight|LoginItems))/</key>
|
|
74
|
+
<dict>
|
|
75
|
+
<key>nested</key>
|
|
76
|
+
<true/>
|
|
77
|
+
<key>weight</key>
|
|
78
|
+
<real>10</real>
|
|
79
|
+
</dict>
|
|
80
|
+
<key>^.*</key>
|
|
81
|
+
<true/>
|
|
82
|
+
<key>^Info\.plist$</key>
|
|
83
|
+
<dict>
|
|
84
|
+
<key>omit</key>
|
|
85
|
+
<true/>
|
|
86
|
+
<key>weight</key>
|
|
87
|
+
<real>20</real>
|
|
88
|
+
</dict>
|
|
89
|
+
<key>^PkgInfo$</key>
|
|
90
|
+
<dict>
|
|
91
|
+
<key>omit</key>
|
|
92
|
+
<true/>
|
|
93
|
+
<key>weight</key>
|
|
94
|
+
<real>20</real>
|
|
95
|
+
</dict>
|
|
96
|
+
<key>^Resources/</key>
|
|
97
|
+
<dict>
|
|
98
|
+
<key>weight</key>
|
|
99
|
+
<real>20</real>
|
|
100
|
+
</dict>
|
|
101
|
+
<key>^Resources/.*\.lproj/</key>
|
|
102
|
+
<dict>
|
|
103
|
+
<key>optional</key>
|
|
104
|
+
<true/>
|
|
105
|
+
<key>weight</key>
|
|
106
|
+
<real>1000</real>
|
|
107
|
+
</dict>
|
|
108
|
+
<key>^Resources/.*\.lproj/locversion.plist$</key>
|
|
109
|
+
<dict>
|
|
110
|
+
<key>omit</key>
|
|
111
|
+
<true/>
|
|
112
|
+
<key>weight</key>
|
|
113
|
+
<real>1100</real>
|
|
114
|
+
</dict>
|
|
115
|
+
<key>^Resources/Base\.lproj/</key>
|
|
116
|
+
<dict>
|
|
117
|
+
<key>weight</key>
|
|
118
|
+
<real>1010</real>
|
|
119
|
+
</dict>
|
|
120
|
+
<key>^[^/]+$</key>
|
|
121
|
+
<dict>
|
|
122
|
+
<key>nested</key>
|
|
123
|
+
<true/>
|
|
124
|
+
<key>weight</key>
|
|
125
|
+
<real>10</real>
|
|
126
|
+
</dict>
|
|
127
|
+
<key>^embedded\.provisionprofile$</key>
|
|
128
|
+
<dict>
|
|
129
|
+
<key>weight</key>
|
|
130
|
+
<real>20</real>
|
|
131
|
+
</dict>
|
|
132
|
+
<key>^version\.plist$</key>
|
|
133
|
+
<dict>
|
|
134
|
+
<key>weight</key>
|
|
135
|
+
<real>20</real>
|
|
136
|
+
</dict>
|
|
137
|
+
</dict>
|
|
138
|
+
</dict>
|
|
139
|
+
</plist>
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
3
|
+
<plist version="1.0">
|
|
4
|
+
<dict>
|
|
5
|
+
<!-- App sandbox disabled — Lattices needs direct access to tmux, processes, and the filesystem -->
|
|
6
|
+
<key>com.apple.security.app-sandbox</key>
|
|
7
|
+
<false/>
|
|
8
|
+
|
|
9
|
+
<!-- Network: localhost WebSocket daemon for CLI/agent communication -->
|
|
10
|
+
<key>com.apple.security.network.server</key>
|
|
11
|
+
<true/>
|
|
12
|
+
<key>com.apple.security.network.client</key>
|
|
13
|
+
<true/>
|
|
14
|
+
</dict>
|
|
15
|
+
</plist>
|
package/app/Package.swift
CHANGED
|
@@ -7,7 +7,14 @@ let package = Package(
|
|
|
7
7
|
targets: [
|
|
8
8
|
.executableTarget(
|
|
9
9
|
name: "Lattices",
|
|
10
|
-
path: "Sources"
|
|
10
|
+
path: "Sources",
|
|
11
|
+
resources: [
|
|
12
|
+
.copy("../Resources/tap.wav"),
|
|
13
|
+
]
|
|
14
|
+
),
|
|
15
|
+
.testTarget(
|
|
16
|
+
name: "LatticesTests",
|
|
17
|
+
path: "Tests"
|
|
11
18
|
)
|
|
12
19
|
]
|
|
13
20
|
)
|
|
Binary file
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import Foundation
|
|
2
|
+
|
|
3
|
+
/// Captures moments where the Claude advisor resolved something the local matcher couldn't.
|
|
4
|
+
/// Each entry records the transcript, what the local system matched (or missed), and what
|
|
5
|
+
/// the advisor suggested that the user accepted.
|
|
6
|
+
///
|
|
7
|
+
/// For now this is append-only — just growing the dataset. Future work can use it to
|
|
8
|
+
/// improve local matching without needing the advisor.
|
|
9
|
+
|
|
10
|
+
final class AdvisorLearningStore {
|
|
11
|
+
static let shared = AdvisorLearningStore()
|
|
12
|
+
|
|
13
|
+
struct Entry: Codable {
|
|
14
|
+
let timestamp: String
|
|
15
|
+
let transcript: String
|
|
16
|
+
let localIntent: String?
|
|
17
|
+
let localSlots: [String: String]
|
|
18
|
+
let localResultCount: Int
|
|
19
|
+
let advisorIntent: String
|
|
20
|
+
let advisorSlots: [String: String]
|
|
21
|
+
let advisorLabel: String
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
private let fileURL: URL
|
|
25
|
+
private let queue = DispatchQueue(label: "com.lattices.advisor-learning")
|
|
26
|
+
private static let isoFmt: ISO8601DateFormatter = {
|
|
27
|
+
let f = ISO8601DateFormatter()
|
|
28
|
+
f.formatOptions = [.withInternetDateTime, .withFractionalSeconds]
|
|
29
|
+
return f
|
|
30
|
+
}()
|
|
31
|
+
|
|
32
|
+
private init() {
|
|
33
|
+
let dir = FileManager.default.homeDirectoryForCurrentUser
|
|
34
|
+
.appendingPathComponent(".lattices")
|
|
35
|
+
try? FileManager.default.createDirectory(at: dir, withIntermediateDirectories: true)
|
|
36
|
+
fileURL = dir.appendingPathComponent("advisor-learning.jsonl")
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/// Record that the user engaged with an advisor suggestion.
|
|
40
|
+
func record(
|
|
41
|
+
transcript: String,
|
|
42
|
+
localIntent: String?,
|
|
43
|
+
localSlots: [String: String],
|
|
44
|
+
localResultCount: Int,
|
|
45
|
+
advisorIntent: String,
|
|
46
|
+
advisorSlots: [String: String],
|
|
47
|
+
advisorLabel: String
|
|
48
|
+
) {
|
|
49
|
+
let entry = Entry(
|
|
50
|
+
timestamp: Self.isoFmt.string(from: Date()),
|
|
51
|
+
transcript: transcript,
|
|
52
|
+
localIntent: localIntent,
|
|
53
|
+
localSlots: localSlots,
|
|
54
|
+
localResultCount: localResultCount,
|
|
55
|
+
advisorIntent: advisorIntent,
|
|
56
|
+
advisorSlots: advisorSlots,
|
|
57
|
+
advisorLabel: advisorLabel
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
queue.async {
|
|
61
|
+
guard let data = try? JSONEncoder().encode(entry),
|
|
62
|
+
var line = String(data: data, encoding: .utf8) else { return }
|
|
63
|
+
line += "\n"
|
|
64
|
+
|
|
65
|
+
if let handle = try? FileHandle(forWritingTo: self.fileURL) {
|
|
66
|
+
handle.seekToEndOfFile()
|
|
67
|
+
handle.write(line.data(using: .utf8)!)
|
|
68
|
+
handle.closeFile()
|
|
69
|
+
} else {
|
|
70
|
+
try? line.data(using: .utf8)?.write(to: self.fileURL)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
DiagnosticLog.shared.info("AdvisorLearning: captured [\(transcript)] → \(advisorIntent)(\(advisorSlots))")
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/// Read all entries (for analysis).
|
|
78
|
+
func allEntries() -> [Entry] {
|
|
79
|
+
guard let data = try? String(contentsOf: fileURL, encoding: .utf8) else { return [] }
|
|
80
|
+
return data.components(separatedBy: "\n").compactMap { line in
|
|
81
|
+
guard !line.isEmpty, let d = line.data(using: .utf8) else { return nil }
|
|
82
|
+
return try? JSONDecoder().decode(Entry.self, from: d)
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
var entryCount: Int {
|
|
87
|
+
guard let data = try? String(contentsOf: fileURL, encoding: .utf8) else { return 0 }
|
|
88
|
+
return data.components(separatedBy: "\n").filter { !$0.isEmpty }.count
|
|
89
|
+
}
|
|
90
|
+
}
|