js-gnome-extensions

Build, debug, and maintain GNOME Shell extensions using GJS (GNOME JavaScript). Covers extension anatomy (metadata.json, extension.js, prefs.js, stylesheet.css), ESModule imports, GSettings preferences, popup menus, quick settings, panel indicators, dialogs, notifications, search providers, translations, and session modes. Use when the user wants to: (1) Create a new GNOME Shell extension, (2) Add UI elements like panel buttons, popup menus, quick settings toggles/sliders, or modal dialogs, (3) Implement extension preferences with GTK4/Adwaita, (4) Debug or test an extension, (5) Port an extension to a newer GNOME Shell version (45-49+), (6) Prepare an extension for submission to extensions.gnome.org, (7) Work with GNOME Shell internal APIs (Clutter, St, Meta, Shell, Main).

16 stars

Best use case

js-gnome-extensions is best used when you need a repeatable AI agent workflow instead of a one-off prompt.

Build, debug, and maintain GNOME Shell extensions using GJS (GNOME JavaScript). Covers extension anatomy (metadata.json, extension.js, prefs.js, stylesheet.css), ESModule imports, GSettings preferences, popup menus, quick settings, panel indicators, dialogs, notifications, search providers, translations, and session modes. Use when the user wants to: (1) Create a new GNOME Shell extension, (2) Add UI elements like panel buttons, popup menus, quick settings toggles/sliders, or modal dialogs, (3) Implement extension preferences with GTK4/Adwaita, (4) Debug or test an extension, (5) Port an extension to a newer GNOME Shell version (45-49+), (6) Prepare an extension for submission to extensions.gnome.org, (7) Work with GNOME Shell internal APIs (Clutter, St, Meta, Shell, Main).

Teams using js-gnome-extensions should expect a more consistent output, faster repeated execution, less prompt rewriting.

When to use this skill

  • You want a reusable workflow that can be run more than once with consistent structure.

When not to use this skill

  • You only need a quick one-off answer and do not need a reusable workflow.
  • You cannot install or maintain the underlying files, dependencies, or repository context.

Installation

Claude Code / Cursor / Codex

$curl -o ~/.claude/skills/js-gnome-extensions/SKILL.md --create-dirs "https://raw.githubusercontent.com/diegosouzapw/awesome-omni-skill/main/skills/development/js-gnome-extensions/SKILL.md"

Manual Installation

  1. Download SKILL.md from GitHub
  2. Place it in .claude/skills/js-gnome-extensions/SKILL.md inside your project
  3. Restart your AI agent — it will auto-discover the skill

How js-gnome-extensions Compares

Feature / Agentjs-gnome-extensionsStandard Approach
Platform SupportNot specifiedLimited / Varies
Context Awareness High Baseline
Installation ComplexityUnknownN/A

Frequently Asked Questions

What does this skill do?

Build, debug, and maintain GNOME Shell extensions using GJS (GNOME JavaScript). Covers extension anatomy (metadata.json, extension.js, prefs.js, stylesheet.css), ESModule imports, GSettings preferences, popup menus, quick settings, panel indicators, dialogs, notifications, search providers, translations, and session modes. Use when the user wants to: (1) Create a new GNOME Shell extension, (2) Add UI elements like panel buttons, popup menus, quick settings toggles/sliders, or modal dialogs, (3) Implement extension preferences with GTK4/Adwaita, (4) Debug or test an extension, (5) Port an extension to a newer GNOME Shell version (45-49+), (6) Prepare an extension for submission to extensions.gnome.org, (7) Work with GNOME Shell internal APIs (Clutter, St, Meta, Shell, Main).

Where can I find the source code?

You can find the source code on GitHub using the link provided at the top of the page.

SKILL.md Source

# GNOME Shell Extensions

Build extensions for GNOME Shell 45+ using GJS with ESModules.

## Key Resources

- **Public APIs (GJS Docs)**: https://gjs-docs.gnome.org/
- **GNOME Shell UI Source**: https://gitlab.gnome.org/GNOME/gnome-shell/-/tree/main/js/ui
- **Extensions Guide**: https://gjs.guide/extensions/
- **Review Guidelines**: https://gjs.guide/extensions/review-guidelines/review-guidelines.html

## Architecture Overview

Extensions run inside the `gnome-shell` process using Clutter/St toolkits (not GTK).
Preferences run in a separate GTK4/Adwaita process.

**Library stack** (bottom-up):

