@arach/lattices 0.2.0 → 0.6.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.
Files changed (143) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +172 -86
  3. package/apps/mac/Info.plist +43 -0
  4. package/apps/mac/Lattices.app/Contents/Info.plist +43 -0
  5. package/apps/mac/Lattices.app/Contents/MacOS/Lattices +0 -0
  6. package/apps/mac/Lattices.app/Contents/Resources/AppIcon.icns +0 -0
  7. package/apps/mac/Lattices.app/Contents/Resources/docs/assistant-knowledge.md +130 -0
  8. package/apps/mac/Lattices.app/Contents/Resources/tap.wav +0 -0
  9. package/apps/mac/Lattices.app/Contents/_CodeSignature/CodeResources +150 -0
  10. package/apps/mac/Lattices.entitlements +21 -0
  11. package/apps/mac/Resources/Pets/assistant-spark/pet.json +62 -0
  12. package/apps/mac/Resources/Pets/assistant-spark/spritesheet.webp +0 -0
  13. package/apps/mac/Resources/Pets/scout-ranger/pet.json +6 -0
  14. package/apps/mac/Resources/Pets/scout-ranger/spritesheet.webp +0 -0
  15. package/apps/mac/Resources/tap.wav +0 -0
  16. package/assets/AppIcon.icns +0 -0
  17. package/bin/assistant-intelligence.ts +912 -0
  18. package/bin/cli/capture.ts +252 -0
  19. package/bin/cli/daemon.ts +22 -0
  20. package/bin/cli/helpers.ts +105 -0
  21. package/bin/cli/layer.ts +178 -0
  22. package/bin/cli/runs.ts +43 -0
  23. package/bin/cli/search.ts +141 -0
  24. package/bin/cli/session.ts +32 -0
  25. package/bin/client.ts +17 -0
  26. package/bin/cua.ts +26 -0
  27. package/bin/{daemon-client.js → daemon-client.ts} +49 -30
  28. package/bin/handsoff-infer.ts +96 -0
  29. package/bin/handsoff-worker.ts +531 -0
  30. package/bin/infer.ts +424 -0
  31. package/bin/keychain.ts +75 -0
  32. package/bin/lattices-app.ts +655 -0
  33. package/bin/lattices-build +125 -0
  34. package/bin/lattices-build-env.ts +77 -0
  35. package/bin/lattices-dev +362 -0
  36. package/bin/lattices.ts +3260 -0
  37. package/bin/project-twin.ts +645 -0
  38. package/docs/agent-execution-plan.md +562 -0
  39. package/docs/agent-layer-guide.md +207 -0
  40. package/docs/agents.md +233 -0
  41. package/docs/ai-chat-ux-review.md +416 -0
  42. package/docs/api.md +1041 -47
  43. package/docs/app.md +96 -13
  44. package/docs/assistant-knowledge.md +130 -0
  45. package/docs/companion-deck.md +209 -0
  46. package/docs/component-extraction-roadmap.md +392 -0
  47. package/docs/concepts.md +13 -12
  48. package/docs/config.md +83 -10
  49. package/docs/gesture-customization-proposal.md +520 -0
  50. package/docs/handsoff-test-scenarios.md +84 -0
  51. package/docs/hyperspace-grid-snappiness.md +210 -0
  52. package/docs/layers.md +176 -28
  53. package/docs/mouse-gestures.md +244 -0
  54. package/docs/ocr.md +21 -9
  55. package/docs/overview.md +42 -23
  56. package/docs/presentation-execution-review.md +491 -0
  57. package/docs/prompts/hands-off-system.md +382 -0
  58. package/docs/prompts/hands-off-turn.md +30 -0
  59. package/docs/prompts/voice-advisor.md +31 -0
  60. package/docs/prompts/voice-fallback.md +23 -0
  61. package/docs/proposals/LAT-001-gesture-visual-customization.md +522 -0
  62. package/docs/proposals/LAT-002-shared-overlay-canvas.md +353 -0
  63. package/docs/proposals/LAT-003-menu-bar-controller-architecture.md +291 -0
  64. package/docs/proposals/LAT-004-interactive-overlay-actors.md +534 -0
  65. package/docs/proposals/LAT-005-action-runtime-product-spine.md +914 -0
  66. package/docs/proposals/LAT-006-followup-gaps.md +103 -0
  67. package/docs/proposals/LAT-006-runs-and-capture-in-lattices.md +566 -0
  68. package/docs/proposals/LAT-007-unified-app-shell.md +128 -0
  69. package/docs/quickstart.md +8 -12
  70. package/docs/reference/dewey.config.ts +74 -0
  71. package/docs/reference/install-agent.md +79 -0
  72. package/docs/release.md +172 -0
  73. package/docs/repo-structure.md +100 -0
  74. package/docs/terminal-kit.md +87 -0
  75. package/docs/tiling-reference.md +224 -0
  76. package/docs/twins.md +138 -0
  77. package/docs/voice-command-protocol.md +278 -0
  78. package/docs/voice-error-model.md +73 -0
  79. package/docs/voice.md +221 -0
  80. package/package.json +69 -16
  81. package/packages/npm/sdk/cua.d.mts +1 -0
  82. package/packages/npm/sdk/cua.d.ts +188 -0
  83. package/packages/npm/sdk/cua.mjs +376 -0
  84. package/app/Lattices.app/Contents/Info.plist +0 -24
  85. package/app/Package.swift +0 -13
  86. package/app/Sources/ActionRow.swift +0 -61
  87. package/app/Sources/App.swift +0 -10
  88. package/app/Sources/AppDelegate.swift +0 -234
  89. package/app/Sources/AppShellView.swift +0 -62
  90. package/app/Sources/AppTypeClassifier.swift +0 -70
  91. package/app/Sources/AppWindowShell.swift +0 -63
  92. package/app/Sources/CheatSheetHUD.swift +0 -332
  93. package/app/Sources/CommandModeState.swift +0 -1362
  94. package/app/Sources/CommandModeView.swift +0 -1405
  95. package/app/Sources/CommandModeWindow.swift +0 -192
  96. package/app/Sources/CommandPaletteView.swift +0 -307
  97. package/app/Sources/CommandPaletteWindow.swift +0 -134
  98. package/app/Sources/DaemonProtocol.swift +0 -101
  99. package/app/Sources/DaemonServer.swift +0 -414
  100. package/app/Sources/DesktopModel.swift +0 -121
  101. package/app/Sources/DesktopModelTypes.swift +0 -71
  102. package/app/Sources/DiagnosticLog.swift +0 -271
  103. package/app/Sources/EventBus.swift +0 -30
  104. package/app/Sources/HotkeyManager.swift +0 -250
  105. package/app/Sources/HotkeyStore.swift +0 -338
  106. package/app/Sources/InventoryManager.swift +0 -35
  107. package/app/Sources/InventoryPath.swift +0 -43
  108. package/app/Sources/KeyRecorderView.swift +0 -210
  109. package/app/Sources/LatticesApi.swift +0 -1125
  110. package/app/Sources/MainView.swift +0 -467
  111. package/app/Sources/MainWindow.swift +0 -83
  112. package/app/Sources/OcrModel.swift +0 -309
  113. package/app/Sources/OcrStore.swift +0 -295
  114. package/app/Sources/OmniSearchState.swift +0 -283
  115. package/app/Sources/OmniSearchView.swift +0 -288
  116. package/app/Sources/OmniSearchWindow.swift +0 -105
  117. package/app/Sources/OrphanRow.swift +0 -129
  118. package/app/Sources/PaletteCommand.swift +0 -419
  119. package/app/Sources/PermissionChecker.swift +0 -125
  120. package/app/Sources/Preferences.swift +0 -92
  121. package/app/Sources/ProcessModel.swift +0 -199
  122. package/app/Sources/ProcessQuery.swift +0 -151
  123. package/app/Sources/Project.swift +0 -28
  124. package/app/Sources/ProjectRow.swift +0 -368
  125. package/app/Sources/ProjectScanner.swift +0 -121
  126. package/app/Sources/ScreenMapState.swift +0 -2387
  127. package/app/Sources/ScreenMapView.swift +0 -2820
  128. package/app/Sources/ScreenMapWindowController.swift +0 -89
  129. package/app/Sources/SessionManager.swift +0 -72
  130. package/app/Sources/SettingsView.swift +0 -1053
  131. package/app/Sources/SettingsWindow.swift +0 -20
  132. package/app/Sources/TabGroupRow.swift +0 -178
  133. package/app/Sources/Terminal.swift +0 -259
  134. package/app/Sources/TerminalQuery.swift +0 -156
  135. package/app/Sources/TerminalSynthesizer.swift +0 -200
  136. package/app/Sources/Theme.swift +0 -163
  137. package/app/Sources/TilePickerView.swift +0 -209
  138. package/app/Sources/TmuxModel.swift +0 -53
  139. package/app/Sources/TmuxQuery.swift +0 -81
  140. package/app/Sources/WindowTiler.swift +0 -1755
  141. package/app/Sources/WorkspaceManager.swift +0 -434
  142. package/bin/lattices-app.js +0 -221
  143. package/bin/lattices.js +0 -1418
