claude-session-continuity-mcp 1.0.0

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.
Files changed (67) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +307 -0
  3. package/dist/dashboard-v2.d.ts +11 -0
  4. package/dist/dashboard-v2.js +1321 -0
  5. package/dist/dashboard.d.ts +2 -0
  6. package/dist/dashboard.js +1196 -0
  7. package/dist/db/database.d.ts +8 -0
  8. package/dist/db/database.js +208 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.js +3426 -0
  11. package/dist/schemas.d.ts +381 -0
  12. package/dist/schemas.js +119 -0
  13. package/dist/tools/context.d.ts +6 -0
  14. package/dist/tools/context.js +227 -0
  15. package/dist/tools/embedding.d.ts +5 -0
  16. package/dist/tools/embedding.js +191 -0
  17. package/dist/tools/feedback.d.ts +5 -0
  18. package/dist/tools/feedback.js +200 -0
  19. package/dist/tools/filter.d.ts +5 -0
  20. package/dist/tools/filter.js +169 -0
  21. package/dist/tools/index.d.ts +12 -0
  22. package/dist/tools/index.js +38 -0
  23. package/dist/tools/learning.d.ts +8 -0
  24. package/dist/tools/learning.js +395 -0
  25. package/dist/tools/memory.d.ts +8 -0
  26. package/dist/tools/memory.js +356 -0
  27. package/dist/tools/project.d.ts +9 -0
  28. package/dist/tools/project.js +396 -0
  29. package/dist/tools/relation.d.ts +4 -0
  30. package/dist/tools/relation.js +148 -0
  31. package/dist/tools/session.d.ts +7 -0
  32. package/dist/tools/session.js +272 -0
  33. package/dist/tools/solution.d.ts +5 -0
  34. package/dist/tools/solution.js +182 -0
  35. package/dist/tools/task.d.ts +6 -0
  36. package/dist/tools/task.js +184 -0
  37. package/dist/tools-v2/auto-capture.d.ts +5 -0
  38. package/dist/tools-v2/auto-capture.js +252 -0
  39. package/dist/tools-v2/context.d.ts +4 -0
  40. package/dist/tools-v2/context.js +170 -0
  41. package/dist/tools-v2/embedding.d.ts +3 -0
  42. package/dist/tools-v2/embedding.js +115 -0
  43. package/dist/tools-v2/index.d.ts +13 -0
  44. package/dist/tools-v2/index.js +73 -0
  45. package/dist/tools-v2/learn.d.ts +4 -0
  46. package/dist/tools-v2/learn.js +233 -0
  47. package/dist/tools-v2/memory.d.ts +6 -0
  48. package/dist/tools-v2/memory.js +340 -0
  49. package/dist/tools-v2/projects.d.ts +3 -0
  50. package/dist/tools-v2/projects.js +218 -0
  51. package/dist/tools-v2/task.d.ts +3 -0
  52. package/dist/tools-v2/task.js +193 -0
  53. package/dist/tools-v2/verify.d.ts +3 -0
  54. package/dist/tools-v2/verify.js +164 -0
  55. package/dist/types.d.ts +51 -0
  56. package/dist/types.js +7 -0
  57. package/dist/utils/auto-context.d.ts +58 -0
  58. package/dist/utils/auto-context.js +234 -0
  59. package/dist/utils/cache.d.ts +60 -0
  60. package/dist/utils/cache.js +161 -0
  61. package/dist/utils/embedding.d.ts +7 -0
  62. package/dist/utils/embedding.js +67 -0
  63. package/dist/utils/helpers.d.ts +4 -0
  64. package/dist/utils/helpers.js +45 -0
  65. package/dist/utils/logger.d.ts +17 -0
  66. package/dist/utils/logger.js +111 -0
  67. package/package.json +64 -0
