livepilot 1.21.3 → 1.22.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.
package/CHANGELOG.md CHANGED
@@ -1,5 +1,215 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.22.0 — User atlas separation: ~/.livepilot/atlas/ (April 25 2026)
4
+
5
+ First v1.22 release. Splits the device atlas into two files that serve
6
+ different roles — a long-standing ambiguity that finally bit hard enough
7
+ to be worth fixing at minor-version scope.
8
+
9
+ ### What changed
10
+
11
+ **New: `~/.livepilot/atlas/device_atlas.json`** — the **user atlas**.
12
+ Written by `scan_full_library` on your machine. Reflects YOUR installed
13
+ packs, User Library, and plugins. Lives in the user-data directory
14
+ (same convention as `~/.livepilot/memory/`) so `npm install livepilot`
15
+ upgrades can't blow it away.
16
+
17
+ **Unchanged: `mcp_server/atlas/device_atlas.json`** — the **bundled
18
+ baseline**. Ships with the package (still 5264 devices, 120 enriched
19
+ — stock Ableton 12 Suite inventory). Gives fresh installs a functional
20
+ atlas before any personalized scan has run.
21
+
22
+ **Resolution** at load time: user atlas wins if present, else bundled
23
+ baseline falls through. Writes **always** go to the user path; the
24
+ bundled path is read-only from the scanner's perspective.
25
+
26
+ ### Why this matters
27
+
28
+ Prior to v1.22.0, the single `mcp_server/atlas/device_atlas.json` file
29
+ served three conflicting roles — repo seed, personal scan cache, and
30
+ runtime index. Three bugs resulted:
31
+
32
+ 1. **`npm install livepilot` wiped personal scans.** Every package
33
+ update overwrote the installed atlas with the bundled baseline. Users
34
+ who had carefully scanned their library (30-90 seconds per scan) lost
35
+ that work on every upgrade with no warning.
36
+ 2. **Dev installs polluted the repo.** Contributors running
37
+ LivePilot from a git checkout would scan their library (to test atlas
38
+ tools against live Ableton) and accidentally commit their personal
39
+ scan — pack names, user-library previews — to the public repo. This
40
+ happened once in the v1.21.x cycle and was the proximate trigger for
41
+ v1.22.
42
+ 3. **The "enriched" count ambiguity.** A single atlas file couldn't
43
+ honestly answer "how many devices are enriched?" — the right number
44
+ differed depending on whether you meant the bundled baseline (87 per
45
+ the scanner's last truncated pass), YAML files authored on disk (120),
46
+ or runtime coverage including native/M4L duplicates (137+ on a
47
+ fully-scanned install). The user/bundled split clarifies this by
48
+ letting each file carry its own honest count.
49
+
50
+ ### Migration for existing users
51
+
52
+ Users with a personalized scan in `mcp_server/atlas/device_atlas.json`
53
+ (most dev-install contributors) can migrate in one command:
54
+
55
+ ```bash
56
+ mkdir -p ~/.livepilot/atlas
57
+ cp "$(python3 -c 'import mcp_server.atlas; print(mcp_server.atlas.BUNDLED_ATLAS_PATH)')" \
58
+ ~/.livepilot/atlas/device_atlas.json
59
+ # Then optionally restore the bundled baseline to its shipped state:
60
+ git -C $(python3 -c 'import mcp_server.atlas, pathlib; print(pathlib.Path(mcp_server.atlas.__file__).parents[2])') \
61
+ checkout mcp_server/atlas/device_atlas.json
62
+ ```
63
+
64
+ Users on the npm-installed path (no personalized scan yet) need nothing
65
+ — the next `scan_full_library` call will write to the new user path
66
+ automatically.
67
+
68
+ ### Code changes
69
+
70
+ - `mcp_server/atlas/__init__.py` — new module-level constants
71
+ `BUNDLED_ATLAS_PATH`, `USER_ATLAS_DIR`, `USER_ATLAS_PATH`, and
72
+ function `_resolve_atlas_path()`. Existing `ATLAS_PATH` kept as a
73
+ backward-compat alias pointing at the resolved value.
74
+ - `mcp_server/atlas/tools.py::scan_full_library` — writes to
75
+ `USER_ATLAS_PATH` and creates the user-data directory on demand.
76
+ Enrichments still read from the bundled package (they're authored
77
+ in-repo under `mcp_server/atlas/enrichments/`).
78
+
79
+ ### Tests
80
+
81
+ 8 new TDD regression tests in `tests/test_atlas_user_override.py`:
82
+
83
+ - `test_bundled_path_is_in_package_dir`
84
+ - `test_user_path_is_in_home_dir`
85
+ - `test_resolver_returns_user_path_when_present`
86
+ - `test_resolver_falls_back_to_bundled_when_user_atlas_missing`
87
+ - `test_atlas_manager_loads_from_user_path_when_present`
88
+ - `test_atlas_manager_falls_back_to_bundled_when_user_atlas_missing`
89
+ - `test_scan_full_library_writes_to_user_path`
90
+ - `test_user_atlas_dir_created_if_missing`
91
+
92
+ Full suite: 3136 pass (3128 prior + 8 new), 1 skipped.
93
+
94
+ ### Documentation
95
+
96
+ - `docs/manual/getting-started.md` — new "Step 5 (optional): Personalize
97
+ the device atlas" section.
98
+ - `docs/manual/dev-install.md` — new "3a. User atlas vs bundled atlas"
99
+ subsection, critical for contributors.
100
+ - `docs/manual/device-atlas.md` — header callout on the two-file split.
101
+ - `CLAUDE.md` + `README.md` — short pointers to the new resolver.
102
+
103
+ ### Carried to future releases
104
+
105
+ The atlas schema-canonicalization originally deferred from the v1.21.2
106
+ audit is now **partially closed**: the user/bundled split makes the
107
+ "which number counts as enriched?" question honest per-file, but the
108
+ sync_metadata gate for `stats.enriched_devices` vs YAML file count
109
+ hasn't been added. Deferred to a future patch because it's orthogonal
110
+ to the user-atlas split.
111
+
112
+ ## 1.21.4 — v1.21.2 audit carry-over: slashed-compound filler + dev-install runbook (April 25 2026)
113
+
114
+ Ships two items the v1.21.2 audit #2 deferred to v1.22 but that turned
115
+ out to be safer to close in a patch cycle than to carry forward:
116
+
117
+ 1. **`check_prose_claim` regex widen for slashed/chained compound fillers** —
118
+ `"38 spectral/analyzer tools"` drift across 3 docs silently passed
119
+ `sync_metadata --check` because the filler group required trailing
120
+ whitespace. Now caught.
121
+ 2. **Dev-install runbook** (`docs/manual/dev-install.md`) — documented
122
+ path for contributors to run LivePilot from a local checkout without
123
+ publishing to npm. Previously every contributor had to reverse-engineer
124
+ this from `bin/livepilot.js` + `mcp_server/__main__.py`.
125
+
126
+ No API changes. No user-observable behavior change at runtime. Four new
127
+ TDD regression tests for the regex widening; ~180 lines of new
128
+ documentation. `sync_metadata --check` now catches an entire class of
129
+ compound-form drift that would otherwise have shipped untracked.
130
+
131
+ ### 1. `sync_metadata` slashed/chained compound filler
132
+
133
+ All 4 regex patterns in `scripts/sync_metadata.py` (`check_tool_count`,
134
+ `check_prose_claim`, `fix_tool_count`, `_fix_count`) had an optional
135
+ filler group that required trailing whitespace —
136
+ `[A-Za-z]+\s+` — and matched at most once (`?` quantifier). Slashed
137
+ compounds like `"spectral/analyzer"` were rejected because `/` is not
138
+ whitespace; chained compounds like `"spectral/MCP tools"` (two filler
139
+ segments) were rejected because of the single-match quantifier.
140
+
141
+ v1.21.4 widens the filler to two branches, iterable 0+ times:
142
+
143
+ | Branch | Pattern | Matches |
144
+ |---|---|---|
145
+ | (a) Space-joined, uppercase-anchored | `[A-Z][A-Za-z\-]*\s+` | `"MCP "` in `"430 MCP Tools"` |
146
+ | (b) Slash-joined, any-case | `[A-Za-z][A-Za-z\-]*/` | `"spectral/"` in `"38 spectral/analyzer tools"` |
147
+
148
+ The uppercase anchor on branch (a) is preserved deliberately — without
149
+ it, lowercase English articles ("the tool") would false-positive on
150
+ prose like "released in 2020 the tool was first previewed." The slash
151
+ on branch (b) is itself an unambiguous compound marker that doesn't
152
+ appear in normal English, so any-case there is safe. The `*` quantifier
153
+ lets branches chain: `"300 spectral/MCP tools"` now parses as
154
+ `(b)"spectral/"` + `(a)"MCP "` + `"tools"`.
155
+
156
+ For `check_prose_claim` + its paired `_fix_count`, the filler is a
157
+ simpler single-branch `[A-Za-z][A-Za-z\-]*(?:/|\s+)` — these don't need
158
+ the uppercase anchor (prose-claim nouns predate the v1.21.4 cycle and
159
+ already allowed lowercase-start fillers).
160
+
161
+ **Concrete effect**: `sync_metadata --check` now flags drift in all 3
162
+ real surfaces that previously escaped:
163
+
164
+ - `README.md` — `"38 spectral/analyzer tools"` × 2
165
+ - `docs/manual/getting-started.md` — `"38 spectral/analyzer tools"` × 1
166
+ - `livepilot/skills/livepilot-release/SKILL.md` — `"38 spectral/analyzer tools"` × 1
167
+
168
+ (Today these say "38" and the count is 38, so no drift reported — but
169
+ the next time the analyzer module gains or loses a `@mcp.tool`, CI will
170
+ fail loudly instead of silently drifting.)
171
+
172
+ Tests added to `tests/test_claim_consistency.py` (4):
173
+
174
+ - `test_prose_claim_catches_slashed_compound` — drift detection
175
+ - `test_fix_count_rewrites_slashed_compound` — `--fix` preserves the adjective
176
+ - `test_tool_count_catches_slashed_compound` — chained filler (`"300 spectral/MCP tools"`)
177
+ - `test_tool_count_no_false_positive_on_year_prose` — uppercase anchor guard
178
+
179
+ ### 2. Dev-install runbook
180
+
181
+ New file `docs/manual/dev-install.md` (~180 lines) documents the
182
+ bare-python local-checkout workflow for contributors:
183
+
184
+ 1. **venv setup** — `python3 -m venv .venv && source .venv/bin/activate && pip install -r requirements.txt`
185
+ 2. **Local Remote Script install** — `node bin/livepilot.js --install` (NOT `npx livepilot --install`, which resolves to the registry and silently discards local edits)
186
+ 3. **MCP client config pointing at `python -m mcp_server`** — per-client examples for Claude Desktop, Claude Code, Cursor/VS Code
187
+ 4. **Iterate loop** — client restart for `mcp_server/` edits; `node bin/livepilot.js --install` + `reload_handlers` tool for `remote_script/` edits
188
+ 5. **Test suite** — `python -m pytest tests/ -q` (3128 tests as of v1.21.4)
189
+ 6. **`sync_metadata` drift check** — `python scripts/sync_metadata.py --check` / `--fix`
190
+ 7. **Going back to published** — remove the dev MCP entry or stop using it
191
+
192
+ Plus a troubleshooting section for the 4 most common first-run failures:
193
+ `ModuleNotFoundError: No module named 'mcp_server'`, `Another client is
194
+ already connected` on port 9878, stale `__pycache__` shadowing edits, and
195
+ module-level constants not reloading via `reload_handlers` (full Ableton
196
+ restart required).
197
+
198
+ Cross-links added:
199
+
200
+ - `docs/manual/getting-started.md` — callout at top routing contributors to dev-install before they commit to the npm path
201
+ - `docs/manual/index.md` — new "Contributing" section in the Chapters TOC
202
+ - `CLAUDE.md` — pointer in the Project section
203
+
204
+ ### Carried forward to v1.22
205
+
206
+ Still deferred from the v1.21.2 audit:
207
+
208
+ - Atlas `.enriched` schema canonicalization (`stats.enriched_devices = 87`
209
+ vs 120 YAML files vs 135 historical claim — pick one and derive the
210
+ others at build time; add a sync_metadata gate matching JSON stats to
211
+ YAML file count)
212
+
3
213
  ## 1.21.3 — Third audit-response: manual-docs drift + sync_metadata file-list widening + unicode-escape regex fix (April 24 2026)
4
214
 
5
215
  Fourth same-day patch in 48 hours, third audit-response in 24 hours.
package/README.md CHANGED
@@ -105,7 +105,7 @@ Most MCP servers are tool collections — they execute commands. LivePilot is an
105
105
 
106
106
  **M4L Bridge** (`m4l_device/`) — Optional Max for Live Audio Effect on the master track. Provides deep LOM access through Max's LiveAPI that the ControlSurface API can't reach. UDP 9880 (M4L to server) carries spectral data and LiveAPI responses. OSC 9881 (server to M4L) sends commands. The 38 spectral/analyzer tools strictly require the bridge; device and sample tools that call the bridge also have graceful fallbacks, so core functionality works without it. Backed by 31 bridge commands for hidden parameters, Simpler internals, warp markers, display values, and Simpler warp / Compressor sidechain writes that live on child objects Python can't reach.
107
107
 
108
- **Device Atlas** (`mcp_server/atlas/`) — In-memory indexed JSON database. 5264 devices with browser URIs, 120 enriched with YAML sonic intelligence profiles (mood, genre, texture, recommended chains). 7 indexes: by_id, by_name, by_uri, by_category, by_tag, by_genre, by_pack. Reverse-index `device_techniques_index.json` powers `atlas_techniques_for_device` (146 cross-references across 58 devices). The AI never hallucinates a device name or preset — it always resolves against the atlas first.
108
+ **Device Atlas** (`mcp_server/atlas/`) — In-memory indexed JSON database. 5264 devices with browser URIs (bundled baseline), 120 enriched with YAML sonic intelligence profiles (mood, genre, texture, recommended chains). 7 indexes: by_id, by_name, by_uri, by_category, by_tag, by_genre, by_pack. Reverse-index `device_techniques_index.json` powers `atlas_techniques_for_device` (146 cross-references across 58 devices). The AI never hallucinates a device name or preset — it always resolves against the atlas first. **v1.22.0+**: run `scan_full_library` after install to index YOUR packs + User Library + plugins into `~/.livepilot/atlas/device_atlas.json` — your personal atlas overrides the baseline and survives npm updates.
109
109
 
110
110
  **Sample Engine** (`mcp_server/sample_engine/`) — Searches three sources simultaneously: BrowserSource (Ableton's library), SpliceSource (local Splice catalog via SQLite), FilesystemSource (user directories). Every result passes through a 6-critic fitness battery (key, tempo, spectral, genre, mood, technical). 29 processing techniques (Surgeon precision vs. Alchemist experimentation). Builds complete sample processing plans with warp, slice, and effect recommendations.
111
111
 
Binary file
@@ -1,2 +1,2 @@
1
1
  """LivePilot MCP Server — bridges MCP protocol to Ableton Live."""
2
- __version__ = "1.21.3"
2
+ __version__ = "1.22.0"
@@ -519,28 +519,75 @@ class AtlasManager:
519
519
  from pathlib import Path
520
520
  from ..services.singletons import Singleton
521
521
 
522
- ATLAS_PATH = Path(__file__).parent / "device_atlas.json"
522
+ # v1.22.0: the atlas now has TWO possible homes —
523
+ #
524
+ # BUNDLED_ATLAS_PATH — mcp_server/atlas/device_atlas.json
525
+ # Ships with the package. Gives fresh installs a functional
526
+ # baseline device index before any personalized scan has run.
527
+ # Updated only when the repo's canonical baseline is regenerated.
528
+ #
529
+ # USER_ATLAS_PATH — ~/.livepilot/atlas/device_atlas.json
530
+ # Written by scan_full_library on the user's machine. Reflects
531
+ # their actual installed packs, User Library, and plugins. Lives
532
+ # in the user-data directory (same convention as
533
+ # ~/.livepilot/memory/) so npm updates can't blow it away.
534
+ #
535
+ # Resolution order at load time: user atlas wins if present, else
536
+ # bundled baseline. Written scans ALWAYS go to the user path — never
537
+ # the bundled path — so the repo/npm package stays clean regardless of
538
+ # where a user runs scan_full_library from. This split lands in v1.22.0
539
+ # and solves three prior issues (see tests/test_atlas_user_override.py
540
+ # for the full rationale).
541
+ BUNDLED_ATLAS_PATH = Path(__file__).parent / "device_atlas.json"
542
+ USER_ATLAS_DIR = Path.home() / ".livepilot" / "atlas"
543
+ USER_ATLAS_PATH = USER_ATLAS_DIR / "device_atlas.json"
544
+
545
+
546
+ def _resolve_atlas_path() -> Path:
547
+ """Return the effective atlas path — user if present, bundled if not.
548
+
549
+ Called from _build_atlas and get_atlas. Kept as a module-level
550
+ function (rather than inlined) so tests can monkeypatch HOME and
551
+ reimport the module to re-evaluate USER_ATLAS_PATH cleanly.
552
+ """
553
+ if USER_ATLAS_PATH.exists():
554
+ return USER_ATLAS_PATH
555
+ return BUNDLED_ATLAS_PATH
556
+
557
+
558
+ # Backward-compat alias. External code that imported ATLAS_PATH before
559
+ # v1.22.0 (e.g. pre-existing scripts outside this repo) gets the
560
+ # resolved path. Internal code should call _resolve_atlas_path() so the
561
+ # value is re-evaluated after the user first runs scan_full_library.
562
+ ATLAS_PATH = _resolve_atlas_path()
523
563
 
524
564
  _atlas_instance: Optional[AtlasManager] = None # kept for legacy imports
525
565
 
526
566
 
527
567
  def _build_atlas() -> AtlasManager:
528
- return AtlasManager(str(ATLAS_PATH))
568
+ return AtlasManager(str(_resolve_atlas_path()))
529
569
 
530
570
 
531
571
  _atlas_holder = Singleton(_build_atlas)
532
572
 
533
573
 
534
574
  def get_atlas() -> AtlasManager:
535
- """Thread-safe accessor. Re-reads device_atlas.json if its mtime advanced."""
575
+ """Thread-safe accessor. Re-reads the resolved atlas path if its
576
+ mtime advanced. Uses the resolver, so if the user's first
577
+ scan_full_library call runs mid-session, the next get_atlas()
578
+ picks up the new user path (via invalidate_atlas, which
579
+ scan_full_library already calls on success)."""
536
580
  global _atlas_instance
537
- instance = _atlas_holder.get(reload_if_newer=ATLAS_PATH)
581
+ instance = _atlas_holder.get(reload_if_newer=_resolve_atlas_path())
538
582
  _atlas_instance = instance # keep legacy attribute in sync
539
583
  return instance
540
584
 
541
585
 
542
586
  def invalidate_atlas() -> None:
543
- """Force the next get_atlas() to re-read device_atlas.json from disk."""
587
+ """Force the next get_atlas() to re-read the resolved atlas path
588
+ from disk. Called by scan_full_library after writing a fresh
589
+ user atlas so subsequent tool calls see the new data without
590
+ an MCP server restart."""
544
591
  global _atlas_instance
545
592
  _atlas_holder.invalidate()
546
593
  _atlas_instance = None
@@ -466,11 +466,17 @@ def scan_full_library(
466
466
  """
467
467
  from .scanner import normalize_scan_results
468
468
  from .enrichments import load_enrichments, merge_enrichments
469
- from . import AtlasManager
469
+ from . import AtlasManager, USER_ATLAS_DIR, USER_ATLAS_PATH
470
470
 
471
+ # v1.22.0: scans always write to the user atlas path, never the
472
+ # bundled baseline. The user-data directory is created on demand
473
+ # so a brand-new install (no ~/.livepilot/ at all) still works.
474
+ # Enrichments are read from the bundled package (same as before —
475
+ # they're authored in-repo).
471
476
  atlas_dir = os.path.dirname(os.path.abspath(__file__))
472
- atlas_path = os.path.join(atlas_dir, "device_atlas.json")
473
477
  enrichments_dir = os.path.join(atlas_dir, "enrichments")
478
+ USER_ATLAS_DIR.mkdir(parents=True, exist_ok=True)
479
+ atlas_path = str(USER_ATLAS_PATH)
474
480
 
475
481
  if not force and os.path.exists(atlas_path):
476
482
  age = time.time() - os.path.getmtime(atlas_path)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "livepilot",
3
- "version": "1.21.3",
3
+ "version": "1.22.0",
4
4
  "mcpName": "io.github.dreamrec/livepilot",
5
5
  "description": "Agentic production system for Ableton Live 12 — 430 tools, 53 domains, 44 semantic moves. Device atlas (5264 devices, 120 enriched, 7 indexes), Splice intelligence (gRPC + GraphQL describe-a-sound + preview + collections + presets), 9-band spectral perception auto-loaded via ensure_analyzer_on_master, Creative Director skill, technique memory, 12 creative intelligence engines",
6
6
  "author": "Pilot Studio",
@@ -5,7 +5,7 @@ Entry point for the ControlSurface. Ableton calls create_instance(c_instance)
5
5
  when this script is selected in Preferences > Link, Tempo & MIDI.
6
6
  """
7
7
 
8
- __version__ = "1.21.3"
8
+ __version__ = "1.22.0"
9
9
 
10
10
  from _Framework.ControlSurface import ControlSurface
11
11
  from . import router
package/server.json CHANGED
@@ -6,12 +6,12 @@
6
6
  "url": "https://github.com/dreamrec/LivePilot",
7
7
  "source": "github"
8
8
  },
9
- "version": "1.21.3",
9
+ "version": "1.22.0",
10
10
  "packages": [
11
11
  {
12
12
  "registryType": "npm",
13
13
  "identifier": "livepilot",
14
- "version": "1.21.3",
14
+ "version": "1.22.0",
15
15
  "transport": {
16
16
  "type": "stdio"
17
17
  }