@@ -0,0 +1,125 @@
1
+ #!/usr/bin/env bash
2
+ # lattices-build - explicit dev/package/dist build entry points
3
+
4
+ set -euo pipefail
5
+
6
+ SCRIPT_PATH="$(readlink -f "$0" 2>/dev/null || python3 -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "$0")"
7
+ ROOT="$(cd "$(dirname "$SCRIPT_PATH")/.." && pwd)"
8
+
9
+ DEV_APPS_DIR="${LATTICES_DEV_APPS_DIR:-$HOME/Applications/dev/Lattices}"
10
+ DEV_BUNDLE="${LATTICES_DEV_APP:-$DEV_APPS_DIR/Lattices.app}"
11
+ DEV_BUNDLE_ID="${LATTICES_DEV_BUNDLE_ID:-dev.lattices.app.dev}"
12
+ RELEASE_BUNDLE="$ROOT/apps/mac/Lattices.app"
13
+ RELEASE_BUNDLE_ID="dev.lattices.app"
14
+ DIST_BUNDLE="$ROOT/dist/Lattices.app"
15
+ DIST_DMG="$ROOT/dist/Lattices.dmg"
16
+ PROD_INSTALL_BUNDLE="/Applications/Lattices.app"
17
+
18
+ green() { printf "\033[32m%s\033[0m\n" "$*"; }
19
+ dim() { printf "\033[2m%s\033[0m\n" "$*"; }
20
+ red() { printf "\033[31m%s\033[0m\n" "$*"; }
21
+
22
+ usage() {
23
+ cat <<EOF
24
+ lattices-build - build Lattices without mixing dev and release identities
25
+
26
+ Usage:
27
+ bin/lattices-build dev Build/install dev app
28
+ bin/lattices-build dev:restart Build/install/relaunch dev app
29
+ bin/lattices-build package Build repo app bundle for npm packaging
30
+ bin/lattices-build dist Build signed/notarized release DMG
31
+ bin/lattices-build dist:local Build local release DMG without notarizing
32
+ bin/lattices-build where Show canonical paths and bundle ids
33
+
34
+ Channels:
35
+ dev $DEV_BUNDLE_ID -> $DEV_BUNDLE
36
+ release $RELEASE_BUNDLE_ID -> $PROD_INSTALL_BUNDLE via $DIST_DMG
37
+ EOF
38
+ }
39
+
40
+ plist_value() {
41
+ local app="$1"
42
+ local key="$2"
43
+ [ -d "$app" ] || return 0
44
+ /usr/bin/defaults read "$app/Contents/Info.plist" "$key" 2>/dev/null || true
45
+ }
46
+
47
+ show_app() {
48
+ local label="$1"
49
+ local app="$2"
50
+
51
+ echo "$label"
52
+ echo " path: $app"
53
+ if [ -d "$app" ]; then
54
+ echo " bundle id: $(plist_value "$app" CFBundleIdentifier)"
55
+ echo " channel: $(plist_value "$app" LatticesBuildChannel)"
56
+ echo " built at: $(plist_value "$app" LatticesBuildTimestamp)"
57
+ else
58
+ echo " status: not built"
59
+ fi
60
+ }
61
+
62
+ cmd_dev() {
63
+ "$ROOT/bin/lattices-dev" build
64
+ show_app "Dev app" "$DEV_BUNDLE"
65
+ green "Dev permission target: $DEV_BUNDLE"
66
+ }
67
+
68
+ cmd_dev_restart() {
69
+ "$ROOT/bin/lattices-dev" restart
70
+ show_app "Running dev app" "$DEV_BUNDLE"
71
+ }
72
+
73
+ cmd_package() {
74
+ if ! command -v bun >/dev/null 2>&1; then
75
+ red "bun is required for package builds."
76
+ exit 1
77
+ fi
78
+
79
+ bun "$ROOT/bin/lattices-app.ts" build
80
+ show_app "Package app bundle" "$RELEASE_BUNDLE"
81
+ green "Package bundle ready: $RELEASE_BUNDLE"
82
+ }
83
+
84
+ cmd_dist() {
85
+ "$ROOT/tools/release/build-dmg.sh" "$@"
86
+ show_app "Release app artifact" "$DIST_BUNDLE"
87
+ [ -f "$DIST_DMG" ] && ls -lh "$DIST_DMG"
88
+ }
89
+
90
+ cmd_dist_local() {
91
+ LATTICES_SKIP_NOTARIZE=1 "$ROOT/tools/release/build-dmg.sh" "$@"
92
+ show_app "Local release app artifact" "$DIST_BUNDLE"
93
+ [ -f "$DIST_DMG" ] && ls -lh "$DIST_DMG"
94
+ }
95
+
96
+ cmd_where() {
97
+ show_app "Dev app" "$DEV_BUNDLE"
98
+ echo " expected bundle id: $DEV_BUNDLE_ID"
99
+ echo
100
+ show_app "Package app bundle" "$RELEASE_BUNDLE"
101
+ echo " expected bundle id: $RELEASE_BUNDLE_ID"
102
+ echo
103
+ show_app "Release app artifact" "$DIST_BUNDLE"
104
+ echo " expected install target: $PROD_INSTALL_BUNDLE"
105
+ [ -f "$DIST_DMG" ] && echo " dmg: $DIST_DMG"
106
+ }
107
+
108
+ cmd="${1:-help}"
109
+ shift || true
110
+
111
+ case "$cmd" in
112
+ dev) cmd_dev ;;
113
+ dev:restart|restart) cmd_dev_restart ;;
114
+ package) cmd_package ;;
115
+ dist) cmd_dist "$@" ;;
116
+ dist:local) cmd_dist_local "$@" ;;
117
+ where) cmd_where ;;
118
+ help|-h|--help) usage ;;
119
+ *)
120
+ red "Unknown command: $cmd"
121
+ echo
122
+ usage
123
+ exit 1
124
+ ;;
125
+ esac
@@ -0,0 +1,77 @@
1
+ #!/usr/bin/env bun
2
+ /**
3
+ * lattices-build-env — declarative build-feature resolver.
4
+ *
5
+ * Mirrors openscout's `hkit` style: an app names *features* in a manifest
6
+ * (apps/mac/build.json), never raw env vars. The feature → build-env mapping
7
+ * lives in the catalog below, so every build entrypoint (package / dev / dist)
8
+ * resolves the same way from one source of truth, instead of each hardcoding
9
+ * its own `HUDSONKIT_WITH_*` flags.
10
+ *
11
+ * import { resolveBuildEnv } from "./lattices-build-env"; // TS callers
12
+ * eval "$(bun bin/lattices-build-env.ts shell)" // bash callers
13
+ * bun bin/lattices-build-env.ts json // inspect
14
+ */
15
+
16
+ import { existsSync, readFileSync } from "node:fs";
17
+ import { join } from "node:path";
18
+
19
+ // Feature catalog: feature name -> build env HudsonKit gates on at SwiftPM
20
+ // manifest-eval time. HudsonVoice (Vox/Parakeet dictation) is an optional
21
+ // backend HudsonKit only declares when HUDSONKIT_WITH_VOICE=1 is set at build
22
+ // time. Name a *feature* here; never sprinkle the env var across build scripts.
23
+ export const FEATURE_CATALOG: Record<string, { env: Record<string, string>; note: string }> = {
24
+ voice: { env: { HUDSONKIT_WITH_VOICE: "1" }, note: "HudsonVoice — Vox/Parakeet dictation" },
25
+ };
26
+
27
+ export interface BuildManifest {
28
+ app?: string;
29
+ features?: string[];
30
+ }
31
+
32
+ const MANIFEST_PATH = join(import.meta.dir, "../apps/mac/build.json");
33
+
34
+ export function loadManifest(path = MANIFEST_PATH): BuildManifest {
35
+ if (!existsSync(path)) return {};
36
+ return JSON.parse(readFileSync(path, "utf8")) as BuildManifest;
37
+ }
38
+
39
+ export function resolveFeatureEnv(features: string[] = []): Record<string, string> {
40
+ const env: Record<string, string> = {};
41
+ for (const f of features) {
42
+ const entry = FEATURE_CATALOG[f];
43
+ if (!entry) {
44
+ throw new Error(
45
+ `unknown build feature "${f}" — known features: ${Object.keys(FEATURE_CATALOG).join(", ")}`,
46
+ );
47
+ }
48
+ Object.assign(env, entry.env);
49
+ }
50
+ return env;
51
+ }
52
+
53
+ /** Resolve the manifest's declared features into a build-env map. */
54
+ export function resolveBuildEnv(manifestPath?: string): Record<string, string> {
55
+ const features = loadManifest(manifestPath).features ?? [];
56
+ const env = resolveFeatureEnv(features);
57
+ // HudsonKit enables HudsonVoice by default; opt out unless the voice feature is declared.
58
+ if (!features.includes("voice")) {
59
+ env.HUDSONKIT_WITH_VOICE = "0";
60
+ }
61
+ return env;
62
+ }
63
+
64
+ // --- CLI: emit the resolved env for shell / json consumers -------------------
65
+ if (import.meta.main) {
66
+ const mode = process.argv[2] ?? "shell";
67
+ const env = resolveBuildEnv();
68
+ if (mode === "json") {
69
+ console.log(JSON.stringify(env, null, 2));
70
+ } else if (mode === "shell") {
71
+ // eval-able by bash: `eval "$(bun bin/lattices-build-env.ts shell)"`
72
+ for (const [k, v] of Object.entries(env)) console.log(`export ${k}=${JSON.stringify(v)}`);
73
+ } else {
74
+ console.error(`lattices-build-env: unknown mode "${mode}" (use: shell | json)`);
75
+ process.exit(1);
76
+ }
77
+ }
@@ -0,0 +1,362 @@
1
+ #!/usr/bin/env bash
2
+ # lattices-dev — convenience commands for Lattices development
3
+
4
+ set -euo pipefail
5
+
6
+ SCRIPT_PATH="$(readlink -f "$0" 2>/dev/null || python3 -c "import os,sys; print(os.path.realpath(sys.argv[1]))" "$0")"
7
+ APP_DIR="$(cd "$(dirname "$SCRIPT_PATH")/../apps/mac" && pwd)"
8
+ ROOT="$(cd "$(dirname "$SCRIPT_PATH")/.." && pwd)"
9
+ LOG_FILE="$HOME/.lattices/lattices.log"
10
+ BINARY="$APP_DIR/.build/release/Lattices"
11
+ BUILD_BASE="$ROOT/build/macos/Lattices"
12
+ BUNDLE="$BUILD_BASE/Build/Products/Debug/Lattices.app"
13
+ DEV_APPS_DIR="${LATTICES_DEV_APPS_DIR:-$HOME/Applications/dev/Lattices}"
14
+ INSTALL_BUNDLE="${LATTICES_DEV_APP:-$DEV_APPS_DIR/Lattices.app}"
15
+ BUNDLE_BIN="$BUNDLE/Contents/MacOS/Lattices"
16
+ RESOURCES_DIR="$BUNDLE/Contents/Resources"
17
+ ENTITLEMENTS="$APP_DIR/Lattices.entitlements"
18
+ BUNDLE_ID="${LATTICES_DEV_BUNDLE_ID:-dev.lattices.app.dev}"
19
+ ICON="$ROOT/assets/AppIcon.icns"
20
+ TAP_SOUND="$APP_DIR/Resources/tap.wav"
21
+ VERSION="$(node -p "require('$ROOT/package.json').version" 2>/dev/null || echo '0.1.0')"
22
+ GIT_REVISION="$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo 'unknown')"
23
+ BUILD_TIMESTAMP="$(date -u +"%Y-%m-%dT%H:%M:%SZ")"
24
+
25
+ red() { printf "\033[31m%s\033[0m\n" "$*"; }
26
+ green() { printf "\033[32m%s\033[0m\n" "$*"; }
27
+ dim() { printf "\033[2m%s\033[0m\n" "$*"; }
28
+
29
+ select_sign_identity() {
30
+ local identities identity=""
31
+ identities="$(security find-identity -v -p codesigning 2>/dev/null || true)"
32
+ # Prefer Developer ID Application: its designated requirement pins to the stable
33
+ # team OU, so TCC permissions survive rebuilds and Apple Development cert rotations.
34
+ # Apple Development is only a fallback (its DR pins a specific cert that Xcode rotates,
35
+ # which silently resets every granted permission). Keep this order in sync with
36
+ # resolveSigningIdentity() in bin/lattices-app.ts.
37
+ identity="$(printf '%s\n' "$identities" | sed -n 's/^[[:space:]]*[0-9]*)[[:space:]]*\([A-F0-9]\{40\}\)[[:space:]]*"Developer ID Application:[^"]*".*/\1/p' | head -n 1)"
38
+ if [ -z "$identity" ]; then
39
+ identity="$(printf '%s\n' "$identities" | sed -n 's/^[[:space:]]*[0-9]*)[[:space:]]*\([A-F0-9]\{40\}\)[[:space:]]*"Apple Development:[^"]*".*/\1/p' | head -n 1)"
40
+ fi
41
+ printf '%s' "$identity"
42
+ }
43
+
44
+ sign_bundle() {
45
+ local identity sign_status=0
46
+ local -a ent_flags=()
47
+
48
+ rm -f "$BUNDLE_BIN".cstemp
49
+
50
+ if [ -f "$ENTITLEMENTS" ]; then
51
+ ent_flags=(--entitlements "$ENTITLEMENTS")
52
+ fi
53
+
54
+ identity="$(select_sign_identity)"
55
+ if [ -n "$identity" ]; then
56
+ dim "Signing with: $identity"
57
+ if ! codesign --force --options runtime --deep --sign "$identity" "${ent_flags[@]}" --identifier "$BUNDLE_ID" "$BUNDLE"; then
58
+ red "Signing with '$identity' failed. Falling back to ad-hoc."
59
+ sign_status=1
60
+ fi
61
+ else
62
+ sign_status=1
63
+ fi
64
+
65
+ if [ "$sign_status" -ne 0 ]; then
66
+ dim "No usable signing identity found. Using ad-hoc signature."
67
+ codesign --force --options runtime --deep --sign - "${ent_flags[@]}" --identifier "$BUNDLE_ID" "$BUNDLE"
68
+ fi
69
+
70
+ rm -f "$BUNDLE_BIN".cstemp
71
+ }
72
+
73
+ sync_bundle_copy() {
74
+ local destination="$1"
75
+
76
+ mkdir -p "$(dirname "$destination")"
77
+ rm -rf "$destination"
78
+ ditto "$BUNDLE" "$destination"
79
+ xattr -dr com.apple.quarantine "$destination" 2>/dev/null || true
80
+ touch "$destination" "$destination/Contents"
81
+ }
82
+
83
+ ensure_install_bundle() {
84
+ if [ -d "$INSTALL_BUNDLE" ]; then
85
+ return
86
+ fi
87
+
88
+ if [ -d "$BUNDLE" ]; then
89
+ sync_bundle_copy "$INSTALL_BUNDLE"
90
+ return
91
+ fi
92
+
93
+ cmd_build
94
+ }
95
+
96
+ clear_lattices_drag_cache() {
97
+ local cache_dir
98
+
99
+ for cache_dir in "$HOME"/Library/Caches/com.apple.SwiftUI.Drag-*; do
100
+ [ -d "$cache_dir" ] || continue
101
+ if [ -d "$cache_dir/Lattices.app" ]; then
102
+ rm -rf "$cache_dir/Lattices.app"
103
+ rmdir "$cache_dir" 2>/dev/null || true
104
+ fi
105
+ done
106
+ }
107
+
108
+ is_bundle_running() {
109
+ local bundle="$1"
110
+ local target="$bundle/Contents/MacOS/Lattices"
111
+ local pid args
112
+
113
+ while IFS= read -r pid; do
114
+ [ -n "$pid" ] || continue
115
+ args="$(ps -p "$pid" -o args= 2>/dev/null || true)"
116
+ case "$args" in
117
+ "$target"|"${target} "*) return 0 ;;
118
+ esac
119
+ done < <(pgrep -x Lattices 2>/dev/null || true)
120
+
121
+ return 1
122
+ }
123
+
124
+ is_install_bundle_running() {
125
+ is_bundle_running "$INSTALL_BUNDLE"
126
+ }
127
+
128
+ require_build_targets_not_running() {
129
+ if is_bundle_running "$BUNDLE"; then
130
+ red "Refusing to rebuild the repo-local app artifact while it is running."
131
+ dim "Rewriting or re-signing a live Mach-O can make macOS kill it later with Code Signature Invalid."
132
+ dim "Use \`lattices-dev restart\` to quit, rebuild, and relaunch."
133
+ exit 1
134
+ fi
135
+
136
+ if is_install_bundle_running; then
137
+ red "Refusing to install over the running dev app bundle."
138
+ dim "Rewriting or re-signing a live Mach-O can make macOS kill it later with Code Signature Invalid."
139
+ dim "Use \`lattices-dev restart\` or \`lattices app restart\` to quit, rebuild, and relaunch."
140
+ exit 1
141
+ fi
142
+ }
143
+
144
+ write_info_plist() {
145
+ mkdir -p "$BUNDLE/Contents"
146
+ cat > "$BUNDLE/Contents/Info.plist" <<PLIST
147
+ <?xml version="1.0" encoding="UTF-8"?>
148
+ <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
149
+ <plist version="1.0">
150
+ <dict>
151
+ <key>CFBundleIdentifier</key>
152
+ <string>$BUNDLE_ID</string>
153
+ <key>CFBundleName</key>
154
+ <string>Lattices</string>
155
+ <key>CFBundleDisplayName</key>
156
+ <string>Lattices</string>
157
+ <key>CFBundleExecutable</key>
158
+ <string>Lattices</string>
159
+ <key>CFBundleIconFile</key>
160
+ <string>AppIcon</string>
161
+ <key>CFBundlePackageType</key>
162
+ <string>APPL</string>
163
+ <key>CFBundleURLTypes</key>
164
+ <array>
165
+ <dict>
166
+ <key>CFBundleURLName</key>
167
+ <string>$BUNDLE_ID</string>
168
+ <key>CFBundleURLSchemes</key>
169
+ <array>
170
+ <string>lattices</string>
171
+ </array>
172
+ </dict>
173
+ </array>
174
+ <key>CFBundleVersion</key>
175
+ <string>$VERSION</string>
176
+ <key>CFBundleShortVersionString</key>
177
+ <string>$VERSION</string>
178
+ <key>LatticesBuildChannel</key>
179
+ <string>dev</string>
180
+ <key>LatticesBuildTrack</key>
181
+ <string>latest</string>
182
+ <key>LatticesBuildRevision</key>
183
+ <string>$GIT_REVISION</string>
184
+ <key>LatticesBuildTimestamp</key>
185
+ <string>$BUILD_TIMESTAMP</string>
186
+ <key>LSMinimumSystemVersion</key>
187
+ <string>13.0</string>
188
+ <key>LSUIElement</key>
189
+ <true/>
190
+ <key>NSHighResolutionCapable</key>
191
+ <true/>
192
+ <key>NSMicrophoneUsageDescription</key>
193
+ <string>Lattices uses the microphone for Hudson Voice dictation and voice commands.</string>
194
+ <key>NSSupportsAutomaticTermination</key>
195
+ <true/>
196
+ </dict>
197
+ </plist>
198
+ PLIST
199
+ }
200
+
201
+ cmd_build() {
202
+ echo "Building dev app..."
203
+ require_build_targets_not_running
204
+ # Build features are declared in apps/mac/build.json and resolved to the
205
+ # HUDSONKIT_WITH_* env HudsonKit gates on (see bin/lattices-build-env.ts).
206
+ eval "$(bun "$ROOT/bin/lattices-build-env.ts" shell)"
207
+ cd "$APP_DIR" && swift build -c release
208
+ # Build into a repo-local dev artifact, then install to a stable app path
209
+ # that macOS permissions can trust across rebuilds.
210
+ rm -rf "$BUNDLE"
211
+ mkdir -p "$(dirname "$BUNDLE_BIN")" "$RESOURCES_DIR"
212
+ cp "$BINARY" "$BUNDLE_BIN"
213
+ if [ -f "$ICON" ]; then
214
+ cp "$ICON" "$RESOURCES_DIR/AppIcon.icns"
215
+ fi
216
+ if [ -f "$TAP_SOUND" ]; then
217
+ cp "$TAP_SOUND" "$RESOURCES_DIR/tap.wav"
218
+ fi
219
+ write_info_plist
220
+ # Re-sign so TCC permissions persist across rebuilds
221
+ sign_bundle
222
+ touch "$BUNDLE" "$BUNDLE/Contents"
223
+ sync_bundle_copy "$INSTALL_BUNDLE"
224
+ clear_lattices_drag_cache
225
+ green "Build complete."
226
+ dim "Installed: $INSTALL_BUNDLE"
227
+ }
228
+
229
+ cmd_restart() {
230
+ echo "Restarting Lattices..."
231
+ /usr/bin/osascript -e 'tell application id "dev.lattices.app.dev" to quit' >/dev/null 2>&1 || true
232
+ /usr/bin/osascript -e 'tell application id "dev.lattices.app" to quit' >/dev/null 2>&1 || true
233
+ pkill -x Lattices 2>/dev/null && sleep 1 || true
234
+ cmd_build
235
+ open "$INSTALL_BUNDLE" --args --lattices-cli-root "$ROOT" "$@"
236
+ green "Lattices restarted."
237
+ }
238
+
239
+ cmd_quit() {
240
+ /usr/bin/osascript -e 'tell application id "dev.lattices.app.dev" to quit' >/dev/null 2>&1 || true
241
+ /usr/bin/osascript -e 'tell application id "dev.lattices.app" to quit' >/dev/null 2>&1 || true
242
+ if pkill -x Lattices 2>/dev/null; then
243
+ green "Lattices stopped."
244
+ else
245
+ dim "Lattices is not running."
246
+ fi
247
+ }
248
+
249
+ cmd_launch() {
250
+ if is_install_bundle_running; then
251
+ dim "Lattices dev app is already running."
252
+ else
253
+ if pgrep -x Lattices >/dev/null 2>&1; then
254
+ dim "A different Lattices bundle is running. Switching to the dev permission target."
255
+ cmd_quit
256
+ fi
257
+ ensure_install_bundle
258
+ open "$INSTALL_BUNDLE" --args --lattices-cli-root "$ROOT" "$@"
259
+ green "Lattices launched."
260
+ fi
261
+ }
262
+
263
+ cmd_logs() {
264
+ if [ -f "$LOG_FILE" ]; then
265
+ tail -f "$LOG_FILE"
266
+ else
267
+ red "No log file at $LOG_FILE"
268
+ fi
269
+ }
270
+
271
+ cmd_log() {
272
+ # Show last N lines (default 30)
273
+ local n="${1:-30}"
274
+ if [ -f "$LOG_FILE" ]; then
275
+ tail -n "$n" "$LOG_FILE"
276
+ else
277
+ red "No log file at $LOG_FILE"
278
+ fi
279
+ }
280
+
281
+ cmd_clear_logs() {
282
+ if [ -f "$LOG_FILE" ]; then
283
+ > "$LOG_FILE"
284
+ green "Logs cleared."
285
+ else
286
+ dim "No log file to clear."
287
+ fi
288
+ }
289
+
290
+ cmd_link() {
291
+ if ! command -v bun >/dev/null 2>&1; then
292
+ red "bun not found. Install: curl -fsSL https://bun.sh/install | bash"
293
+ exit 1
294
+ fi
295
+ cd "$ROOT"
296
+ bun link
297
+ bun link @lattices/cli
298
+ if command -v lattices >/dev/null 2>&1; then
299
+ green "Linked. \`lattices\` -> $(command -v lattices)"
300
+ else
301
+ red "Linked, but \`lattices\` is not on PATH. Add bun's global bin to PATH:"
302
+ dim " export PATH=\"\$HOME/.bun/bin:\$PATH\""
303
+ fi
304
+ }
305
+
306
+ cmd_unlink() {
307
+ if ! command -v bun >/dev/null 2>&1; then
308
+ red "bun not found."
309
+ exit 1
310
+ fi
311
+ bun remove -g @lattices/cli >/dev/null 2>&1 || true
312
+ green "Unlinked."
313
+ }
314
+
315
+ cmd_status() {
316
+ if pgrep -x Lattices >/dev/null 2>&1; then
317
+ local pid=$(pgrep -x Lattices)
318
+ green "Lattices running (pid $pid)"
319
+ else
320
+ dim "Lattices is not running."
321
+ fi
322
+ if [ -f "$LOG_FILE" ]; then
323
+ local lines=$(wc -l < "$LOG_FILE" | tr -d ' ')
324
+ local size=$(du -h "$LOG_FILE" | cut -f1 | tr -d ' ')
325
+ dim "Log: $lines lines, $size"
326
+ fi
327
+ if [ -f "$HOME/.lattices/advisor-learning.jsonl" ]; then
328
+ local entries=$(wc -l < "$HOME/.lattices/advisor-learning.jsonl" | tr -d ' ')
329
+ dim "Advisor learning: $entries entries"
330
+ fi
331
+ }
332
+
333
+ cmd_help() {
334
+ echo "lattices-dev — Lattices development commands"
335
+ echo ""
336
+ echo " link Symlink \`lattices\` to this checkout (bun link)"
337
+ echo " unlink Remove the bun link"
338
+ echo " restart Quit + rebuild + relaunch"
339
+ echo " build Build release binary"
340
+ echo " quit Stop the running app"
341
+ echo " launch Start the app (if not running)"
342
+ echo " logs Tail the log file (live)"
343
+ echo " log [N] Show last N log lines (default 30)"
344
+ echo " clear-logs Clear the log file"
345
+ echo " status Show running state and stats"
346
+ echo " help Show this help"
347
+ }
348
+
349
+ case "${1:-help}" in
350
+ link) cmd_link ;;
351
+ unlink) cmd_unlink ;;
352
+ restart) cmd_restart "${@:2}" ;;
353
+ build) cmd_build ;;
354
+ quit|stop) cmd_quit ;;
355
+ launch|start) cmd_launch "${@:2}" ;;
356
+ logs) cmd_logs ;;
357
+ log) cmd_log "${2:-30}" ;;
358
+ clear-logs) cmd_clear_logs ;;
359
+ status) cmd_status ;;
360
+ help|--help|-h) cmd_help ;;
361
+ *) red "Unknown command: $1"; cmd_help; exit 1 ;;
362
+ esac