diff --git a/README.md b/README.md index 74e6388..05f363a 100644 --- a/README.md +++ b/README.md @@ -3,468 +3,216 @@ Squadron Logo -

Squadron

- -

- Autonomous Agent Orchestration for your local machine. -
- Explore the docs » -
-
- Report Bug - · - Request Feature +

Squadron

+ +

+ The AI Agent Command Center for Your Desktop +

+ +

+ Run Claude, Gemini, Codex, and more — all in one native app.
+ 6 terminals. Any AI Provider. Complete control. +

+ +
+ +

+ ⬇️ Download Now +   •   + Features +   •   + Quick Start +   •   + Architecture

- PyPI + Release + Downloads Tests - Coverage - Downloads -
License: AGPL v3 - Python 3.10+ - Last Commit - Issues

- Quick Start • - Dashboard • - Commands • - Architecture • - Skills • - Testing • - Roadmap + Squadron Terminal Hub

--- -## 🔥 New in v0.5.1 +## 🔥 What's New in v2.0 | Feature | Description | |:---|:---| -| **🧪 Comprehensive Test Suite** | Full pytest coverage with mocked dependencies | -| **🔄 GitHub Actions CI/CD** | Automated testing on Python 3.10, 3.11, 3.12 | -| **🐛 Bug Fixes** | Resolved merge conflicts and duplicate dependencies | -| **📊 README Badges** | Tests, Coverage, Downloads, Last Commit, Issues | - ---- - -

- Squadron Banner -

- -## ⚡ Install - -```bash -pip install squadron-agents -``` - -That's it. You're ready. +| **🖥️ Native Desktop App** | Download and run — no terminal required | +| **🤖 Multi-Provider Support** | Claude, Gemini, Codex, OpenCode, Cursor | +| **📺 6-Terminal Grid** | Run 6 AI agents simultaneously | +| **🔐 Secure API Keys** | Encrypted storage with OS-level security | +| **🌳 Git Worktrees** | Safe parallel execution per task | +| **📋 Kanban Board** | Drag-and-drop task management | +| **🚀 Onboarding Wizard** | Get started in under a minute | --- -## 🎮 Squadron Desktop (v2.0) - -Squadron is evolving into a native **Desktop Application**. -The web-based Control Plane is being deprecated in favor of a high-performance Electron + React application. - -### Roadmap for v2.0 -- **Native Experience**: One-click installer (no terminal required). -- **Kanban Board**: Drag-and-drop task management. -- **Agent Terminals**: Watch your agents work in real-time. -- **Git Worktrees**: Safety and parallelism for agent tasks. - -> _Work in Progress: The new desktop app is currently under active development._ - ---- - -## 🐝 Swarm 2.0 — Multi-Agent Orchestration - -Squadron's Swarm system enables intelligent, autonomous agent coordination. - - - -> **Screenshot needed:** Dashboard showing a task being routed from user → Overseer → Agent - -### Key Features - -- **🧠 LLM-Powered Routing** — Gemini decides which agent handles each task -- **🤝 Agent Handoffs** — Agents can delegate tasks to each other with context -- **📋 Task Queue** — Priority-based task management -- **🔗 Delegation Chain** — Full tracking of task ownership - -```python -# Example: Intelligent task routing -from squadron.swarm import route_task - -result = route_task("Analyze the API performance and deploy the fix") -# Swarm routes to the best agent for each subtask -``` - ---- - -## 💬 Agent Communication - -Agents can now communicate with each other through your ticket system. - - - -> **Screenshot needed:** Jira/Linear ticket with agent comments showing @mentions +## ⬇️ Download -### Features +Download the latest release for your platform: -- **@Mention Support** — Tag agents in ticket comments to request help -- **`reply_to_ticket()`** — Agents post updates directly on tickets -- **`tag_agent()`** — Request help from specific agents -- **Auto-Wake** — Overseer wakes agents when tickets are assigned +| Platform | Download | Architecture | +|----------|----------|--------------| +| **Windows** | [Squadron-Setup.exe](https://github.com/MikeeBuilds/Squadron/releases/latest) | x64 | +| **macOS** | [Squadron.dmg](https://github.com/MikeeBuilds/Squadron/releases/latest) | Intel & Apple Silicon | +| **Linux** | [Squadron.AppImage](https://github.com/MikeeBuilds/Squadron/releases/latest) | x64 | -```bash -# Overseer monitors and auto-wakes agents -squadron overseer --auto-wake - -# Example ticket comment that triggers an agent: -# "@YourAgent please review the authentication changes" -``` +> **Windows users**: You may see a SmartScreen warning. Click "More info" → "Run anyway". We're working on code signing. --- -## 🧠 Persistent Memory (Hippocampus) - -Agents now have long-term memory powered by ChromaDB semantic search. - - - -> **Screenshot needed:** Console showing agent recalling relevant past conversations - -### Memory Types - -| Type | Description | -|------|-------------| -| **Conversation** | Past chat history with context | -| **Task** | Completed tasks and their outcomes | -| **Learning** | Insights and knowledge gained | -| **General** | Miscellaneous memories | - -### API - -```python -from squadron.memory import remember, recall, get_context_for_task +## ✨ Features -# Store a memory -remember("User prefers TypeScript over JavaScript", agent="Atlas") +### 🤖 Multi-Provider AI Terminal -# Recall relevant memories -memories = recall("coding preferences", agent="Atlas") +Run any AI coding assistant directly in Squadron: -# Get context for a new task -context = get_context_for_task("Set up the new frontend", agent="Atlas") -``` - ---- +| Provider | Models | Status | +|----------|--------|--------| +| **Claude** | Sonnet 4, Opus 4, Haiku | ✅ Auto-install | +| **Gemini** | 2.5 Pro, 2.5 Flash | ✅ Auto-install | +| **OpenAI Codex** | GPT-4o, o1, o3-mini | ✅ Auto-install | +| **OpenCode** | Multi-model | ✅ Auto-install | +| **Cursor** | Any model | ✅ Auto-install | -## 🧬 Evolution Layer +> **Auto-Install**: CLIs download automatically when you select a provider. No manual setup. -Squadron agents can evolve and improve their skills over time. +### 📺 6-Terminal Grid - - -> **Screenshot needed:** Dashboard showing skill registry with versions and quality scores - -### Features +