- **Clutter** — Actor-based toolkit, layout managers, animations
- **St** — Shell Toolkit: buttons, icons, labels, entries, scroll views (CSS-styleable)
- **Meta** (Mutter) — displays, workspaces, windows, keybindings
- **Shell** — `global` object, app tracking, utilities
- **js/ui/** — GNOME Shell JS modules (Main, Panel, PopupMenu, QuickSettings, etc.)

Import conventions in `extension.js`:

```js
import Clutter from "gi://Clutter";
import GObject from "gi://GObject";
import Gio from "gi://Gio";
import GLib from "gi://GLib";
import Meta from "gi://Meta";
import Shell from "gi://Shell";
import St from "gi://St";

import {
  Extension,
  gettext as _,
} from "resource:///org/gnome/shell/extensions/extension.js";
import * as Main from "resource:///org/gnome/shell/ui/main.js";
import * as PanelMenu from "resource:///org/gnome/shell/ui/panelMenu.js";
import * as PopupMenu from "resource:///org/gnome/shell/ui/popupMenu.js";
import * as QuickSettings from "resource:///org/gnome/shell/ui/quickSettings.js";
```

Import conventions in `prefs.js`:

```js
import Gdk from "gi://Gdk?version=4.0";
import Gtk from "gi://Gtk?version=4.0";
import Adw from "gi://Adw";
import Gio from "gi://Gio";

import {
  ExtensionPreferences,
  gettext as _,
} from "resource:///org/gnome/Shell/Extensions/js/extensions/prefs.js";
```

**Critical rule**: Never import GTK/Gdk/Adw in `extension.js`. Never import Clutter/Meta/St/Shell in `prefs.js`.

## Extension Lifecycle

### Required Files

1. **`metadata.json`** — UUID, name, description, shell-version, url
2. **`extension.js`** — Default export: subclass of `Extension`

### Optional Files

- **`prefs.js`** — Subclass of `ExtensionPreferences` (GTK4/Adwaita)
- **`stylesheet.css`** — CSS for St widgets in gnome-shell (not prefs)
- **`schemas/`** — GSettings schema XML + compiled binary
- **`locale/`** — Gettext translation .mo files

### enable()/disable() Contract

```js
import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";

export default class MyExtension extends Extension {
  enable() {
    // Create objects, connect signals, add UI
  }

  disable() {
    // MUST undo everything done in enable():
    // - Destroy all created widgets
    // - Disconnect all signals
    // - Remove all GLib.timeout/idle sources
    // - Null out all references
  }
}
```

**Rules** (enforced by extension review):

- Do NOT create GObject instances or connect signals in `constructor()`
- `constructor()` may only set up static data (RegExp, Map, etc.) and call `super(metadata)`
- Everything created in `enable()` MUST be cleaned up in `disable()`
- `disable()` is called on lock screen (unless `session-modes` includes `unlock-dialog`)

## Reference Files

Detailed documentation is split into reference files. Read the appropriate file based on the task:

- **[references/review-guidelines.md](references/review-guidelines.md)** — Complete review rules for extensions.gnome.org submission. Read before submitting or when reviewing extension code for compliance.
- **[references/ui-patterns.md](references/ui-patterns.md)** — Panel indicators, popup menus, quick settings (toggles, sliders, menus), dialogs, notifications, and search providers with complete code examples. Read when building any UI component.
- **[references/preferences.md](references/preferences.md)** — GSettings schemas, prefs.js with GTK4/Adwaita, settings binding. Read when implementing extension preferences.
- **[references/development.md](references/development.md)** — Getting started, testing, debugging, translations, InjectionManager, and packaging. Read when setting up a new extension or debugging.
- **[references/porting-guide.md](references/porting-guide.md)** — Breaking changes for GNOME Shell 45–49. Read when porting an extension to a newer version.

## Quick Start: Panel Indicator Extension

Minimal working extension with a panel icon:

### `metadata.json`

```json
{
  "uuid": "my-extension@example.com",
  "name": "My Extension",
  "description": "Does something useful",
  "shell-version": ["47", "48", "49"],
  "url": "https://github.com/user/my-extension"
}
```

### `extension.js`

```js
import St from "gi://St";

import { Extension } from "resource:///org/gnome/shell/extensions/extension.js";
import * as Main from "resource:///org/gnome/shell/ui/main.js";
import * as PanelMenu from "resource:///org/gnome/shell/ui/panelMenu.js";

export default class MyExtension extends Extension {
  enable() {
    this._indicator = new PanelMenu.Button(0.0, this.metadata.name, false);

    const icon = new St.Icon({
      icon_name: "face-laugh-symbolic",
      style_class: "system-status-icon",
    });
    this._indicator.add_child(icon);

    Main.panel.addToStatusArea(this.uuid, this._indicator);
  }

  disable() {
    this._indicator?.destroy();
    this._indicator = null;
  }
}
```

### Install & Test

```sh
# Create extension directory
mkdir -p ~/.local/share/gnome-shell/extensions/my-extension@example.com
# Copy files there, then:

# Wayland: run nested session
dbus-run-session gnome-shell --devkit --wayland   # GNOME 49+
dbus-run-session gnome-shell --nested --wayland    # GNOME 48 and earlier

# Enable extension in nested session
gnome-extensions enable my-extension@example.com

# Watch logs
journalctl -f -o cat /usr/bin/gnome-shell
```

## Common Patterns Cheatsheet

### Connect a signal (and clean up)

```js
enable() {
    this._handlerId = someObject.connect('some-signal', () => { /* ... */ });
}
disable() {
    if (this._handlerId) {
        someObject.disconnect(this._handlerId);
        this._handlerId = null;
    }
}
```

### Add a timeout (and clean up)

```js
enable() {
    this._timeoutId = GLib.timeout_add_seconds(GLib.PRIORITY_DEFAULT, 5, () => {
        // do work
        return GLib.SOURCE_CONTINUE; // or GLib.SOURCE_REMOVE
    });
}
disable() {
    if (this._timeoutId) {
        GLib.Source.remove(this._timeoutId);
        this._timeoutId = null;
    }
}
```

### Use GSettings

```js
enable() {
    this._settings = this.getSettings(); // uses metadata settings-schema
    this._settings.bind('show-indicator', this._indicator, 'visible',
        Gio.SettingsBindFlags.DEFAULT);
}
disable() {
    this._settings = null;
}
```

### Override a method (InjectionManager)

```js
import {
  Extension,
  InjectionManager,
} from "resource:///org/gnome/shell/extensions/extension.js";
import { Panel } from "resource:///org/gnome/shell/ui/panel.js";

