diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index a625ddad905..43c14ea5c55 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -159,12 +159,23 @@ jobs: # the `-c.extraMetadata.main=./...` argument is mis-tokenized and # electron-builder treats it as a config-file path (ENOENT). bash parses # it correctly - which is why the Linux/macOS jobs already pass. + # + # The version/appVersion/cliVersion/buildDate metadata mirrors upstream's + # scripts/package.js, but with Arduino Agent's version: it is the version + # in the window title and About dialog, and buildDate is what makes the + # app load its bundled VS Code plugins (arduino-ide-electron-main.js). shell: bash - run: > - node ../node_modules/electron-builder/cli.js --dir --publish never - -c.electronVersion=30.1.2 - -c.extraMetadata.name=arduino-ide - -c.extraMetadata.main=./arduino-ide-electron-main.js + run: | + version=$(node -p "require('../arduino-mcp-extension/package.json').version") + cli_version=$(node -p "require('../arduino-ide-extension/package.json').arduino['arduino-cli'].version") + node ../node_modules/electron-builder/cli.js --dir --publish never \ + -c.electronVersion=30.1.2 \ + -c.extraMetadata.name=arduino-ide \ + -c.extraMetadata.main=./arduino-ide-electron-main.js \ + -c.extraMetadata.version="$version" \ + -c.extraMetadata.theia.frontend.config.appVersion="$version" \ + -c.extraMetadata.theia.frontend.config.cliVersion="$cli_version" \ + -c.extraMetadata.theia.frontend.config.buildDate="$(date -u +%Y-%m-%dT%H:%M:%SZ)" - name: Zip artifact (Windows) if: runner.os == 'Windows' @@ -192,7 +203,8 @@ jobs: draft: true # publish manually after verifying the artifacts fail_on_unmatched_files: true body: | - Unsigned development build of the Arduino IDE with the embedded MCP server. + Unsigned build of Arduino Agent: the Arduino IDE with an embedded MCP server. + Not affiliated with or endorsed by Arduino SA. Each archive contains the unpacked application for its platform - unzip and run `Arduino IDE` (`Arduino IDE.exe` on Windows). The MCP server starts on diff --git a/CLAUDE.md b/CLAUDE.md index 3e370cbb51b..30db39c3f88 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -5,6 +5,11 @@ Fork of Arduino IDE 2.x (Theia/Electron) with an embedded MCP server. arduino/arduino-ide by targeted file copies — this repo shares no git history with upstream); `arduino-mcp-extension/` is ours; `electron-app/` packages both. +Fork identity (About dialog, Arduino's IDE updater switched off) is overridden +from `arduino-mcp-extension/src/browser/agent-branding.ts` via rebinds, not by +editing vendored files. `arduino-ide-extension` emits no `.d.ts`, so its +classes are `any` in our extension: no `override`, no inherited fields. + ## Build - Yarn 4 via corepack (`corepack enable`). Node 18+ works (engines say <21 but diff --git a/README.md b/README.md index b1b2f24ccbd..92b2c1d145a 100644 --- a/README.md +++ b/README.md @@ -43,10 +43,17 @@ Unsigned development builds of the **[latest release](https://github.com/mixelpi - [**macOS**](https://github.com/mixelpixx/Arduino-Agent/releases/latest/download/arduino-ide-mcp-macos.zip) - [**Linux x64**](https://github.com/mixelpixx/Arduino-Agent/releases/latest/download/arduino-ide-mcp-linux.zip) -Unzip and run `Arduino IDE` (`Arduino IDE.exe` on Windows). Release notes and -older builds are on the +Unzip and run `Arduino IDE` (`Arduino IDE.exe` on Windows). The window, About +box and icon say **Arduino Agent**; only the executable keeps upstream's name for +now. Arduino Agent doesn't check Arduino's update server; get new versions here. +Release notes and older builds are on the [Releases](https://github.com/mixelpixx/Arduino-Agent/releases) page. +> **Have the official Arduino IDE installed too?** Both currently share settings +> and a single-instance lock, so starting one while the other is running hands +> off to the one already open. Close one before starting the other. Separating +> them properly is planned for v0.7.0. + > These are unsigned dev builds. On macOS you may need to allow the app under > **System Settings → Privacy & Security**; on Windows, dismiss SmartScreen with > **More info → Run anyway**. Prefer to build it yourself? See diff --git a/arduino-mcp-extension/src/browser/agent-branding.ts b/arduino-mcp-extension/src/browser/agent-branding.ts new file mode 100644 index 00000000000..7a8bcfdffb8 --- /dev/null +++ b/arduino-mcp-extension/src/browser/agent-branding.ts @@ -0,0 +1,113 @@ +/** + * Arduino Agent identity for the vendored Arduino IDE shell. + * + * arduino-ide-extension is upstream code synced by file copy, so the fork's + * identity lives here as rebinds and contributions rather than as edits there. + * + * - Arduino's IDE updater is switched off. It checks Arduino's official feed, + * so it would offer the official Arduino IDE as an "update" and replace this + * app, MCP server included. Board and library update checks stay on: they + * share the `arduino.checkForUpdates` preference, so that is left alone. + * - The About dialog names Arduino Agent, its own version and the Arduino IDE + * version it is based on, and says it is not an Arduino product. + * + * arduino-ide-extension ships no type declarations, so its classes are `any` + * here: subclasses below cannot use `override` or see inherited fields, and + * inject what they need themselves. + */ + +import { ClipboardService } from '@theia/core/lib/browser/clipboard-service'; +import { ApplicationServer } from '@theia/core/lib/common/application-protocol'; +import { CommandContribution, CommandRegistry } from '@theia/core/lib/common/command'; +import { MenuContribution, MenuModelRegistry } from '@theia/core/lib/common/menu'; +import { isOSX, isWindows } from '@theia/core/lib/common/os'; +import { inject, injectable } from '@theia/core/shared/inversify'; +import { AppService } from 'arduino-ide-extension/lib/browser/app-service'; +import { About } from 'arduino-ide-extension/lib/browser/contributions/about'; +import { CheckForIDEUpdates } from 'arduino-ide-extension/lib/browser/contributions/check-for-ide-updates'; +import { DialogService } from 'arduino-ide-extension/lib/browser/dialog-service'; +import { IDEUpdaterCommands } from 'arduino-ide-extension/lib/browser/ide-updater/ide-updater-commands'; + +/** Replaces Arduino's startup update check: never contacts the update feed. */ +@injectable() +export class DisabledIDEUpdateCheck extends CheckForIDEUpdates { + onStart(): void { + // Updater disabled; see the file header. + } + + async onReady(): Promise { + // Updater disabled; see the file header. + } +} + +/** + * Removes Help > "Check for Arduino IDE Updates". Contributions run in module + * order and this extension loads after arduino-ide-extension, so the command + * and menu item already exist when these run. + */ +@injectable() +export class RemoveIDEUpdaterCommand implements CommandContribution, MenuContribution { + registerCommands(registry: CommandRegistry): void { + registry.unregisterCommand(IDEUpdaterCommands.CHECK_FOR_UPDATES.id); + } + + registerMenus(registry: MenuModelRegistry): void { + registry.unregisterMenuAction(IDEUpdaterCommands.CHECK_FOR_UPDATES.id); + } +} + +/** Arduino's About dialog, reworded for the fork. Menu label comes from applicationName. */ +@injectable() +export class AgentAbout extends About { + @inject(AppService) + private readonly agentAppService!: AppService; + + @inject(ApplicationServer) + private readonly applicationServer!: ApplicationServer; + + @inject(ClipboardService) + private readonly agentClipboard!: ClipboardService; + + @inject(DialogService) + private readonly agentDialogService!: DialogService; + + registerCommands(registry: CommandRegistry): void { + registry.registerCommand(About.Commands.ABOUT_APP, { + execute: () => this.showAgentAbout(), + }); + } + + private async showAgentAbout(): Promise { + const [{ appVersion, cliVersion, buildDate }, extensions] = await Promise.all([ + this.agentAppService.info(), + this.applicationServer.getExtensionsInfos(), + ]); + const versionOf = (name: string) => extensions.find((e) => e.name === name)?.version; + const detail = [ + `Version: ${versionOf('arduino-mcp-extension') ?? appVersion}`, + `Date: ${buildDate || 'dev build'}`, + `CLI Version: ${cliVersion}`, + `Based on Arduino IDE ${versionOf('arduino-ide-extension') ?? ''}`, + '', + 'Arduino Agent is an independent fork of the Arduino IDE. It is not affiliated with or endorsed by Arduino SA.', + `Arduino IDE: Copyright © ${new Date().getFullYear()} Arduino s.r.l. and/or its affiliated companies`, + ].join('\n'); + + const ok = 'OK'; + const copy = 'Copy'; + const buttons = !isWindows && !isOSX ? [copy, ok] : [ok, copy]; + const { response } = await this.agentDialogService.showMessageBox({ + message: 'Arduino Agent', + title: 'Arduino Agent', + type: 'info', + detail, + buttons, + noLink: true, + defaultId: buttons.indexOf(ok), + cancelId: buttons.indexOf(ok), + }); + if (buttons[response] === copy) { + await this.agentClipboard.writeText(detail); + } + } +} diff --git a/arduino-mcp-extension/src/browser/arduino-mcp-frontend-module.ts b/arduino-mcp-extension/src/browser/arduino-mcp-frontend-module.ts index 7ba74283b8d..6e3fe3714bb 100644 --- a/arduino-mcp-extension/src/browser/arduino-mcp-frontend-module.ts +++ b/arduino-mcp-extension/src/browser/arduino-mcp-frontend-module.ts @@ -10,14 +10,26 @@ import { ContainerModule } from '@theia/core/shared/inversify'; import { WebSocketConnectionProvider } from '@theia/core/lib/browser/messaging/ws-connection-provider'; import { FrontendApplicationContribution } from '@theia/core/lib/browser/frontend-application-contribution'; +import { CommandContribution } from '@theia/core/lib/common/command'; +import { MenuContribution } from '@theia/core/lib/common/menu'; +import { About } from 'arduino-ide-extension/lib/browser/contributions/about'; +import { CheckForIDEUpdates } from 'arduino-ide-extension/lib/browser/contributions/check-for-ide-updates'; import { bindMCPPreferences, MCPPreferences } from './mcp-preferences'; import { MCPService, MCPServicePath } from '../common/mcp-service'; import { MCPFrontendContribution } from './mcp-frontend-contribution'; +import { AgentAbout, DisabledIDEUpdateCheck, RemoveIDEUpdaterCommand } from './agent-branding'; export default new ContainerModule((bind, unbind, isBound, rebind) => { - // Bind MCP preferences to add settings to Arduino IDE preferences panel + // Bind MCP preferences to add settings to the IDE preferences panel bindMCPPreferences(bind); + // Arduino Agent identity: no Arduino IDE updater, fork-specific About dialog + rebind(CheckForIDEUpdates).to(DisabledIDEUpdateCheck).inSingletonScope(); + rebind(About).to(AgentAbout).inSingletonScope(); + bind(RemoveIDEUpdaterCommand).toSelf().inSingletonScope(); + bind(CommandContribution).toService(RemoveIDEUpdaterCommand); + bind(MenuContribution).toService(RemoveIDEUpdaterCommand); + // Bind the MCP service proxy to communicate with backend bind(MCPService).toDynamicValue(ctx => { const connection = ctx.container.get(WebSocketConnectionProvider); diff --git a/arduino-mcp-extension/src/browser/mcp-frontend-contribution.ts b/arduino-mcp-extension/src/browser/mcp-frontend-contribution.ts index 8af0418b07f..48a2024b8b7 100644 --- a/arduino-mcp-extension/src/browser/mcp-frontend-contribution.ts +++ b/arduino-mcp-extension/src/browser/mcp-frontend-contribution.ts @@ -70,7 +70,7 @@ export class MCPFrontendContribution break; case 'arduino.mcp.requireAuth': this.messageService.info( - 'The MCP authentication setting takes effect after restarting the Arduino IDE.', + 'The MCP authentication setting takes effect after restarting Arduino Agent.', { timeout: 8000 } ); break; diff --git a/arduino-mcp-extension/src/browser/mcp-preferences.ts b/arduino-mcp-extension/src/browser/mcp-preferences.ts index c477ba80462..20ed4074eb0 100644 --- a/arduino-mcp-extension/src/browser/mcp-preferences.ts +++ b/arduino-mcp-extension/src/browser/mcp-preferences.ts @@ -50,7 +50,7 @@ export const mcpPreferenceSchema: PreferenceSchema = { type: 'boolean', description: nls.localize( 'arduino/mcp/preferences.enabled', - 'Enable MCP (Model Context Protocol) server integration. When enabled, AI assistants like Claude Code can interact with the Arduino IDE programmatically - compiling, uploading, managing libraries, and more.' + 'Enable MCP (Model Context Protocol) server integration. When enabled, AI assistants like Claude Code can interact with Arduino Agent programmatically - compiling, uploading, managing libraries, and more.' ), default: MCPConfigurationDefaults['arduino.mcp.enabled'], order: 1, @@ -59,7 +59,7 @@ export const mcpPreferenceSchema: PreferenceSchema = { type: 'boolean', description: nls.localize( 'arduino/mcp/preferences.autoConnect', - 'Automatically start the MCP server when Arduino IDE launches. Disable this if you want to manually control when the MCP server is available.' + 'Automatically start the MCP server when Arduino Agent launches. Disable this if you want to manually control when the MCP server is available.' ), default: MCPConfigurationDefaults['arduino.mcp.autoConnect'], order: 2, diff --git a/arduino-mcp-extension/src/node/mcp-server.ts b/arduino-mcp-extension/src/node/mcp-server.ts index ff45e211a43..b72d66c4a90 100644 --- a/arduino-mcp-extension/src/node/mcp-server.ts +++ b/arduino-mcp-extension/src/node/mcp-server.ts @@ -680,7 +680,7 @@ export class ArduinoMCPServer { res.end( JSON.stringify({ error: - 'Unauthorized. Pass the MCP token as an "Authorization: Bearer " header. The token is shown in the Arduino IDE (Preferences > MCP) and stored in ~/.arduinoIDE/mcp-token.', + 'Unauthorized. Pass the MCP token as an "Authorization: Bearer " header. The token is shown in Arduino Agent (Preferences > MCP) and stored in ~/.arduinoIDE/mcp-token.', }) ); return; diff --git a/electron-app/package.json b/electron-app/package.json index 0d3e168330f..8c673100710 100644 --- a/electron-app/package.json +++ b/electron-app/package.json @@ -60,7 +60,7 @@ "target": "electron", "frontend": { "config": { - "applicationName": "Arduino IDE", + "applicationName": "Arduino Agent", "defaultTheme": { "light": "arduino-theme", "dark": "arduino-theme-dark" @@ -103,8 +103,8 @@ } } }, - "author": "Arduino SA", - "description": "Arduino IDE", + "author": "mixelpixx", + "description": "Arduino Agent: the Arduino IDE with a built-in MCP server", "build": { "appId": "cc.arduino.IDE2", "productName": "Arduino IDE", diff --git a/electron-app/resources/icon.icns b/electron-app/resources/icon.icns index ff6e10a8963..cda8ec03b70 100644 Binary files a/electron-app/resources/icon.icns and b/electron-app/resources/icon.icns differ diff --git a/electron-app/resources/icon.ico b/electron-app/resources/icon.ico index fa2400c9426..0b660545cc1 100644 Binary files a/electron-app/resources/icon.ico and b/electron-app/resources/icon.ico differ diff --git a/electron-app/resources/icons/512x512.png b/electron-app/resources/icons/512x512.png index 9338ee92ff1..2ceb017d3dc 100644 Binary files a/electron-app/resources/icons/512x512.png and b/electron-app/resources/icons/512x512.png differ diff --git a/electron-app/resources/preload.html b/electron-app/resources/preload.html index e11e1c6bbac..f7b272c210b 100644 --- a/electron-app/resources/preload.html +++ b/electron-app/resources/preload.html @@ -74,119 +74,17 @@
- - - - - - - - - - - + + + + + - - - - - - - - - - - - - - - + + + + +