@things-factory/integration-codemind 9.0.0-beta.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6
+ and adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7
+
8
+ <!-- ## [Unreleased] -->
Binary file
@@ -0,0 +1 @@
1
+ export default function bootstrap() {}
File without changes
@@ -0,0 +1,11 @@
1
+ {
2
+ "extends": "../../tsconfig-base.json",
3
+ "compilerOptions": {
4
+ "strict": true,
5
+ "declaration": true,
6
+ "module": "esnext",
7
+ "outDir": "../dist-client",
8
+ "baseUrl": "./"
9
+ },
10
+ "include": ["./**/*"]
11
+ }
package/package.json ADDED
@@ -0,0 +1,38 @@
1
+ {
2
+ "name": "@things-factory/integration-codemind",
3
+ "version": "9.0.0-beta.27",
4
+ "main": "dist-server/index.js",
5
+ "browser": "dist-client/index.js",
6
+ "things-factory": true,
7
+ "license": "MIT",
8
+ "author": "heartyoh <heartyoh@hatiolab.com>",
9
+ "description": "Module to provide code analysis on integration engine",
10
+ "publishConfig": {
11
+ "access": "public",
12
+ "@things-factory:registry": "https://registry.npmjs.org"
13
+ },
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "git+https://github.com/hatiolab/things-factory.git",
17
+ "directory": "packages/integration-codemind"
18
+ },
19
+ "scripts": {
20
+ "build": "npm run build:server && npm run build:client",
21
+ "copy:files": "copyfiles -e \"./client/**/*.{ts,js,json}\" -u 1 \"./client/**/*\" dist-client",
22
+ "build:client": "npm run copy:files && npx tsc --p ./client/tsconfig.json",
23
+ "build:server": "npx tsc --p ./server/tsconfig.json",
24
+ "clean:client": "npx rimraf dist-client",
25
+ "clean:server": "npx rimraf dist-server",
26
+ "clean": "npm run clean:server && npm run clean:client",
27
+ "migration:create": "node ../../node_modules/typeorm/cli.js migration:create ./server/migrations/migration"
28
+ },
29
+ "dependencies": {
30
+ "@things-factory/integration-base": "^9.0.0-beta.27",
31
+ "@xenova/transformers": "^2.17.2",
32
+ "onnxruntime-node": "^1.20.1",
33
+ "tree-sitter": "^0.22.4",
34
+ "tree-sitter-javascript": "^0.23.1",
35
+ "tree-sitter-typescript": "^0.23.2"
36
+ },
37
+ "gitHead": "3038f56087036b06717f8e4910383c6b1320218d"
38
+ }
File without changes
@@ -0,0 +1,2 @@
1
+ import './connector/index.js'
2
+ import './task/index.js'
@@ -0,0 +1,78 @@
1
+ import dotenv from 'dotenv'
2
+
3
+ dotenv.config()
4
+
5
+ const OPENAI_API_KEY = process.env.OPENAI_API_KEY // OpenAI API 키
6
+
7
+ /**
8
+ * OpenAI API를 호출하여 코드 분석 수행 (fetch 사용)
9
+ * @param {string} prompt - AI에게 입력할 프롬프트
10
+ * @returns {Promise<string>} - AI 응답 결과
11
+ */
12
+ async function callOpenAI(prompt: string): Promise<string> {
13
+ try {
14
+ const response = await fetch('https://api.openai.com/v1/completions', {
15
+ method: 'POST',
16
+ headers: {
17
+ 'Content-Type': 'application/json',
18
+ Authorization: `Bearer ${OPENAI_API_KEY}`
19
+ },
20
+ body: JSON.stringify({
21
+ model: 'gpt-4', // 또는 'text-davinci-003'
22
+ prompt,
23
+ max_tokens: 300,
24
+ temperature: 0.5
25
+ })
26
+ })
27
+
28
+ if (!response.ok) {
29
+ throw new Error(`OpenAI API error: ${response.statusText}`)
30
+ }
31
+
32
+ const data = await response.json()
33
+ return data.choices[0].text.trim()
34
+ } catch (error) {
35
+ console.error('OpenAI API call failed:', error.message)
36
+ throw new Error(`OpenAI API error: ${error.message}`)
37
+ }
38
+ }
39
+
40
+ /**
41
+ * 코드 자동 요약
42
+ * @param {string} code - 분석할 코드
43
+ * @returns {Promise<string>} - 요약 결과
44
+ */
45
+ export async function generateCodeSummary(code: string): Promise<string> {
46
+ const prompt = `다음 코드의 기능을 한 줄 요약해 주세요:\n\n${code}`
47
+ return callOpenAI(prompt)
48
+ }
49
+
50
+ /**
51
+ * 코드 리뷰 및 개선점 제안
52
+ * @param {string} code - 분석할 코드
53
+ * @returns {Promise<string>} - 리뷰 결과
54
+ */
55
+ export async function generateCodeReview(code: string): Promise<string> {
56
+ const prompt = `다음 코드를 검토하고, 보안 문제, 성능 개선점 및 가독성을 향상시킬 방법을 제안해 주세요:\n\n${code}`
57
+ return callOpenAI(prompt)
58
+ }
59
+
60
+ /**
61
+ * 코드 리팩토링 추천
62
+ * @param {string} code - 분석할 코드
63
+ * @returns {Promise<string>} - 리팩토링 제안
64
+ */
65
+ export async function generateRefactorSuggestions(code: string): Promise<string> {
66
+ const prompt = `다음 코드에 대해 리팩토링을 추천해 주세요. 더 효율적인 방식으로 수정해야 할 부분이 있다면 알려주세요:\n\n${code}`
67
+ return callOpenAI(prompt)
68
+ }
69
+
70
+ /**
71
+ * 코드 분류 (패턴 분석)
72
+ * @param {string} code - 분석할 코드
73
+ * @returns {Promise<string>} - 코드 분류 결과
74
+ */
75
+ export async function classifyCode(code: string): Promise<string> {
76
+ const prompt = `다음 코드가 어떤 역할을 하는지 간단한 카테고리(예: "API 처리", "데이터 처리", "UI 렌더링")로 분류해 주세요:\n\n${code}`
77
+ return callOpenAI(prompt)
78
+ }
@@ -0,0 +1,53 @@
1
+ import Parser from 'tree-sitter'
2
+ import JavaScript from 'tree-sitter-javascript'
3
+ import TypeScript from 'tree-sitter-typescript'
4
+
5
+ /**
6
+ * AST 변환을 위한 Tree-sitter 초기화
7
+ */
8
+ const parser = new Parser()
9
+ parser.setLanguage(JavaScript as unknown as Parser.Language) // 타입 캐스팅
10
+
11
+ /**
12
+ * 코드의 AST(추상 구문 트리)를 생성하고, 주요 요소(함수, 클래스, 변수 등)를 추출
13
+ * @param {string} code - 분석할 코드 문자열
14
+ * @returns {object} - AST 구조 분석 결과
15
+ */
16
+ export function parseAst(code: string): { functions: string[]; classes: string[]; variables: string[] } {
17
+ const tree = parser.parse(code)
18
+
19
+ /**
20
+ * 특정 타입의 노드를 추출하는 함수
21
+ * @param {string} type - 추출할 노드 타입 (예: 'function_declaration', 'class_declaration', 'variable_declarator')
22
+ * @returns {string[]} - 추출된 노드의 텍스트 배열
23
+ */
24
+ const extractNodes = (type: string): string[] => {
25
+ const results: string[] = []
26
+
27
+ /**
28
+ * 재귀적으로 노드를 탐색하는 함수
29
+ * @param {any} node - 현재 노드
30
+ */
31
+ function visitNode(node: any) {
32
+ if (node.type === type) {
33
+ // 노드의 텍스트를 추출하여 결과에 추가
34
+ results.push(code.substring(node.startIndex, node.endIndex))
35
+ }
36
+
37
+ // 자식 노드 재귀적으로 탐색
38
+ for (const child of node.namedChildren) {
39
+ visitNode(child)
40
+ }
41
+ }
42
+
43
+ // 루트 노드부터 탐색 시작
44
+ visitNode(tree.rootNode)
45
+ return results
46
+ }
47
+
48
+ return {
49
+ functions: extractNodes('function_declaration'),
50
+ classes: extractNodes('class_declaration'),
51
+ variables: extractNodes('variable_declarator')
52
+ }
53
+ }
@@ -0,0 +1,75 @@
1
+ import { ConnectionManager, Context, TaskRegistry } from '@things-factory/integration-base'
2
+ import fs from 'fs-extra'
3
+ import path from 'path'
4
+ import { parseAst } from './ast-parser.js'
5
+
6
+ async function codemindAstAnalyze(step, { data }: Context) {
7
+ const { filePath, content } = step.params
8
+
9
+ if (!filePath && !content) {
10
+ throw new Error(`Either "filePath" or "content" must be provided.`)
11
+ }
12
+
13
+ let results = []
14
+
15
+ function traverseDirectory(directory) {
16
+ const entries = fs.readdirSync(directory, { withFileTypes: true })
17
+
18
+ for (const entry of entries) {
19
+ const fullPath = path.join(directory, entry.name)
20
+
21
+ if (entry.isDirectory()) {
22
+ // ✅ 하위 폴더 재귀적으로 탐색
23
+ traverseDirectory(fullPath)
24
+ } else if (['.js', '.ts'].includes(path.extname(entry.name))) {
25
+ // ✅ 유효한 파일이면 분석 실행
26
+ console.log(`[DEBUG] Analyzing file: ${fullPath}`)
27
+ const code = fs.readFileSync(fullPath, 'utf-8')
28
+ const ast = parseAst(code)
29
+ results.push({ file: fullPath, ast })
30
+ }
31
+ }
32
+ }
33
+
34
+ if (filePath) {
35
+ if (!fs.existsSync(filePath)) {
36
+ throw new Error(`File does not exist: ${filePath}`)
37
+ }
38
+
39
+ const stats = fs.statSync(filePath)
40
+
41
+ if (stats.isDirectory()) {
42
+ // ✅ 폴더이면 탐색 시작
43
+ console.log(`[DEBUG] Traversing directory: ${filePath}`)
44
+ traverseDirectory(filePath)
45
+
46
+ if (results.length === 0) {
47
+ throw new Error(`No valid files found in directory and subdirectories: ${filePath}`)
48
+ }
49
+ } else {
50
+ // ✅ 단일 파일 처리
51
+ console.log(`[DEBUG] Analyzing single file: ${filePath}`)
52
+ const code = fs.readFileSync(filePath, 'utf-8')
53
+ const ast = parseAst(code)
54
+ results.push({ file: filePath, ast })
55
+ }
56
+ } else {
57
+ // ✅ 코드 직접 입력 시
58
+ console.log(`[DEBUG] Analyzing inline content`)
59
+ const ast = parseAst(content)
60
+ results.push({ file: 'inline-content', ast })
61
+ }
62
+
63
+ return { data: results }
64
+ }
65
+
66
+ // ✅ 파라미터 정의
67
+ codemindAstAnalyze.parameterSpec = [
68
+ { type: 'string', name: 'filePath', label: 'codemind.filepath' },
69
+ { type: 'string', name: 'content', label: 'codemind.content' }
70
+ ]
71
+
72
+ codemindAstAnalyze.connectorFree = true
73
+
74
+ // ✅ 태스크 등록
75
+ TaskRegistry.registerTaskHandler('codemind-ast-analyze', codemindAstAnalyze)
@@ -0,0 +1,21 @@
1
+ import { Context, TaskRegistry } from '@things-factory/integration-base'
2
+ import { classifyCode } from './ai-models.js' // AI 기반 코드 분류 함수
3
+ import fs from 'fs-extra'
4
+
5
+ async function codemindCodeClassify(step, { data }: Context) {
6
+ const { filePath, content } = step.params
7
+
8
+ if (!filePath && !content) {
9
+ throw new Error(`Either "filePath" or "content" must be provided.`)
10
+ }
11
+
12
+ let code = content || fs.readFileSync(filePath, 'utf-8')
13
+ const classification = await classifyCode(code) // AI 코드 분류 실행
14
+
15
+ return { data: classification }
16
+ }
17
+
18
+ codemindCodeClassify.connectorFree = true
19
+
20
+ // ✅ 태스크 등록
21
+ TaskRegistry.registerTaskHandler('codemind-code-classify', codemindCodeClassify)
@@ -0,0 +1,26 @@
1
+ import { Context, TaskRegistry } from '@things-factory/integration-base'
2
+ import fs from 'fs-extra'
3
+ import { diff } from 'diff' // 코드 변경 비교 라이브러리
4
+
5
+ async function codemindCodeCompare(step, { data }: Context) {
6
+ const { oldFilePath, newFilePath, oldContent, newContent } = step.params
7
+
8
+ if (!oldFilePath && !oldContent) {
9
+ throw new Error(`Either "oldFilePath" or "oldContent" must be provided.`)
10
+ }
11
+ if (!newFilePath && !newContent) {
12
+ throw new Error(`Either "newFilePath" or "newContent" must be provided.`)
13
+ }
14
+
15
+ const oldCode = oldContent || fs.readFileSync(oldFilePath, 'utf-8')
16
+ const newCode = newContent || fs.readFileSync(newFilePath, 'utf-8')
17
+
18
+ const changes = diff(oldCode, newCode) // 코드 변경 비교 실행
19
+
20
+ return { data: changes }
21
+ }
22
+
23
+ codemindCodeCompare.connectorFree = true
24
+
25
+ // ✅ 태스크 등록
26
+ TaskRegistry.registerTaskHandler('codemind-code-compare', codemindCodeCompare)
@@ -0,0 +1,21 @@
1
+ import { Context, TaskRegistry } from '@things-factory/integration-base'
2
+ import { generateCodeReview } from './ai-models.js' // AI 기반 코드 리뷰 함수
3
+ import fs from 'fs-extra'
4
+
5
+ async function codemindCodeReview(step, { data }: Context) {
6
+ const { filePath, content } = step.params
7
+
8
+ if (!filePath && !content) {
9
+ throw new Error(`Either "filePath" or "content" must be provided.`)
10
+ }
11
+
12
+ let code = content || fs.readFileSync(filePath, 'utf-8')
13
+ const review = await generateCodeReview(code) // AI 코드 리뷰 실행
14
+
15
+ return { data: review }
16
+ }
17
+
18
+ codemindCodeReview.connectorFree = true
19
+
20
+ // ✅ 태스크 등록
21
+ TaskRegistry.registerTaskHandler('codemind-code-review', codemindCodeReview)
@@ -0,0 +1,34 @@
1
+ import { ConnectionManager, Context, TaskRegistry } from '@things-factory/integration-base'
2
+ import fs from 'fs-extra'
3
+
4
+ import { generateEmbedding } from './embedding.js' // AI 임베딩 모듈
5
+
6
+ async function codemindEmbedding(step, { data }: Context) {
7
+ const { filePath, content } = step.params
8
+
9
+ if (!filePath && !content) {
10
+ throw new Error(`Either "filePath" or "content" must be provided.`)
11
+ }
12
+
13
+ let code = content
14
+ if (filePath) {
15
+ if (!fs.existsSync(filePath)) {
16
+ throw new Error(`File does not exist: ${filePath}`)
17
+ }
18
+ code = fs.readFileSync(filePath, 'utf-8')
19
+ }
20
+
21
+ const embedding = await generateEmbedding(code) // AI 임베딩 생성
22
+ return { data: embedding }
23
+ }
24
+
25
+ // ✅ 파라미터 정의
26
+ codemindEmbedding.parameterSpec = [
27
+ { type: 'string', name: 'filePath', label: 'codemind.filepath' },
28
+ { type: 'string', name: 'content', label: 'codemind.content' }
29
+ ]
30
+
31
+ codemindEmbedding.connectorFree = true
32
+
33
+ // ✅ 태스크 등록
34
+ TaskRegistry.registerTaskHandler('codemind-embedding', codemindEmbedding)
@@ -0,0 +1,138 @@
1
+ import { Context, TaskRegistry } from '@things-factory/integration-base'
2
+ import fs from 'fs-extra'
3
+ import path from 'path'
4
+ import { access } from '@things-factory/utils'
5
+ import { parseAst } from './ast-parser.js' // AST 변환 모듈
6
+ import { generateEmbedding } from './embedding.js' // AI 임베딩 모듈
7
+
8
+ async function processFile(filePath, results) {
9
+ try {
10
+ console.log(`[DEBUG] Processing file: ${filePath}`)
11
+ const code = await fs.readFile(filePath, 'utf-8') // ✅ 비동기적 처리
12
+
13
+ // 📢 AST 분석 후 벡터화
14
+ const ast = parseAst(code)
15
+ const jsonAst = JSON.stringify(ast)
16
+ const astEmbedding = await generateEmbedding(jsonAst)
17
+
18
+ // 📢 원본 코드 자체도 벡터화
19
+ const codeEmbedding = await generateEmbedding(code)
20
+
21
+ results.push({ file: filePath, embedding: astEmbedding, type: 'ast' })
22
+ results.push({ file: filePath, embedding: codeEmbedding, type: 'code' })
23
+ } catch (error) {
24
+ console.error(`[ERROR] Failed to process file: ${filePath}`, error)
25
+ }
26
+ }
27
+
28
+ async function processDirectory(directoryPath, results) {
29
+ try {
30
+ console.log(`[DEBUG] Processing directory: ${directoryPath}`)
31
+ const files = await fs.readdir(directoryPath) // ✅ 비동기적 처리
32
+
33
+ for (const file of files) {
34
+ const fullPath = path.join(directoryPath, file)
35
+ const stats = await fs.stat(fullPath) // ✅ 비동기적 처리
36
+
37
+ if (stats.isDirectory()) {
38
+ // 🔁 재귀적으로 하위 폴더 비동기 처리
39
+ await processDirectory(fullPath, results)
40
+ } else if (stats.isFile() && /\.(js|ts)$/i.test(fullPath)) {
41
+ // ✅ 특정 파일 비동기 처리
42
+ await processFile(fullPath, results)
43
+ }
44
+ }
45
+ } catch (error) {
46
+ console.error(`[ERROR] Failed to process directory: ${directoryPath}`, error)
47
+ }
48
+ }
49
+
50
+ async function codemindEmbedding(step, { data }: Context) {
51
+ const { filePath, content, astDataAccessor, searchTextAccessor } = step.params
52
+
53
+ // ✅ 1. AST 데이터 접근
54
+ let astData = access(astDataAccessor, data)
55
+ let searchText = access(searchTextAccessor, data)
56
+
57
+ if (!astData && !filePath && !content && !searchText) {
58
+ throw new Error(`Either "astData", "filePath", "content", or "searchText" must be provided.`)
59
+ }
60
+
61
+ let results = []
62
+
63
+ // ✅ 2. AST 데이터 벡터화
64
+ if (astData && Array.isArray(astData) && astData.length > 0) {
65
+ console.log(`[DEBUG] Using AST data for embedding`)
66
+ for (const fileData of astData) {
67
+ if (!fileData.ast || !fileData.file) {
68
+ console.warn(`[WARN] Skipping invalid AST data:`, fileData)
69
+ continue
70
+ }
71
+ const jsonAst = JSON.stringify(fileData.ast)
72
+ const astEmbedding = await generateEmbedding(jsonAst)
73
+ results.push({ file: fileData.file, embedding: astEmbedding, type: 'ast' })
74
+ }
75
+ }
76
+
77
+ // ✅ 3. 파일 또는 디렉토리 벡터화
78
+ if (filePath) {
79
+ if (!fs.existsSync(filePath)) {
80
+ throw new Error(`File or directory does not exist: ${filePath}`)
81
+ }
82
+
83
+ const stats = fs.statSync(filePath)
84
+
85
+ if (stats.isDirectory()) {
86
+ // 🔁 **디렉토리 내부 파일들 처리**
87
+ await processDirectory(filePath, results)
88
+ } else if (stats.isFile()) {
89
+ // ✅ **단일 파일 처리**
90
+ await processFile(filePath, results)
91
+ }
92
+ }
93
+
94
+ // ✅ 4. 직접 입력된 코드 벡터화
95
+ if (content) {
96
+ try {
97
+ console.log(`[DEBUG] Processing inline content`)
98
+ // 📢 **AST 분석 후 벡터화**
99
+ const ast = parseAst(content)
100
+ const jsonAst = JSON.stringify(ast)
101
+ const astEmbedding = await generateEmbedding(jsonAst)
102
+
103
+ // 📢 **원본 코드 벡터화**
104
+ const codeEmbedding = await generateEmbedding(content)
105
+
106
+ results.push({ file: 'inline-content', embedding: astEmbedding, type: 'ast' })
107
+ results.push({ file: 'inline-content', embedding: codeEmbedding, type: 'code' })
108
+ } catch (error) {
109
+ console.error(`[ERROR] Failed to process content`, error)
110
+ }
111
+ }
112
+
113
+ // ✅ 5. 검색어(searchText) 벡터화
114
+ if (searchText) {
115
+ try {
116
+ console.log(`[DEBUG] Processing searchText: "${searchText}"`)
117
+ const queryEmbedding = await generateEmbedding(searchText)
118
+ results.push({ file: 'search-query', embedding: queryEmbedding, type: 'query' })
119
+ } catch (error) {
120
+ console.error(`[ERROR] Failed to process searchText`, error)
121
+ }
122
+ }
123
+
124
+ return { data: results }
125
+ }
126
+
127
+ // ✅ 파라미터 정의
128
+ codemindEmbedding.parameterSpec = [
129
+ { type: 'string', name: 'astDataAccessor', label: 'codemind.ast-data-accessor' },
130
+ { type: 'string', name: 'filePath', label: 'codemind.filepath' },
131
+ { type: 'string', name: 'content', label: 'codemind.content' },
132
+ { type: 'string', name: 'searchTextAccessor', label: 'codemind.search-text-accessor' } // ✅ 검색어 추가 지원
133
+ ]
134
+
135
+ codemindEmbedding.connectorFree = true
136
+
137
+ // ✅ 태스크 등록
138
+ TaskRegistry.registerTaskHandler('codemind-embedding', codemindEmbedding)
@@ -0,0 +1,38 @@
1
+ import { Context, TaskRegistry } from '@things-factory/integration-base'
2
+ import fs from 'fs-extra'
3
+ import path from 'path'
4
+ import { diffLines, applyPatch } from 'diff'
5
+
6
+ async function codemindRefactorApply(step, { data }: Context) {
7
+ const { filePath, newCode, applyDirectly = true } = step.params
8
+
9
+ if (!filePath || !newCode) {
10
+ throw new Error(`filePath와 newCode는 필수 파라미터입니다.`)
11
+ }
12
+
13
+ const fullPath = path.resolve(filePath)
14
+
15
+ if (!fs.existsSync(fullPath)) {
16
+ throw new Error(`파일이 존재하지 않습니다: ${fullPath}`)
17
+ }
18
+
19
+ // ✅ 기존 코드 읽기
20
+ const originalCode = fs.readFileSync(fullPath, 'utf-8')
21
+
22
+ // ✅ 코드 차이점 분석 (diff)
23
+ const patch = diffLines(originalCode, newCode)
24
+ const diffText = patch
25
+ .map(change => (change.added ? `+ ${change.value}` : change.removed ? `- ${change.value}` : ` ${change.value}`))
26
+ .join('\n')
27
+
28
+ // ✅ 자동 적용 여부 확인
29
+ if (applyDirectly) {
30
+ // ✅ AI가 추천한 코드로 기존 파일 업데이트
31
+ fs.writeFileSync(fullPath, newCode)
32
+ }
33
+
34
+ return { data: { success: true, filePath, diff: diffText } }
35
+ }
36
+
37
+ // ✅ 태스크 등록
38
+ TaskRegistry.registerTaskHandler('codemind-refactor-apply', codemindRefactorApply)
@@ -0,0 +1,21 @@
1
+ import { Context, TaskRegistry } from '@things-factory/integration-base'
2
+ import { generateRefactorSuggestions } from './ai-models.js' // AI 기반 코드 리팩토링 추천
3
+ import fs from 'fs-extra'
4
+
5
+ async function codemindRefactorSuggestion(step, { data }: Context) {
6
+ const { filePath, content } = step.params
7
+
8
+ if (!filePath && !content) {
9
+ throw new Error(`Either "filePath" or "content" must be provided.`)
10
+ }
11
+
12
+ let code = content || fs.readFileSync(filePath, 'utf-8')
13
+ const suggestions = await generateRefactorSuggestions(code) // AI 리팩토링 실행
14
+
15
+ return { data: suggestions }
16
+ }
17
+
18
+ codemindRefactorSuggestion.connectorFree = true
19
+
20
+ // ✅ 태스크 등록
21
+ TaskRegistry.registerTaskHandler('codemind-refactor-suggestion', codemindRefactorSuggestion)
@@ -0,0 +1,40 @@
1
+ class EmbeddingGenerator {
2
+ private model: any
3
+ private tokenizer: any
4
+ private modelName: string
5
+
6
+ constructor(modelName: string = 'Xenova/all-MiniLM-L6-v2') {
7
+ this.modelName = modelName
8
+ }
9
+
10
+ // 모델 초기화
11
+ async initialize() {
12
+ /*
13
+ @xenova/transformers 가 ESM 모듈이므로, 동적 import(..)를 해야함.
14
+ 그런데, tsc 나 webpack 이 컴파일하면서 require(..)로 변경하므로 이를 방지할 수 있도록 eval로 감싼다.
15
+ */
16
+ const { pipeline } = await eval('import("@xenova/transformers")')
17
+ this.model = await pipeline('feature-extraction', this.modelName)
18
+ }
19
+
20
+ // 코드 임베딩 생성
21
+ async generateEmbedding(code: string): Promise<number[]> {
22
+ if (!this.model) {
23
+ await this.initialize()
24
+ }
25
+
26
+ // 코드를 입력으로 사용하여 임베딩 생성
27
+ const output = await this.model(code, { pooling: 'mean' })
28
+ const embedding: number[] = Array.from(output.data) // Tensor를 배열로 변환
29
+
30
+ return embedding
31
+ }
32
+ }
33
+
34
+ // 싱글톤 인스턴스 생성
35
+ const embeddingGenerator = new EmbeddingGenerator()
36
+
37
+ // 외부에서 사용할 수 있도록 export
38
+ export async function generateEmbedding(code: string): Promise<number[]> {
39
+ return await embeddingGenerator.generateEmbedding(code)
40
+ }
@@ -0,0 +1,7 @@
1
+ import './codemind-ast-analyze.js'
2
+ import './codemind-code-classify.js'
3
+ import './codemind-code-compare.js'
4
+ import './codemind-code-review.js'
5
+ import './codemind-code-summary.js'
6
+ import './codemind-embedding.js'
7
+ import './codemind-refactor-suggestion.js'
@@ -0,0 +1 @@
1
+ import './engine/index.js'
@@ -0,0 +1,10 @@
1
+ {
2
+ "extends": "../../tsconfig-base.json",
3
+ "compilerOptions": {
4
+ "strict": false,
5
+ "outDir": "../dist-server",
6
+ "baseUrl": "./",
7
+ "target": "ESNext"
8
+ },
9
+ "include": ["./**/*"]
10
+ }
@@ -0,0 +1,5 @@
1
+ import bootstrap from './dist-client/bootstrap'
2
+
3
+ export default {
4
+ bootstrap
5
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "label.codemind.ast-data-accessor": "AST Data Accessor (Preferred)",
3
+ "label.codemind.content": "Code Content (Optional)",
4
+ "label.codemind.content-optional-ast": "Code Content (Optional if AST provided)",
5
+ "label.codemind.filepath": "File Path (File or Directory)",
6
+ "label.codemind.filepath-optional-ast": "File Path or Directory (Optional if AST provided)",
7
+ "label.codemind.search-text-accessor": "Search Query Accessor (Optional)"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "label.codemind.ast-data-accessor": "ASTデータアクセサ(推奨)",
3
+ "label.codemind.content": "コード内容(任意)",
4
+ "label.codemind.content-optional-ast": "コード内容(ASTが提供されている場合は任意)",
5
+ "label.codemind.filepath": "ファイルパス(ファイルまたはディレクトリ)",
6
+ "label.codemind.filepath-optional-ast": "ファイルパスまたはディレクトリ(ASTが提供されている場合は任意)",
7
+ "label.codemind.search-text-accessor": "検索クエリアクセサ(任意)"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "label.codemind.ast-data-accessor": "AST 데이터 접근자 (선호됨)",
3
+ "label.codemind.content": "코드 내용 (선택 사항)",
4
+ "label.codemind.content-optional-ast": "코드 내용 (AST가 제공된 경우 선택 사항)",
5
+ "label.codemind.filepath": "파일 경로 (파일 또는 디렉토리)",
6
+ "label.codemind.filepath-optional-ast": "파일 경로 또는 디렉토리 (AST가 제공된 경우 선택 사항)",
7
+ "label.codemind.search-text-accessor": "검색 쿼리 접근자 (선택 사항)"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "label.codemind.ast-data-accessor": "AST Pengakses Data (Disukai)",
3
+ "label.codemind.content": "Kandungan Kod (Pilihan)",
4
+ "label.codemind.content-optional-ast": "Kandungan Kod (Pilihan jika AST disediakan)",
5
+ "label.codemind.filepath": "Laluan Fail (Fail atau Direktori)",
6
+ "label.codemind.filepath-optional-ast": "Laluan Fail atau Direktori (Pilihan jika AST disediakan)",
7
+ "label.codemind.search-text-accessor": "Pengakses Pertanyaan Carian (Pilihan)"
8
+ }
@@ -0,0 +1,8 @@
1
+ {
2
+ "label.codemind.ast-data-accessor": "AST数据访问器(首选)",
3
+ "label.codemind.content": "代码内容(可选)",
4
+ "label.codemind.content-optional-ast": "代码内容(如果提供AST则可选)",
5
+ "label.codemind.filepath": "文件路径(文件或目录)",
6
+ "label.codemind.filepath-optional-ast": "文件路径或目录(如果提供AST则可选)",
7
+ "label.codemind.search-text-accessor": "搜索查询访问器(可选)"
8
+ }