export default class MyExtension extends Extension {
  enable() {
    this._injectionManager = new InjectionManager();
    this._injectionManager.overrideMethod(
      Panel.prototype,
      "toggleCalendar",
      (originalMethod) => {
        return function (...args) {
          console.debug("Calendar toggled!");
          originalMethod.call(this, ...args);
        };
      },
    );
  }
  disable() {
    this._injectionManager.clear();
    this._injectionManager = null;
  }
}
```

## Packaging for Submission

```sh
cd ~/.local/share/gnome-shell/extensions/my-extension@example.com
gnome-extensions pack --podir=po --extra-source=utils.js .

# GNOME 49+: upload directly
gnome-extensions upload --accept-tos
```

Related Skills

axiom-extensions-widgets-ref

16
from diegosouzapw/awesome-omni-skill

Use when implementing widgets, Live Activities, Control Center controls, or app extensions - comprehensive API reference for WidgetKit, ActivityKit, App Groups, and extension lifecycle for iOS 14+

microsoft-azure-webjobs-extensions-authentication-events-dotnet

16
from diegosouzapw/awesome-omni-skill

Microsoft Entra Authentication Events SDK for .NET. Azure Functions triggers for custom authentication extensions.

bgo

16
from diegosouzapw/awesome-omni-skill

Automated Blender build-go workflow. Automatically builds, removes old version, installs, enables, and launches Blender with your extension/add-on. Use when you want to quickly test changes, execute complete build-to-launch cycle, or run custom packaging scripts with automatic Blender launch.

Coding & Development

conversion-tools-automation

16
from diegosouzapw/awesome-omni-skill

Automate Conversion Tools tasks via Rube MCP (Composio). Always search tools first for current schemas.

conventional-commits

16
from diegosouzapw/awesome-omni-skill

Writes and reviews Conventional Commits commit messages (v1.0.0) to support semantic versioning and automated changelogs. Use when drafting git commit messages, PR titles, release notes, or when enforcing a conventional commit format (type(scope): subject, BREAKING CHANGE, footers, revert).

Conventional Commit Generator

16
from diegosouzapw/awesome-omni-skill

This skill should be used when the user asks to "create a conventional commit", "generate conventional commits", "commit with conventional format", "group my changes for commits", "make a conventional commit message", or mentions "semantic commits", "commitizen", "commit conventions". Analyzes staged and unstaged changes, groups related modifications, and generates properly formatted conventional commit messages with interactive commit grouping options.

control-d-automation

16
from diegosouzapw/awesome-omni-skill

Automate Control D tasks via Rube MCP (Composio). Always search tools first for current schemas.

context-session-end

16
from diegosouzapw/awesome-omni-skill

AI behavioral guideline for autonomously detecting work session boundaries and proposing updates to current_focus.md. The AI monitors conversation flow for natural breakpoints and acts without explicit invocation.

consensus-persona-generator

16
from diegosouzapw/awesome-omni-skill

Generate and persist reusable persona panels (persona_set artifacts) for consensus decision workflows. This skill initializes lightweight multi-agent disagreement with weighted reputations so downstream guards can make auditable, policy-governed decisions.

connecteam-automation

16
from diegosouzapw/awesome-omni-skill

Automate Connecteam tasks via Rube MCP (Composio). Always search tools first for current schemas.

confluence-cli

16
from diegosouzapw/awesome-omni-skill

Use confluence-cli (NPM package) to manage Confluence content, pages, and spaces from the command line. Ideal for documentation workflows, bulk content operations, page migration, and when users request CLI-based Confluence interactions. Trigger on requests like "use Confluence CLI", "create Confluence pages via CLI", "migrate Confluence content", "automate documentation workflows", or when users want to script Confluence operations.

configuring-devenv

16
from diegosouzapw/awesome-omni-skill

Initializes and configures devenv development environments. Searches packages, sets up languages, services, scripts, git hooks, and processes. Use when setting up devenv, adding packages to devenv.nix, configuring languages, services, git hooks, or searching for devenv options.