+ 6-Terminal Grid +

-- **Skill Registry** — Track all skills with version history -- **Quality Scoring** — Success/failure rate tracking -- **Version Control** — Rollback to previous skill versions -- **Skill Creation** — Agents can propose new skills +- **3x2 Grid Layout** — See all agents at once +- **Per-Terminal Provider** — Run Claude in one, Gemini in another +- **Task Linking** — Connect terminals to Kanban tasks +- **Context Injection** — Task details auto-sent to AI ---- +### 📋 Kanban Board -## ⏰ Wake Protocol +

+ Kanban Board +

-The Wake Protocol orchestrates autonomous agent execution. +Visual task management: +- **Columns**: Backlog → Planning → In Progress → Review → Done +- **Real-time Updates** via Server-Sent Events +- **Task Wizard** for quick task creation +- **Agent Assignment** per task - - -> **Recording needed:** Full flow of Overseer detecting ticket → waking agent → executing → reporting back +### 🌳 Git Worktree Isolation -```bash -# Manually trigger the Wake Protocol -squadron wake --summary "Deploy the hotfix to production" +Each agent task gets its own isolated workspace: -# Auto-wake runs automatically with overseer -squadron overseer --auto-wake --interval 30 ``` - -### Flow - -1. **Trigger** — New ticket detected or manual wake command -2. **Route** — Swarm selects the best agent for the task -3. **Execute** — Agent completes the work -4. **Report** — Results posted back to the ticket - ---- - -## 🎬 See It In Action - -```bash -$ squadron report --msg "Refactored the auth module." --ticket "KAN-1" - -🚀 Squadron Bridge Activated... -✅ Slack: Message sent to #general -✅ Jira: Comment added to KAN-1 +Task Started → .worktrees/task-XXX created → Agent works in isolation + ↓ + Merge or Discard via Dashboard ``` -**One command. Multiple integrations. Zero context switching.** +**Benefits:** +- ✅ Agents work in parallel without conflicts +- ✅ Main branch stays protected +- ✅ Easy to discard failed work +- ✅ Each task gets its own branch for review - - -> **Recording needed:** Terminal showing `squadron report`, `squadron broadcast`, and `squadron pr` commands - ---- - -## 😤 The Problem - -You're building with AI agents. They're powerful. They can write code, refactor systems, and solve complex problems. - -But here's the frustrating reality: - -| What You Want | What Actually Happens | -|--------------|----------------------| -| Agent finishes a task | You don't know unless you check the terminal | -| Jira ticket should update | It stays in "To Do" forever | -| Team needs visibility | They have no idea what the AI is building | -| Agent A needs Agent B's help | They can't communicate | -| Agent worked on this before | It doesn't remember | - -**Your agents are trapped in a chat window.** They can think, but they can't *act* in your team's workflow. - ---- +### 🔐 Secure Settings -## ✨ The Solution - -Squadron is a **bridge** that connects your local AI agents to your team's real tools. - -``` -┌─────────────────┐ ┌─────────────────┐ -│ AI AGENTS │ │ YOUR TEAM │ -│ (Your Agents) │ │ │ -│ │ │ 📋 Jira/Linear │ -│ "Task done!" │────────▶│ 💬 Slack │ -│ │Squadron │ 🔔 Discord │ -│ 🧠 Memory │ Bridge │ 🐙 GitHub │ -│ 🔄 Handoffs │ │ 🎮 Dashboard │ -└─────────────────┘ └─────────────────┘ -``` +

+ Settings Panel +

-**Squadron gives your agents:** -- 🗣️ **A Voice** — Post updates to Slack/Discord -- ✋ **Hands** — Update Jira tickets, create GitHub PRs -- 👀 **Awareness** — Overseer watches for new assignments -- 🧠 **Memory** — Remember past work and context -- 🤝 **Collaboration** — Hand off tasks to each other -- 🎮 **Control** — Real-time dashboard for management +- **Encrypted at Rest** using OS-level security (Windows DPAPI / macOS Keychain) +- **11 Integrations**: Slack, Discord, Jira, Linear, GitHub + AI providers +- **Export to .env** for Python backend +- **Never Transmitted** — keys stay local --- ## 🚀 Quick Start -### 1. Install - -```bash -pip install squadron-agents -``` - -### 2. Configure - -Create a `.env` file in your project root: - -```env -# Required: LLM Provider -GEMINI_API_KEY=your-gemini-key - -# Jira -JIRA_SERVER=https://your-domain.atlassian.net -JIRA_EMAIL=your-email@example.com -JIRA_TOKEN=your-api-token - -# Slack -SLACK_BOT_TOKEN=xoxb-your-bot-token - -# Discord (optional) -DISCORD_WEBHOOK_URL=https://discord.com/api/webhooks/... -DISCORD_BOT_TOKEN=your-bot-token - -# GitHub (optional) -GITHUB_TOKEN=ghp_your-token - -# Linear (optional) -LINEAR_API_KEY=lin_api_... -``` - -### 3. Initialize - -```bash -squadron init -``` -This creates a `squadron/knowledge/` folder in your project. Customize `TEAM.md` and `ROLES.md` here. - -### 4. Learn - -```bash -squadron learn -``` -This scans your code and builds a map for the agent to use. - -### 5. Test +### 1. Download & Install -```bash -squadron report --msg "Hello from Squadron!" --channel "#general" -``` - -If you see `✅ Slack: Message sent` — you're live! 🎉 - ---- +Download from [Releases](https://github.com/MikeeBuilds/Squadron/releases/latest) and install for your platform. -## 📖 Commands +### 2. Complete Onboarding -### Core Commands - -| Command | Description | -|---------|-------------| -| `squadron init` | Initialize Squadron in your project | -| `squadron learn` | Scan codebase and update knowledge | -| `squadron server` | Start the Control Plane API | -| `squadron listen` | Listen for @mentions in Slack | -| `squadron listen --discord` | Listen for @mentions in Discord | -| `squadron overseer` | Watch for new ticket assignments | -| `squadron wake` | Manually trigger the Wake Protocol | +

