From 045f20991251a161a0a630236887e50cedc03dd5 Mon Sep 17 00:00:00 2001 From: xierenhong Date: Fri, 12 Sep 2025 21:38:36 +0800 Subject: [PATCH 1/2] feat: add stagewise plugin integration with project-based agent server --- plugins/plugin-stagewise/package.json | 19 ++++++++ pnpm-workspace.yaml | 1 + src/cli.ts | 3 +- src/plugins/stagewise.ts | 67 ++++++++++++++++----------- 4 files changed, 61 insertions(+), 29 deletions(-) create mode 100644 plugins/plugin-stagewise/package.json diff --git a/plugins/plugin-stagewise/package.json b/plugins/plugin-stagewise/package.json new file mode 100644 index 00000000..7ed9efeb --- /dev/null +++ b/plugins/plugin-stagewise/package.json @@ -0,0 +1,19 @@ +{ + "name": "@neovate/plugin-stagewise", + "version": "0.0.1", + "description": "Stagewise plugin for neovate", + "type": "module", + "main": "dist/index.mjs", + "types": "dist/index.d.ts", + "source": "src/index.ts", + "scripts": { + "build": "bun build src/index.ts --minify --outfile dist/index.mjs --target=node" + }, + "dependencies": { + "@stagewise/agent-interface": "^0.2.3" + }, + "keywords": [], + "author": "", + "license": "ISC", + "packageManager": "pnpm@10.13.1" +} diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index f2f1d351..5805b456 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -2,6 +2,7 @@ packages: - browser - vscode-extension - examples/* + - plugins/* patchedDependencies: '@openai/agents-core': patches/@openai__agents-core.patch diff --git a/src/cli.ts b/src/cli.ts index e61dd2ca..b91b8f3b 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -4,6 +4,7 @@ import path from 'path'; import { fileURLToPath } from 'url'; import { runNeovate } from '.'; import { PRODUCT_ASCII_ART, PRODUCT_NAME } from './constants'; +import { createStagewisePlugin } from './plugins/stagewise'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const pkg = JSON.parse( @@ -14,7 +15,7 @@ runNeovate({ productName: PRODUCT_NAME, productASCIIArt: PRODUCT_ASCII_ART, version: pkg.version, - plugins: [], + plugins: [createStagewisePlugin()], upgrade: { registryBase: 'https://registry.npmjs.org', name: pkg.name, diff --git a/src/plugins/stagewise.ts b/src/plugins/stagewise.ts index 9d23a457..3230160d 100644 --- a/src/plugins/stagewise.ts +++ b/src/plugins/stagewise.ts @@ -1,4 +1,3 @@ -// @ts-nocheck import { type AgentServer, AgentStateType, @@ -7,18 +6,18 @@ import { import createDebug from 'debug'; import { Context } from '../context'; import { type Plugin } from '../plugin'; -import { Service } from '../service'; +import { Project } from '../project'; import { relativeToHome } from '../utils/path'; const debug = createDebug('neovate:plugins:stagewise'); -type CreateStagewisePluginOpts = {}; - -export const createStagewisePlugin = (opts: CreateStagewisePluginOpts) => { +export const createStagewisePlugin = () => { let sw: StagewiseAgent | null = null; + return { name: 'stagewise', - async cliStart() { + + async initialized(this: Context) { try { sw = new StagewiseAgent({ context: this, @@ -29,10 +28,12 @@ export const createStagewisePlugin = (opts: CreateStagewisePluginOpts) => { debug('Failed to start Stagewise agent:', error); } }, + async destroy() { await sw?.stop(); }, - async status() { + + async status(this: Context) { const port = sw?.port; const status = port ? `Connected, port: ${port}` : 'Disconnected'; return { @@ -50,8 +51,8 @@ export interface StagewiseAgentOpts { export class StagewiseAgent { private context: Context; - private service?: Service; private server: AgentServer | null = null; + private activeProjects: Map = new Map(); public port: number = 0; constructor(opts: StagewiseAgentOpts) { @@ -59,12 +60,6 @@ export class StagewiseAgent { } async start() { - // Create a separate service for Stagewise with independent chat history - this.service = await Service.create({ - agentType: 'code', - context: this.context, - }); - this.server = await createAgentServer(); this.server.setAgentName(`${this.context.productName} AI Agent`); @@ -88,6 +83,17 @@ export class StagewiseAgent { await this.server.wss.close(); await this.server.server.close(); } + this.activeProjects.clear(); + } + + private getOrCreateProject(connectionId: string): Project { + if (!this.activeProjects.has(connectionId)) { + const project = new Project({ + context: this.context, + }); + this.activeProjects.set(connectionId, project); + } + return this.activeProjects.get(connectionId)!; } private async processUserMessage(message: any) { @@ -116,13 +122,12 @@ export class StagewiseAgent { debug('Processing user message:', userText); const { metadata } = message; + const connectionId = message.connectionId || 'default'; - // Build enhanced content with selected elements context let enhancedContent = userText; enhancedContent += `\n\nIMPORTANT: don't need to run test or build to check if the code is working, speed is more important.`; - // Add current page context if (metadata.currentUrl) { enhancedContent += `\n\nCurrent page context:`; enhancedContent += `\n- URL: ${metadata.currentUrl}`; @@ -131,7 +136,6 @@ export class StagewiseAgent { } } - // Add selected elements context if (metadata.selectedElements && metadata.selectedElements.length > 0) { enhancedContent += `\n\nSelected elements context (${metadata.selectedElements.length} element(s)):`; @@ -154,7 +158,6 @@ export class StagewiseAgent { if (val && val.value && val.value._debugSource) { enhancedContent += `\n- ${property} value debug source: ${val.value._debugSource}`; } - // Add other useful properties if (val && typeof val === 'string' && val.length < 100) { enhancedContent += `\n- ${property}: ${val}`; } @@ -168,18 +171,26 @@ export class StagewiseAgent { 'Generating response...', ); - const { query } = await import('../query'); - const { isReasoningModel } = await import('../provider'); - - const result = await query({ - input: [{ role: 'user', content: enhancedContent }], - service: this.service!, - thinking: isReasoningModel(this.service!.context.config.model), + const project = this.getOrCreateProject(connectionId); + + const result = await project.send(enhancedContent, { + onToolApprove: () => Promise.resolve(true), + onTextDelta: async (text: string) => { + this.server!.interface.messaging.set([ + { + type: 'text', + text, + }, + ]); + }, }); - let response = - result.finalText || - "I processed your request but didn't generate a text response."; + let response: string; + if (result.success) { + response = result.data.text || 'I processed your request successfully.'; + } else { + response = `I encountered an error: ${result.error.message}`; + } this.server!.interface.messaging.set([ { From 6d3c121d4fe1699d3bf90340a03e08d82b100e9f Mon Sep 17 00:00:00 2001 From: xierenhong Date: Fri, 12 Sep 2025 22:30:43 +0800 Subject: [PATCH 2/2] feat: move stagewise plugin to separate package and update build config --- .gitignore | 2 ++ package.json | 1 - plugins/plugin-stagewise/package.json | 8 +++++--- .../plugin-stagewise/src/index.ts | 10 ++++++---- pnpm-lock.yaml | 12 +++++++++--- src/cli.ts | 3 +-- src/index.ts | 1 + 7 files changed, 24 insertions(+), 13 deletions(-) rename src/plugins/stagewise.ts => plugins/plugin-stagewise/src/index.ts (97%) diff --git a/.gitignore b/.gitignore index 9792407a..0ffad5c5 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ /CLAUDE.md .takumi/todos /tmp +/plugins/*/node_modules +/plugins/*/dist diff --git a/package.json b/package.json index 15caecfc..8afee543 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,6 @@ "@openai/agents-extensions": "^0.0.16", "@openrouter/ai-sdk-provider": "^0.7.5", "@sinclair/typebox": "^0.34.38", - "@stagewise/agent-interface": "^0.2.3", "@trivago/prettier-plugin-sort-imports": "^5.2.2", "@types/bun": "^1.2.21", "@types/debug": "^4.1.12", diff --git a/plugins/plugin-stagewise/package.json b/plugins/plugin-stagewise/package.json index 7ed9efeb..4741ec74 100644 --- a/plugins/plugin-stagewise/package.json +++ b/plugins/plugin-stagewise/package.json @@ -7,10 +7,12 @@ "types": "dist/index.d.ts", "source": "src/index.ts", "scripts": { - "build": "bun build src/index.ts --minify --outfile dist/index.mjs --target=node" + "build": "bun build src/index.ts --minify --external @neovate/code --outfile dist/index.mjs --target=node", + "dev": "bun build src/index.ts --watch --external @neovate/code --outfile dist/index.mjs --target=node" }, - "dependencies": { - "@stagewise/agent-interface": "^0.2.3" + "devDependencies": { + "@stagewise/agent-interface": "^0.2.3", + "@neovate/code": "workspace:*" }, "keywords": [], "author": "", diff --git a/src/plugins/stagewise.ts b/plugins/plugin-stagewise/src/index.ts similarity index 97% rename from src/plugins/stagewise.ts rename to plugins/plugin-stagewise/src/index.ts index 3230160d..b865e84a 100644 --- a/src/plugins/stagewise.ts +++ b/plugins/plugin-stagewise/src/index.ts @@ -1,13 +1,11 @@ +import { type Context, type Plugin, _Project as Project } from '@neovate/code'; import { type AgentServer, AgentStateType, createAgentServer, } from '@stagewise/agent-interface/agent'; import createDebug from 'debug'; -import { Context } from '../context'; -import { type Plugin } from '../plugin'; -import { Project } from '../project'; -import { relativeToHome } from '../utils/path'; +import os from 'os'; const debug = createDebug('neovate:plugins:stagewise'); @@ -221,3 +219,7 @@ export class StagewiseAgent { } } } + +export function relativeToHome(p: string) { + return p.replace(os.homedir(), '~'); +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0b79fe96..ac91da0e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -67,9 +67,6 @@ importers: '@sinclair/typebox': specifier: ^0.34.38 version: 0.34.38 - '@stagewise/agent-interface': - specifier: ^0.2.3 - version: 0.2.3 '@trivago/prettier-plugin-sort-imports': specifier: ^5.2.2 version: 5.2.2(prettier@3.6.2) @@ -395,6 +392,15 @@ importers: examples/mcp: {} + plugins/plugin-stagewise: + devDependencies: + '@neovate/code': + specifier: workspace:* + version: link:../.. + '@stagewise/agent-interface': + specifier: ^0.2.3 + version: 0.2.3 + vscode-extension: devDependencies: '@types/vscode': diff --git a/src/cli.ts b/src/cli.ts index b91b8f3b..e61dd2ca 100644 --- a/src/cli.ts +++ b/src/cli.ts @@ -4,7 +4,6 @@ import path from 'path'; import { fileURLToPath } from 'url'; import { runNeovate } from '.'; import { PRODUCT_ASCII_ART, PRODUCT_NAME } from './constants'; -import { createStagewisePlugin } from './plugins/stagewise'; const __dirname = path.dirname(fileURLToPath(import.meta.url)); const pkg = JSON.parse( @@ -15,7 +14,7 @@ runNeovate({ productName: PRODUCT_NAME, productASCIIArt: PRODUCT_ASCII_ART, version: pkg.version, - plugins: [createStagewisePlugin()], + plugins: [], upgrade: { registryBase: 'https://registry.npmjs.org', name: pkg.name, diff --git a/src/index.ts b/src/index.ts index fbb5c192..476bee0e 100644 --- a/src/index.ts +++ b/src/index.ts @@ -24,6 +24,7 @@ export { createTool } from './tool'; export { z as _zod } from 'zod'; export { ConfigManager as _ConfigManager } from './config'; export { query as _query } from './query'; +export { Project as _Project } from './project'; export type { Plugin, Context };