jlegewie/zotfile
Zotero plugin to manage your attachments: automatically rename, move, and attach PDFs (or other files) to Zotero items, sync PDFs from your Zotero library to your (mobile) PDF reader (e.g. an iPad, Android tablet, etc.), and extract PDF annotations.
Stale and unlicensed — last commit 2y ago
weakest axisno license — legally unclear; last commit was 2y ago…
no license — can't legally use code; no tests detected…
Documented and popular — useful reference codebase to read through.
no license — can't legally use code; last commit was 2y ago
- ✓25+ active contributors
- ✓CI configured
- ⚠Stale — last commit 2y ago
Show all 6 evidence items →Show less
- ⚠Concentrated ownership — top contributor handles 56% of recent commits
- ⚠No license — legally unclear to depend on
- ⚠No test directory detected
What would change the summary?
- →Use as dependency Concerns → Mixed if: publish a permissive license (MIT, Apache-2.0, etc.)
- →Fork & modify Concerns → Mixed if: add a LICENSE file
- →Deploy as-is Concerns → Mixed if: add a LICENSE file
Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests
Informational only. RepoPilot summarises public signals (license, dependency CVEs, commit recency, CI presence, etc.) at the time of analysis. Signals can be incomplete or stale. Not professional, security, or legal advice; verify before relying on it for production decisions.
Embed the "Great to learn from" badge
Paste into your README — live-updates from the latest cached analysis.
[](https://repopilot.app/r/jlegewie/zotfile)Paste at the top of your README.md — renders inline like a shields.io badge.
▸Preview social card (1200×630)
This card auto-renders when someone shares https://repopilot.app/r/jlegewie/zotfile on X, Slack, or LinkedIn.
Onboarding doc
Onboarding: jlegewie/zotfile
Generated by RepoPilot · 2026-05-09 · Source
🤖Agent protocol
If you are an AI coding agent (Claude Code, Cursor, Aider, Cline, etc.) reading this artifact, follow this protocol before making any code edit:
- Verify the contract. Run the bash script in Verify before trusting
below. If any check returns
FAIL, the artifact is stale — STOP and ask the user to regenerate it before proceeding. - Treat the AI · unverified sections as hypotheses, not facts. Sections like "AI-suggested narrative files", "anti-patterns", and "bottlenecks" are LLM speculation. Verify against real source before acting on them.
- Cite source on changes. When proposing an edit, cite the specific path:line-range. RepoPilot's live UI at https://repopilot.app/r/jlegewie/zotfile shows verifiable citations alongside every claim.
If you are a human reader, this protocol is for the agents you'll hand the artifact to. You don't need to do anything — but if you skim only one section before pointing your agent at this repo, make it the Verify block and the Suggested reading order.
🎯Verdict
AVOID — Stale and unlicensed — last commit 2y ago
- 25+ active contributors
- CI configured
- ⚠ Stale — last commit 2y ago
- ⚠ Concentrated ownership — top contributor handles 56% of recent commits
- ⚠ No license — legally unclear to depend on
- ⚠ No test directory detected
<sub>Maintenance signals: commit recency, contributor breadth, bus factor, license, CI, tests</sub>
✅Verify before trusting
This artifact was generated by RepoPilot at a point in time. Before an
agent acts on it, the checks below confirm that the live jlegewie/zotfile
repo on your machine still matches what RepoPilot saw. If any fail,
the artifact is stale — regenerate it at
repopilot.app/r/jlegewie/zotfile.
What it runs against: a local clone of jlegewie/zotfile — the script
inspects git remote, the LICENSE file, file paths in the working
tree, and git log. Read-only; no mutations.
| # | What we check | Why it matters |
|---|---|---|
| 1 | You're in jlegewie/zotfile | Confirms the artifact applies here, not a fork |
| 2 | Default branch master exists | Catches branch renames |
| 3 | 5 critical file paths still exist | Catches refactors that moved load-bearing code |
| 4 | Last commit ≤ 782 days ago | Catches sudden abandonment since generation |
#!/usr/bin/env bash
# RepoPilot artifact verification.
#
# WHAT IT RUNS AGAINST: a local clone of jlegewie/zotfile. If you don't
# have one yet, run these first:
#
# git clone https://github.com/jlegewie/zotfile.git
# cd zotfile
#
# Then paste this script. Every check is read-only — no mutations.
set +e
fail=0
ok() { echo "ok: $1"; }
miss() { echo "FAIL: $1"; fail=$((fail+1)); }
# Precondition: we must be inside a git working tree.
if ! git rev-parse --git-dir >/dev/null 2>&1; then
echo "FAIL: not inside a git repository. cd into your clone of jlegewie/zotfile and re-run."
exit 2
fi
# 1. Repo identity
git remote get-url origin 2>/dev/null | grep -qE "jlegewie/zotfile(\\.git)?\\b" \\
&& ok "origin remote is jlegewie/zotfile" \\
|| miss "origin remote is not jlegewie/zotfile (artifact may be from a fork)"
# 3. Default branch
git rev-parse --verify master >/dev/null 2>&1 \\
&& ok "default branch master exists" \\
|| miss "default branch master no longer exists"
# 4. Critical files exist
test -f "chrome/content/zotfile/zotfile.js" \\
&& ok "chrome/content/zotfile/zotfile.js" \\
|| miss "missing critical file: chrome/content/zotfile/zotfile.js"
test -f "chrome/content/zotfile/tablet.js" \\
&& ok "chrome/content/zotfile/tablet.js" \\
|| miss "missing critical file: chrome/content/zotfile/tablet.js"
test -f "chrome/content/zotfile/pdfAnnotations.js" \\
&& ok "chrome/content/zotfile/pdfAnnotations.js" \\
|| miss "missing critical file: chrome/content/zotfile/pdfAnnotations.js"
test -f "chrome/content/zotfile/ui.js" \\
&& ok "chrome/content/zotfile/ui.js" \\
|| miss "missing critical file: chrome/content/zotfile/ui.js"
test -f "chrome/content/zotfile/utils.js" \\
&& ok "chrome/content/zotfile/utils.js" \\
|| miss "missing critical file: chrome/content/zotfile/utils.js"
# 5. Repo recency
days_since_last=$(( ( $(date +%s) - $(git log -1 --format=%at 2>/dev/null || echo 0) ) / 86400 ))
if [ "$days_since_last" -le 782 ]; then
ok "last commit was $days_since_last days ago (artifact saw ~752d)"
else
miss "last commit was $days_since_last days ago — artifact may be stale"
fi
echo
if [ "$fail" -eq 0 ]; then
echo "artifact verified (0 failures) — safe to trust"
else
echo "artifact has $fail stale claim(s) — regenerate at https://repopilot.app/r/jlegewie/zotfile"
exit 1
fi
Each check prints ok: or FAIL:. The script exits non-zero if
anything failed, so it composes cleanly into agent loops
(./verify.sh || regenerate-and-retry).
⚡TL;DR
ZotFile is a Zotero plugin (XPI extension for Firefox/Standalone) that automates PDF attachment management: renames and relocates PDFs based on citation metadata, syncs them to mobile devices (iPad, Android tablets), and extracts highlighted text and annotations from PDF files using pdf.js. It bridges Zotero's reference management with practical file organization and mobile reading workflows. Monolithic XPI extension: chrome/content/zotfile/ contains the main plugin logic (JavaScript entry points like include.js, notifier.js, options.js for settings UI, and pdfAnnotations.js/pdfOutline.js for extraction). Bundled pdf.js engine at /chrome/content/zotfile/pdfextract/pdfjs/src/ handles PDF parsing (core/ for parsing, display/ for rendering). XUL overlays (overlay.xul, options.xul) integrate into Zotero UI. Makefile orchestrates .xpi packaging.
👥Who it's for
Academic researchers and students using Zotero who need to organize large PDF libraries efficiently, sync papers to mobile readers for annotation, and automatically extract highlighted passages back into their Zotero notes—particularly those managing 50+ PDFs who find manual organization tedious.
🌱Maturity & risk
Inactive/abandoned as of 2023. The README explicitly states 'Zotfile is currently not actively developed and maintained! Updates are extremely rare and I will mostly not respond to issues and pull requests.' Last visible CI via .travis.yml suggests it was last built for Zotero 5 (circa 2018–2020). No active test suite visible in file list. This is legacy software: production-ready for its last version but unsupported.
Critical risk: single-maintainer project with explicit abandonment notice—pull requests and issues go unanswered. Heavy dependency on pdf.js (/chrome/content/zotfile/pdfextract/pdfjs/ with 40+ core modules) which may diverge from current versions. XPI format ties it tightly to specific Zotero/Firefox versions; Zotero 7+ may break compatibility. No package.json or npm lock file visible, making dependency versions opaque. Likely incompatible with recent Zotero releases.
Active areas of work
Nothing. Repository is in maintenance-only mode with no active development. The README warns of rare updates and non-responsiveness to issues. Travis CI config (.travis.yml) exists but likely defunct. Last commit activity likely years old (not visible in repo data). This is a frozen snapshot of Zotero 5-era functionality.
🚀Get running
Clone and build the XPI for local testing: git clone https://github.com/jlegewie/zotfile.git && cd zotfile && make. This creates a .xpi file (via the Makefile) which you can then install into Zotero Standalone via Tools → Add-ons → Install Add-on From File. For development, edit JavaScript files in chrome/content/zotfile/ and rebuild with make.
Daily commands:
This is not a runnable application; it's an extension. To test: 1) make to build .xpi, 2) Start Zotero Standalone, 3) Tools → Add-ons → Install from File → select .xpi. For active development, modify files in chrome/content/zotfile/ and re-run make. No dev server or npm start command.
🗺️Map of the codebase
chrome/content/zotfile/zotfile.js— Main plugin entry point and controller orchestrating all ZotFile operations (renaming, moving, syncing, annotations)chrome/content/zotfile/tablet.js— Core logic for syncing PDFs to mobile devices and managing tablet-based workflows; essential for sync functionalitychrome/content/zotfile/pdfAnnotations.js— Handles PDF annotation extraction and processing; critical for the annotation feature pipelinechrome/content/zotfile/ui.js— UI layer that bridges Zotero integration and user interactions; manages context menus and dialogschrome/content/zotfile/utils.js— Utility functions for file operations, path handling, and wildcard expansion used across the pluginchrome/content/zotfile/notifier.js— Event notification system integrating with Zotero's observer pattern for item updates and triggersdefaults/preferences/defaults.js— Default configuration values that establish all plugin behavior; referenced throughout codebase
🛠️How to make changes
Add a new PDF annotation type to extract
- Define the annotation type handler in the PDF.js annotation model (
chrome/content/zotfile/pdfextract/pdfjs/src/core/annotation.js) - Add extraction logic in the main extraction engine to handle the new type (
chrome/content/zotfile/pdfextract/extract.js) - Format and return annotation in getPDFAnnotations API (
chrome/content/zotfile/pdfextract/pdfjs/src/getPDFAnnotations.js) - Process extracted annotations in the main annotation handler (
chrome/content/zotfile/pdfAnnotations.js)
Add a new file naming wildcard variable
- Add the wildcard pattern and replacement logic to the wildcard expander (
chrome/content/zotfile/wildcards.js) - Document the wildcard in the UI help text within options (
chrome/content/zotfile/options.js) - Add UI label for the wildcard in the options panel (
chrome/content/zotfile/options.xul) - Add localization strings for the wildcard in all language files (
chrome/locale/en-US/options.properties)
Add a new tablet sync format or destination
- Define the new sync target configuration in default preferences (
defaults/preferences/defaults.js) - Implement sync protocol logic for the new tablet format or folder structure (
chrome/content/zotfile/tablet.js) - Add UI configuration options for the new sync target (
chrome/content/zotfile/options.xul) - Test by triggering sync via the main zotfile controller (
chrome/content/zotfile/zotfile.js)
Add a new user preference setting
- Add the default value in the preferences module (
defaults/preferences/defaults.js) - Create UI control in the options form (
chrome/content/zotfile/options.xul) - Add change handler in the options controller (
chrome/content/zotfile/options.js) - Add localization string for the label (
chrome/locale/en-US/options.properties) - Read and use the preference value in the relevant business logic (
chrome/content/zotfile/zotfile.js)
🔧Why these technologies
- XUL/Firefox extension API — Zotero 4.x was built on Firefox platform; ZotFile must integrate as a Zotero plugin using its extension model
- PDF.js library — Enables client-side PDF parsing and annotation extraction without external dependencies or server calls
- Zotero JavaScript API — Required to read/write item metadata, manage attachments, and observe library changes within Zotero
- File system I/O via JavaScript — Must rename, move, and organize PDF files on disk according to user preferences and patterns
⚖️Trade-offs already made
-
Bundled PDF.js instead of external viewer
- Why: Avoids network calls and external dependencies; allows offline annotation extraction
- Consequence: Large library footprint (~2MB) increases plugin size; must maintain PDF.js version
-
Synchronous file operations in some paths
- Why: Simpler control flow for rename/move operations; required for deterministic folder operations
- Consequence: UI can freeze on slow file systems or large directory scans; mobile sync is slow over network
-
Single-threaded event processing
- Why: Maintains plugin simplicity and Zotero API compatibility; avoids concurrency issues
- Consequence: Cannot process multiple PDFs in parallel; batch operations block UI
-
Wildcard-based naming instead of templating engine
- Why: User-friendly simple syntax; easy to extend without dependencies
- Consequence: Limited expressiveness for complex naming rules; no conditional logic possible
🚫Non-goals (don't propose these)
- Real-time bi-directional sync with tablet devices (unidirectional push/pull only)
- Support for Zotero versions before 4.0
- Cloud storage integration (Google Drive, Dropbox); only local file system and USB sync
- Annotation editing or modification; extraction and insertion into notes only
- Web-based or mobile app interface; browser extension for Firefox/Zotero only
🪤Traps & gotchas
- Zotero version pinning: Extension targets Zotero 4.x and 5.x (see install.rdf, not in file list but implied). Likely broken on Zotero 6+/7+ due to API changes. 2) XPCOM/XUL era code: Uses deprecated Mozilla technologies; cannot run on modern Firefox or WebExtensions-only versions. 3) pdf.js vendoring: Bundled pdf.js is likely outdated; updating it requires careful compatibility testing with pdfAnnotations.js. 4) No modern build tooling: Manual Makefile-based build; no webpack/babel. 5) Implicit Java dependency: 1.5MB Java codebase in repo but not referenced in visible files—may be in a compiled JAR not shown. 6) Sync target filesystem access: Requires nsFile/file:// URIs (XPCOM APIs) which won't work in WebExtensions sandboxes.
🏗️Architecture
💡Concepts to learn
- Firefox XPI (Extension Package Interface) — ZotFile is distributed as an .xpi file; understanding XPI structure (chrome manifest, overlay registration, resource URLs) is essential for debugging extension integration and packaging.
- XPCOM (Cross Platform Component Object Model) — ZotFile uses XPCOM APIs (nsIFile, nsIObserverService, etc.) for file I/O, event listening, and Zotero integration; understanding this legacy Mozilla architecture is needed to maintain or update the extension.
- PDF.js Worker Architecture — The bundled pdf.js uses a worker thread model (
/src/core/worker.js) for parsing PDFs without blocking the UI; modifying annotation extraction requires understanding this async parsing pipeline. - XUL Overlay & Manifest Registration — ZotFile injects UI elements (context menus, preference panes) via XUL overlay files (
overlay.xul) registered inchrome.manifest; understanding overlay binding is needed to add new UI features. - PDF Annotation Objects (PDF spec) — ZotFile extracts annotations from the PDF standard's annotation dictionary; knowing how highlights, comments, and ink annotations are stored in PDFs helps debug extraction failures.
- Zotero Item-to-File Mapping — ZotFile's core feature is syncing Zotero bibliographic items to filesystem PDFs; understanding Zotero's attachment API (itemID ↔ filepath) is critical for the rename/move logic in
include.js. - Event-Driven UI Updates (Observer Pattern) — ZotFile responds to Zotero item changes via the observer pattern (
notifier.js); understanding how to hook into Zotero's event stream is essential for triggering plugin actions.
🔗Related repos
zotero/zotero— The Zotero reference manager itself; ZotFile is an extension that depends entirely on Zotero's API and data model.mozilla/pdf.js— The PDF parsing/rendering engine bundled in/pdfextract/pdfjs/; upstream source for annotation extraction logic.Zotero/zotero-connectors— Official Zotero browser connector extensions; similar XPI/WebExtension architecture for integrating with Zotero from external tools.retorquere/zotero-better-bibtex— Active Zotero extension (Better BibTeX) that shows modern approaches to extending Zotero; demonstrates current best practices for maintaining Zotero plugins.jlegewie/zotfile-mobile— Companion project (if it exists) for the mobile sync feature of ZotFile, enabling annotation sync from iPad/Android back to Zotero.
🪄PR ideas
To work on one of these in Claude Code or Cursor, paste:
Implement the "<title>" PR idea from CLAUDE.md, working through the checklist as the task list.
Add unit tests for pdfAnnotations.js and extract PDF annotation parsing logic
The pdfAnnotations.js file handles critical PDF annotation extraction functionality, but there are no visible test files in the repo. Given that this is a core feature (mentioned in the README), adding unit tests would ensure robustness and make future maintenance safer. This is especially important since the PDF.js dependency is vendored and complex.
- [ ] Create test directory structure (e.g., chrome/content/zotfile/tests/)
- [ ] Write unit tests for pdfAnnotations.js focusing on annotation parsing, color extraction, and formatting
- [ ] Write unit tests for pdfExtract/extract.js to test PDF text and annotation extraction
- [ ] Add test runner configuration (Jest or Mocha compatible with Zotero's JavaScript environment)
- [ ] Document testing approach in README under 'Development' section
Refactor tablet.js into feature-specific modules and add sync operation tests
tablet.js likely contains all mobile PDF reader sync logic (iPad, Android tablet), making it a large, complex file. Splitting it into modules (e.g., tablet-sync.js, tablet-conflict-resolution.js, tablet-annotation-import.js) would improve maintainability. Additionally, there are no visible tests for the critical sync functionality mentioned in the README.
- [ ] Analyze tablet.js to identify distinct sync operations and extract into separate modules in chrome/content/zotfile/tablet/
- [ ] Create tablet-sync.js for core sync operations, tablet-annotations.js for annotation handling, tablet-storage.js for file operations
- [ ] Extract utility functions into tablet-utils.js and update include.js to require the new modules
- [ ] Add integration tests for sync workflow (chrome/content/zotfile/tests/tablet-sync.test.js)
- [ ] Update README with architecture documentation for tablet sync feature
Add comprehensive inline documentation to zotfile.js and create API reference for extension hooks
zotfile.js is the main plugin entry point but appears to lack JSDoc comments explaining the module's structure, major functions, and how it integrates with Zotero's extension API. This is critical for new contributors. The notifier.js and overlay.xul integration points are also undocumented, making it hard to understand the event flow.
- [ ] Add JSDoc headers to all major functions in zotfile.js documenting parameters, return types, and Zotero API dependencies
- [ ] Document the extension lifecycle in zotfile.js: initialization, notifier registration, menu binding
- [ ] Add JSDoc to notifier.js explaining how Zotero item events are captured and processed
- [ ] Create ARCHITECTURE.md documenting: (1) module dependencies, (2) Zotero API integration points, (3) event flow from UI interaction to file operations
- [ ] Document key configuration object structures (e.g., Zotfile.prefs usage) with examples
🌿Good first issues
- Add unit tests for
chrome/content/zotfile/pdfAnnotations.js: extract a few test PDFs with known annotations, write Jest/Mocha tests verifying extracted text matches expected output. Currently no test files visible; this would prevent regressions when updating pdf.js. - Document the Java component: The 1.5MB of Java code is unexplained. Create a
ARCHITECTURE.mdfile explaining what Java modules do, where they're compiled, and how they integrate with the JavaScript extension. This will help future maintainers. - Fix deprecated XPCOM/XUL warnings: Scan
include.js,notifier.js, andoptions.jsfor deprecated nsIFile/nsIDOMWindow calls and replace with modern Zotero 5 APIs (e.g., using Services.obs for event handling, promiseUtils for async). Create a lint rule to catch future violations.
⭐Top contributors
Click to expand
Top contributors
- @jlegewie — 56 commits
- [@Joscha Legewie](https://github.com/Joscha Legewie) — 10 commits
- @dstillman — 8 commits
- @qqobb — 3 commits
- @vancleve — 2 commits
📝Recent commits
Click to expand
Recent commits
7b2c98b— Update icon (jlegewie)4596c38— CollegeWhiz icon (jlegewie)e4b2099— Create Socie.png (jlegewie)a6c9e02— Merge branch 'master' of https://github.com/jlegewie/zotfile (jlegewie)42a6c72— No automatic zotero7transition (jlegewie)e0c1fa1— Update wildcards.js for BBT/Zotero citekey updates (#662) (retorquere)90cdc1e— Fix repeated call to zotero7transition on startup (jlegewie)2e444f5— zotero7transition (jlegewie)90b3bbc— Menu item for zotero 7 transition (fix) (jlegewie)25b9c27— Revert "Update updateURL to point at updates.json" (jlegewie)
🔒Security observations
- High · Outdated PDF.js Library —
chrome/content/zotfile/pdfextract/pdfjs/. The codebase includes an embedded and likely outdated version of PDF.js (pdfjs). PDF.js is a complex library for parsing and rendering PDF files, and older versions may contain security vulnerabilities related to malformed PDF handling, potential RCE, or DoS attacks. Fix: Update PDF.js to the latest stable version. Implement a dependency management system to track and update third-party libraries regularly. Consider using npm/yarn to manage the PDF.js dependency instead of embedding it directly. - High · Potential XSS in PDF Annotation Extraction —
chrome/content/zotfile/pdfAnnotations.js, chrome/content/zotfile/pdfextract/extract.js. The pdfAnnotations.js and pdfextract/extract.js files handle PDF annotations and may process untrusted data from PDF files. If user-controlled annotation content (text, markup) is rendered without proper sanitization, it could lead to DOM-based XSS attacks. Fix: Implement strict output encoding for all annotation content before rendering. Use textContent instead of innerHTML where possible. Implement a Content Security Policy (CSP) to restrict inline scripts. Validate and sanitize all data extracted from PDF annotations. - High · File Path Traversal Risk in File Management —
chrome/content/zotfile/zotfile.js, chrome/content/zotfile/utils.js. The plugin handles file renaming, moving, and attachment operations. Without proper path validation, an attacker could potentially use path traversal sequences (../, ..) to escape the intended directory and access or modify files outside the intended location. Fix: Implement strict path validation using canonical path resolution. Reject any paths containing '..', '~', or other traversal sequences. Use allowlists for permitted file extensions and directories. Validate all file paths against a base directory before performing file operations. - Medium · Lack of Input Validation on Wildcard Processing —
chrome/content/zotfile/wildcards.js. The wildcards.js file processes user-defined wildcard patterns for file naming. Insufficient validation of wildcard patterns could lead to command injection or unintended file system access if these patterns are used in system commands. Fix: Implement strict whitelisting of allowed wildcard patterns. Avoid using wildcard patterns directly in system commands. Parse and validate patterns before use. Consider using a sandboxed template engine rather than pattern matching for file naming. - Medium · Potential Information Disclosure via Error Messages —
chrome/content/zotfile/utils.js, chrome/content/zotfile/ui.js, chrome/content/zotfile/options.js. Error handling in JavaScript files (utils.js, ui.js) may expose sensitive file paths, system information, or library versions in error messages that could be logged or displayed to users. Fix: Implement generic error messages for end users while logging detailed errors server-side. Sanitize error output to remove file paths, system information, and library versions. Implement proper logging with restricted access. - Medium · No Visible Dependency Management —
chrome/content/zotfile/pdfextract/pdfjs/, entire project structure. The codebase contains embedded third-party libraries (PDF.js) without visible version tracking or dependency manifest files (package.json, requirements.txt). This makes it difficult to identify and track known vulnerabilities. Fix: Implement a proper package management system (npm/yarn for JavaScript). Create and maintain a dependency manifest file with version pinning. Implement automated dependency scanning using tools like OWASP Dependency-Check or Snyk. - Medium · Insecure Communication in Tablet/Sync Features —
chrome/content/zotfile/tablet.js. The tablet.js file handles syncing PDFs to mobile devices. Without verification of HTTPS usage, certificate validation, and secure authentication, data could be intercepted during transfer. Fix: Ensure all network communications use HTTPS with proper certificate validation. Implement certificate pinning for sensitive connections. Use secure authentication tokens instead of passwords. Implement proper TLS/SSL configuration. - Low · Exposed Debug or Development Code —
undefined. The presence of files like .travis.yml and Makefile suggests development Fix: undefined
LLM-derived; treat as a starting point, not a security audit.
👉Where to read next
- Open issues — current backlog
- Recent PRs — what's actively shipping
- Source on GitHub
Generated by RepoPilot. Verdict based on maintenance signals — see the live page for receipts. Re-run on a new commit to refresh.