@@ -0,0 +1,58 @@
1
+ export interface ProjectContext {
2
+ project: string;
3
+ fixed: {
4
+ techStack: Record<string, string>;
5
+ architectureDecisions: string[];
6
+ codePatterns: string[];
7
+ specialNotes: string | null;
8
+ };
9
+ active: {
10
+ currentState: string;
11
+ recentFiles: string[];
12
+ blockers: string | null;
13
+ lastVerification: string | null;
14
+ updatedAt: string | null;
15
+ };
16
+ pendingTasks: Array<{
17
+ id: number;
18
+ title: string;
19
+ status: string;
20
+ priority: number;
21
+ }>;
22
+ }
23
+ export interface ContextSnapshot {
24
+ project: string;
25
+ timestamp: string;
26
+ tokenEstimate: number;
27
+ context: ProjectContext;
28
+ }
29
+ /**
30
+ * 컨텍스트의 총 토큰 수 추정
31
+ */
32
+ export declare function estimateContextTokens(context: ProjectContext): number;
33
+ /**
34
+ * 프로젝트 컨텍스트 자동 로드 (캐시 우선)
35
+ * 목표: < 5ms (캐시 히트 시)
36
+ */
37
+ export declare function loadContext(project: string): Promise<ProjectContext>;
38
+ export interface SaveContextOptions {
39
+ currentState: string;
40
+ recentFiles?: string[];
41
+ blockers?: string | null;
42
+ verification?: 'passed' | 'failed' | null;
43
+ architectureDecision?: string;
44
+ codePattern?: string;
45
+ techStack?: Record<string, string>;
46
+ }
47
+ /**
48
+ * 프로젝트 컨텍스트 자동 저장
49
+ */
50
+ export declare function saveContext(project: string, options: SaveContextOptions): Promise<void>;
51
+ /**
52
+ * 현재 컨텍스트의 스냅샷 생성 (토큰 추정 포함)
53
+ */
54
+ export declare function createContextSnapshot(project: string): Promise<ContextSnapshot>;
55
+ /**
56
+ * 토큰 효율적 컨텍스트 요약 (650토큰 목표)
57
+ */
58
+ export declare function getCompactContext(project: string): Promise<string>;
@@ -0,0 +1,234 @@
1
+ // 자동 컨텍스트 캡처 시스템
2
+ // 세션 시작 시 자동 로드, 세션 종료 시 자동 저장
3
+ import { db } from '../db/database.js';
4
+ import { logger } from './logger.js';
5
+ import { contextCache, makeContextKey, invalidateContext } from './cache.js';
6
+ // ===== 토큰 추정 =====
7
+ /**
8
+ * 문자열의 토큰 수 추정 (평균 4자 = 1토큰)
9
+ */
10
+ function estimateTokens(text) {
11
+ return Math.ceil(text.length / 4);
12
+ }
13
+ /**
14
+ * 컨텍스트의 총 토큰 수 추정
15
+ */
16
+ export function estimateContextTokens(context) {
17
+ const json = JSON.stringify(context);
18
+ return estimateTokens(json);
19
+ }
20
+ // ===== 자동 컨텍스트 로드 =====
21
+ /**
22
+ * 프로젝트 컨텍스트 자동 로드 (캐시 우선)
23
+ * 목표: < 5ms (캐시 히트 시)
24
+ */
25
+ export async function loadContext(project) {
26
+ const startTime = performance.now();
27
+ const cacheKey = makeContextKey(project);
28
+ // 캐시 확인
29
+ const cached = contextCache.get(cacheKey);
30
+ if (cached) {
31
+ const elapsed = performance.now() - startTime;
32
+ logger.debug('Context loaded from cache', { project, elapsed: `${elapsed.toFixed(2)}ms` });
33
+ return cached;
34
+ }
35
+ // DB에서 로드
36
+ const context = await loadContextFromDB(project);
37
+ // 캐시에 저장
38
+ contextCache.set(cacheKey, context);
39
+ const elapsed = performance.now() - startTime;
40
+ logger.info('Context loaded from DB', { project, elapsed: `${elapsed.toFixed(2)}ms` });
41
+ return context;
42
+ }
43
+ /**
44
+ * DB에서 컨텍스트 로드 (내부용)
45
+ */
46
+ async function loadContextFromDB(project) {
47
+ // Layer 1: 고정 컨텍스트
48
+ const projectContextStmt = db.prepare('SELECT * FROM project_context WHERE project = ?');
49
+ const projectContext = projectContextStmt.get(project);
50
+ // Layer 2: 활성 컨텍스트
51
+ const activeContextStmt = db.prepare('SELECT * FROM active_context WHERE project = ?');
52
+ const activeContext = activeContextStmt.get(project);
53
+ // Layer 3: 미완료 태스크 (최대 3개, 우선순위순)
54
+ const tasksStmt = db.prepare(`
55
+ SELECT id, title, status, priority
56
+ FROM tasks
57
+ WHERE project = ? AND status IN ('pending', 'in_progress')
58
+ ORDER BY priority DESC, created_at DESC
59
+ LIMIT 3
60
+ `);
61
+ const tasks = tasksStmt.all(project);
62
+ return {
63
+ project,
64
+ fixed: {
65
+ techStack: projectContext?.tech_stack ? JSON.parse(projectContext.tech_stack) : {},
66
+ architectureDecisions: projectContext?.architecture_decisions ? JSON.parse(projectContext.architecture_decisions) : [],
67
+ codePatterns: projectContext?.code_patterns ? JSON.parse(projectContext.code_patterns) : [],
68
+ specialNotes: projectContext?.special_notes || null
69
+ },
70
+ active: {
71
+ currentState: activeContext?.current_state || 'No active context',
72
+ recentFiles: activeContext?.recent_files ? JSON.parse(activeContext.recent_files) : [],
73
+ blockers: activeContext?.blockers || null,
74
+ lastVerification: activeContext?.last_verification || null,
75
+ updatedAt: activeContext?.updated_at || null
76
+ },
77
+ pendingTasks: tasks
78
+ };
79
+ }
80
+ /**
81
+ * 프로젝트 컨텍스트 자동 저장
82
+ */
83
+ export async function saveContext(project, options) {
84
+ const startTime = performance.now();
85
+ const transaction = db.transaction(() => {
86
+ // 활성 컨텍스트 업데이트
87
+ const activeStmt = db.prepare(`
88
+ INSERT OR REPLACE INTO active_context (project, current_state, recent_files, blockers, last_verification, updated_at)
89
+ VALUES (?, ?, ?, ?, ?, CURRENT_TIMESTAMP)
90
+ `);
91
+ activeStmt.run(project, options.currentState, options.recentFiles ? JSON.stringify(options.recentFiles.slice(0, 10)) : null, options.blockers || null, options.verification || null);
92
+ // 아키텍처 결정 추가 (있으면)
93
+ if (options.architectureDecision) {
94
+ updateArchitectureDecision(project, options.architectureDecision);
95
+ }
96
+ // 코드 패턴 추가 (있으면)
97
+ if (options.codePattern) {
98
+ updateCodePattern(project, options.codePattern);
99
+ }
100
+ // 기술 스택 업데이트 (있으면)
101
+ if (options.techStack) {
102
+ updateTechStack(project, options.techStack);
103
+ }
104
+ });
105
+ transaction();
106
+ // 캐시 무효화
107
+ invalidateContext(project);
108
+ const elapsed = performance.now() - startTime;
109
+ logger.info('Context saved', { project, elapsed: `${elapsed.toFixed(2)}ms` });
110
+ }
111
+ // ===== 고정 컨텍스트 업데이트 헬퍼 =====
112
+ function updateArchitectureDecision(project, decision) {
113
+ const getStmt = db.prepare('SELECT architecture_decisions FROM project_context WHERE project = ?');
114
+ const row = getStmt.get(project);
115
+ let decisions = [];
116
+ if (row?.architecture_decisions) {
117
+ try {
118
+ decisions = JSON.parse(row.architecture_decisions);
119
+ }
120
+ catch { /* ignore */ }
121
+ }
122
+ // 중복 제거 후 앞에 추가 (최대 5개)
123
+ decisions = decisions.filter(d => d !== decision);
124
+ decisions.unshift(decision);
125
+ decisions = decisions.slice(0, 5);
126
+ const upsertStmt = db.prepare(`
127
+ INSERT INTO project_context (project, architecture_decisions, updated_at)
128
+ VALUES (?, ?, CURRENT_TIMESTAMP)
129
+ ON CONFLICT(project) DO UPDATE SET
130
+ architecture_decisions = ?,
131
+ updated_at = CURRENT_TIMESTAMP
132
+ `);
133
+ const json = JSON.stringify(decisions);
134
+ upsertStmt.run(project, json, json);
135
+ }
136
+ function updateCodePattern(project, pattern) {
137
+ const getStmt = db.prepare('SELECT code_patterns FROM project_context WHERE project = ?');
138
+ const row = getStmt.get(project);
139
+ let patterns = [];
140
+ if (row?.code_patterns) {
141
+ try {
142
+ patterns = JSON.parse(row.code_patterns);
143
+ }
144
+ catch { /* ignore */ }
145
+ }
146
+ // 중복 제거 후 앞에 추가 (최대 5개)
147
+ patterns = patterns.filter(p => p !== pattern);
148
+ patterns.unshift(pattern);
149
+ patterns = patterns.slice(0, 5);
150
+ const upsertStmt = db.prepare(`
151
+ INSERT INTO project_context (project, code_patterns, updated_at)
152
+ VALUES (?, ?, CURRENT_TIMESTAMP)
153
+ ON CONFLICT(project) DO UPDATE SET
154
+ code_patterns = ?,
155
+ updated_at = CURRENT_TIMESTAMP
156
+ `);
157
+ const json = JSON.stringify(patterns);
158
+ upsertStmt.run(project, json, json);
159
+ }
160
+ function updateTechStack(project, newStack) {
161
+ const getStmt = db.prepare('SELECT tech_stack FROM project_context WHERE project = ?');
162
+ const row = getStmt.get(project);
163
+ let stack = {};
164
+ if (row?.tech_stack) {
165
+ try {
166
+ stack = JSON.parse(row.tech_stack);
167
+ }
168
+ catch { /* ignore */ }
169
+ }
170
+ // 병합 (새 값이 기존 값 덮어씀)
171
+ stack = { ...stack, ...newStack };
172
+ const upsertStmt = db.prepare(`
173
+ INSERT INTO project_context (project, tech_stack, updated_at)
174
+ VALUES (?, ?, CURRENT_TIMESTAMP)
175
+ ON CONFLICT(project) DO UPDATE SET
176
+ tech_stack = ?,
177
+ updated_at = CURRENT_TIMESTAMP
178
+ `);
179
+ const json = JSON.stringify(stack);
180
+ upsertStmt.run(project, json, json);
181
+ }
182
+ // ===== 컨텍스트 스냅샷 =====
183
+ /**
184
+ * 현재 컨텍스트의 스냅샷 생성 (토큰 추정 포함)
185
+ */
186
+ export async function createContextSnapshot(project) {
187
+ const context = await loadContext(project);
188
+ const tokenEstimate = estimateContextTokens(context);
189
+ return {
190
+ project,
191
+ timestamp: new Date().toISOString(),
192
+ tokenEstimate,
193
+ context
194
+ };
195
+ }
196
+ // ===== 컨텍스트 요약 =====
197
+ /**
198
+ * 토큰 효율적 컨텍스트 요약 (650토큰 목표)
199
+ */
200
+ export async function getCompactContext(project) {
201
+ const context = await loadContext(project);
202
+ const lines = [
203
+ `# ${project}`,
204
+ '',
205
+ ];
206
+ // 기술 스택 (간결하게)
207
+ if (Object.keys(context.fixed.techStack).length > 0) {
208
+ const stackStr = Object.entries(context.fixed.techStack)
209
+ .map(([k, v]) => `${k}: ${v}`)
210
+ .join(', ');
211
+ lines.push(`**Stack**: ${stackStr}`);
212
+ }
213
+ // 아키텍처 결정 (최대 3개)
214
+ if (context.fixed.architectureDecisions.length > 0) {
215
+ lines.push(`**Decisions**: ${context.fixed.architectureDecisions.slice(0, 3).join(' | ')}`);
216
+ }
217
+ // 현재 상태
218
+ lines.push(`**State**: ${context.active.currentState}`);
219
+ // 최근 파일 (최대 5개)
220
+ if (context.active.recentFiles.length > 0) {
221
+ const files = context.active.recentFiles.slice(0, 5).map(f => f.split('/').pop()).join(', ');
222
+ lines.push(`**Files**: ${files}`);
223
+ }
224
+ // 블로커
225
+ if (context.active.blockers) {
226
+ lines.push(`**Blocker**: ${context.active.blockers}`);
227
+ }
228
+ // 미완료 태스크
229
+ if (context.pendingTasks.length > 0) {
230
+ const tasks = context.pendingTasks.map(t => `[P${t.priority}] ${t.title}`).join(' | ');
231
+ lines.push(`**Tasks**: ${tasks}`);
232
+ }
233
+ return lines.join('\n');
234
+ }
@@ -0,0 +1,60 @@
1
+ interface CacheOptions {
2
+ maxSize: number;
3
+ ttlMs: number;
4
+ }
5
+ export declare class QueryCache<T = unknown> {
6
+ private cache;
7
+ private options;
8
+ private hits;
9
+ private misses;
10
+ constructor(options?: Partial<CacheOptions>);
11
+ /**
12
+ * 캐시에서 값 조회
13
+ * @returns 캐시된 값 또는 undefined
14
+ */
15
+ get(key: string): T | undefined;
16
+ /**
17
+ * 캐시에 값 저장
18
+ */
19
+ set(key: string, value: T): void;
20
+ /**
21
+ * 캐시에서 값 조회 또는 생성
22
+ */
23
+ getOrSet(key: string, factory: () => T | Promise<T>): Promise<T>;
24
+ /**
25
+ * 특정 키 무효화
26
+ */
27
+ invalidate(key: string): boolean;
28
+ /**
29
+ * 패턴과 일치하는 모든 키 무효화
30
+ */
31
+ invalidatePattern(pattern: string | RegExp): number;
32
+ /**
33
+ * 전체 캐시 초기화
34
+ */
35
+ clear(): void;
36
+ /**
37
+ * 캐시 통계
38
+ */
39
+ getStats(): {
40
+ size: number;
41
+ maxSize: number;
42
+ hits: number;
43
+ misses: number;
44
+ hitRate: number;
45
+ };
46
+ /**
47
+ * LRU 항목 제거
48
+ */
49
+ private evictLRU;
50
+ }
51
+ export declare const contextCache: QueryCache<unknown>;
52
+ export declare const memoryCache: QueryCache<unknown>;
53
+ export declare const projectCache: QueryCache<unknown>;
54
+ export declare function makeContextKey(project: string): string;
55
+ export declare function makeMemoryKey(query: string, project?: string, type?: string): string;
56
+ export declare function makeProjectKey(): string;
57
+ export declare function invalidateContext(project: string): void;
58
+ export declare function invalidateMemory(project?: string): void;
59
+ export declare function invalidateProjects(): void;
60
+ export {};
@@ -0,0 +1,161 @@
1
+ // 쿼리 캐싱 시스템 - 5ms 읽기 목표
2
+ // LRU 캐시 + TTL 기반 무효화
3
+ const DEFAULT_OPTIONS = {
4
+ maxSize: 100,
5
+ ttlMs: 30000 // 30초
6
+ };
7
+ export class QueryCache {
8
+ cache;
9
+ options;
10
+ hits = 0;
11
+ misses = 0;
12
+ constructor(options = {}) {
13
+ this.cache = new Map();
14
+ this.options = { ...DEFAULT_OPTIONS, ...options };
15
+ }
16
+ /**
17
+ * 캐시에서 값 조회
18
+ * @returns 캐시된 값 또는 undefined
19
+ */
20
+ get(key) {
21
+ const entry = this.cache.get(key);
22
+ if (!entry) {
23
+ this.misses++;
24
+ return undefined;
25
+ }
26
+ // TTL 체크
27
+ if (Date.now() - entry.createdAt > this.options.ttlMs) {
28
+ this.cache.delete(key);
29
+ this.misses++;
30
+ return undefined;
31
+ }
32
+ // 접근 횟수 증가
33
+ entry.accessCount++;
34
+ this.hits++;
35
+ return entry.value;
36
+ }
37
+ /**
38
+ * 캐시에 값 저장
39
+ */
40
+ set(key, value) {
41
+ // 최대 크기 초과 시 LRU 제거
42
+ if (this.cache.size >= this.options.maxSize) {
43
+ this.evictLRU();
44
+ }
45
+ this.cache.set(key, {
46
+ value,
47
+ createdAt: Date.now(),
48
+ accessCount: 0
49
+ });
50
+ }
51
+ /**
52
+ * 캐시에서 값 조회 또는 생성
53
+ */
54
+ async getOrSet(key, factory) {
55
+ const cached = this.get(key);
56
+ if (cached !== undefined) {
57
+ return cached;
58
+ }
59
+ const value = await factory();
60
+ this.set(key, value);
61
+ return value;
62
+ }
63
+ /**
64
+ * 특정 키 무효화
65
+ */
66
+ invalidate(key) {
67
+ return this.cache.delete(key);
68
+ }
69
+ /**
70
+ * 패턴과 일치하는 모든 키 무효화
71
+ */
72
+ invalidatePattern(pattern) {
73
+ const regex = typeof pattern === 'string' ? new RegExp(pattern) : pattern;
74
+ let count = 0;
75
+ for (const key of this.cache.keys()) {
76
+ if (regex.test(key)) {
77
+ this.cache.delete(key);
78
+ count++;
79
+ }
80
+ }
81
+ return count;
82
+ }
83
+ /**
84
+ * 전체 캐시 초기화
85
+ */
86
+ clear() {
87
+ this.cache.clear();
88
+ this.hits = 0;
89
+ this.misses = 0;
90
+ }
91
+ /**
92
+ * 캐시 통계
93
+ */
94
+ getStats() {
95
+ const total = this.hits + this.misses;
96
+ return {
97
+ size: this.cache.size,
98
+ maxSize: this.options.maxSize,
99
+ hits: this.hits,
100
+ misses: this.misses,
101
+ hitRate: total > 0 ? this.hits / total : 0
102
+ };
103
+ }
104
+ /**
105
+ * LRU 항목 제거
106
+ */
107
+ evictLRU() {
108
+ let lruKey;
109
+ let lruCount = Infinity;
110
+ for (const [key, entry] of this.cache.entries()) {
111
+ if (entry.accessCount < lruCount) {
112
+ lruCount = entry.accessCount;
113
+ lruKey = key;
114
+ }
115
+ }
116
+ if (lruKey) {
117
+ this.cache.delete(lruKey);
118
+ }
119
+ }
120
+ }
121
+ // ===== 전역 캐시 인스턴스 =====
122
+ // 컨텍스트 캐시 (자주 조회)
123
+ export const contextCache = new QueryCache({
124
+ maxSize: 50,
125
+ ttlMs: 60000 // 1분
126
+ });
127
+ // 메모리 검색 캐시
128
+ export const memoryCache = new QueryCache({
129
+ maxSize: 100,
130
+ ttlMs: 30000 // 30초
131
+ });
132
+ // 프로젝트 목록 캐시
133
+ export const projectCache = new QueryCache({
134
+ maxSize: 20,
135
+ ttlMs: 120000 // 2분
136
+ });
137
+ // ===== 캐시 키 생성 헬퍼 =====
138
+ export function makeContextKey(project) {
139
+ return `context:${project}`;
140
+ }
141
+ export function makeMemoryKey(query, project, type) {
142
+ return `memory:${project || '*'}:${type || '*'}:${query}`;
143
+ }
144
+ export function makeProjectKey() {
145
+ return 'projects:list';
146
+ }
147
+ // ===== 캐시 무효화 트리거 =====
148
+ export function invalidateContext(project) {
149
+ contextCache.invalidate(makeContextKey(project));
150
+ }
151
+ export function invalidateMemory(project) {
152
+ if (project) {
153
+ memoryCache.invalidatePattern(`memory:${project}:`);
154
+ }
155
+ else {
156
+ memoryCache.clear();
157
+ }
158
+ }
159
+ export function invalidateProjects() {
160
+ projectCache.invalidate(makeProjectKey());
161
+ }
@@ -0,0 +1,7 @@
1
+ export declare function initEmbedding(): Promise<void>;
2
+ export declare function generateEmbedding(text: string): Promise<number[] | null>;
3
+ export declare function cosineSimilarity(a: number[], b: number[]): number;
4
+ export declare function embeddingToBuffer(embedding: number[]): Buffer;
5
+ export declare function bufferToEmbedding(buffer: Buffer): number[];
6
+ export declare function isEmbeddingReady(): boolean;
7
+ export declare function getEmbeddingPipeline(): unknown;
@@ -0,0 +1,67 @@
1
+ // 임베딩 관련 유틸리티
2
+ import * as path from 'path';
3
+ // @ts-ignore - transformers.js
4
+ import { pipeline, env } from '@xenova/transformers';
5
+ // 모델 캐시 설정
6
+ env.cacheDir = path.join(process.env.HOME || '/tmp', '.cache', 'transformers');
7
+ env.allowLocalModels = true;
8
+ // 임베딩 파이프라인
9
+ let embeddingPipeline = null;
10
+ let embeddingReady = false;
11
+ export async function initEmbedding() {
12
+ if (embeddingPipeline)
13
+ return;
14
+ try {
15
+ console.error('Loading embedding model (first time may take a while)...');
16
+ embeddingPipeline = await pipeline('feature-extraction', 'Xenova/all-MiniLM-L6-v2');
17
+ embeddingReady = true;
18
+ console.error('Embedding model loaded successfully!');
19
+ }
20
+ catch (error) {
21
+ console.error('Failed to load embedding model:', error);
22
+ }
23
+ }
24
+ // 백그라운드에서 모델 로드 시작
25
+ initEmbedding();
26
+ export async function generateEmbedding(text) {
27
+ if (!embeddingPipeline) {
28
+ await initEmbedding();
29
+ }
30
+ if (!embeddingPipeline)
31
+ return null;
32
+ try {
33
+ const output = await embeddingPipeline(text, { pooling: 'mean', normalize: true });
34
+ return Array.from(output.data);
35
+ }
36
+ catch (error) {
37
+ console.error('Embedding generation error:', error);
38
+ return null;
39
+ }
40
+ }
41
+ export function cosineSimilarity(a, b) {
42
+ if (a.length !== b.length)
43
+ return 0;
44
+ let dotProduct = 0;
45
+ let normA = 0;
46
+ let normB = 0;
47
+ for (let i = 0; i < a.length; i++) {
48
+ dotProduct += a[i] * b[i];
49
+ normA += a[i] * a[i];
50
+ normB += b[i] * b[i];
51
+ }
52
+ return dotProduct / (Math.sqrt(normA) * Math.sqrt(normB));
53
+ }
54
+ export function embeddingToBuffer(embedding) {
55
+ const float32Array = new Float32Array(embedding);
56
+ return Buffer.from(float32Array.buffer);
57
+ }
58
+ export function bufferToEmbedding(buffer) {
59
+ const float32Array = new Float32Array(buffer.buffer, buffer.byteOffset, buffer.length / 4);
60
+ return Array.from(float32Array);
61
+ }
62
+ export function isEmbeddingReady() {
63
+ return embeddingReady;
64
+ }
65
+ export function getEmbeddingPipeline() {
66
+ return embeddingPipeline;
67
+ }
@@ -0,0 +1,4 @@
1
+ export declare function fileExists(filePath: string): Promise<boolean>;
2
+ export declare function readFileContent(filePath: string): Promise<string | null>;
3
+ export declare function writeFileContent(filePath: string, content: string): Promise<void>;
4
+ export declare function parseMarkdownTable(content: string, tableName: string): Record<string, string>;
@@ -0,0 +1,45 @@
1
+ // 공통 유틸리티 함수
2
+ import * as fs from 'fs/promises';
3
+ import * as path from 'path';
4
+ export async function fileExists(filePath) {
5
+ try {
6
+ await fs.access(filePath);
7
+ return true;
8
+ }
9
+ catch {
10
+ return false;
11
+ }
12
+ }
13
+ export async function readFileContent(filePath) {
14
+ try {
15
+ return await fs.readFile(filePath, 'utf-8');
16
+ }
17
+ catch {
18
+ return null;
19
+ }
20
+ }
21
+ export async function writeFileContent(filePath, content) {
22
+ await fs.mkdir(path.dirname(filePath), { recursive: true });
23
+ await fs.writeFile(filePath, content, 'utf-8');
24
+ }
25
+ export function parseMarkdownTable(content, tableName) {
26
+ const result = {};
27
+ const lines = content.split('\n');
28
+ let inTable = false;
29
+ for (const line of lines) {
30
+ if (line.includes(tableName)) {
31
+ inTable = true;
32
+ continue;
33
+ }
34
+ if (inTable && line.startsWith('|') && !line.includes('---')) {
35
+ const cells = line.split('|').map(c => c.trim()).filter(Boolean);
36
+ if (cells.length >= 2 && cells[0] !== '항목' && cells[0] !== '작업') {
37
+ result[cells[0]] = cells[1];
38
+ }
39
+ }
40
+ if (inTable && line.trim() === '') {
41
+ inTable = false;
42
+ }
43
+ }
44
+ return result;
45
+ }
@@ -0,0 +1,17 @@
1
+ export type LogLevel = 'debug' | 'info' | 'warn' | 'error';
2
+ declare class Logger {
3
+ private level;
4
+ private logFile;
5
+ constructor();
6
+ private shouldLog;
7
+ private maskSensitive;
8
+ private formatEntry;
9
+ private write;
10
+ debug(message: string, data?: Record<string, unknown>, tool?: string): void;
11
+ info(message: string, data?: Record<string, unknown>, tool?: string): void;
12
+ warn(message: string, data?: Record<string, unknown>, tool?: string): void;
13
+ error(message: string, data?: Record<string, unknown>, tool?: string): void;
14
+ withTool<T>(toolName: string, fn: () => Promise<T>, args?: Record<string, unknown>): Promise<T>;
15
+ }
16
+ export declare const logger: Logger;
17
+ export {};