From 908f1977bb3fa35af31f5741ff69671deb115398 Mon Sep 17 00:00:00 2001 From: BeatSeat <137616995+BeatSeat@users.noreply.github.com> Date: Wed, 8 Oct 2025 06:34:59 +0800 Subject: [PATCH] chore: migrate to claude agent sdk * chore: migrate to claude agent sdk * feat: add CI workflow to build VSIX package --- .github/workflows/build.yml | 43 ++++++++++++++++++++++++++++++ esbuild.js | 2 +- package.json | 2 +- pnpm-lock.yaml | 38 +++++--------------------- src/services/claude/claude.ts | 43 ++++++++++++++++++++---------- src/services/claude/client.ts | 2 +- src/services/claude/session.ts | 2 +- src/services/claude/types.ts | 2 +- src/types/global.d.ts | 2 +- webview/src/services/messageBus.ts | 25 +++++++++++++---- webview/src/utils/messageUtils.ts | 6 +++-- webview/types/messages.ts | 7 ++++- 12 files changed, 114 insertions(+), 60 deletions(-) create mode 100644 .github/workflows/build.yml diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..8f34a82 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,43 @@ +name: Build VS Code Extension + +on: + push: + pull_request: + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Setup pnpm + uses: pnpm/action-setup@v3 + with: + version: 9 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 20 + cache: pnpm + + - name: Install dependencies + run: pnpm install --frozen-lockfile + + - name: Build Webview assets + run: pnpm run build:webview + + - name: Compile extension + run: pnpm run compile + + - name: Package VSIX + run: pnpm run vsix + + - name: Upload VSIX artifact + uses: actions/upload-artifact@v4 + with: + name: claudex-extension + path: "*.vsix" + if-no-files-found: error diff --git a/esbuild.js b/esbuild.js index 2de67bd..f284571 100644 --- a/esbuild.js +++ b/esbuild.js @@ -39,7 +39,7 @@ const copyClaudeCliPlugin = { const require = createRequire(import.meta.url); build.onEnd(async () => { try { - const pkgDir = path.dirname(require.resolve('@anthropic-ai/claude-code/cli.js')); + const pkgDir = path.dirname(require.resolve('@anthropic-ai/claude-agent-sdk/cli.js')); const outDir = path.resolve(process.cwd(), 'dist'); await fs.mkdir(outDir, { recursive: true }); diff --git a/package.json b/package.json index 92412ec..7103821 100644 --- a/package.json +++ b/package.json @@ -410,7 +410,7 @@ "vue-tsc": "^3.0.8" }, "dependencies": { - "@anthropic-ai/claude-code": "^1.0.123", + "@anthropic-ai/claude-agent-sdk": "^0.1.9", "@anthropic-ai/sdk": "^0.63.1", "@mdi/font": "^7.4.47", "@modelcontextprotocol/sdk": "^1.18.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 78aaa6a..9ca73ec 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -8,9 +8,9 @@ importers: .: dependencies: - '@anthropic-ai/claude-code': - specifier: ^1.0.123 - version: 1.0.128 + '@anthropic-ai/claude-agent-sdk': + specifier: ^0.1.9 + version: 0.1.9 '@anthropic-ai/sdk': specifier: ^0.63.1 version: 0.63.1(zod@4.1.11) @@ -105,10 +105,9 @@ importers: packages: - '@anthropic-ai/claude-code@1.0.128': - resolution: {integrity: sha512-uUg5cFMJfeQetQzFw76Vpbro6DAXst2Lpu8aoZWRFSoQVYu5ZSAnbBoxaWmW/IgnHSqIIvtMwzCoqmcA9j9rNQ==} + '@anthropic-ai/claude-agent-sdk@0.1.9': + resolution: {integrity: sha512-vQ1pJWGvc9f7qmfkgRoq/RUeqtXCbBE5jnn8zqXcY/nArZzL7nlwYQbsLDse53U105Idx3tBl6AdjHgisSww/w==} engines: {node: '>=18.0.0'} - hasBin: true '@anthropic-ai/sdk@0.63.1': resolution: {integrity: sha512-wMA/Xx5GLO+npV992YKUfsmlI6699XG/jFjCPTf/nsMBfUh3e3KmNiOKuhqSMZibOjoLOlhYc7L4pfLPI8A+RA==} @@ -433,40 +432,34 @@ packages: resolution: {integrity: sha512-9B+taZ8DlyyqzZQnoeIvDVR/2F4EbMepXMc/NdVbkzsJbzkUjhXv/70GQJ7tdLA4YJgNP25zukcxpX2/SueNrA==} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-arm@1.0.5': resolution: {integrity: sha512-gvcC4ACAOPRNATg/ov8/MnbxFDJqf/pDePbBnuBDcjsI8PssmjoKMAz4LtLaVi+OnSb5FK/yIOamqDwGmXW32g==} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-libvips-linux-x64@1.0.4': resolution: {integrity: sha512-MmWmQ3iPFZr0Iev+BAgVMb3ZyC4KeFc3jFxnNbEPas60e1cIfevbtuyf9nDGIzOaW9PdnDciJm+wFFaTlj5xYw==} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm64@0.33.5': resolution: {integrity: sha512-JMVv+AMRyGOHtO1RFBiJy/MBsgz0x4AWrT6QoEVVTyh1E39TrCUpTRI7mx9VksGX4awWASxqCYLCV4wBZHAYxA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm64] os: [linux] - libc: [glibc] '@img/sharp-linux-arm@0.33.5': resolution: {integrity: sha512-JTS1eldqZbJxjvKaAkxhZmBqPRGmxgu+qFKSInv8moZ2AmT5Yib3EQ1c6gp493HvrvV8QgdOXdyaIBrhvFhBMQ==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [arm] os: [linux] - libc: [glibc] '@img/sharp-linux-x64@0.33.5': resolution: {integrity: sha512-opC+Ok5pRNAzuvq1AG0ar+1owsu842/Ab+4qvU879ippJBHvyY5n2mxF1izXqkPYlGuP/M556uh53jRLJmzTWA==} engines: {node: ^18.17.0 || ^20.3.0 || >=21.0.0} cpu: [x64] os: [linux] - libc: [glibc] '@img/sharp-win32-x64@0.33.5': resolution: {integrity: sha512-MpY/o8/8kj+EcnxwvrP4aTJSWw/aZ7JIGR4aBeZkZw5B7/Jn+tY9/VNwtcoGmdT7GfggGIU4kygOMSbYnOrAbg==} @@ -570,67 +563,56 @@ packages: resolution: {integrity: sha512-IoerZJ4l1wRMopEHRKOO16e04iXRDyZFZnNZKrWeNquh5d6bucjezgd+OxG03mOMTnS1x7hilzb3uURPkJ0OfA==} cpu: [arm] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm-musleabihf@4.52.3': resolution: {integrity: sha512-ZYdtqgHTDfvrJHSh3W22TvjWxwOgc3ThK/XjgcNGP2DIwFIPeAPNsQxrJO5XqleSlgDux2VAoWQ5iJrtaC1TbA==} cpu: [arm] os: [linux] - libc: [musl] '@rollup/rollup-linux-arm64-gnu@4.52.3': resolution: {integrity: sha512-NcViG7A0YtuFDA6xWSgmFb6iPFzHlf5vcqb2p0lGEbT+gjrEEz8nC/EeDHvx6mnGXnGCC1SeVV+8u+smj0CeGQ==} cpu: [arm64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-arm64-musl@4.52.3': resolution: {integrity: sha512-d3pY7LWno6SYNXRm6Ebsq0DJGoiLXTb83AIPCXl9fmtIQs/rXoS8SJxxUNtFbJ5MiOvs+7y34np77+9l4nfFMw==} cpu: [arm64] os: [linux] - libc: [musl] '@rollup/rollup-linux-loong64-gnu@4.52.3': resolution: {integrity: sha512-3y5GA0JkBuirLqmjwAKwB0keDlI6JfGYduMlJD/Rl7fvb4Ni8iKdQs1eiunMZJhwDWdCvrcqXRY++VEBbvk6Eg==} cpu: [loong64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-ppc64-gnu@4.52.3': resolution: {integrity: sha512-AUUH65a0p3Q0Yfm5oD2KVgzTKgwPyp9DSXc3UA7DtxhEb/WSPfbG4wqXeSN62OG5gSo18em4xv6dbfcUGXcagw==} cpu: [ppc64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-gnu@4.52.3': resolution: {integrity: sha512-1makPhFFVBqZE+XFg3Dkq+IkQ7JvmUrwwqaYBL2CE+ZpxPaqkGaiWFEWVGyvTwZace6WLJHwjVh/+CXbKDGPmg==} cpu: [riscv64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-riscv64-musl@4.52.3': resolution: {integrity: sha512-OOFJa28dxfl8kLOPMUOQBCO6z3X2SAfzIE276fwT52uXDWUS178KWq0pL7d6p1kz7pkzA0yQwtqL0dEPoVcRWg==} cpu: [riscv64] os: [linux] - libc: [musl] '@rollup/rollup-linux-s390x-gnu@4.52.3': resolution: {integrity: sha512-jMdsML2VI5l+V7cKfZx3ak+SLlJ8fKvLJ0Eoa4b9/vCUrzXKgoKxvHqvJ/mkWhFiyp88nCkM5S2v6nIwRtPcgg==} cpu: [s390x] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-gnu@4.52.3': resolution: {integrity: sha512-tPgGd6bY2M2LJTA1uGq8fkSPK8ZLYjDjY+ZLK9WHncCnfIz29LIXIqUgzCR0hIefzy6Hpbe8Th5WOSwTM8E7LA==} cpu: [x64] os: [linux] - libc: [glibc] '@rollup/rollup-linux-x64-musl@4.52.3': resolution: {integrity: sha512-BCFkJjgk+WFzP+tcSMXq77ymAPIxsX9lFJWs+2JzuZTLtksJ2o5hvgTdIcZ5+oKzUDMwI0PfWzRBYAydAHF2Mw==} cpu: [x64] os: [linux] - libc: [musl] '@rollup/rollup-openharmony-arm64@4.52.3': resolution: {integrity: sha512-KTD/EqjZF3yvRaWUJdD1cW+IQBk4fbQaHYJUmP8N4XoKFZilVL8cobFSTDnjTtxWJQ3JYaMgF4nObY/+nYkumA==} @@ -744,28 +726,24 @@ packages: engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [glibc] '@tailwindcss/oxide-linux-arm64-musl@4.1.13': resolution: {integrity: sha512-hZQrmtLdhyqzXHB7mkXfq0IYbxegaqTmfa1p9MBj72WPoDD3oNOh1Lnxf6xZLY9C3OV6qiCYkO1i/LrzEdW2mg==} engines: {node: '>= 10'} cpu: [arm64] os: [linux] - libc: [musl] '@tailwindcss/oxide-linux-x64-gnu@4.1.13': resolution: {integrity: sha512-uaZTYWxSXyMWDJZNY1Ul7XkJTCBRFZ5Fo6wtjrgBKzZLoJNrG+WderJwAjPzuNZOnmdrVg260DKwXCFtJ/hWRQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [glibc] '@tailwindcss/oxide-linux-x64-musl@4.1.13': resolution: {integrity: sha512-oXiPj5mi4Hdn50v5RdnuuIms0PVPI/EG4fxAfFiIKQh5TgQgX7oSuDWntHW7WNIi/yVLAiS+CRGW4RkoGSSgVQ==} engines: {node: '>= 10'} cpu: [x64] os: [linux] - libc: [musl] '@tailwindcss/oxide-wasm32-wasi@4.1.13': resolution: {integrity: sha512-+LC2nNtPovtrDwBc/nqnIKYh/W2+R69FA0hgoeOn64BdCX522u19ryLh3Vf3F8W49XBcMIxSe665kwy21FkhvA==} @@ -2492,28 +2470,24 @@ packages: engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [glibc] lightningcss-linux-arm64-musl@1.30.1: resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] - libc: [musl] lightningcss-linux-x64-gnu@1.30.1: resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [glibc] lightningcss-linux-x64-musl@1.30.1: resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] - libc: [musl] lightningcss-win32-arm64-msvc@1.30.1: resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==} @@ -3885,7 +3859,7 @@ packages: snapshots: - '@anthropic-ai/claude-code@1.0.128': + '@anthropic-ai/claude-agent-sdk@0.1.9': optionalDependencies: '@img/sharp-darwin-arm64': 0.33.5 '@img/sharp-darwin-x64': 0.33.5 diff --git a/src/services/claude/claude.ts b/src/services/claude/claude.ts index 3dd6b85..7beabb4 100644 --- a/src/services/claude/claude.ts +++ b/src/services/claude/claude.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { Options, PermissionResult, Query, SDKMessage, SDKUserMessage, CanUseTool } from '@anthropic-ai/claude-code'; +import type { Options, PermissionResult, Query, SDKMessage, SDKUserMessage, CanUseTool } from '@anthropic-ai/claude-agent-sdk'; import Anthropic from '@anthropic-ai/sdk'; import { DeferredPromise } from '../common/deferred'; import { Disposable } from '../common/lifecycle'; @@ -88,12 +88,12 @@ class ClaudeCodeSession { try { // Build options for the Claude Code SDK const isDebugEnabled = this.configService.getConfig(ConfigKey.Internal.ClaudeCodeDebugEnabled); - const options: Options = { - cwd: this.workspaceService.getWorkspaceFolders().at(0)?.fsPath, - abortController, - executable: 'node', - pathToClaudeCodeExecutable: this.envService.claudeCliPath, - env: { + const options: Options = { + cwd: this.workspaceService.getWorkspaceFolders().at(0)?.fsPath, + abortController, + executable: 'node', + pathToClaudeCodeExecutable: this.envService.claudeCliPath, + env: { ...process.env, ...(isDebugEnabled ? { DEBUG: '1' } : {}), CLAUDE_CODE_DISABLE_NONESSENTIAL_TRAFFIC: '1', @@ -101,15 +101,30 @@ class ClaudeCodeSession { resume: this.sessionId, model: 'claude-sonnet-4-20250514', permissionMode: 'default', - includePartialMessages: true, - canUseTool: async (name, input, opts): Promise => { - return this.canUseTool(name, input, bus, opts as any); - }, - ...optionsOverride, - }; + includePartialMessages: true, + canUseTool: async (name, input, opts): Promise => { + return this.canUseTool(name, input, bus, opts as any); + }, + ...optionsOverride, + }; + + if (!optionsOverride?.systemPrompt) { + const customSystemPrompt = this.configService.getConfig('claudex.customSystemPrompt')?.trim(); + const appendSystemPrompt = this.configService.getConfig('claudex.appendSystemPrompt')?.trim(); + + if (customSystemPrompt) { + options.systemPrompt = customSystemPrompt; + } else { + options.systemPrompt = { + type: 'preset', + preset: 'claude_code', + ...(appendSystemPrompt ? { append: appendSystemPrompt } : {}) + } as const; + } + } this.logService.trace(`Claude CLI SDK: Starting query with options: ${JSON.stringify({ ...options, env: undefined })}`); - const { query } = await import('@anthropic-ai/claude-code'); + const { query } = await import('@anthropic-ai/claude-agent-sdk'); const def = new DeferredPromise(); async function* createPromptIterable(promptText: string, sessionId?: string): AsyncIterable { diff --git a/src/services/claude/client.ts b/src/services/claude/client.ts index 886b259..c309625 100644 --- a/src/services/claude/client.ts +++ b/src/services/claude/client.ts @@ -8,7 +8,7 @@ import { IClaudeCodeSessionService } from './session'; import { ClaudeAgentManager } from './claude'; import { UiMessageBus } from './uiMessageBus'; import { UiEvent, ClaudeRequest } from './types'; -import { SDKMessage } from '@anthropic-ai/claude-code'; +import { SDKMessage } from '@anthropic-ai/claude-agent-sdk'; import { ILogService } from '../log/logService'; import type { CancellationToken } from '../common/types'; diff --git a/src/services/claude/session.ts b/src/services/claude/session.ts index db2d532..49ec988 100644 --- a/src/services/claude/session.ts +++ b/src/services/claude/session.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { SDKMessage, SDKUserMessage, SDKUserMessageReplay } from '@anthropic-ai/claude-code'; +import { SDKMessage, SDKUserMessage, SDKUserMessageReplay } from '@anthropic-ai/claude-agent-sdk'; import Anthropic from '@anthropic-ai/sdk'; // import type { CancellationToken } from 'vscode'; import { ResourceMap, ResourceSet } from '../common/map'; diff --git a/src/services/claude/types.ts b/src/services/claude/types.ts index de9aaa4..e94cdfc 100644 --- a/src/services/claude/types.ts +++ b/src/services/claude/types.ts @@ -14,7 +14,7 @@ import type { SDKSystemMessage, SDKUserMessage, SDKUserMessageReplay, -} from '@anthropic-ai/claude-code'; +} from '@anthropic-ai/claude-agent-sdk'; // 供 UI 使用的消息事件总线类型(仅数据透传与轻量增强) diff --git a/src/types/global.d.ts b/src/types/global.d.ts index b1a4f78..be72ec1 100644 --- a/src/types/global.d.ts +++ b/src/types/global.d.ts @@ -1,6 +1,6 @@ // 全局类型声明 -// 修复 @anthropic-ai/claude-code SDK 中缺失的 Dict 类型 +// 修复 @anthropic-ai/claude-agent-sdk SDK 中缺失的 Dict 类型 declare global { type Dict = Record; } diff --git a/webview/src/services/messageBus.ts b/webview/src/services/messageBus.ts index 2fdbdaf..530d30a 100644 --- a/webview/src/services/messageBus.ts +++ b/webview/src/services/messageBus.ts @@ -10,10 +10,11 @@ import type { ChatMessage, ChatState, SessionState, - PermissionState + PermissionState, + MessageMetadata } from '../../types/messages'; import type { QueuedMessage, MessageQueueState } from '../../types/queue'; -import type { SDKMessage } from '@anthropic-ai/claude-code'; +import type { SDKMessage } from '@anthropic-ai/claude-agent-sdk'; import { extractTextContent, extractContentBlocks, @@ -21,7 +22,7 @@ import { getMessageDisplayTitle, isMessageStreaming } from '../utils/messageUtils'; -import { isSDKUserMessage, isSDKAssistantMessage } from '../../types/messages'; +import { isSDKUserMessage, isSDKAssistantMessage, isSDKSystemMessage } from '../../types/messages'; import { setToolResult, setToolUse, setPermissionRequest, setPermissionResponse, getToolMessage, upsertToolMessage } from '../stores/toolMessageStore'; // ========== 全局状态 ========== @@ -761,7 +762,19 @@ class WebviewMessageBus { return messages.length > 0 ? (messages.length === 1 ? messages[0] : messages) : null; } case 'result': - case 'system': + case 'system': { + const metadata: MessageMetadata | undefined = + sdkMessage.type === 'system' && isSDKSystemMessage(sdkMessage) + ? { + model: sdkMessage.model, + permissionMode: sdkMessage.permissionMode, + tools: sdkMessage.tools, + cwd: sdkMessage.cwd, + agents: sdkMessage.agents, + apiKeySource: sdkMessage.apiKeySource + } + : undefined; + return { id: `${sdkMessage.type}_${Date.now()}_${Math.random()}`, sdkMessage, @@ -770,8 +783,10 @@ class WebviewMessageBus { contentBlocks, timestamp: Date.now(), status: 'completed', - sessionId: sdkMessage.session_id + sessionId: sdkMessage.session_id, + metadata }; + } default: return null; } diff --git a/webview/src/utils/messageUtils.ts b/webview/src/utils/messageUtils.ts index 0adc7fc..fce2e95 100644 --- a/webview/src/utils/messageUtils.ts +++ b/webview/src/utils/messageUtils.ts @@ -10,7 +10,7 @@ import type { SDKSystemMessage, SDKCompactBoundaryMessage, SDKPartialAssistantMessage -} from '@anthropic-ai/claude-code'; +} from '@anthropic-ai/claude-agent-sdk'; // 内容块类型定义 export interface ContentBlock { @@ -159,7 +159,9 @@ function extractSystemMessageText(message: SDKMessage): string { // 类型守卫:检查是否为 SDKSystemMessage if (isSDKSystemMessage(message)) { - return `系统初始化 - 模型: ${message.model || '未知'}`; + const agentInfo = message.agents?.length ? `,启用代理:${message.agents.join(', ')}` : ''; + const toolInfo = message.tools?.length ? `,可用工具:${message.tools.join(', ')}` : ''; + return `系统初始化 - 模型: ${message.model || '未知'}${agentInfo}${toolInfo}`; } // 类型守卫:检查是否为 SDKCompactBoundaryMessage diff --git a/webview/types/messages.ts b/webview/types/messages.ts index 691f988..d04151d 100644 --- a/webview/types/messages.ts +++ b/webview/types/messages.ts @@ -11,7 +11,7 @@ import type { SDKSystemMessage, SDKPartialAssistantMessage, SDKCompactBoundaryMessage -} from '@anthropic-ai/claude-code'; +} from '@anthropic-ai/claude-agent-sdk'; // VSCode API接口 export interface VSCodeAPI { @@ -212,6 +212,11 @@ export interface MessageMetadata { error?: string; cost?: number; model?: string; + agents?: string[]; + permissionMode?: string; + tools?: string[]; + cwd?: string; + apiKeySource?: string; correlationId?: string; [key: string]: any; }