Skip to content

ZviBaratz/gnome-extension-reviewer

Tests License: GPL-2.0-or-later Python 3

🔍 gnome-extension-reviewer

Automated pre-submission checks for GNOME Shell extensions, built from analysis of real EGO review decisions. ego-lint catches the mechanical issues that cause the most common rejections — so extensions arrive cleaner and reviewers spend less time on round-trips.

Fully deterministic: bash + python + YAML rules. No AI at runtime, no network access, no dependencies beyond coreutils.

🚀 Quick Start

git clone https://github.com/ZviBaratz/gnome-extension-reviewer.git
cd gnome-extension-reviewer
./ego-lint /path/to/your-extension@username

Exit code 0 = no blocking issues. Exit code 1 = blocking issues that will likely cause rejection.

Run ./ego-lint --help for the full check list and options.

Try it on a real extension

unzip extension.zip -d /tmp/some-extension@author
./ego-lint /tmp/some-extension@author --show all

Try it on a bundled test fixture:

./ego-lint tests/fixtures/lifecycle-imbalance@test --show all

Runs in ~2 seconds on typical extensions, ~4 seconds on large ones (11 modules). No network access.

📋 Sample Output

================================================================
  ego-lint — GNOME Shell Extension Compliance Checker
================================================================

Extension: /path/to/my-extension@username

[PASS] file-structure/extension.js            extension.js exists
[PASS] file-structure/metadata.json           metadata.json exists
[PASS] license                                License file found (appears GPL-compatible)
[FAIL] R-DEPR-04                              extension.js:4: Legacy imports.* syntax; use ESM imports for GNOME 45+
[FAIL] R-DEPR-05                              extension.js:2: ExtensionUtils removed in GNOME 45+; use Extension base class
[WARN] R-SEC-17                               lib/controller.js:10: Writing to /tmp is insecure; use GLib.get_tmp_dir()
[PASS] metadata/required-fields               All required fields present
[FAIL] metadata/session-modes                 session-modes ["user"] is redundant and MUST be dropped
[WARN] metadata/shell-version-current         shell-version does not include GNOME 49
[PASS] lifecycle/enable-disable               enable() and disable() both defined
[WARN] lifecycle/file-monitor-cleanup         File monitor created but no .cancel() found
[WARN] resource-tracking/orphan-signal        lib/manager.js:9 — this._handlerId not cleaned up in destroy()
[PASS] quality/code-provenance                provenance-score=1; signals=[consistent-naming-style]
...

----------------------------------------------------------------
  Results: 196 checks — 167 passed, 3 failed, 4 warnings, 22 skipped
----------------------------------------------------------------

🔍 What Gets Checked

200+ checks across 14 categories — 64 blocking rules (FAIL) and 60 advisory rules (WARN), plus structural checks.

Full check categories (14)
Category Checks
Metadata UUID format/match, required fields, shell-version format, session-modes, GNOME trademark, donations
Imports GTK/Gdk/Adw banned in extension.js; Clutter/Meta/St/Shell banned in prefs.js; transitive dependency analysis
Schema Schema ID matches metadata, path format, glib-compile-schemas dry-run
Lifecycle enable/disable symmetry, signal cleanup, timeout removal, InjectionManager, D-Bus unexport, widget destroy, settings null, subprocess cancellation
Async _destroyed guards, cancellable usage, per-call cancellable verification
GObject GObject.registerClass patterns, GTypeName validation
Resources Cross-file resource graph (signals, timeouts, widgets, D-Bus, file monitors, GSettings), orphan detection
Security Subprocess validation, pkexec targets, clipboard/network disclosure, /tmp writes, telemetry, curl/gsettings spawn
Deprecated Mainloop, Lang, ByteArray, ExtensionUtils, Tweener, legacy imports.* syntax
Web APIs setTimeout, setInterval, fetch, XMLHttpRequest, WebSocket, localStorage
Version Compat GNOME 44–50 migration rules (version-gated, only fire for declared shell-versions)
CSS Unscoped class names, !important usage, GNOME Shell theme class overrides
Code Quality AI slop detection (try-catch density, impossible states, empty catches, obfuscation, code provenance scoring)
Package Forbidden files in zip, required files, compiled schemas for GNOME 45+
Preferences ExtensionPreferences base class, GTK4/Adwaita patterns, memory leak detection

See rules/patterns.yaml for the full list with rationale.

⚙️ How It Works

ego-lint uses a three-tier rule system:

  • Tier 1 — Pattern rules (rules/patterns.yaml): 124 regex-based rules in declarative YAML, with version-gating, guard patterns, and file-level suppression
  • Tier 2 — Structural checks (Python/bash scripts): Cross-file resource graph analysis, lifecycle symmetry verification, import BFS, AI slop heuristics
  • Tier 3 — Semantic checklists (Markdown): Applied by Claude during ego-review — lifecycle, security, code quality, AI slop, licensing, accessibility

Adding a new Tier 1 check is 4 lines of YAML:

- id: R-DEPR-08
  pattern: "\\bnew\\s+Lang\\.Class\\b"
  scope: ["*.js"]
  severity: blocking
  message: "Lang.Class is deprecated; use standard ES6 classes"
  category: deprecated

ego-lint does not:

  • Make approval/rejection decisions
  • Use AI inference or network access at runtime
  • Check logic correctness or functionality
  • Replace human review judgment

See docs/ARCHITECTURE.md for the full architecture, including why AI is only in Tier 3.