+ Onboarding Wizard +

-### Communication Commands +The wizard guides you through: +- Choosing your AI provider +- Entering your API key +- Selecting your project folder -| Command | Description | -|---------|-------------| -| `squadron report` | Send updates to Slack + update tickets | -| `squadron broadcast` | Announce to Discord | -| `squadron pr` | Create GitHub Pull Request | -| `squadron issue` | Create GitHub Issue | +### 3. Start Coding -See [COMMANDS.md](COMMANDS.md) for full documentation. +- Click the provider dropdown in any terminal +- Select your AI (Claude, Gemini, etc.) +- CLI auto-downloads if needed +- Start prompting! --- ## 🏗️ Architecture -Squadron uses a **Skill-Based Architecture** inspired by the [Model Context Protocol (MCP)](https://modelcontextprotocol.io). +Squadron is a **hybrid desktop application**: ``` -squadron/ -├── cli.py # 🎯 The Router (entry point) -├── server.py # 🎮 Control Plane API -├── overseer.py # 👀 Background ticket watcher -├── brain.py # 🧠 Intelligence router (LLM decisions) -│ -├── swarm/ # 🐝 ORCHESTRATION LAYER -│ ├── agent.py # Base agent class -│ ├── delegator.py # Task handoff logic -│ └── overseer.py # LLM-powered routing -│ -├── memory/ # 🧠 MEMORY LAYER -│ └── hippocampus.py # Semantic memory (ChromaDB) -│ -├── evolution/ # 🧬 EVOLUTION LAYER -│ ├── skill_registry.py # Skill tracking & versioning -│ ├── improver.py # Self-improvement engine -│ └── watcher.py # Performance monitoring -│ -├── skills/ # 🛠️ ACTION LAYER (The Hands) -│ ├── jira_bridge/ # Jira API integration -│ ├── slack_bridge/ # Slack API integration -│ ├── discord_bridge/ # Discord webhooks + bot -│ ├── github_bridge/ # GitHub API integration -│ ├── linear_bridge/ # Linear API integration -│ ├── ssh_skill/ # Remote command execution -│ └── browser_skill/ # Web navigation & screenshots -│ -├── services/ # 🔧 SERVICE LAYER -│ └── llm/ # LLM providers (Gemini) -│ -└── knowledge/ # 📚 CONTEXT LAYER (The Brain) - ├── TEAM.md # Who is on the team? - ├── WORKFLOW.md # How does work flow? - └── ROLES.md # What does each agent do? +┌─────────────────────────────────────────────────────────┐ +│ ELECTRON SHELL │ +│ ┌─────────────────┐ ┌────────────────────────────────┐│ +│ │ React + Vite │ │ Main Process ││ +│ │ ───────────── │ │ ──────────────── ││ +│ │ • 6-Terminal │ │ • PTY Management ││ +│ │ • Kanban UI │◄─┤ • API Key Storage ││ +│ │ • Settings │ │ • CLI Auto-Install ││ +│ │ • Onboarding │ │ • Process Manager ││ +│ └─────────────────┘ └────────────────────────────────┘│ +│ ▲ ▲ │ +│ │ IPC │ Spawn │ +│ ▼ ▼ │ +│ ┌─────────────────────────────────────────────────────┐│ +│ │ TERMINAL SESSIONS (node-pty + xterm.js) ││ +│ └─────────────────────────────────────────────────────┘│ +└─────────────────────────────────────────────────────────┘ + │ + │ HTTP API (Port 8000) + ▼ +┌─────────────────────────────────────────────────────────┐ +│ PYTHON BACKEND │ +│ • Swarm Orchestration • Git Worktrees │ +│ • Task Queue • Integrations │ +└─────────────────────────────────────────────────────────┘ ``` -### Why This Structure? - -| Layer | Purpose | Example | -|-------|---------|---------| -| **Swarm** | Task routing & handoffs | Route task to best agent | -| **Memory** | Long-term context | Remember past conversations | -| **Evolution** | Self-improvement | Track skill performance | -| **Skills** | Executable actions | `JiraTool.update_ticket()` | -| **Knowledge** | Context for decisions | "Move to Done only after tests pass" | - ---- - -## 🔌 Skills - -| Skill | Status | What It Does | -|-------|--------|--------------| -| **Jira Bridge** | ✅ Live | Update tickets, add comments, transition status | -| **Slack Bridge** | ✅ Live | Send formatted messages to channels | -| **Discord Bridge** | ✅ Live | Broadcast via webhooks & Reply via Bot | -| **GitHub Bridge** | ✅ Live | Create PRs and Issues | -| **Linear Bridge** | ✅ Live | Update Linear issues | -| **Overseer** | ✅ Live | Watch Jira/Linear for new assignments | -| **SSH Skill** | ✅ Live | Execute remote commands | -| **Browser Skill** | ✅ Live | Navigate & Screenshot Web | - --- -## 📝 Customizing for Your Team +## 🛠️ Build from Source -After running `squadron init`, you'll have example templates to customize: - -| File | What to Customize | -|------|-------------------| -| `squadron/agents.yaml` | Define your agent personas (names, roles, avatars) | -| `squadron/knowledge/TEAM.md` | Your team members (human and AI) | -| `squadron/knowledge/ROLES.md` | Agent specializations for task routing | -| `squadron/knowledge/WORKFLOW.md` | Your team's development process | - -The templates include example agents (**Atlas** and **Sage**) — replace them with your own! +For developers who want to run from source: ```bash -# Customize your agents -code squadron/agents.yaml -code squadron/knowledge/ROLES.md -``` - ---- - -## 🤖 Teaching Your Agents - -Add this to your agent's system prompt: +# Clone the repo +git clone https://github.com/MikeeBuilds/Squadron.git +cd Squadron/desktop -```markdown -## Tool: Squadron - -You have access to the `squadron` CLI for team communication. +# Install dependencies +npm install -### When to use: -- After completing a coding task -- When you hit a blocker and need help -- To update ticket status -- To hand off work to another agent +# Run in development mode +npm run electron:dev -### Commands: -- Start task: `squadron report --msg "Starting auth work" --ticket "KAN-1" --status "In Progress"` -- Complete task: `squadron report --msg "Auth complete" --ticket "KAN-1" --status "Done"` -- Announce: `squadron broadcast --msg "Shipped new feature!"` -- Hand off: Tag another agent in the ticket comment with @AgentName +# Build for production +npm run electron:build ``` --- @@ -473,83 +221,35 @@ You have access to the `squadron` CLI for team communication. ### Completed ✅ -- [x] **Core CLI** — `squadron report` command -- [x] **Jira Integration** — Comments + status transitions -- [x] **Slack Integration** — Rich block messages -- [x] **Discord Integration** — Webhook broadcasts + Bot -- [x] **GitHub Integration** — PRs and Issues -- [x] **Linear Integration** — Issue management -- [x] **Overseer Mode** — Background ticket watcher -- [x] **PyPI Release** — `pip install squadron-agents` -- [x] **Control Plane Dashboard** — Real-time web UI -- [x] **Swarm 2.0** — LLM-powered intelligent routing -- [x] **Agent Communication** — @mentions and ticket comments -- [x] **Persistent Memory** — Hippocampus with ChromaDB -- [x] **Evolution Layer** — Skill versioning and quality tracking -- [x] **Wake Protocol** — Autonomous agent execution +- [x] **Native Desktop App** — Electron cross-platform +- [x] **6-Terminal Grid** — Multi-agent workspace +- [x] **Multi-Provider Support** — Claude, Gemini, Codex, OpenCode, Cursor +- [x] **Auto-Install CLIs** — Zero setup for users +- [x] **Secure API Keys** — Encrypted local storage +- [x] **Git Worktree Isolation** — Safe parallel execution +- [x] **Kanban Board** — Visual task management +- [x] **Task Wizard** — Easy task creation +- [x] **Onboarding Wizard** — First-launch setup +- [x] **Integration Settings** — Slack, Discord, Jira, Linear, GitHub ### Coming Soon 🚧 -- [ ] **Active Inference** — Predictive agent behavior -- [ ] **Hive Mind** — Collective intelligence layer -- [ ] **Multi-LLM Support** — OpenAI, Anthropic, local models -- [ ] **Email Notifications** — SMTP integration -- [ ] **Calendar Integration** — Scheduling and reminders - ---- - -## 🌟 The Origin Story - -Squadron was born out of necessity. - -We're building [BlackCircleTerminal](https://blackcircleterminal.com), a quantitative trading platform managed by AI agents. Our virtual developers — **Marcus** (Strategy) and **Caleb** (Data) — needed a way to communicate with us when we weren't at the keyboard. - -We realized that for agents to be truly useful, they need to be part of the **workflow**, not just the **code editor**. - -Squadron is the nervous system that connects our AI workforce to our human tools. - ---- - -## 🧪 Testing - -Squadron has a comprehensive test suite with mocked dependencies for fast, reliable tests. - -```bash -# Install dependencies -pip install -r requirements.txt -pip install -e . - -# Run all tests -python -m pytest tests/ -v - -# Run with coverage -python -m pytest tests/ --cov=squadron --cov-report=html - -# Run only unit tests (fast) -python -m pytest tests/unit/ -v - -# Run integration tests -python -m pytest tests/integration/ -v -``` - -Tests run automatically on push/PR via GitHub Actions. See `tests/README.md` for details on fixtures and writing new tests. +- [ ] **AI Merge Resolution** — Automatic conflict resolution +- [ ] **Insights Panel** — ChatGPT-style project Q&A +- [ ] **Changelog Generator** — Auto-generate from tasks +- [ ] **Auto-Updates** — In-app updates --- ## 🤝 Contributing -We're building the future of **Agent-First Development**. Want to add a new skill? +We welcome contributions! 1. Fork the repo -2. Create a skill in `squadron/skills/your_skill/` -3. Add `tool.py` (logic) and `SKILL.md` (instructions) -4. Open a PR! - -**Ideas for new skills:** -- Trello / Asana integrations -- Email notifications -- CI/CD triggers -- Calendar scheduling +2. Create a feature branch: `git checkout -b feature/amazing-feature` +3. Commit your changes: `git commit -m 'Add amazing feature'` +4. Push to the branch: `git push origin feature/amazing-feature` +5. Open a Pull Request --- @@ -560,11 +260,11 @@ AGPL-3.0 © [MikeeBuilds](https://github.com/MikeeBuilds) ---

