diff --git a/extensions/claude/icon.png b/extensions/claude/icon.png new file mode 100644 index 000000000..e29be47a1 Binary files /dev/null and b/extensions/claude/icon.png differ diff --git a/extensions/claude/package.json b/extensions/claude/package.json new file mode 100644 index 000000000..4b53122ad --- /dev/null +++ b/extensions/claude/package.json @@ -0,0 +1,30 @@ +{ + "name": "claude", + "displayName": "Claude Skills", + "description": "Registers skills from the Claude skills directory ($HOME/.claude/skills)", + "version": "0.0.1-next", + "icon": "icon.png", + "publisher": "kortex", + "license": "Apache-2.0", + "engines": { + "kortex": "^0.0.1" + }, + "main": "./dist/extension.js", + "source": "./src/extension.ts", + "scripts": { + "build": "vite build && node ./scripts/build.js", + "test": "vitest run --coverage", + "test:watch": "vitest watch --coverage", + "watch": "vite build --watch" + }, + "dependencies": { + "inversify": "^7.7.1" + }, + "devDependencies": { + "@kortex-app/api": "workspace:*", + "adm-zip": "^0.5.16", + "mkdirp": "^3.0.1", + "vite": "^7.1.2", + "vitest": "^4.0.10" + } +} diff --git a/extensions/claude/scripts/build.js b/extensions/claude/scripts/build.js new file mode 100644 index 000000000..f49867d05 --- /dev/null +++ b/extensions/claude/scripts/build.js @@ -0,0 +1,46 @@ +#!/usr/bin/env node +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +const AdmZip = require('adm-zip'); +const path = require('path'); +const packageJson = require('../package.json'); +const fs = require('fs'); +const { mkdirp } = require('mkdirp'); + +const destFile = path.resolve(__dirname, `../${packageJson.name}.cdix`); +const builtinDirectory = path.resolve(__dirname, '../builtin'); +const unzippedDirectory = path.resolve(builtinDirectory, `${packageJson.name}.cdix`); +// remove the .cdix file before zipping +if (fs.existsSync(destFile)) { + fs.rmSync(destFile); +} +// remove the builtin folder before zipping +if (fs.existsSync(builtinDirectory)) { + fs.rmSync(builtinDirectory, { recursive: true, force: true }); +} + +const zip = new AdmZip(); +zip.addLocalFolder(path.resolve(__dirname, '../')); +zip.writeZip(destFile); + +// create unzipped built-in +mkdirp(unzippedDirectory).then(() => { + const unzip = new AdmZip(destFile); + unzip.extractAllTo(unzippedDirectory); +}); diff --git a/extensions/claude/src/claude-extension.spec.ts b/extensions/claude/src/claude-extension.spec.ts new file mode 100644 index 000000000..3dec490ab --- /dev/null +++ b/extensions/claude/src/claude-extension.spec.ts @@ -0,0 +1,63 @@ +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import type { ExtensionContext } from '@kortex-app/api'; +import type { Container } from 'inversify'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; + +import { ClaudeExtension } from '/@/claude-extension'; +import { ClaudeSkillsManager } from '/@/manager/claude-skills-manager'; + +vi.mock(import('@kortex-app/api')); +vi.mock(import('/@/manager/claude-skills-manager')); + +class TestClaudeExtension extends ClaudeExtension { + getContainer(): Container | undefined { + return super.getContainer(); + } +} + +describe('ClaudeExtension', () => { + let extensionContext: ExtensionContext; + let claudeExtension: TestClaudeExtension; + + beforeEach(() => { + vi.resetAllMocks(); + extensionContext = { subscriptions: [] } as unknown as ExtensionContext; + claudeExtension = new TestClaudeExtension(extensionContext); + }); + + test('activate', async () => { + await claudeExtension.activate(); + expect(ClaudeSkillsManager.prototype.init).toHaveBeenCalled(); + }); + + test('activate handles error during container creation', async () => { + const faultyGetAsync = vi.fn().mockRejectedValue(new Error('Container creation failed')); + vi.spyOn(claudeExtension, 'getContainer').mockReturnValue({ + getAsync: faultyGetAsync, + } as unknown as Container); + await expect(claudeExtension.activate()).rejects.toThrow('Container creation failed'); + }); + + test('deactivate disposes subscriptions', async () => { + await claudeExtension.activate(); + await claudeExtension.deactivate(); + expect(ClaudeSkillsManager.prototype.dispose).toHaveBeenCalled(); + }); +}); diff --git a/extensions/claude/src/claude-extension.ts b/extensions/claude/src/claude-extension.ts new file mode 100644 index 000000000..3bfef5d3c --- /dev/null +++ b/extensions/claude/src/claude-extension.ts @@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import type { ExtensionContext } from '@kortex-app/api'; +import { provider } from '@kortex-app/api'; +import type { Container } from 'inversify'; + +import { InversifyBinding } from '/@/inject/inversify-binding'; +import { ClaudeSkillsManager } from '/@/manager/claude-skills-manager'; + +export class ClaudeExtension { + #extensionContext: ExtensionContext; + + #inversifyBinding: InversifyBinding | undefined; + #container: Container | undefined; + #claudeSkillsManager: ClaudeSkillsManager | undefined; + + constructor(extensionContext: ExtensionContext) { + this.#extensionContext = extensionContext; + } + + async activate(): Promise { + const claudeProvider = provider.createProvider({ + name: 'Claude', + status: 'unknown', + id: 'claude', + images: { + icon: './icon.png', + logo: { + dark: './icon.png', + light: './icon.png', + }, + }, + }); + + this.#inversifyBinding = new InversifyBinding(claudeProvider, this.#extensionContext); + this.#container = await this.#inversifyBinding.initBindings(); + + try { + this.#claudeSkillsManager = await this.getContainer()?.getAsync(ClaudeSkillsManager); + } catch (e) { + console.error('Error while creating the Claude skills manager', e); + throw e; + } + + await this.#claudeSkillsManager?.init(); + } + + protected getContainer(): Container | undefined { + return this.#container; + } + + async deactivate(): Promise { + await this.#inversifyBinding?.dispose(); + this.#claudeSkillsManager?.dispose(); + this.#claudeSkillsManager = undefined; + } +} diff --git a/extensions/claude/src/extension.spec.ts b/extensions/claude/src/extension.spec.ts new file mode 100644 index 000000000..67acc209a --- /dev/null +++ b/extensions/claude/src/extension.spec.ts @@ -0,0 +1,48 @@ +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import type { ExtensionContext } from '@kortex-app/api'; +import { beforeEach, expect, test, vi } from 'vitest'; + +import { ClaudeExtension } from './claude-extension'; +import { activate, deactivate } from './extension'; + +let extensionContextMock: ExtensionContext; + +vi.mock(import('./claude-extension')); + +beforeEach(() => { + vi.restoreAllMocks(); + vi.resetAllMocks(); + + extensionContextMock = {} as ExtensionContext; +}); + +test('should initialize and activate the ClaudeExtension when activate is called', async () => { + await activate(extensionContextMock); + + expect(ClaudeExtension.prototype.activate).toHaveBeenCalled(); +}); + +test('should call deactivate when deactivate is called', async () => { + await activate(extensionContextMock); + + await deactivate(); + + expect(ClaudeExtension.prototype.deactivate).toHaveBeenCalled(); +}); diff --git a/extensions/claude/src/extension.ts b/extensions/claude/src/extension.ts new file mode 100644 index 000000000..33b260282 --- /dev/null +++ b/extensions/claude/src/extension.ts @@ -0,0 +1,33 @@ +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import type { ExtensionContext } from '@kortex-app/api'; + +import { ClaudeExtension } from './claude-extension'; + +let claudeExtension: ClaudeExtension | undefined; + +export async function activate(extensionContext: ExtensionContext): Promise { + claudeExtension ??= new ClaudeExtension(extensionContext); + await claudeExtension.activate(); +} + +export async function deactivate(): Promise { + await claudeExtension?.deactivate(); + claudeExtension = undefined; +} diff --git a/extensions/claude/src/inject/inversify-binding.spec.ts b/extensions/claude/src/inject/inversify-binding.spec.ts new file mode 100644 index 000000000..007547c2c --- /dev/null +++ b/extensions/claude/src/inject/inversify-binding.spec.ts @@ -0,0 +1,63 @@ +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import type { ExtensionContext, Provider } from '@kortex-app/api'; +import { Container } from 'inversify'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; + +import { managersModule } from '/@/manager/_manager-module'; +import { ClaudeSkillsManager } from '/@/manager/claude-skills-manager'; + +import { InversifyBinding } from './inversify-binding'; +import { ClaudeProviderSymbol, ExtensionContextSymbol } from './symbol'; + +let inversifyBinding: InversifyBinding; + +const providerMock = {} as Provider; +const extensionContextMock = {} as ExtensionContext; + +vi.mock(import('inversify')); + +describe('InversifyBinding', () => { + beforeEach(() => { + vi.resetAllMocks(); + inversifyBinding = new InversifyBinding(providerMock, extensionContextMock); + vi.mocked(Container.prototype.bind).mockReturnValue({ + toConstantValue: vi.fn(), + } as unknown as ReturnType); + }); + + test('should initialize bindings correctly', async () => { + const container = await inversifyBinding.initBindings(); + + await container.getAsync(ClaudeSkillsManager); + + expect(vi.mocked(Container.prototype.bind)).toHaveBeenCalledWith(ExtensionContextSymbol); + expect(vi.mocked(Container.prototype.bind)).toHaveBeenCalledWith(ClaudeProviderSymbol); + + expect(vi.mocked(Container.prototype.load)).toHaveBeenCalledWith(managersModule); + }); + + test('should dispose of the container', async () => { + const container = await inversifyBinding.initBindings(); + + await inversifyBinding.dispose(); + + expect(container.unbindAll).toHaveBeenCalled(); + }); +}); diff --git a/extensions/claude/src/inject/inversify-binding.ts b/extensions/claude/src/inject/inversify-binding.ts new file mode 100644 index 000000000..8ef0b5b3a --- /dev/null +++ b/extensions/claude/src/inject/inversify-binding.ts @@ -0,0 +1,54 @@ +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import type { ExtensionContext, Provider } from '@kortex-app/api'; +import { Container } from 'inversify'; + +import { ClaudeProviderSymbol, ExtensionContextSymbol } from '/@/inject/symbol'; +import { managersModule } from '/@/manager/_manager-module'; +import { ClaudeSkillsManager } from '/@/manager/claude-skills-manager'; + +export class InversifyBinding { + #container: Container | undefined; + + readonly #provider: Provider; + readonly #extensionContext: ExtensionContext; + + constructor(provider: Provider, extensionContext: ExtensionContext) { + this.#provider = provider; + this.#extensionContext = extensionContext; + } + + public async initBindings(): Promise { + this.#container = new Container(); + + this.#container.bind(ExtensionContextSymbol).toConstantValue(this.#extensionContext); + this.#container.bind(ClaudeProviderSymbol).toConstantValue(this.#provider); + + await this.#container.load(managersModule); + + await this.#container.getAsync(ClaudeSkillsManager); + return this.#container; + } + + async dispose(): Promise { + if (this.#container) { + await this.#container.unbindAll(); + } + } +} diff --git a/extensions/claude/src/inject/symbol.ts b/extensions/claude/src/inject/symbol.ts new file mode 100644 index 000000000..1a9b0685e --- /dev/null +++ b/extensions/claude/src/inject/symbol.ts @@ -0,0 +1,20 @@ +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +export const ExtensionContextSymbol = Symbol.for('ExtensionContext'); +export const ClaudeProviderSymbol = Symbol.for('ClaudeProvider'); diff --git a/extensions/claude/src/manager/_manager-module.ts b/extensions/claude/src/manager/_manager-module.ts new file mode 100644 index 000000000..fd1797762 --- /dev/null +++ b/extensions/claude/src/manager/_manager-module.ts @@ -0,0 +1,27 @@ +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import { ContainerModule } from 'inversify'; + +import { ClaudeSkillsManager } from './claude-skills-manager'; + +const managersModule = new ContainerModule(options => { + options.bind(ClaudeSkillsManager).toSelf().inSingletonScope(); +}); + +export { managersModule }; diff --git a/extensions/claude/src/manager/claude-skills-manager.spec.ts b/extensions/claude/src/manager/claude-skills-manager.spec.ts new file mode 100644 index 000000000..290e5c53a --- /dev/null +++ b/extensions/claude/src/manager/claude-skills-manager.spec.ts @@ -0,0 +1,73 @@ +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import { homedir } from 'node:os'; +import { join } from 'node:path'; + +import type { Disposable, Provider } from '@kortex-app/api'; +import { Container } from 'inversify'; +import { beforeEach, describe, expect, test, vi } from 'vitest'; + +import { ClaudeProviderSymbol } from '/@/inject/symbol'; + +import { ClaudeSkillsManager } from './claude-skills-manager'; + +vi.mock(import('node:os')); + +const MOCK_HOME = '/home/testuser'; +const CLAUDE_SKILLS_DIR = join(MOCK_HOME, '.claude', 'skills'); + +const disposableMock: Disposable = { dispose: vi.fn() }; +const providerMock: Provider = { + registerSkill: vi.fn(), +} as unknown as Provider; + +describe('ClaudeSkillsManager', () => { + let claudeSkillsManager: ClaudeSkillsManager; + + beforeEach(async () => { + vi.resetAllMocks(); + vi.mocked(homedir).mockReturnValue(MOCK_HOME); + vi.mocked(providerMock.registerSkill).mockReturnValue(disposableMock); + + const container = new Container(); + container.bind(ClaudeSkillsManager).toSelf(); + container.bind(ClaudeProviderSymbol).toConstantValue(providerMock); + claudeSkillsManager = await container.getAsync(ClaudeSkillsManager); + }); + + test('getClaudeSkillsDir returns expected path', () => { + expect(ClaudeSkillsManager.getClaudeSkillsDir()).toBe(CLAUDE_SKILLS_DIR); + }); + + test('registers skill with correct parameters via provider', async () => { + await claudeSkillsManager.init(); + + expect(providerMock.registerSkill).toHaveBeenCalledWith({ + label: 'Claude', + path: CLAUDE_SKILLS_DIR, + }); + }); + + test('dispose cleans up skill registration', async () => { + await claudeSkillsManager.init(); + claudeSkillsManager.dispose(); + + expect(disposableMock.dispose).toHaveBeenCalled(); + }); +}); diff --git a/extensions/claude/src/manager/claude-skills-manager.ts b/extensions/claude/src/manager/claude-skills-manager.ts new file mode 100644 index 000000000..04d7bfb60 --- /dev/null +++ b/extensions/claude/src/manager/claude-skills-manager.ts @@ -0,0 +1,51 @@ +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import { homedir } from 'node:os'; +import { join } from 'node:path'; + +import type { Disposable, Provider } from '@kortex-app/api'; +import { inject, injectable } from 'inversify'; + +import { ClaudeProviderSymbol } from '/@/inject/symbol'; + +@injectable() +export class ClaudeSkillsManager { + @inject(ClaudeProviderSymbol) + private claudeProvider: Provider; + + #skillDisposable: Disposable | undefined; + + static getClaudeSkillsDir(): string { + return join(homedir(), '.claude', 'skills'); + } + + async init(): Promise { + const claudeSkillsDir = ClaudeSkillsManager.getClaudeSkillsDir(); + + this.#skillDisposable = this.claudeProvider.registerSkill({ + label: 'Claude', + path: claudeSkillsDir, + }); + } + + dispose(): void { + this.#skillDisposable?.dispose(); + this.#skillDisposable = undefined; + } +} diff --git a/extensions/claude/tsconfig.json b/extensions/claude/tsconfig.json new file mode 100644 index 000000000..b0b8fa910 --- /dev/null +++ b/extensions/claude/tsconfig.json @@ -0,0 +1,23 @@ +{ + "compilerOptions": { + "strict": true, + "module": "esnext", + "lib": ["ES2024"], + "sourceMap": true, + "rootDir": "src", + "outDir": "dist", + "target": "esnext", + "moduleResolution": "Node", + "allowSyntheticDefaultImports": true, + "experimentalDecorators": true, + "emitDecoratorMetadata": true, + "strictPropertyInitialization": false, + "resolveJsonModule": true, + "skipLibCheck": true, + "types": ["node"], + "paths": { + "/@/*": ["./src/*"] + } + }, + "include": ["src", "types/*.d.ts"] +} diff --git a/extensions/claude/vite.config.js b/extensions/claude/vite.config.js new file mode 100644 index 000000000..9cd4a5e54 --- /dev/null +++ b/extensions/claude/vite.config.js @@ -0,0 +1,68 @@ +/********************************************************************** + * Copyright (C) 2026 Red Hat, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + * SPDX-License-Identifier: Apache-2.0 + ***********************************************************************/ + +import { join } from 'path'; +import { builtinModules } from 'module'; + +const PACKAGE_ROOT = __dirname; + +/** + * @type {import('vite').UserConfig} + * @see https://vitejs.dev/config/ + */ +const config = { + mode: process.env.MODE, + root: PACKAGE_ROOT, + envDir: process.cwd(), + resolve: { + alias: { + '/@/': join(PACKAGE_ROOT, 'src') + '/', + }, + mainFields: ['module', 'jsnext:main', 'jsnext'], + }, + build: { + sourcemap: 'inline', + target: 'esnext', + outDir: 'dist', + assetsDir: '.', + minify: process.env.MODE === 'production' ? 'esbuild' : false, + lib: { + entry: 'src/extension.ts', + formats: ['cjs'], + }, + rollupOptions: { + external: ['@kortex-app/api', ...builtinModules.flatMap(p => [p, `node:${p}`])], + output: { + entryFileNames: '[name].js', + }, + }, + emptyOutDir: true, + reportCompressedSize: false, + }, + test: { + globals: true, + environment: 'node', + include: ['src/**/*.{test,spec}.?(c|m)[jt]s?(x)'], + globalSetup: [join(PACKAGE_ROOT, '..', '..', '__mocks__', 'vitest-generate-api-global-setup.ts')], + alias: { + '@kortex-app/api': join(PACKAGE_ROOT, '..', '..', '__mocks__/@kortex-app/api.js'), + }, + }, +}; + +export default config; diff --git a/package.json b/package.json index ba94aee0d..7f608acc4 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,8 @@ "scripts": { "build": "pnpm run build:main && pnpm run build:preload && pnpm run build:preload-webview && npm run build:preload:types && pnpm run build:renderer && pnpm run build:extensions", "build:main": "cd ./packages/main && vite build", - "build:extensions": "pnpm run build:extensions:container && pnpm run build:extensions:docling && pnpm run build:extensions:gemini && pnpm run build:extensions:goose && pnpm run build:extensions:mcp-registries && pnpm run build:extensions:milvus && pnpm run build:extensions:ollama && pnpm run build:extensions:openai-compatible && pnpm run build:extensions:openshift-ai && pnpm run build:extensions:ramalama", + "build:extensions": "pnpm run build:extensions:claude && pnpm run build:extensions:container && pnpm run build:extensions:docling && pnpm run build:extensions:gemini && pnpm run build:extensions:goose && pnpm run build:extensions:mcp-registries && pnpm run build:extensions:milvus && pnpm run build:extensions:ollama && pnpm run build:extensions:openai-compatible && pnpm run build:extensions:openshift-ai && pnpm run build:extensions:ramalama", + "build:extensions:claude": "cd ./extensions/claude && pnpm run build", "build:extensions:container": "cd ./extensions/container/packages/extension && pnpm run build", "build:extensions:docling": "cd ./extensions/docling && pnpm run build", "build:extensions:gemini": "cd ./extensions/gemini && pnpm run build", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 0bd730407..e6ad9131b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -381,6 +381,28 @@ importers: specifier: ^0.2.1 version: 0.2.1 + extensions/claude: + dependencies: + inversify: + specifier: ^7.7.1 + version: 7.11.0(reflect-metadata@0.2.2) + devDependencies: + '@kortex-app/api': + specifier: workspace:* + version: link:../../packages/extension-api + adm-zip: + specifier: ^0.5.16 + version: 0.5.16 + mkdirp: + specifier: ^3.0.1 + version: 3.0.1 + vite: + specifier: ^7.1.2 + version: 7.3.1(@types/node@24.12.0)(jiti@2.6.1)(lightningcss@1.31.1)(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.2) + vitest: + specifier: ^4.0.10 + version: 4.0.18(@opentelemetry/api@1.9.0)(@types/node@24.12.0)(jiti@2.6.1)(jsdom@27.4.0)(lightningcss@1.31.1)(msw@2.12.10(@types/node@24.12.0)(typescript@5.9.3))(terser@5.36.0)(tsx@4.20.3)(yaml@2.8.2) + extensions/container/packages/api: dependencies: '@kortex-app/api':