👩‍💻 For Extension Developers

Run ego-lint before submitting to EGO — it catches the mechanical issues that cause the most common rejections.

CI integration: Pure bash + python, exits 0/1, no network access. Tested against 186 fixtures with 495 assertions. See docs/ci-integration.md for GitHub Actions and GitLab CI examples.

All rules: rules/patterns.yaml — 124 pattern rules with rationale, plus rules reference for the full R-XXXX-NN catalog.

👁️ For EGO Reviewers

This tool encodes the mechanical checks you already do by hand — import segregation, lifecycle symmetry, metadata validation, resource cleanup — into automated, reproducible rules. The rules are grounded in real EGO review analysis and designed to be co-owned: adding a new check is 4 lines of YAML.

Your First 10 Minutes

  1. Run ./ego-lint on an extension from your review queue
  2. Compare findings against your own review notes
  3. If something is wrong: open an issue
  4. If something is missing: add a 4-line YAML rule

You are invited to shape the rules, adjust severity, and add checks for rejection patterns you see often.

🧰 Complete Toolkit

ego-lint is the standalone core — deterministic, no dependencies, works in CI. The Claude Code skills extend it with AI-powered analysis for developers who want deeper review coverage:

Skill Description
ego-review Multi-phase code review applying 6 semantic checklists (lifecycle, security, code quality, AI slop, licensing, accessibility)
ego-simulate Estimates review readiness using rejection taxonomy and published review criteria
ego-scaffold Generates EGO-compliant extension boilerplate from templates
ego-submit Full pipeline: lint → review → package validation → readiness report
claude plugins add github:ZviBaratz/gnome-extension-reviewer

The four skills above use Claude to analyze extension source code via Anthropic's API. ego-lint itself makes no API calls — it's the same deterministic tool whether or not you use the plugin.

📖 How This Was Built

  • Claude Code wrote the code — scripts, rules, tests, and docs were developed using Claude Code (Anthropic's AI coding tool). Every design decision was human-reviewed. The AI slop detection rules are based on patterns observed in real EGO rejections of AI-generated submissions.
  • Research was AI-assisted — Discourse mining, guideline extraction, cross-source synthesis, and gap analysis were performed with Claude Code and verified against real EGO reviews on extensions.gnome.org, gjs.guide requirements, and GNOME Shell GitLab history. Regression-tested against a real 11-module extension as baseline.
  • ego-lint itself is AI-free — The output artifact is deterministic bash + python + YAML. No API calls. No network access. No model inference. AI was the development tool, not the runtime tool.

📚 Research Background

The rules and checks are grounded in analysis of real EGO review behavior — not just the official documentation.

  • Analyzed 9 real EGO reviews on extensions.gnome.org by active reviewers
  • Identified 26 real-world findings including 8 unwritten rules not in official docs
  • Cross-referenced gjs.guide guidelines (109 extracted requirements) with actual reviewer behavior
  • Traced GNOME Shell guideline evolution across versions 44–50 via GitLab history
  • Reverse-engineered patterns from 5 popular approved extensions
  • Regression-tested all checks against hara-hachi-bu (an 11-module power management extension whose initial EGO submission was rejected) as baseline

Key unwritten rules discovered:

  1. No "GNOME" in UUID, extension name, or schema ID (trademark)
  2. shell-version must be major-only for GNOME 40+ (no minor versions like "45.1")
  3. D-Bus interfaces must be unexported in disable()
  4. destroy() must be followed by null assignment
  5. No compiled schemas in package for GNOME 45+
  6. Timeout IDs must be removed before reassignment
  7. Subprocesses must have cancellation path in disable()
  8. No convenience.js patterns

Full research: docs/research/ | Coverage gaps: docs/research/gap-analysis.md | Field testing: field-tests/ — 10 real-world extensions tested with TP/FP classification and calibration lessons

🗺️ Roadmap

  • Polkit action ID validation (verify .policy file when pkexec is used)
  • Schema filename validation (ensure .gschema.xml filename matches schema ID)
  • Module-scope mutable state detection (Map/Set at module level)
  • Per-extension configuration (.ego-lint.yml for rule overrides)

Full gap list: docs/research/gap-analysis.md

⚠️ Known Limitations

  • Does not guarantee EGO approval — use as guidance, not certification
  • Rules are based primarily on active EGO reviewer patterns; individual reviewers may have different preferences
  • Some checks are heuristic (AI slop detection, code quality scoring) and may produce false positives
  • Per-line _async() cancellable check is a heuristic — some null cancellable calls are valid
  • Full gap list: docs/research/gap-analysis.md

🔧 Troubleshooting

Problem Fix
python3: command not found sudo apt install python3 (Ubuntu/Debian) or sudo dnf install python3 (Fedora)
Schema check skipped Install glib-compile-schemas: sudo apt install libglib2.0-dev-bin or sudo dnf install glib2-devel
ESLint check skipped Install Node.js: sudo apt install nodejs npm or sudo dnf install nodejs npm
zipinfo: not found sudo apt install unzip or sudo dnf install unzip

Requirements

  • Required: bash, python3
  • Optional: npm/node (ESLint checks), glib-compile-schemas (schema validation), zipinfo/unzip (package checks)

🤝 Contributing & Community

License

GPL-2.0-or-later

About

Claude Code plugin for GNOME Shell extension EGO review compliance

Topics

Resources

License

Code of conduct

Contributing

Security policy

Stars

Watchers

Forks

Packages

 
 
 

Contributors