- Don't just build agents. Give them a job. + Command your AI agents. Ship faster.

⭐ Star this repo • - 📦 PyPI • + 📦 Download🐛 Report Bug

diff --git a/desktop/electron/main.ts b/desktop/electron/main.ts index 29cfa72..07f6296 100644 --- a/desktop/electron/main.ts +++ b/desktop/electron/main.ts @@ -135,6 +135,38 @@ function setupTerminalIPC(mainWindow: BrowserWindow) { }); }); }); + + // Onboarding & Project IPC Handlers + ipcMain.handle('settings-onboarding-complete', async () => { + return settingsStore.isOnboardingComplete(); + }); + + ipcMain.handle('settings-set-onboarding', async (_, { complete }) => { + settingsStore.setOnboardingComplete(complete); + return true; + }); + + ipcMain.handle('settings-set-project', async (_, { projectPath }) => { + settingsStore.setProjectPath(projectPath); + return true; + }); + + ipcMain.handle('settings-get-integrations', async () => { + return settingsStore.getIntegrationConfig(); + }); + + ipcMain.handle('settings-export-env', async (_, { targetPath }) => { + return settingsStore.exportToEnvFile(targetPath); + }); + + ipcMain.handle('dialog-select-folder', async () => { + const { dialog } = require('electron'); + const result = await dialog.showOpenDialog({ + properties: ['openDirectory'], + title: 'Select Project Folder' + }); + return result.canceled ? null : result.filePaths[0]; + }); } // Handle creating/removing shortcuts on Windows when installing/uninstalling. diff --git a/desktop/electron/preload.ts b/desktop/electron/preload.ts index b50f779..3a860c6 100644 --- a/desktop/electron/preload.ts +++ b/desktop/electron/preload.ts @@ -32,6 +32,13 @@ contextBridge.exposeInMainWorld('electronAPI', { getSettings: () => ipcRenderer.invoke('settings-get-all'), getEnabledProviders: () => ipcRenderer.invoke('settings-get-providers'), hasApiKey: (provider: string) => ipcRenderer.invoke('settings-has-key', { provider }), + // Onboarding & Project IPC + isOnboardingComplete: () => ipcRenderer.invoke('settings-onboarding-complete'), + setOnboardingComplete: (complete: boolean) => ipcRenderer.invoke('settings-set-onboarding', { complete }), + setProjectPath: (projectPath: string) => ipcRenderer.invoke('settings-set-project', { projectPath }), + getIntegrationConfig: () => ipcRenderer.invoke('settings-get-integrations'), + exportEnvFile: (targetPath: string) => ipcRenderer.invoke('settings-export-env', { targetPath }), + selectFolder: () => ipcRenderer.invoke('dialog-select-folder'), // CLI Installation IPC checkCliInstalled: (cli: string) => ipcRenderer.invoke('cli-check-installed', { cli }), installCli: (installCommand: string) => ipcRenderer.invoke('cli-install', { installCommand }), diff --git a/desktop/electron/settings-store.ts b/desktop/electron/settings-store.ts index b9318c3..66cb0e3 100644 --- a/desktop/electron/settings-store.ts +++ b/desktop/electron/settings-store.ts @@ -5,11 +5,37 @@ import { app } from 'electron' const SETTINGS_FILE = 'squadron-settings.json' +// All configurable integration keys +export const INTEGRATION_KEYS = { + // AI Providers + anthropic: { label: 'Claude (Anthropic)', envKey: 'ANTHROPIC_API_KEY', category: 'ai' }, + google: { label: 'Gemini (Google)', envKey: 'GOOGLE_API_KEY', category: 'ai' }, + openai: { label: 'OpenAI (Codex)', envKey: 'OPENAI_API_KEY', category: 'ai' }, + + // Communication + slack_token: { label: 'Slack Bot Token', envKey: 'SLACK_BOT_TOKEN', category: 'communication' }, + discord_webhook: { label: 'Discord Webhook URL', envKey: 'DISCORD_WEBHOOK_URL', category: 'communication' }, + discord_token: { label: 'Discord Bot Token', envKey: 'DISCORD_BOT_TOKEN', category: 'communication' }, + + // Project Management + jira_server: { label: 'Jira Server URL', envKey: 'JIRA_SERVER', category: 'project', placeholder: 'https://your-domain.atlassian.net' }, + jira_email: { label: 'Jira Email', envKey: 'JIRA_EMAIL', category: 'project', placeholder: 'your-email@example.com' }, + jira_token: { label: 'Jira API Token', envKey: 'JIRA_TOKEN', category: 'project' }, + linear: { label: 'Linear API Key', envKey: 'LINEAR_API_KEY', category: 'project' }, + + // Development + github: { label: 'GitHub Token', envKey: 'GITHUB_TOKEN', category: 'development', placeholder: 'ghp_...' }, +} as const + +export type IntegrationKey = keyof typeof INTEGRATION_KEYS + interface Settings { apiKeys: Record // encrypted keys enabledProviders: string[] defaultProvider: string defaultModel: string + onboardingComplete: boolean + projectPath: string | null } const getSettingsPath = (): string => { @@ -30,7 +56,9 @@ const loadSettings = (): Settings => { apiKeys: {}, enabledProviders: ['shell'], defaultProvider: 'shell', - defaultModel: 'default' + defaultModel: 'default', + onboardingComplete: false, + projectPath: null } } @@ -50,7 +78,7 @@ export const saveApiKey = (provider: string, key: string): boolean => { console.warn('[Settings] Encryption not available, storing in plain text') const settings = loadSettings() settings.apiKeys[provider] = key - if (!settings.enabledProviders.includes(provider)) { + if (!settings.enabledProviders.includes(provider) && ['anthropic', 'google', 'openai'].includes(provider)) { settings.enabledProviders.push(provider) } saveSettings(settings) @@ -60,7 +88,7 @@ export const saveApiKey = (provider: string, key: string): boolean => { const encrypted = safeStorage.encryptString(key).toString('base64') const settings = loadSettings() settings.apiKeys[provider] = encrypted - if (!settings.enabledProviders.includes(provider)) { + if (!settings.enabledProviders.includes(provider) && ['anthropic', 'google', 'openai'].includes(provider)) { settings.enabledProviders.push(provider) } saveSettings(settings) @@ -118,6 +146,8 @@ export const getAllSettings = (): Omit & { hasKeys: Record< enabledProviders: settings.enabledProviders, defaultProvider: settings.defaultProvider, defaultModel: settings.defaultModel, + onboardingComplete: settings.onboardingComplete, + projectPath: settings.projectPath, hasKeys: Object.fromEntries( Object.keys(settings.apiKeys).map(k => [k, true]) ) @@ -130,3 +160,54 @@ export const setDefaultProvider = (provider: string, model: string): void => { settings.defaultModel = model saveSettings(settings) } + +export const setOnboardingComplete = (complete: boolean): void => { + const settings = loadSettings() + settings.onboardingComplete = complete + saveSettings(settings) +} + +export const setProjectPath = (projectPath: string | null): void => { + const settings = loadSettings() + settings.projectPath = projectPath + saveSettings(settings) +} + +export const isOnboardingComplete = (): boolean => { + const settings = loadSettings() + return settings.onboardingComplete +} + +// Export settings to .env file for Python backend +export const exportToEnvFile = (targetPath: string): boolean => { + try { + const settings = loadSettings() + const envLines: string[] = ['# Generated by Squadron Desktop App', ''] + + for (const [key, config] of Object.entries(INTEGRATION_KEYS)) { + const stored = settings.apiKeys[key] + if (stored) { + let value = stored + // Decrypt if encrypted + if (safeStorage.isEncryptionAvailable()) { + try { + const buffer = Buffer.from(stored, 'base64') + value = safeStorage.decryptString(buffer) + } catch { /* use as-is */ } + } + envLines.push(`${config.envKey}=${value}`) + } + } + + const envPath = path.join(targetPath, '.env') + fs.writeFileSync(envPath, envLines.join('\n')) + console.log(`[Settings] Exported .env to ${envPath}`) + return true + } catch (err) { + console.error('[Settings] Failed to export .env:', err) + return false + } +} + +// Get integration config for UI +export const getIntegrationConfig = () => INTEGRATION_KEYS diff --git a/desktop/package.json b/desktop/package.json index 12fa742..b1e0d9f 100644 --- a/desktop/package.json +++ b/desktop/package.json @@ -1,11 +1,15 @@ { - "name": "desktop", + "name": "squadron-desktop", "productName": "Squadron", - "description": "The Operating System for Autonomous Software Teams", + "description": "The AI Agent Command Center for Your Desktop", "author": "MikeeBuilds", "private": true, - "version": "0.0.0", + "version": "2.0.0", "main": "dist-electron/main.js", + "repository": { + "type": "git", + "url": "https://github.com/MikeeBuilds/Squadron" + }, "scripts": { "dev": "vite", "build": "tsc -b && vite build", @@ -13,8 +17,64 @@ "preview": "vite preview", "electron:dev": "concurrently -k \"cross-env BROWSER=none npm run dev\" \"npm run build:main && wait-on http://localhost:5173 && cross-env VITE_DEV_SERVER_URL=http://localhost:5173 electron .\"", "electron:build": "npm run build && npm run build:main && electron-builder", + "electron:build:mac": "npm run build && npm run build:main && electron-builder --mac", + "electron:build:win": "npm run build && npm run build:main && electron-builder --win", + "electron:build:linux": "npm run build && npm run build:main && electron-builder --linux", "build:main": "tsc -p electron/tsconfig.json" }, + "build": { + "appId": "com.squadron.desktop", + "productName": "Squadron", + "directories": { + "output": "release" + }, + "files": [ + "dist/**/*", + "dist-electron/**/*" + ], + "mac": { + "category": "public.app-category.developer-tools", + "target": [ + { + "target": "dmg", + "arch": [ + "x64", + "arm64" + ] + } + ], + "icon": "public/icon.icns", + "hardenedRuntime": true, + "gatekeeperAssess": false + }, + "win": { + "target": [ + { + "target": "nsis", + "arch": [ + "x64" + ] + } + ], + "icon": "public/icon.ico" + }, + "linux": { + "target": [ + { + "target": "AppImage", + "arch": [ + "x64" + ] + } + ], + "category": "Development", + "icon": "public/icon.png" + }, + "nsis": { + "oneClick": false, + "allowToChangeInstallationDirectory": true + } + }, "dependencies": { "@dnd-kit/core": "^6.3.1", "@dnd-kit/sortable": "^10.0.0", @@ -41,7 +101,7 @@ "@eslint/js": "^9.39.1", "@tailwindcss/postcss": "^4.1.18", "@types/node": "^24.10.1", - "@types/react": "^19.2.5", + "@types/react": "^19.2.0", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^5.1.1", "autoprefixer": "^10.4.23", @@ -63,4 +123,4 @@ "vite": "^7.2.4", "wait-on": "^9.0.3" } -} +} \ No newline at end of file diff --git a/desktop/src/App.tsx b/desktop/src/App.tsx index 3b50f0c..b3d0221 100644 --- a/desktop/src/App.tsx +++ b/desktop/src/App.tsx @@ -17,6 +17,7 @@ import { TaskWizard } from '@/components/TaskWizard' import { AgentCard } from '@/components/AgentCard' import { TerminalHub } from '@/components/TerminalHub' import { SettingsPanel } from '@/components/SettingsPanel' +import { OnboardingWizard } from '@/components/OnboardingWizard' // Shadcn Sidebar Imports import { @@ -41,6 +42,25 @@ export default function App() { const [isWizardOpen, setIsWizardOpen] = useState(false) const [isSettingsOpen, setIsSettingsOpen] = useState(false) const [kanbanKey, setKanbanKey] = useState(0) + const [showOnboarding, setShowOnboarding] = useState(false) + const [loading, setLoading] = useState(true) + + const api = (window as any).electronAPI + + // Check if onboarding is needed + useEffect(() => { + const checkOnboarding = async () => { + try { + const isComplete = await api.isOnboardingComplete() + setShowOnboarding(!isComplete) + } catch (err) { + console.error('Failed to check onboarding:', err) + } finally { + setLoading(false) + } + } + checkOnboarding() + }, []) useEffect(() => { const fetchStatus = async () => { @@ -53,6 +73,19 @@ export default function App() { return () => clearInterval(interval) }, []) + // Show loading or onboarding + if (loading) { + return ( +
+
Loading...
+
+ ) + } + + if (showOnboarding) { + return setShowOnboarding(false)} /> + } + const navItems = [ { id: 'kanban', label: 'Operations', icon: LayoutDashboard }, { id: 'terminals', label: 'Terminal Hub', icon: Terminal }, diff --git a/desktop/src/components/OnboardingWizard.tsx b/desktop/src/components/OnboardingWizard.tsx new file mode 100644 index 0000000..d18b7b8 --- /dev/null +++ b/desktop/src/components/OnboardingWizard.tsx @@ -0,0 +1,224 @@ +import { useState } from 'react' +import { Rocket, Key, FolderOpen, ChevronRight, ChevronLeft, Check, SkipForward } from 'lucide-react' +import { cn } from '@/lib/utils' + +interface OnboardingWizardProps { + onComplete: () => void +} + +const AI_PROVIDERS = [ + { id: 'anthropic', name: 'Claude', description: 'Anthropic Claude 4 Sonnet/Opus', color: 'from-orange-600 to-orange-400', envKey: 'ANTHROPIC_API_KEY' }, + { id: 'google', name: 'Gemini', description: 'Google Gemini 2.5 Pro/Flash', color: 'from-blue-600 to-blue-400', envKey: 'GOOGLE_API_KEY' }, + { id: 'openai', name: 'Codex', description: 'OpenAI GPT-4o / o1 / o3', color: 'from-green-600 to-green-400', envKey: 'OPENAI_API_KEY' }, +] + +export function OnboardingWizard({ onComplete }: OnboardingWizardProps) { + const [step, setStep] = useState(1) + const [selectedProvider, setSelectedProvider] = useState(null) + const [apiKey, setApiKey] = useState('') + const [saving, setSaving] = useState(false) + const [projectPath, setProjectPath] = useState(null) + + const api = (window as any).electronAPI + + const totalSteps = 3 + + const handleNext = async () => { + if (step === 2 && apiKey && selectedProvider) { + setSaving(true) + await api.saveApiKey(selectedProvider, apiKey) + setSaving(false) + } + + if (step === 3 && projectPath) { + await api.setProjectPath(projectPath) + await api.exportEnvFile(projectPath) + } + + if (step < totalSteps) { + setStep(step + 1) + } else { + await api.setOnboardingComplete(true) + onComplete() + } + } + + const handleSkip = async () => { + await api.setOnboardingComplete(true) + onComplete() + } + + const handleSelectFolder = async () => { + const folder = await api.selectFolder() + if (folder) { + setProjectPath(folder) + } + } + + const canProceed = () => { + if (step === 1) return !!selectedProvider + if (step === 2) return !!apiKey.trim() + return true // Step 3 is optional + } + + return ( +
+
+ {/* Logo & Title */} +
+
+ +
+

Welcome to Squadron

+

Let's get you set up in under a minute

+
+ + {/* Progress */} +
+ {[1, 2, 3].map(i => ( +
+ ))} +
+ + {/* Card */} +
+
+ {/* Step 1: Choose Provider */} + {step === 1 && ( +
+
+ + Step 1: Choose Your AI Provider +
+ +
+ {AI_PROVIDERS.map(provider => ( + + ))} +
+
+ )} + + {/* Step 2: Enter API Key */} + {step === 2 && ( +
+
+ + Step 2: Enter Your API Key +
+ +
+ + setApiKey(e.target.value)} + className="w-full bg-transparent border-0 text-white text-lg focus:outline-none placeholder:text-zinc-600" + autoFocus + /> +
+ +

+ Your key is encrypted and stored securely on your device. It never leaves your machine. +

+
+ )} + + {/* Step 3: Project Folder (Optional) */} + {step === 3 && ( +
+
+ + Step 3: Select Project Folder (Optional) +
+ +

+ Choose a project folder to export your settings as a .env file for the Python backend. +

+ +
+
+ {projectPath || 'No folder selected'} +
+ +
+
+ )} +
+ + {/* Footer */} +
+ + +
+ {step > 1 && ( + + )} + +
+
+
+ + {/* Step indicator */} +
+ Step {step} of {totalSteps} +
+
+
+ ) +} diff --git a/desktop/src/components/SettingsPanel.tsx b/desktop/src/components/SettingsPanel.tsx index f6f8ec3..46aaa86 100644 --- a/desktop/src/components/SettingsPanel.tsx +++ b/desktop/src/components/SettingsPanel.tsx @@ -1,7 +1,45 @@ import { useState, useEffect } from 'react' -import { X, Key, Check, AlertCircle, Eye, EyeOff, Trash2 } from 'lucide-react' +import { X, Key, Check, AlertCircle, Eye, EyeOff, Trash2, Folder, FolderOpen, Download } from 'lucide-react' import { cn } from '@/lib/utils' -import { PROVIDERS } from '@/lib/providers' + +// Integration configuration matching settings-store.ts +const INTEGRATIONS = { + ai: { + label: 'AI Providers', + icon: '🤖', + items: [ + { id: 'anthropic', label: 'Claude (Anthropic)', envKey: 'ANTHROPIC_API_KEY', color: 'text-orange-400' }, + { id: 'google', label: 'Gemini (Google)', envKey: 'GOOGLE_API_KEY', color: 'text-blue-400' }, + { id: 'openai', label: 'OpenAI (Codex)', envKey: 'OPENAI_API_KEY', color: 'text-green-400' }, + ] + }, + communication: { + label: 'Communication', + icon: '💬', + items: [ + { id: 'slack_token', label: 'Slack Bot Token', envKey: 'SLACK_BOT_TOKEN', color: 'text-purple-400', placeholder: 'xoxb-...' }, + { id: 'discord_webhook', label: 'Discord Webhook', envKey: 'DISCORD_WEBHOOK_URL', color: 'text-indigo-400', placeholder: 'https://discord.com/api/webhooks/...' }, + { id: 'discord_token', label: 'Discord Bot Token', envKey: 'DISCORD_BOT_TOKEN', color: 'text-indigo-400' }, + ] + }, + project: { + label: 'Project Management', + icon: '📋', + items: [ + { id: 'jira_server', label: 'Jira Server URL', envKey: 'JIRA_SERVER', color: 'text-blue-500', placeholder: 'https://your-domain.atlassian.net' }, + { id: 'jira_email', label: 'Jira Email', envKey: 'JIRA_EMAIL', color: 'text-blue-500', placeholder: 'your-email@example.com' }, + { id: 'jira_token', label: 'Jira API Token', envKey: 'JIRA_TOKEN', color: 'text-blue-500' }, + { id: 'linear', label: 'Linear API Key', envKey: 'LINEAR_API_KEY', color: 'text-violet-400', placeholder: 'lin_api_...' }, + ] + }, + development: { + label: 'Development', + icon: '🔧', + items: [ + { id: 'github', label: 'GitHub Token', envKey: 'GITHUB_TOKEN', color: 'text-gray-300', placeholder: 'ghp_...' }, + ] + } +} interface SettingsPanelProps { isOpen: boolean @@ -14,6 +52,8 @@ export function SettingsPanel({ isOpen, onClose }: SettingsPanelProps) { const [showKey, setShowKey] = useState>({}) const [saving, setSaving] = useState(null) const [saved, setSaved] = useState(null) + const [projectPath, setProjectPath] = useState(null) + const [activeSection, setActiveSection] = useState('ai') const api = (window as any).electronAPI @@ -22,138 +62,230 @@ export function SettingsPanel({ isOpen, onClose }: SettingsPanelProps) { if (!isOpen) return const loadKeyStatus = async () => { - const status: Record = {} - for (const providerId of Object.keys(PROVIDERS)) { - if (providerId === 'shell') continue - try { - status[providerId] = await api.hasApiKey(providerId) - } catch { - status[providerId] = false - } - } - setHasKeys(status) + const settings = await api.getSettings() + setHasKeys(settings.hasKeys || {}) + setProjectPath(settings.projectPath) } loadKeyStatus() }, [isOpen]) - const handleSaveKey = async (providerId: string) => { - const key = apiKeys[providerId] + const handleSaveKey = async (integrationId: string) => { + const key = apiKeys[integrationId] if (!key?.trim()) return - setSaving(providerId) + setSaving(integrationId) try { - await api.saveApiKey(providerId, key.trim()) - setHasKeys(prev => ({ ...prev, [providerId]: true })) - setApiKeys(prev => ({ ...prev, [providerId]: '' })) - setSaved(providerId) + await api.saveApiKey(integrationId, key.trim()) + setHasKeys(prev => ({ ...prev, [integrationId]: true })) + setApiKeys(prev => ({ ...prev, [integrationId]: '' })) + setSaved(integrationId) setTimeout(() => setSaved(null), 2000) } catch (err) { - console.error(`Failed to save API key for ${providerId}:`, err) + console.error(`Failed to save key for ${integrationId}:`, err) } finally { setSaving(null) } } - const handleDeleteKey = async (providerId: string) => { + const handleDeleteKey = async (integrationId: string) => { try { - await api.deleteApiKey(providerId) - setHasKeys(prev => ({ ...prev, [providerId]: false })) + await api.deleteApiKey(integrationId) + setHasKeys(prev => ({ ...prev, [integrationId]: false })) } catch (err) { - console.error(`Failed to delete API key for ${providerId}:`, err) + console.error(`Failed to delete key for ${integrationId}:`, err) + } + } + + const handleSelectFolder = async () => { + const folder = await api.selectFolder() + if (folder) { + await api.setProjectPath(folder) + setProjectPath(folder) + } + } + + const handleExportEnv = async () => { + if (!projectPath) return + const success = await api.exportEnvFile(projectPath) + if (success) { + setSaved('env-export') + setTimeout(() => setSaved(null), 2000) } } if (!isOpen) return null - const providerList = Object.values(PROVIDERS).filter(p => p.id !== 'shell') + const sections = Object.entries(INTEGRATIONS) return (
-
+
{/* Header */}

