* feat: adapt LEDMatrix for monorepo plugin architecture
Update store_manager to fetch manifests from subdirectories within the
monorepo (plugin_path/manifest.json) instead of repo root. Remove 21
plugin submodule entries from .gitmodules, simplify workspace file to
reference the monorepo, and clean up scripts for the new layout.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: auto-reinstall plugins when registry repo URL changes
When a user clicks "Update" on a git-cloned plugin, detect if the
local git remote URL no longer matches the registry's repo URL (e.g.
after monorepo migration). Instead of pulling from the stale archived
repo, automatically remove and reinstall from the new registry source.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: plugin store "View" button links to correct monorepo subdirectory
When a plugin has a plugin_path (monorepo plugin), construct the GitHub
URL as repo/tree/main/plugin_path so users land on the specific plugin
directory. Pass plugin_path through the store API response to the
frontend.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: monorepo manifest fetch in search + version-based update detection
Fix search_plugins() to pass plugin_path when fetching manifests from
GitHub, matching the fix already in get_plugin_info(). Without this,
monorepo plugin descriptions 404 in search results.
Add version comparison for non-git plugins (monorepo installs) so
"Update All" skips plugins already at latest_version instead of blindly
reinstalling every time.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* feat: show plugin version instead of misleading monorepo commit info
Replace commit hash, date, and stars on plugin cards with the plugin's
version number. In a monorepo all plugins share the same commit history
and star count, making those fields identical and misleading. Version
is the meaningful per-plugin signal users care about.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* docs: add CLAUDE.md with project structure and plugin store docs
Documents plugin store architecture, monorepo install flow, version-
based update detection, and the critical version bump workflow.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* perf: extract only target plugin from monorepo ZIP instead of all files
Previously _install_from_monorepo() called extractall() on the entire
monorepo ZIP (~13MB, 600+ files) just to grab one plugin subdirectory.
Now filter zip members by the plugin prefix and extract only matching
files, reducing disk I/O by ~96% per install/update.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* perf: download only target plugin files via GitHub Trees API
Replace full monorepo ZIP download (~5MB) with targeted file downloads
(~200KB per plugin) using the GitHub Git Trees API for directory listing
and raw.githubusercontent.com for individual file content.
One API call fetches the repo tree, client filters for the target
plugin's files, then downloads each file individually. Falls back to
ZIP if the API is unavailable (rate limited, no network, etc.).
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: clean up partial files between API and ZIP install fallbacks
Ensure target_path is fully removed before the ZIP fallback runs, and
before shutil.move() in the ZIP method. Prevents directory nesting if
the API method creates target_path then fails mid-download.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: harden scripts and fix monorepo URL handling
- setup_plugin_repos.py: add type hints, remove unnecessary f-string,
wrap manifest parsing in try/except to skip malformed manifests
- update_plugin_repos.py: add 120s timeout to git pull with
TimeoutExpired handling
- store_manager.py: fix rstrip('.zip') stripping valid branch chars,
use removesuffix('.zip'); remove redundant import json
- plugins_manager.js: View button uses dynamic branch, disables when
repo is missing, encodes plugin_path in URL
- CLAUDE.md: document plugin repo naming convention
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: harden monorepo install security and cleanup
- store_manager: fix temp dir leak in _install_from_monorepo_zip by
moving cleanup to finally block
- store_manager: add zip-slip guard validating extracted paths stay
inside temp directory
- store_manager: add 500-file sanity cap to API-based install
- store_manager: extract _normalize_repo_url as @staticmethod
- setup_plugin_repos: propagate create_symlinks() failure via sys.exit,
narrow except to OSError
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: add path traversal guard to API-based monorepo installer
Validate that each file's resolved destination stays inside
target_path before creating directories or writing bytes, mirroring
the zip-slip guard in _install_from_monorepo_zip.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
* fix: use _safe_remove_directory for monorepo migration cleanup
Replace shutil.rmtree(ignore_errors=True) with _safe_remove_directory
which handles permission errors gracefully and returns status, preventing
install_plugin from running against a partially-removed directory.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
---------
Co-authored-by: Chuck <chuck@example.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
* refactor: migrate from submodules to multi-root workspace for plugins
- Updated LEDMatrix.code-workspace to include all plugin repos as root folders
- Removed symlinks from plugin-repos/ and plugins/ directories
- Updated .gitignore to reflect new plugin management approach
- Added setup_plugin_repos.py script for managing plugin symlinks (if needed)
- Added MULTI_ROOT_WORKSPACE_SETUP.md documentation
Plugins are now managed as independent repositories via multi-root workspace,
allowing for easier development and independent updates without modifying
the LEDMatrix project structure.
* Fix MULTI_ROOT_WORKSPACE_SETUP.md and add JSON error handling
- Remove deprecated clone_plugin_repos.py command reference
- Add language tag to directory tree code fence (fixes MD040)
- Add JSONDecodeError handling in setup_plugin_repos.py with user-friendly error messages
---------
Co-authored-by: Chuck <chuck@example.com>