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