- API Configuration + Settings & Integrations

- {/* Content */} -
-

- Configure API keys for AI providers. Keys are encrypted and stored securely on your device. -

- - {providerList.map(provider => ( -
-
- - {hasKeys[provider.id] && ( - - - Configured - +
+ {/* Sidebar */} +
+ {sections.map(([key, section]) => ( +
+ > + {section.icon} + {section.label} + + ))} +
+ +
+ + {/* Content */} +
+ {activeSection === 'project-folder' ? ( +
+

Project Folder

+

+ Select your Squadron project folder. Integration keys will be exported to a .env file here. +

-
-
- setApiKeys(prev => ({ ...prev, [provider.id]: e.target.value }))} - className="w-full bg-zinc-950 border border-zinc-800 rounded-xl px-4 py-2.5 text-sm text-zinc-100 placeholder:text-zinc-600 focus:outline-none focus:border-yellow-400/50 transition-colors pr-10" - /> +
+
+ {projectPath || 'No project folder selected'} +
- - - {hasKeys[provider.id] && ( + {projectPath && ( )}
+ ) : ( +
+

+ {INTEGRATIONS[activeSection as keyof typeof INTEGRATIONS]?.label} +

-

- Environment variable: {provider.envKey} -

-
- ))} + {INTEGRATIONS[activeSection as keyof typeof INTEGRATIONS]?.items.map(item => ( +
+
+ + {hasKeys[item.id] && ( + + + Set + + )} +
+ +
+
+ setApiKeys(prev => ({ ...prev, [item.id]: e.target.value }))} + className="w-full bg-zinc-950 border border-zinc-800 rounded-lg px-3 py-2 text-xs text-zinc-100 placeholder:text-zinc-600 focus:outline-none focus:border-yellow-400/50 transition-colors pr-8" + /> + +
- {/* Security notice */} -
- -
- Security: API keys are encrypted using your operating system's secure storage (Windows DPAPI / macOS Keychain) and never leave your device. + + + {hasKeys[item.id] && ( + + )} +
+ +

+ {item.envKey} +

+
+ ))} +
+ )} + + {/* Security notice */} +
+ +
+ Security: Keys are encrypted using your OS secure storage. +
diff --git a/setup.py b/setup.py index c3985fa..7938dca 100644 --- a/setup.py +++ b/setup.py @@ -5,7 +5,7 @@ setup( name="squadron-agents", - version="0.6.0", + version="2.0.0", author="MikeeBuilds", author_email="mludlow000@icloud.com", description="The Operating System for Autonomous Software Teams",