activo 0.4.0 → 0.4.2
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/README.md +86 -148
- package/demo.gif +0 -0
- package/demo.tape +53 -0
- package/dist/cli/index.js +0 -0
- package/package.json +2 -2
- package/src/core/tools/standards.test.ts +186 -0
package/README.md
CHANGED
|
@@ -1,190 +1,128 @@
|
|
|
1
1
|
# ACTIVO
|
|
2
2
|
|
|
3
|
-
AI 기반 코드 품질 분석 CLI
|
|
3
|
+
AI 기반 코드 품질 분석 CLI (Ollama)
|
|
4
4
|
|
|
5
|
-

|
|
6
|
+
|
|
7
|
+
Ollama를 활용한 대화형 코드 분석 에이전트. Java, TypeScript, Python, SQL, CSS 등 다국어·멀티스택 프로젝트를 자동 감지하고 품질 이슈를 분석합니다.
|
|
8
|
+
|
|
9
|
+
- **Tool Calling**: LLM이 상황에 맞는 도구를 직접 호출
|
|
10
|
+
- **MCP 지원**: Model Context Protocol 연동
|
|
11
|
+
- **React Ink TUI**: 터미널용 대화형 UI
|
|
12
|
+
|
|
13
|
+
## 주요 기능
|
|
14
|
+
|
|
15
|
+
| 카테고리 | 도구 | 설명 |
|
|
16
|
+
|----------|------|------|
|
|
17
|
+
| **전체 분석** | `analyze_all` | 디렉토리 전체 자동 감지 및 분석 (권장) |
|
|
18
|
+
| **코드 분석** | Java, JS/TS, Python, React, Vue | AST, 순환 복잡도, 프레임워크 패턴 |
|
|
19
|
+
| **SQL/DB** | `sql_check`, `mybatis_check` | SQL Injection, N+1, 동적 SQL 분석 |
|
|
20
|
+
| **웹** | `css_check`, `html_check` | !important, 접근성(a11y), SEO 검사 |
|
|
21
|
+
| **의존성** | `dependency_check` | npm/Maven/Gradle 보안 취약점 검출 |
|
|
22
|
+
| **표준/RAG** | `import_hwp_standards`, `check_quality_rag` | HWP/PDF → 마크다운, RAG 기반 품질 검사 |
|
|
6
23
|
|
|
7
24
|
## 설치
|
|
8
25
|
|
|
9
26
|
```bash
|
|
10
|
-
# 글로벌 설치 (권장)
|
|
11
27
|
npm install -g activo
|
|
12
|
-
|
|
13
|
-
# 또는 npx로 바로 실행
|
|
14
|
-
npx activo
|
|
15
28
|
```
|
|
16
29
|
|
|
30
|
+
## 요구사항
|
|
31
|
+
|
|
32
|
+
- Node.js 18+
|
|
33
|
+
- [Ollama](https://ollama.ai) 실행 중
|
|
34
|
+
- 모델: `ollama pull mistral:latest` (또는 `qwen2.5:7b` 등)
|
|
35
|
+
|
|
17
36
|
## 사용법
|
|
18
37
|
|
|
19
38
|
```bash
|
|
20
39
|
# 대화형 모드
|
|
21
40
|
activo
|
|
22
41
|
|
|
23
|
-
# 프롬프트와 함께 실행
|
|
24
|
-
activo "src 폴더
|
|
25
|
-
|
|
26
|
-
# 비대화형 모드
|
|
27
|
-
activo --print "package.json 분석해줘"
|
|
42
|
+
# 프롬프트와 함께 실행 (권장)
|
|
43
|
+
activo "src 폴더 품질 분석해줘"
|
|
28
44
|
|
|
29
45
|
# 특정 모델 사용
|
|
30
|
-
activo --model qwen2.5:7b
|
|
31
|
-
```
|
|
32
|
-
|
|
33
|
-
## 주요 기능
|
|
34
|
-
|
|
35
|
-
### 기본 도구
|
|
36
|
-
- **파일 작업**: 읽기, 쓰기, 디렉토리 목록
|
|
37
|
-
- **검색**: grep (패턴), glob (파일명)
|
|
38
|
-
- **명령 실행**: 셸 명령 (안전 필터 적용)
|
|
39
|
-
|
|
40
|
-
### 코드 분석
|
|
41
|
-
- **AST 분석**: 함수, 클래스, 타입 구조 파악 (TypeScript Compiler API)
|
|
42
|
-
- **복잡도 분석**: 순환 복잡도 계산 및 리포트
|
|
43
|
-
- **호출 그래프**: 함수 간 호출 관계 추적
|
|
44
|
-
- **심볼 사용처**: 특정 심볼이 어디서 사용되는지 검색
|
|
45
|
-
|
|
46
|
-
### 파일 요약 캐싱
|
|
47
|
-
- **LLM 요약**: 파일 내용을 LLM으로 요약 후 캐싱
|
|
48
|
-
- **아웃라인**: 함수/클래스 시그니처 빠른 추출 (LLM 없음)
|
|
49
|
-
- **변경 감지**: 파일 해시로 변경 시에만 재생성
|
|
50
|
-
|
|
51
|
-
### 의미 기반 검색 (RAG)
|
|
52
|
-
- **코드베이스 인덱싱**: 벡터 임베딩 생성 (`nomic-embed-text`)
|
|
53
|
-
- **의미 검색**: "로그인 관련 코드" → 관련 코드 찾기
|
|
54
|
-
- **유사 코드 찾기**: 코드 패턴 검색
|
|
55
|
-
|
|
56
|
-
### 프로젝트 메모리
|
|
57
|
-
- **컨텍스트 저장**: 프로젝트 정보, 기술 스택, 컨벤션
|
|
58
|
-
- **노트/사실**: 중요 정보 기억
|
|
59
|
-
- **대화 요약**: 세션 간 컨텍스트 유지
|
|
60
|
-
|
|
61
|
-
### 프론트엔드 분석
|
|
62
|
-
- **React**: 컴포넌트 구조, Hooks 사용, 클래스 컴포넌트 감지
|
|
63
|
-
- **Vue**: Options/Composition/Script Setup API 분석
|
|
64
|
-
- **jQuery**: deprecated 메서드 검출 (bind, live, size 등)
|
|
65
|
-
|
|
66
|
-
### SQL/데이터베이스 분석
|
|
67
|
-
- **SQL**: Java 내 @Query, JDBC 쿼리 분석 (SELECT *, N+1 패턴)
|
|
68
|
-
- **MyBatis**: XML 매퍼 분석, ${} 인젝션 위험, 동적 SQL
|
|
69
|
-
|
|
70
|
-
### 웹 표준 분석
|
|
71
|
-
- **CSS/SCSS/LESS**: !important, 중첩 깊이, vendor prefix
|
|
72
|
-
- **HTML/JSP**: 접근성(a11y), SEO, 시맨틱 태그, deprecated 태그
|
|
73
|
-
|
|
74
|
-
### 의존성/API/Python
|
|
75
|
-
- **의존성 검사**: package.json, pom.xml 취약점, deprecated 패키지
|
|
76
|
-
- **OpenAPI 분석**: Swagger 스펙 검증, 엔드포인트 문서화 품질
|
|
77
|
-
- **Python 분석**: Django/Flask/FastAPI 패턴, PEP8, 보안 이슈
|
|
78
|
-
|
|
79
|
-
### 개발 표준
|
|
80
|
-
- **PDF 변환**: 개발표준 PDF → Markdown 변환
|
|
81
|
-
- **코드 품질 분석**: 규칙 기반 코드 점검
|
|
46
|
+
activo --model qwen2.5:7b "Java 코드 복잡도 점검해줘"
|
|
82
47
|
|
|
83
|
-
|
|
48
|
+
# Headless 모드 (CI/스크립트)
|
|
49
|
+
activo --headless "analyze_all 해줘"
|
|
84
50
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
| **기본** | `read_file` | 파일 읽기 |
|
|
88
|
-
| | `write_file` | 파일 쓰기 |
|
|
89
|
-
| | `list_directory` | 디렉토리 목록 |
|
|
90
|
-
| | `grep_search` | 패턴 검색 |
|
|
91
|
-
| | `glob_search` | 파일명 검색 |
|
|
92
|
-
| | `run_command` | 셸 명령 실행 |
|
|
93
|
-
| **캐시** | `summarize_file` | 파일 요약 (LLM + 캐싱) |
|
|
94
|
-
| | `get_file_outline` | 구조 추출 (빠름) |
|
|
95
|
-
| | `batch_summarize` | 다중 파일 요약 |
|
|
96
|
-
| **AST (TS/JS)** | `ast_analyze` | 심층 코드 분석 |
|
|
97
|
-
| | `get_call_graph` | 호출 그래프 |
|
|
98
|
-
| | `find_symbol_usage` | 심볼 사용처 |
|
|
99
|
-
| | `complexity_report` | 복잡도 리포트 |
|
|
100
|
-
| **Java** | `java_analyze` | Java AST 분석 |
|
|
101
|
-
| | `java_complexity` | Java 복잡도 리포트 |
|
|
102
|
-
| | `spring_check` | Spring 패턴 검사 |
|
|
103
|
-
| **Frontend** | `react_check` | React 컴포넌트/Hooks 분석 |
|
|
104
|
-
| | `vue_check` | Vue 컴포넌트 분석 |
|
|
105
|
-
| | `jquery_check` | jQuery deprecated 메서드 검사 |
|
|
106
|
-
| **SQL/DB** | `sql_check` | Java 내 SQL 쿼리 분석 |
|
|
107
|
-
| | `mybatis_check` | MyBatis XML 매퍼 분석 |
|
|
108
|
-
| **Web** | `css_check` | CSS/SCSS/LESS 분석 |
|
|
109
|
-
| | `html_check` | HTML/JSP 접근성/SEO 분석 |
|
|
110
|
-
| **의존성** | `dependency_check` | package.json, pom.xml 취약점 검사 |
|
|
111
|
-
| **API** | `openapi_check` | OpenAPI/Swagger 스펙 분석 |
|
|
112
|
-
| **Python** | `python_check` | Python/Django/Flask 분석 |
|
|
113
|
-
| **통합** | `analyze_all` | 디렉토리 전체 자동 분석 |
|
|
114
|
-
| **RAG** | `index_codebase` | 벡터 인덱싱 |
|
|
115
|
-
| | `semantic_search` | 의미 검색 |
|
|
116
|
-
| | `find_similar_code` | 유사 코드 찾기 |
|
|
117
|
-
| **메모리** | `init_project_memory` | 프로젝트 컨텍스트 |
|
|
118
|
-
| | `add_note` | 노트 저장 |
|
|
119
|
-
| | `get_project_context` | 컨텍스트 조회 |
|
|
120
|
-
| | `search_memory` | 메모리 검색 |
|
|
121
|
-
|
|
122
|
-
## 요구사항
|
|
123
|
-
|
|
124
|
-
- Node.js 18+
|
|
125
|
-
- [Ollama](https://ollama.ai) 실행 중
|
|
51
|
+
# 프롬프트만 출력 후 종료
|
|
52
|
+
activo --print "분석 요약해줘"
|
|
126
53
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
54
|
+
# 이전 세션 이어서
|
|
55
|
+
activo --resume
|
|
56
|
+
```
|
|
130
57
|
|
|
131
|
-
|
|
132
|
-
# macOS
|
|
133
|
-
brew install ollama
|
|
58
|
+
## 설정
|
|
134
59
|
|
|
135
|
-
|
|
136
|
-
# https://ollama.ai 에서 다운로드
|
|
60
|
+
설정 파일: `~/.activo/config.json` (전역), `.activo/config.json` (프로젝트별)
|
|
137
61
|
|
|
138
|
-
|
|
139
|
-
|
|
62
|
+
```json
|
|
63
|
+
{
|
|
64
|
+
"ollama": {
|
|
65
|
+
"baseUrl": "http://localhost:11434",
|
|
66
|
+
"model": "mistral:latest",
|
|
67
|
+
"contextLength": 8192,
|
|
68
|
+
"keepAlive": 1800
|
|
69
|
+
},
|
|
70
|
+
"standards": {
|
|
71
|
+
"directory": ".activo/standards"
|
|
72
|
+
},
|
|
73
|
+
"mcp": {
|
|
74
|
+
"servers": {
|
|
75
|
+
"my-server": {
|
|
76
|
+
"command": "npx",
|
|
77
|
+
"args": ["-y", "my-mcp-server"],
|
|
78
|
+
"env": {}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
140
83
|
```
|
|
141
84
|
|
|
142
|
-
|
|
85
|
+
| 옵션 | 설명 | 기본값 |
|
|
86
|
+
|------|------|--------|
|
|
87
|
+
| `ollama.baseUrl` | Ollama API URL | `http://localhost:11434` |
|
|
88
|
+
| `ollama.model` | 사용할 모델 | `mistral:latest` |
|
|
89
|
+
| `ollama.contextLength` | 컨텍스트 길이 | `8192` |
|
|
90
|
+
| `ollama.keepAlive` | 모델 유지 시간(초) | `1800` |
|
|
143
91
|
|
|
144
|
-
|
|
145
|
-
# 권장 모델
|
|
146
|
-
ollama pull mistral:latest
|
|
92
|
+
## 프로젝트 데이터
|
|
147
93
|
|
|
148
|
-
|
|
149
|
-
ollama pull qwen2.5:7b
|
|
94
|
+
분석 시 프로젝트 루트에 `.activo/` 디렉토리가 생성됩니다.
|
|
150
95
|
|
|
151
|
-
|
|
152
|
-
|
|
96
|
+
| 경로 | 용도 |
|
|
97
|
+
|------|------|
|
|
98
|
+
| `.activo/cache/` | 파일 요약 캐시 |
|
|
99
|
+
| `.activo/embeddings/` | 코드 임베딩 인덱스 |
|
|
100
|
+
| `.activo/memory/` | 프로젝트 메모리·대화 요약 |
|
|
101
|
+
| `.activo/standards-rag/` | 표준 문서 RAG (HWP/PDF import 후) |
|
|
153
102
|
|
|
154
|
-
|
|
155
|
-
ollama pull nomic-embed-text
|
|
156
|
-
```
|
|
103
|
+
## MCP 연동
|
|
157
104
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
```bash
|
|
161
|
-
ollama serve
|
|
162
|
-
```
|
|
105
|
+
`~/.activo/config.json`의 `mcp.servers`에 MCP 서버를 등록하면 activo가 해당 도구를 자동으로 사용할 수 있습니다.
|
|
163
106
|
|
|
164
|
-
### 4. activo 설정
|
|
165
|
-
|
|
166
|
-
`~/.activo/config.json`:
|
|
167
107
|
```json
|
|
168
108
|
{
|
|
169
|
-
"
|
|
170
|
-
"
|
|
171
|
-
|
|
109
|
+
"mcp": {
|
|
110
|
+
"servers": {
|
|
111
|
+
"filesystem": {
|
|
112
|
+
"command": "npx",
|
|
113
|
+
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/path/to/allowed"]
|
|
114
|
+
}
|
|
115
|
+
}
|
|
172
116
|
}
|
|
173
117
|
}
|
|
174
118
|
```
|
|
175
119
|
|
|
176
|
-
|
|
177
|
-
```bash
|
|
178
|
-
activo --model qwen2.5:7b
|
|
179
|
-
```
|
|
180
|
-
|
|
181
|
-
## 단축키
|
|
120
|
+
## 기술 스택
|
|
182
121
|
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
| `Ctrl+C` x2 | 종료 |
|
|
122
|
+
- **Ollama** - 로컬 LLM
|
|
123
|
+
- **TypeScript Compiler API** - JS/TS AST 분석
|
|
124
|
+
- **java-ast** - Java 파싱 (ANTLR4)
|
|
125
|
+
- **React Ink** - 터미널 UI
|
|
188
126
|
|
|
189
127
|
## 라이선스
|
|
190
128
|
|
package/demo.gif
ADDED
|
Binary file
|
package/demo.tape
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
# VHS Demo for ACTIVO - Full Feature Showcase
|
|
2
|
+
Output demo.gif
|
|
3
|
+
|
|
4
|
+
Set FontSize 13
|
|
5
|
+
Set Width 900
|
|
6
|
+
Set Height 600
|
|
7
|
+
Set Theme "Dracula"
|
|
8
|
+
Set TypingSpeed 50ms
|
|
9
|
+
|
|
10
|
+
# 1. Start activo in centerpoint project
|
|
11
|
+
Type "cd /Users/mhb8436/Workspaces/centerpoint/server && activo"
|
|
12
|
+
Enter
|
|
13
|
+
Sleep 3s
|
|
14
|
+
|
|
15
|
+
# 2. Directory structure
|
|
16
|
+
Type "프로젝트 구조 보여줘"
|
|
17
|
+
Enter
|
|
18
|
+
Sleep 6s
|
|
19
|
+
|
|
20
|
+
# 3. Java code analysis
|
|
21
|
+
Type "api-server 자바 코드 분석해줘"
|
|
22
|
+
Enter
|
|
23
|
+
Sleep 10s
|
|
24
|
+
|
|
25
|
+
# 4. Spring pattern check
|
|
26
|
+
Type "Spring 패턴 검사해줘"
|
|
27
|
+
Enter
|
|
28
|
+
Sleep 8s
|
|
29
|
+
|
|
30
|
+
# 5. Dependency check
|
|
31
|
+
Type "pom.xml 의존성 분석해줘"
|
|
32
|
+
Enter
|
|
33
|
+
Sleep 6s
|
|
34
|
+
|
|
35
|
+
# 6. Find specific code
|
|
36
|
+
Type "WebSecurityConfig 파일 찾아서 보여줘"
|
|
37
|
+
Enter
|
|
38
|
+
Sleep 6s
|
|
39
|
+
|
|
40
|
+
# 7. SQL analysis
|
|
41
|
+
Type "SQL 쿼리 분석해줘"
|
|
42
|
+
Enter
|
|
43
|
+
Sleep 6s
|
|
44
|
+
|
|
45
|
+
# 8. Full analysis
|
|
46
|
+
Type "전체 코드 품질 분석해줘"
|
|
47
|
+
Enter
|
|
48
|
+
Sleep 10s
|
|
49
|
+
|
|
50
|
+
# 9. Exit
|
|
51
|
+
Type "/exit"
|
|
52
|
+
Enter
|
|
53
|
+
Sleep 1s
|
package/dist/cli/index.js
CHANGED
|
File without changes
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "activo",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
4
4
|
"description": "AI-powered code quality analyzer with React Ink TUI, Tool Calling, and MCP support",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -51,6 +51,7 @@
|
|
|
51
51
|
"java-ast": "^0.4.1",
|
|
52
52
|
"pdf-parse": "^1.1.1",
|
|
53
53
|
"react": "^19.1.0",
|
|
54
|
+
"typescript": "^5.8.0",
|
|
54
55
|
"uuid": "^10.0.0"
|
|
55
56
|
},
|
|
56
57
|
"devDependencies": {
|
|
@@ -60,7 +61,6 @@
|
|
|
60
61
|
"@types/react": "^19.1.0",
|
|
61
62
|
"@types/uuid": "^10.0.0",
|
|
62
63
|
"tsx": "^4.20.0",
|
|
63
|
-
"typescript": "^5.8.0",
|
|
64
64
|
"vitest": "^3.2.0"
|
|
65
65
|
}
|
|
66
66
|
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from "vitest";
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
// Test helpers
|
|
6
|
+
const TEST_DIR = ".activo-test";
|
|
7
|
+
const STANDARDS_DIR = `${TEST_DIR}/standards`;
|
|
8
|
+
const RAG_DIR = `${TEST_DIR}/standards-rag`;
|
|
9
|
+
|
|
10
|
+
// Helper to create test directory
|
|
11
|
+
function setupTestDir() {
|
|
12
|
+
if (fs.existsSync(TEST_DIR)) {
|
|
13
|
+
fs.rmSync(TEST_DIR, { recursive: true });
|
|
14
|
+
}
|
|
15
|
+
fs.mkdirSync(STANDARDS_DIR, { recursive: true });
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Helper to cleanup test directory
|
|
19
|
+
function cleanupTestDir() {
|
|
20
|
+
if (fs.existsSync(TEST_DIR)) {
|
|
21
|
+
fs.rmSync(TEST_DIR, { recursive: true });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Helper to create test markdown file
|
|
26
|
+
function createTestMarkdown(filename: string, content: string) {
|
|
27
|
+
fs.writeFileSync(path.join(STANDARDS_DIR, filename), content);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
describe("Standards Tools", () => {
|
|
31
|
+
beforeEach(() => {
|
|
32
|
+
setupTestDir();
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
afterEach(() => {
|
|
36
|
+
cleanupTestDir();
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
describe("splitStandardsIntoChunks", () => {
|
|
40
|
+
it("should split markdown by sections", () => {
|
|
41
|
+
const content = `# Development Standards
|
|
42
|
+
|
|
43
|
+
## Introduction
|
|
44
|
+
This is the introduction.
|
|
45
|
+
|
|
46
|
+
## RULE-001: Variable Naming
|
|
47
|
+
- Severity: error
|
|
48
|
+
- Rule: Use camelCase for variables
|
|
49
|
+
|
|
50
|
+
## RULE-002: Function Naming
|
|
51
|
+
- Severity: warning
|
|
52
|
+
- Rule: Use descriptive names
|
|
53
|
+
`;
|
|
54
|
+
createTestMarkdown("test.md", content);
|
|
55
|
+
|
|
56
|
+
// Read and verify file was created
|
|
57
|
+
const savedContent = fs.readFileSync(path.join(STANDARDS_DIR, "test.md"), "utf-8");
|
|
58
|
+
expect(savedContent).toContain("RULE-001");
|
|
59
|
+
expect(savedContent).toContain("RULE-002");
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it("should handle empty files", () => {
|
|
63
|
+
createTestMarkdown("empty.md", "");
|
|
64
|
+
const savedContent = fs.readFileSync(path.join(STANDARDS_DIR, "empty.md"), "utf-8");
|
|
65
|
+
expect(savedContent).toBe("");
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it("should handle files without rules", () => {
|
|
69
|
+
const content = `# Simple Document
|
|
70
|
+
|
|
71
|
+
Just some text without rules.
|
|
72
|
+
`;
|
|
73
|
+
createTestMarkdown("simple.md", content);
|
|
74
|
+
const savedContent = fs.readFileSync(path.join(STANDARDS_DIR, "simple.md"), "utf-8");
|
|
75
|
+
expect(savedContent).toContain("Simple Document");
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe("cosineSimilarity", () => {
|
|
80
|
+
it("should return 1 for identical vectors", () => {
|
|
81
|
+
const a = [1, 2, 3];
|
|
82
|
+
const b = [1, 2, 3];
|
|
83
|
+
// Inline test for cosine similarity logic
|
|
84
|
+
let dotProduct = 0;
|
|
85
|
+
let normA = 0;
|
|
86
|
+
let normB = 0;
|
|
87
|
+
for (let i = 0; i < a.length; i++) {
|
|
88
|
+
dotProduct += a[i] * b[i];
|
|
89
|
+
normA += a[i] * a[i];
|
|
90
|
+
normB += b[i] * b[i];
|
|
91
|
+
}
|
|
92
|
+
const similarity = dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
93
|
+
expect(similarity).toBeCloseTo(1, 5);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it("should return 0 for orthogonal vectors", () => {
|
|
97
|
+
const a = [1, 0];
|
|
98
|
+
const b = [0, 1];
|
|
99
|
+
let dotProduct = 0;
|
|
100
|
+
let normA = 0;
|
|
101
|
+
let normB = 0;
|
|
102
|
+
for (let i = 0; i < a.length; i++) {
|
|
103
|
+
dotProduct += a[i] * b[i];
|
|
104
|
+
normA += a[i] * a[i];
|
|
105
|
+
normB += b[i] * b[i];
|
|
106
|
+
}
|
|
107
|
+
const similarity = dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
|
|
108
|
+
expect(similarity).toBeCloseTo(0, 5);
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
it("should handle different length vectors", () => {
|
|
112
|
+
const a = [1, 2, 3];
|
|
113
|
+
const b = [1, 2];
|
|
114
|
+
// Should return 0 or handle gracefully
|
|
115
|
+
if (a.length !== b.length) {
|
|
116
|
+
expect(true).toBe(true); // Different lengths not comparable
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
describe("File hash calculation", () => {
|
|
122
|
+
it("should generate consistent hashes for same content", () => {
|
|
123
|
+
const crypto = require("crypto");
|
|
124
|
+
const content = "test content";
|
|
125
|
+
const hash1 = crypto.createHash("md5").update(content).digest("hex");
|
|
126
|
+
const hash2 = crypto.createHash("md5").update(content).digest("hex");
|
|
127
|
+
expect(hash1).toBe(hash2);
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
it("should generate different hashes for different content", () => {
|
|
131
|
+
const crypto = require("crypto");
|
|
132
|
+
const hash1 = crypto.createHash("md5").update("content1").digest("hex");
|
|
133
|
+
const hash2 = crypto.createHash("md5").update("content2").digest("hex");
|
|
134
|
+
expect(hash1).not.toBe(hash2);
|
|
135
|
+
});
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe("RAG Directory Structure", () => {
|
|
140
|
+
beforeEach(() => {
|
|
141
|
+
setupTestDir();
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
afterEach(() => {
|
|
145
|
+
cleanupTestDir();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it("should create RAG directory when needed", () => {
|
|
149
|
+
fs.mkdirSync(RAG_DIR, { recursive: true });
|
|
150
|
+
expect(fs.existsSync(RAG_DIR)).toBe(true);
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
it("should save and load index file", () => {
|
|
154
|
+
fs.mkdirSync(RAG_DIR, { recursive: true });
|
|
155
|
+
const index = {
|
|
156
|
+
version: "1.0",
|
|
157
|
+
model: "nomic-embed-text",
|
|
158
|
+
createdAt: new Date().toISOString(),
|
|
159
|
+
updatedAt: new Date().toISOString(),
|
|
160
|
+
totalChunks: 10,
|
|
161
|
+
};
|
|
162
|
+
const indexPath = path.join(RAG_DIR, "index.json");
|
|
163
|
+
fs.writeFileSync(indexPath, JSON.stringify(index, null, 2));
|
|
164
|
+
|
|
165
|
+
const loaded = JSON.parse(fs.readFileSync(indexPath, "utf-8"));
|
|
166
|
+
expect(loaded.version).toBe("1.0");
|
|
167
|
+
expect(loaded.totalChunks).toBe(10);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("should save and load embeddings file", () => {
|
|
171
|
+
fs.mkdirSync(RAG_DIR, { recursive: true });
|
|
172
|
+
const embeddings = [
|
|
173
|
+
{
|
|
174
|
+
chunk: { filepath: "test.md", section: "Test", content: "Test content" },
|
|
175
|
+
embedding: [0.1, 0.2, 0.3],
|
|
176
|
+
hash: "abc123",
|
|
177
|
+
},
|
|
178
|
+
];
|
|
179
|
+
const dataPath = path.join(RAG_DIR, "embeddings.json");
|
|
180
|
+
fs.writeFileSync(dataPath, JSON.stringify(embeddings));
|
|
181
|
+
|
|
182
|
+
const loaded = JSON.parse(fs.readFileSync(dataPath, "utf-8"));
|
|
183
|
+
expect(loaded.length).toBe(1);
|
|
184
|
+
expect(loaded[0].chunk.filepath).toBe("test.md");
|
|
185
|
+
});
|
|
186
|
+
});
|