diff --git a/docs/architecture.md b/docs/architecture.md new file mode 100644 index 0000000..576120e --- /dev/null +++ b/docs/architecture.md @@ -0,0 +1,259 @@ +--- +title: Architecture +description: Internal architecture of Core IDE -- Go backend, Angular frontend, MCP bridge, headless job runner, and data flow between components. +--- + +# Architecture + +Core IDE is a single binary that operates in two distinct modes, selected at startup. + +## Mode selection + +In `main.go`, the entry point inspects the command-line arguments and the display environment: + +```go +if headless || !hasDisplay() { + startHeadless() + return +} +``` + +`hasDisplay()` returns `true` on Windows unconditionally, and on Linux/macOS only when `DISPLAY` or `WAYLAND_DISPLAY` is set. This means the binary automatically falls back to headless mode on servers. + +## GUI mode + +### Wails application + +The GUI is a Wails 3 application. The Angular frontend is embedded into the binary at compile time via `//go:embed`: + +```go +//go:embed all:frontend/dist/wails-angular-template/browser +var assets embed.FS +``` + +Two Wails services are registered: + +- `GreetService` -- a minimal service demonstrating Wails method bindings. +- `MCPBridge` -- the main service that wires up the MCP HTTP server, WebSocket hub, and webview automation. + +### System tray + +The application runs as a macOS "accessory" (no Dock icon) with a system tray icon. Clicking the tray icon reveals a 380x480 frameless window at `/tray` that shows quick stats, recent projects, and action buttons. + +On macOS, a template icon (`icons/apptray.png`) is used so it adapts to light/dark mode automatically. On other platforms, the same icon is set as both light and dark variants. + +### Angular frontend + +The frontend is an Angular 20+ application using standalone components (no NgModules). It has two routes: + +| Route | Component | Purpose | +|-------|-----------|---------| +| `/tray` | `TrayComponent` | Compact system tray panel -- status, quick actions, recent projects | +| `/ide` | `IdeComponent` | Full IDE layout with sidebar navigation | + +The root route (`/`) redirects to `/tray` because the tray panel is the default window. + +#### Wails runtime bridge + +Components communicate with the Go backend through the `@wailsio/runtime` package: + +- **Events.On** -- subscribes to events emitted by Go (e.g. `time` events for the clock display). +- **Events.Emit** -- sends events to Go (e.g. `action` events for quick actions like `new-project`, `run-dev`). +- **Generated bindings** -- `frontend/bindings/` contains auto-generated TypeScript functions that call Go methods by ID (e.g. `GreetService.Greet`). + +The `IdeComponent` lazily imports `@wailsio/runtime` to avoid SSR issues: + +```typescript +import('@wailsio/runtime').then(({ Events }) => { + this.timeEventCleanup = Events.On('time', (time: { data: string }) => { + this.currentTime.set(time.data); + }); +}); +``` + +### MCPBridge + +`MCPBridge` is the central orchestrator in GUI mode. It is registered as a Wails service and receives the `ServiceStartup` lifecycle callback once the application initialises. + +#### Initialisation sequence + +1. Obtain the Wails `application.App` reference. +2. Wire the `webview.Service` with the app so it can access windows. +3. Set up a console message listener on all windows. +4. Inject console capture JavaScript into existing windows (with a polling delay to wait for window creation). +5. Start the HTTP server on `127.0.0.1:9877`. + +#### HTTP endpoints (GUI mode) + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/health` | GET | Returns `{"status":"ok","mcp":true,"webview":true}` | +| `/mcp` | GET | Server metadata (name, version, capabilities, WebSocket URL) | +| `/mcp/tools` | GET | Lists all 27 available webview tools | +| `/mcp/call` | POST | Executes a tool -- accepts `{"tool":"...","params":{...}}` | +| `/ws` | GET | WebSocket endpoint for GUI clients | + +#### Webview tools + +The MCP bridge exposes 27 tools for programmatic interaction with the application's webview windows. These are grouped by function: + +**Window management:** +- `webview_list` -- enumerate all windows (name, title, URL) + +**JavaScript execution:** +- `webview_eval` -- execute arbitrary JavaScript in a named window + +**Console and errors:** +- `webview_console` -- retrieve captured console messages (filterable by level) +- `webview_console_clear` -- clear the console buffer +- `webview_errors` -- retrieve captured error messages + +**DOM interaction:** +- `webview_click` -- click an element by CSS selector +- `webview_type` -- type text into an element +- `webview_query` -- query DOM elements by selector +- `webview_hover` -- hover over an element +- `webview_select` -- select an option in a dropdown +- `webview_check` -- check/uncheck a checkbox or radio button +- `webview_scroll` -- scroll to an element or position + +**DOM inspection:** +- `webview_element_info` -- get detailed information about an element +- `webview_computed_style` -- get computed CSS styles for an element +- `webview_highlight` -- visually highlight an element with a coloured overlay +- `webview_dom_tree` -- get the DOM tree structure (configurable depth) + +**Navigation:** +- `webview_navigate` -- navigate to a URL (http/https only) +- `webview_url` -- get the current page URL +- `webview_title` -- get the current page title +- `webview_source` -- get the full page HTML source + +**Capture:** +- `webview_screenshot` -- capture the full page as a base64 PNG +- `webview_screenshot_element` -- capture a specific element as a base64 PNG +- `webview_pdf` -- export the page as a PDF (base64 data URI) +- `webview_print` -- open the native print dialogue + +**Performance and network:** +- `webview_performance` -- get performance timing metrics +- `webview_resources` -- list loaded resources (scripts, stylesheets, images) +- `webview_network` -- get logged network requests +- `webview_network_clear` -- clear the network request log +- `webview_network_inject` -- inject a network interceptor for detailed logging + +All tools accept parameters as JSON. Most require a `window` parameter (the window name, e.g. `"tray-panel"`) and a `selector` parameter (a CSS selector). + +### ClaudeBridge + +The `ClaudeBridge` is a WebSocket relay designed to forward messages between GUI WebSocket clients and an upstream MCP core server at `ws://localhost:9876/ws`. It maintains: + +- A persistent connection to the upstream server with automatic reconnection (5-second backoff). +- A set of client connections (GUI browsers connecting via `/ws`). +- A broadcast channel that fans out messages from the upstream server to all connected clients. +- Message filtering -- only `claude_message` type messages from clients are forwarded upstream. + +The Claude bridge is currently disabled in the code (`b.claudeBridge.Start()` is commented out) because port 9876 does not host an MCP WebSocket server at present. + +## Headless mode + +### Overview + +Headless mode turns the binary into a CI/CD daemon. It polls Forgejo repositories for actionable events and dispatches work to AI agents. + +### Components + +``` +startHeadless() + | + +-- Journal (filesystem, ~/.core/journal/) + | + +-- Forge client (Forgejo API) + | + +-- Forgejo source (repo list from CORE_REPOS env or defaults) + | + +-- Job handlers (ordered, first-match-wins): + | 1. PublishDraftHandler + | 2. SendFixCommandHandler + | 3. DismissReviewsHandler + | 4. EnableAutoMergeHandler + | 5. TickParentHandler + | 6. DispatchHandler (agent dispatch via Clotho spinner) + | + +-- Poller (60-second interval) + | + +-- Daemon (PID file at ~/.core/core-ide.pid, health at 127.0.0.1:9878) + | + +-- Headless MCP server (127.0.0.1:9877) +``` + +### Job handler pipeline + +The poller queries the Forgejo source for jobs, then passes each job through the handler list. The first handler that matches claims the job. The handlers are ordered so that lightweight operations (publishing drafts, dismissing stale reviews) run before the heavy-weight agent dispatch. + +The `DispatchHandler` uses the Clotho spinner to allocate work to configured AI agents. Agent targets and the Clotho configuration are loaded from the Core config system. + +### Journal + +The journal persists job state to `~/.core/journal/` on the filesystem. This prevents the same job from being processed twice across daemon restarts. + +### HTTP endpoints (headless mode) + +| Endpoint | Method | Description | +|----------|--------|-------------| +| `/health` | GET | Returns `{"status":"ok","mode":"headless","cycle":}` | +| `/mcp` | GET | Server metadata with `"mode":"headless"` | +| `/mcp/tools` | GET | Lists 3 tools: `job_status`, `job_set_dry_run`, `job_run_once` | +| `/mcp/call` | POST | Executes a headless tool | + +### Headless tools + +| Tool | Description | +|------|-------------| +| `job_status` | Returns current poll cycle count and dry-run state | +| `job_set_dry_run` | Enable or disable dry-run mode at runtime (`{"enabled": true}`) | +| `job_run_once` | Trigger a single poll-dispatch cycle immediately | + +### Health check + +The daemon exposes a separate health endpoint on port 9878 via the `go-process` package. This is distinct from the MCP health on port 9877 and is intended for process supervisors (systemd, launchd). + +## Port summary + +| Port | Service | Mode | +|------|---------|------| +| 9877 | MCP HTTP server (tools, WebSocket) | Both | +| 9878 | Daemon health check | Headless only | +| 9876 | Upstream MCP core (external, not started by this binary) | N/A | + +## Data flow diagrams + +### GUI mode + +``` +System Tray Click + --> Tray Panel Window (/tray) + --> Angular TrayComponent + --> Events.Emit('action', ...) + --> Go event handler + +MCP Client (e.g. Claude Code) + --> POST /mcp/call {"tool":"webview_eval","params":{"window":"tray-panel","code":"..."}} + --> MCPBridge.handleMCPCall() + --> MCPBridge.executeWebviewTool() + --> webview.Service.ExecJS() + --> Wails webview +``` + +### Headless mode + +``` +Poller (every 60s) + --> Forgejo Source (API query) + --> Job list + --> Handler pipeline + --> PublishDraft / SendFix / DismissReviews / ... + --> DispatchHandler + --> Clotho Spinner + --> Agent target +``` diff --git a/docs/development.md b/docs/development.md new file mode 100644 index 0000000..f7d022c --- /dev/null +++ b/docs/development.md @@ -0,0 +1,294 @@ +--- +title: Development +description: How to build, test, and contribute to Core IDE. +--- + +# Development + +## Prerequisites + +| Tool | Version | Purpose | +|------|---------|---------| +| Go | 1.26+ | Backend compilation | +| Node.js | 22+ | Angular frontend build | +| npm | (bundled with Node) | Frontend dependency management | +| Wails 3 CLI | alpha.64+ | Desktop application framework (`wails3`) | +| `core` CLI | latest | Build system (`core build`) | + +The module lives inside a Go workspace (`~/Code/go.work`) and depends on three sibling modules via `replace` directives in `go.mod`: + +``` +replace ( + forge.lthn.ai/core/go => ../go + forge.lthn.ai/core/go-process => ../go-process + forge.lthn.ai/core/gui => ../gui +) +``` + +Ensure these directories exist alongside the `ide/` directory before building. + +## Building + +### Development mode (hot-reload) + +```bash +wails3 dev +``` + +This starts the Angular dev server, watches Go files for changes, rebuilds, and re-launches the application automatically. The Wails dev mode configuration is in `build/config.yml` under the `dev_mode` key. + +### Production build + +Using the Core CLI (preferred): + +```bash +core build +``` + +This reads `.core/build.yaml` and produces a platform-native binary. The build configuration enables CGO (required by Wails), strips debug symbols (`-s -w`), and uses `-trimpath`. + +Using Wails directly: + +```bash +wails3 build +``` + +### Cross-compilation targets + +The `.core/build.yaml` defines three targets: + +| OS | Architecture | +|----|-------------| +| darwin | arm64 | +| linux | amd64 | +| windows | amd64 | + +### Frontend-only build + +To build or serve the Angular frontend independently (useful for UI-only development): + +```bash +cd frontend +npm install +npm run dev # Development server with hot-reload +npm run build # Production build to frontend/dist/ +npm run test # Run Jasmine/Karma tests +``` + +## Testing + +### Go tests + +```bash +core go test # Run all tests +core go test --run TestName # Run a specific test +core go cov # Generate coverage report +core go cov --open # Open coverage HTML in browser +``` + +### Frontend tests + +```bash +cd frontend +npm run test +``` + +This runs the Angular test suite via Karma with Chrome. + +### Quality assurance + +```bash +core go qa # Format + vet + lint + test +core go qa full # + race detector, vulnerability scan, security audit +``` + +### CI pipelines + +Two Forgejo workflows are configured in `.forgejo/workflows/`: + +| Workflow | Trigger | Description | +|----------|---------|-------------| +| `test.yml` | Push to `main`/`dev`, PRs to `main` | Runs Go tests with race detection and coverage | +| `security-scan.yml` | Push to `main`/`dev`/`feat/*`, PRs to `main` | Runs security scanning | + +Both workflows are reusable, imported from `core/go-devops`. + +## Project structure + +``` +ide/ + main.go # Entry point + mcp_bridge.go # MCPBridge Wails service (GUI mode) + claude_bridge.go # WebSocket relay to upstream MCP + headless.go # Headless daemon (job runner) + headless_mcp.go # Headless MCP HTTP server + greetservice.go # Sample Wails service + icons/ + icons.go # Embedded icon assets + apptray.png # System tray icon + systray-mac-template.png + systray-default.png + frontend/ + src/ + app/ + app.ts # Root component (router outlet) + app.routes.ts # Route definitions (/tray, /ide) + app.config.ts # Angular providers + pages/ + tray/ # System tray panel component + ide/ # Full IDE layout component + components/ + sidebar/ # Navigation sidebar component + bindings/ # Auto-generated Go method bindings + package.json # npm dependencies + angular.json # Angular CLI configuration + build/ + config.yml # Wails 3 project configuration + appicon.png # Application icon + darwin/ # macOS-specific (Info.plist, .icns) + linux/ # Linux-specific (systemd, AppImage, nfpm) + windows/ # Windows-specific (NSIS, MSIX, manifest) + .core/ + build.yaml # core build configuration + .forgejo/ + workflows/ # CI pipeline definitions + go.mod + go.sum +``` + +## Adding a new Wails service + +1. Create a new Go file with a struct and methods you want to expose to the frontend: + +```go +// myservice.go +package main + +type MyService struct{} + +func (s *MyService) DoSomething(input string) (string, error) { + return "result: " + input, nil +} +``` + +2. Register the service in `main.go`: + +```go +Services: []application.Service{ + application.NewService(&GreetService{}), + application.NewService(mcpBridge), + application.NewService(&MyService{}), // add here +}, +``` + +3. Run `wails3 dev` or `wails3 build` to regenerate TypeScript bindings in `frontend/bindings/`. + +4. Import and call the generated function from Angular: + +```typescript +import { DoSomething } from '../bindings/changeme/myservice'; + +const result = await DoSomething('hello'); +``` + +## Adding a new MCP tool + +To add a new webview tool to the MCP bridge: + +1. Add the tool metadata to the `handleMCPTools` method in `mcp_bridge.go`: + +```go +{"name": "webview_my_tool", "description": "What it does"}, +``` + +2. Add a case to the `executeWebviewTool` switch in `mcp_bridge.go`: + +```go +case "webview_my_tool": + windowName := getStringParam(params, "window") + // Call webview.Service methods... + result, err := b.webview.SomeMethod(windowName) + if err != nil { + return map[string]any{"error": err.Error()} + } + return map[string]any{"result": result} +``` + +3. The tool is immediately available via `POST /mcp/call`. + +## Adding a new Angular page + +1. Create a new component: + +```bash +cd frontend +npx ng generate component pages/my-page --standalone +``` + +2. Add a route in `frontend/src/app/app.routes.ts`: + +```typescript +import { MyPageComponent } from './pages/my-page/my-page.component'; + +export const routes: Routes = [ + { path: 'my-page', component: MyPageComponent }, + // ... existing routes +]; +``` + +3. If the page needs to communicate with Go, use the Wails runtime: + +```typescript +import { Events } from '@wailsio/runtime'; + +// Listen for Go events +Events.On('my-event', (data) => { ... }); + +// Send events to Go +Events.Emit('my-action', payload); +``` + +## Platform-specific notes + +### macOS + +- The application runs as an "accessory" (`ActivationPolicyAccessory`), meaning it has no Dock icon and only appears in the system tray. +- Bundle identifier: `com.lethean.core-ide` +- Minimum macOS version: 10.15 (Catalina) +- The `Info.plist` is at `build/darwin/Info.plist`. + +### Linux + +- A systemd service file is provided at `build/linux/core-ide.service` for running in headless mode. +- A user-scoped service file is also available at `build/linux/core-ide.user.service`. +- nfpm packaging configuration is at `build/linux/nfpm/nfpm.yaml`. +- AppImage build script is at `build/linux/appimage/build.sh`. +- Display detection uses `DISPLAY` or `WAYLAND_DISPLAY` environment variables. + +### Windows + +- NSIS installer configuration: `build/windows/nsis/project.nsi` +- MSIX package template: `build/windows/msix/template.xml` +- `hasDisplay()` always returns `true` on Windows. + +## Coding standards + +- **UK English** in all documentation and user-facing strings (colour, organisation, centre). +- **Strict typing** -- all Go functions have explicit parameter and return types. +- **Formatting** -- run `core go fmt` before committing. +- **Linting** -- run `core go lint` to catch issues. +- **Licence** -- EUPL-1.2. Include the copyright header where appropriate. + +## Commit conventions + +Use conventional commits: + +``` +type(scope): description +``` + +Include the co-author line: + +``` +Co-Authored-By: Virgil +``` diff --git a/docs/index.md b/docs/index.md new file mode 100644 index 0000000..44c19ce --- /dev/null +++ b/docs/index.md @@ -0,0 +1,97 @@ +--- +title: Core IDE +description: A native desktop development environment and headless CI job runner built with Wails 3 and Angular, providing MCP-driven webview automation and Forgejo-integrated agent dispatch. +--- + +# Core IDE + +Core IDE is a native desktop application that serves two roles: + +1. **GUI mode** -- a Wails 3 desktop application with an Angular frontend, system tray panel, and an embedded MCP HTTP server that exposes webview automation tools (DOM inspection, JavaScript execution, screenshots, network monitoring, and more). + +2. **Headless mode** -- a daemon that polls Forgejo repositories for actionable signals (draft PRs, review state, fix commands) and dispatches work to AI agents via the Clotho spinner, with a minimal MCP HTTP interface for remote control. + +Both modes listen on `127.0.0.1:9877` and expose `/health`, `/mcp`, `/mcp/tools`, and `/mcp/call` endpoints. + +## Quick start + +### Prerequisites + +- Go 1.26+ +- Node.js 22+ and npm +- Wails 3 CLI (`wails3`) +- Access to the Go workspace at `~/Code/go.work` (the module uses `replace` directives for local siblings) + +### Development + +```bash +cd /path/to/core/ide + +# Install frontend dependencies and start dev mode (hot-reload) +wails3 dev + +# Or build a production binary +core build # uses .core/build.yaml +wails3 build # alternative, uses build/config.yml +``` + +### Headless mode + +```bash +# Run as a headless daemon (no GUI required) +./bin/core-ide --headless + +# Dry-run mode -- logs what would happen without side-effects +./bin/core-ide --headless --dry-run + +# Specify which repos to poll (comma-separated) +CORE_REPOS=host-uk/core,host-uk/core-php ./bin/core-ide --headless +``` + +A systemd unit is provided at `build/linux/core-ide.service` for running headless mode as a system service. + +## Package layout + +| Path | Purpose | +|------|---------| +| `main.go` | Entry point -- decides GUI or headless based on `--headless` flag / display availability | +| `mcp_bridge.go` | `MCPBridge` -- Wails service that starts the MCP HTTP server, WebSocket hub, and webview tool dispatcher (GUI mode) | +| `claude_bridge.go` | `ClaudeBridge` -- WebSocket relay between GUI clients and an upstream MCP core server | +| `headless.go` | `startHeadless()` -- daemon with Forgejo poller, job handlers, agent dispatch, PID file, and health endpoint | +| `headless_mcp.go` | `startHeadlessMCP()` -- minimal MCP HTTP server for headless mode (job status, dry-run toggle, manual poll trigger) | +| `greetservice.go` | `GreetService` -- sample Wails-bound service | +| `icons/` | Embedded PNG assets for system tray (macOS template icon, default icon) | +| `frontend/` | Angular 20 application (standalone components, SCSS, Wails runtime bindings) | +| `frontend/src/app/pages/tray/` | `TrayComponent` -- compact system tray panel (380x480 frameless window) | +| `frontend/src/app/pages/ide/` | `IdeComponent` -- full IDE layout with sidebar, dashboard, file explorer, terminal placeholders | +| `frontend/src/app/components/sidebar/` | `SidebarComponent` -- icon-based navigation rail | +| `frontend/bindings/` | Auto-generated TypeScript bindings for Go services | +| `build/` | Platform-specific build configuration (macOS plist, Windows NSIS/MSIX, Linux systemd/AppImage/nfpm) | +| `.core/build.yaml` | `core build` configuration (Wails type, CGO enabled, cross-compilation targets) | +| `build/config.yml` | Wails 3 project configuration (app metadata, dev mode settings, file associations) | + +## Dependencies + +### Go modules + +| Module | Role | +|--------|------| +| `forge.lthn.ai/core/go` | Core framework -- config, forge client, job runner, agent CI | +| `forge.lthn.ai/core/go-process` | Daemon utilities (PID file, health check endpoint) | +| `forge.lthn.ai/core/gui` | WebView service (`pkg/webview`) and WebSocket hub (`pkg/ws`) | +| `github.com/wailsapp/wails/v3` | Native desktop application framework | +| `github.com/gorilla/websocket` | WebSocket client for the Claude bridge | + +All three `forge.lthn.ai` dependencies are resolved via `replace` directives pointing to sibling directories (`../go`, `../go-process`, `../gui`). + +### Frontend + +| Package | Role | +|---------|------| +| `@angular/core` ^21 | Component framework | +| `@wailsio/runtime` 3.0.0-alpha.72 | Go/JS bridge (events, method calls) | +| `rxjs` ~7.8 | Reactive extensions | + +## Licence + +EUPL-1.2. See the copyright notice in `build/config.yml` and `build/darwin/Info.plist`.