The Challenge
As a Linux user relying on GlobalProtect VPN, I was tired of managing connections through command-line tools. Every connection meant opening a terminal, typing commands, handling MFA in the browser, and manually checking status. I wanted to build a native GNOME Shell extension that would make VPN management as simple as clicking a system tray icon.
But GNOME extension development is notoriously complex:
Strict compliance with extensions.gnome.org review guidelines
Async subprocess management with proper cancellation
Memory leak prevention and resource cleanup
Race condition handling
GJS (GNOME JavaScript) runtime quirks
This wasn't my first GNOME extension, but it was definitely the most complex one I'd attempted.
Enter Kiro
Kiro transformed what could have been months of development into weeks. Here's how:
- Intelligent Code Navigation Working with 1000+ lines of GJS code across multiple modules (extension.js, gpClient.js, statusMonitor.js, indicator.js, errorHandler.js), Kiro helped me maintain architectural consistency. It understood the separation of concerns and suggested improvements that aligned with GNOME best practices.
// Kiro helped refactor this deprecated pattern:
log('Connection status: ' + status);
// To the modern approach:
console.info('Connection status:', status);
- Async Operations Done Right One of the trickiest parts was implementing proper async subprocess operations with Gio.Cancellable. Kiro helped me implement patterns like:
async connect(portal, gateway = null, username = null) {
const cancellable = new Gio.Cancellable();
this._cancellables.add(cancellable);
try {
const proc = new Gio.Subprocess({
argv: args,
flags: Gio.SubprocessFlags.STDOUT_PIPE |
Gio.SubprocessFlags.STDERR_PIPE
});
proc.init(cancellable);
const [stdout, stderr] = await proc.communicate_utf8_async(
null,
cancellable
);
// Handle result...
} finally {
this._cancellables.delete(cancellable);
}
}
- Memory Leak Prevention GNOME extensions must meticulously clean up resources. Kiro helped me implement proper lifecycle management:
destroy() {
// Cancel all operations
this._cancellables.forEach(c => c.cancel());
this._cancellables.clear();
// Remove timeouts
if (this._timeoutId) {
GLib.Source.remove(this._timeoutId);
this._timeoutId = null;
}
// Disconnect signals
this._signals.forEach(id => this.disconnect(id));
this._signals = [];
}
- Race Condition Prevention Kiro identified potential race conditions when users rapidly clicked connect/disconnect and helped implement operation locks:
async _connect() {
if (this._operationInProgress) {
return; // Prevent concurrent operations
}
this._operationInProgress = true;
try {
await this._gpClient.connect(portal, gateway, username);
} finally {
this._operationInProgress = false;
}
}
- Review Guidelines Compliance The extensions.gnome.org review process is rigorous. Kiro helped me address every requirement:
✅ Replaced deprecated APIs
✅ Converted PNG icons to SVG
✅ Implemented proper input validation
✅ Added Gio.Cancellable support everywhere
✅ Proper timeout tracking and cleanup
✅ Removed compiled schemas from repo
✅ Fixed cleanup order (reverse of creation)
Result: Passed review on first submission! 🎉
The Architecture
Kiro helped me maintain a clean, modular architecture:
┌─────────────────────────────────────────┐
│ Extension (extension.js) │
│ Lifecycle Management │
└─────────────────┬───────────────────────┘
│
┌─────────┴─────────┐
│ │
┌───────▼────────┐ ┌──────▼──────────┐
│ Indicator │ │ StatusMonitor │
│ (UI Layer) │ │ (Polling) │
└───────┬────────┘ └──────┬──────────┘
│ │
└─────────┬─────────┘
│
┌─────────▼──────────┐
│ GlobalProtect │
│ Client │
│ (CLI Wrapper) │
└────────────────────┘
Key Features Implemented
🔒 Connect/Disconnect with MFA support
📊 Real-time status monitoring
🌐 Gateway selection and switching
🔄 Auto-disconnect on logout
🎨 Custom SVG icons for all states
🔔 Smart notifications with throttling
⚙️ Interactive settings dialogs
🔍 Advanced operations (HIP, logs, network rediscovery)
🛡️ Comprehensive error handling with data sanitization
Testing with Kiro
Kiro helped me implement comprehensive testing:
Property-Based Tests (using fast-check):
fc.assert(
fc.property(fc.string(), async (input) => {
const sanitized = ErrorHandler.sanitize(input);
// Ensure no sensitive data leaks
expect(sanitized).not.toContain('password');
expect(sanitized).not.toContain('token');
}),
{ numRuns: 100 }
);
Unit Tests (using Jasmine):
describe('GlobalProtectClient', () => {
it('should handle connection timeout', async () => {
const client = new GlobalProtectClient();
await expectAsync(
client.connect('invalid-portal')
).toBeRejectedWithError(/timeout/i);
});
});
The Results
✅ Published on extensions.gnome.org
✅ 100% Review Compliance
✅ Zero Memory Leaks
✅ Comprehensive Test Coverage
✅ 1.3k+ lines of production code
✅ Compatible with GNOME Shell 45-49
What Changed in My Development Approach
Before Kiro:
Manual code review for patterns
Trial and error with async operations
Debugging memory leaks after the fact
Multiple review submission attempts
With Kiro:
AI-assisted best practices from the start
Proper patterns implemented first time
Proactive memory management
First-submission approval
Kiro didn't just help me write code faster - it helped me write better code from the beginning. The AI understands context, suggests improvements, and catches issues before they become problems.
Try It Yourself
The extension is open source and available now:
🔗 GitHub: https://github.com/totoshko88/gp-gnome 📦 Install: gnome-extensions install gp-gnome@totoshko88.github.io.zip ⭐ GNOME Extensions: [Coming soon to extensions.gnome.org]
Conclusion
Building a production-ready GNOME extension is challenging, but with Kiro as a development partner, it became manageable and even enjoyable. The combination of intelligent code suggestions, architectural guidance, and best practice enforcement made the difference between a hobby project and a professional, publishable extension.
If you're working on complex projects - especially those with strict compliance requirements - Kiro is a game-changer.
What's your experience with AI-assisted development? Share in the comments!
Top comments (0)