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.
- package/LICENSE +21 -0
- package/README.md +307 -0
- package/dist/dashboard-v2.d.ts +11 -0
- package/dist/dashboard-v2.js +1321 -0
- package/dist/dashboard.d.ts +2 -0
- package/dist/dashboard.js +1196 -0
- package/dist/db/database.d.ts +8 -0
- package/dist/db/database.js +208 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +3426 -0
- package/dist/schemas.d.ts +381 -0
- package/dist/schemas.js +119 -0
- package/dist/tools/context.d.ts +6 -0
- package/dist/tools/context.js +227 -0
- package/dist/tools/embedding.d.ts +5 -0
- package/dist/tools/embedding.js +191 -0
- package/dist/tools/feedback.d.ts +5 -0
- package/dist/tools/feedback.js +200 -0
- package/dist/tools/filter.d.ts +5 -0
- package/dist/tools/filter.js +169 -0
- package/dist/tools/index.d.ts +12 -0
- package/dist/tools/index.js +38 -0
- package/dist/tools/learning.d.ts +8 -0
- package/dist/tools/learning.js +395 -0
- package/dist/tools/memory.d.ts +8 -0
- package/dist/tools/memory.js +356 -0
- package/dist/tools/project.d.ts +9 -0
- package/dist/tools/project.js +396 -0
- package/dist/tools/relation.d.ts +4 -0
- package/dist/tools/relation.js +148 -0
- package/dist/tools/session.d.ts +7 -0
- package/dist/tools/session.js +272 -0
- package/dist/tools/solution.d.ts +5 -0
- package/dist/tools/solution.js +182 -0
- package/dist/tools/task.d.ts +6 -0
- package/dist/tools/task.js +184 -0
- package/dist/tools-v2/auto-capture.d.ts +5 -0
- package/dist/tools-v2/auto-capture.js +252 -0
- package/dist/tools-v2/context.d.ts +4 -0
- package/dist/tools-v2/context.js +170 -0
- package/dist/tools-v2/embedding.d.ts +3 -0
- package/dist/tools-v2/embedding.js +115 -0
- package/dist/tools-v2/index.d.ts +13 -0
- package/dist/tools-v2/index.js +73 -0
- package/dist/tools-v2/learn.d.ts +4 -0
- package/dist/tools-v2/learn.js +233 -0
- package/dist/tools-v2/memory.d.ts +6 -0
- package/dist/tools-v2/memory.js +340 -0
- package/dist/tools-v2/projects.d.ts +3 -0
- package/dist/tools-v2/projects.js +218 -0
- package/dist/tools-v2/task.d.ts +3 -0
- package/dist/tools-v2/task.js +193 -0
- package/dist/tools-v2/verify.d.ts +3 -0
- package/dist/tools-v2/verify.js +164 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.js +7 -0
- package/dist/utils/auto-context.d.ts +58 -0
- package/dist/utils/auto-context.js +234 -0
- package/dist/utils/cache.d.ts +60 -0
- package/dist/utils/cache.js +161 -0
- package/dist/utils/embedding.d.ts +7 -0
- package/dist/utils/embedding.js +67 -0
- package/dist/utils/helpers.d.ts +4 -0
- package/dist/utils/helpers.js +45 -0
- package/dist/utils/logger.d.ts +17 -0
- package/dist/utils/logger.js +111 -0
- package/package.json +64 -0
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
// Content Filtering 학습/회피 도구 (3개)
|
|
2
|
+
import { db, contentFilterPatterns, loadContentFilterPatterns } from '../db/database.js';
|
|
3
|
+
// ===== 도구 정의 =====
|
|
4
|
+
export const filterTools = [
|
|
5
|
+
{
|
|
6
|
+
name: 'record_filter_pattern',
|
|
7
|
+
description: 'API content filtering에 걸린 패턴을 기록합니다. 비슷한 상황 회피에 사용됩니다.',
|
|
8
|
+
inputSchema: {
|
|
9
|
+
type: 'object',
|
|
10
|
+
properties: {
|
|
11
|
+
patternType: {
|
|
12
|
+
type: 'string',
|
|
13
|
+
enum: ['code_block', 'file_content', 'long_output', 'sensitive_keyword', 'binary_like', 'other'],
|
|
14
|
+
description: '패턴 유형'
|
|
15
|
+
},
|
|
16
|
+
patternDescription: { type: 'string', description: '어떤 상황에서 발생했는지 설명' },
|
|
17
|
+
fileExtension: { type: 'string', description: '관련 파일 확장자 (선택, 예: .kt, .tsx)' },
|
|
18
|
+
exampleContext: { type: 'string', description: '발생 컨텍스트 예시 (민감 정보 제외)' },
|
|
19
|
+
mitigationStrategy: { type: 'string', description: '회피 전략 (예: 청크 분할, 요약만 출력)' }
|
|
20
|
+
},
|
|
21
|
+
required: ['patternType', 'patternDescription']
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
name: 'get_filter_patterns',
|
|
26
|
+
description: '기록된 content filtering 패턴 목록을 조회합니다. 응답 생성 시 참고용.',
|
|
27
|
+
inputSchema: {
|
|
28
|
+
type: 'object',
|
|
29
|
+
properties: {
|
|
30
|
+
patternType: { type: 'string', description: '패턴 유형 필터 (선택)' },
|
|
31
|
+
fileExtension: { type: 'string', description: '파일 확장자 필터 (선택)' }
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
name: 'get_safe_output_guidelines',
|
|
37
|
+
description: '현재 학습된 패턴 기반으로 안전한 출력 가이드라인을 반환합니다.',
|
|
38
|
+
inputSchema: {
|
|
39
|
+
type: 'object',
|
|
40
|
+
properties: {
|
|
41
|
+
context: { type: 'string', description: '현재 작업 컨텍스트 (예: kotlin 파일 분석, 긴 코드 출력)' }
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
];
|
|
46
|
+
// ===== 핸들러 =====
|
|
47
|
+
export function recordFilterPattern(patternType, patternDescription, fileExtension, exampleContext, mitigationStrategy) {
|
|
48
|
+
try {
|
|
49
|
+
// 기존 패턴 확인
|
|
50
|
+
const existingStmt = db.prepare(`
|
|
51
|
+
SELECT id, occurrence_count FROM content_filter_patterns
|
|
52
|
+
WHERE pattern_type = ? AND pattern_description = ?
|
|
53
|
+
`);
|
|
54
|
+
const existing = existingStmt.get(patternType, patternDescription);
|
|
55
|
+
if (existing) {
|
|
56
|
+
const updateStmt = db.prepare(`
|
|
57
|
+
UPDATE content_filter_patterns
|
|
58
|
+
SET occurrence_count = ?, last_occurred = CURRENT_TIMESTAMP
|
|
59
|
+
WHERE id = ?
|
|
60
|
+
`);
|
|
61
|
+
updateStmt.run(existing.occurrence_count + 1, existing.id);
|
|
62
|
+
// 캐시 갱신
|
|
63
|
+
loadContentFilterPatterns();
|
|
64
|
+
return {
|
|
65
|
+
content: [{
|
|
66
|
+
type: 'text',
|
|
67
|
+
text: JSON.stringify({
|
|
68
|
+
success: true,
|
|
69
|
+
updated: true,
|
|
70
|
+
id: existing.id,
|
|
71
|
+
occurrenceCount: existing.occurrence_count + 1
|
|
72
|
+
})
|
|
73
|
+
}]
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
else {
|
|
77
|
+
const insertStmt = db.prepare(`
|
|
78
|
+
INSERT INTO content_filter_patterns (pattern_type, pattern_description, file_extension, example_context, mitigation_strategy)
|
|
79
|
+
VALUES (?, ?, ?, ?, ?)
|
|
80
|
+
`);
|
|
81
|
+
const result = insertStmt.run(patternType, patternDescription, fileExtension || null, exampleContext || null, mitigationStrategy || null);
|
|
82
|
+
// 캐시 갱신
|
|
83
|
+
loadContentFilterPatterns();
|
|
84
|
+
return {
|
|
85
|
+
content: [{
|
|
86
|
+
type: 'text',
|
|
87
|
+
text: JSON.stringify({
|
|
88
|
+
success: true,
|
|
89
|
+
created: true,
|
|
90
|
+
id: result.lastInsertRowid
|
|
91
|
+
})
|
|
92
|
+
}]
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
catch (error) {
|
|
97
|
+
return {
|
|
98
|
+
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
99
|
+
isError: true
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
export function getFilterPatterns(patternType, fileExtension) {
|
|
104
|
+
try {
|
|
105
|
+
let filtered = contentFilterPatterns;
|
|
106
|
+
if (patternType) {
|
|
107
|
+
filtered = filtered.filter(p => p.patternType === patternType);
|
|
108
|
+
}
|
|
109
|
+
if (fileExtension) {
|
|
110
|
+
filtered = filtered.filter(p => p.fileExtension === fileExtension);
|
|
111
|
+
}
|
|
112
|
+
return {
|
|
113
|
+
content: [{
|
|
114
|
+
type: 'text',
|
|
115
|
+
text: JSON.stringify({
|
|
116
|
+
found: filtered.length,
|
|
117
|
+
patterns: filtered
|
|
118
|
+
}, null, 2)
|
|
119
|
+
}]
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
catch (error) {
|
|
123
|
+
return {
|
|
124
|
+
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
125
|
+
isError: true
|
|
126
|
+
};
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
export function getSafeOutputGuidelines(context) {
|
|
130
|
+
try {
|
|
131
|
+
const guidelines = [
|
|
132
|
+
'1. 긴 코드 출력 시 청크로 분할 (500줄 이하)',
|
|
133
|
+
'2. 파일 전체 읽기보다 특정 섹션만 Read (offset, limit 사용)',
|
|
134
|
+
'3. 불완전한 Edit 작업 후 남은 코드 정리',
|
|
135
|
+
'4. 민감할 수 있는 키워드 피하기'
|
|
136
|
+
];
|
|
137
|
+
// 컨텍스트에 맞는 추가 가이드라인
|
|
138
|
+
if (context) {
|
|
139
|
+
const contextLower = context.toLowerCase();
|
|
140
|
+
if (contextLower.includes('kotlin') || contextLower.includes('.kt')) {
|
|
141
|
+
guidelines.push('5. Kotlin 파일: 긴 클래스는 메서드별로 분석');
|
|
142
|
+
}
|
|
143
|
+
if (contextLower.includes('긴') || contextLower.includes('long')) {
|
|
144
|
+
guidelines.push('5. 긴 출력: 요약 먼저 제공, 상세 내용은 요청 시');
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
// 학습된 패턴 기반 추가 가이드라인
|
|
148
|
+
const patternGuidelines = contentFilterPatterns
|
|
149
|
+
.filter(p => p.mitigationStrategy)
|
|
150
|
+
.slice(0, 3)
|
|
151
|
+
.map((p, i) => `${guidelines.length + i + 1}. ${p.mitigationStrategy}`);
|
|
152
|
+
return {
|
|
153
|
+
content: [{
|
|
154
|
+
type: 'text',
|
|
155
|
+
text: JSON.stringify({
|
|
156
|
+
context: context || 'general',
|
|
157
|
+
guidelines: [...guidelines, ...patternGuidelines],
|
|
158
|
+
learnedPatternsCount: contentFilterPatterns.length
|
|
159
|
+
}, null, 2)
|
|
160
|
+
}]
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
catch (error) {
|
|
164
|
+
return {
|
|
165
|
+
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
166
|
+
isError: true
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
export * from './project.js';
|
|
2
|
+
export * from './session.js';
|
|
3
|
+
export * from './memory.js';
|
|
4
|
+
export * from './embedding.js';
|
|
5
|
+
export * from './relation.js';
|
|
6
|
+
export * from './feedback.js';
|
|
7
|
+
export * from './filter.js';
|
|
8
|
+
export * from './learning.js';
|
|
9
|
+
export * from './context.js';
|
|
10
|
+
export * from './task.js';
|
|
11
|
+
export * from './solution.js';
|
|
12
|
+
export declare const allTools: import("../types.js").Tool[];
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// 모든 도구를 내보내는 인덱스 파일
|
|
2
|
+
export * from './project.js';
|
|
3
|
+
export * from './session.js';
|
|
4
|
+
export * from './memory.js';
|
|
5
|
+
export * from './embedding.js';
|
|
6
|
+
export * from './relation.js';
|
|
7
|
+
export * from './feedback.js';
|
|
8
|
+
export * from './filter.js';
|
|
9
|
+
export * from './learning.js';
|
|
10
|
+
export * from './context.js';
|
|
11
|
+
export * from './task.js';
|
|
12
|
+
export * from './solution.js';
|
|
13
|
+
// 도구 정의 배열들
|
|
14
|
+
import { projectTools } from './project.js';
|
|
15
|
+
import { sessionTools } from './session.js';
|
|
16
|
+
import { memoryTools } from './memory.js';
|
|
17
|
+
import { embeddingTools } from './embedding.js';
|
|
18
|
+
import { relationTools } from './relation.js';
|
|
19
|
+
import { feedbackTools } from './feedback.js';
|
|
20
|
+
import { filterTools } from './filter.js';
|
|
21
|
+
import { learningTools } from './learning.js';
|
|
22
|
+
import { contextTools } from './context.js';
|
|
23
|
+
import { taskTools } from './task.js';
|
|
24
|
+
import { solutionTools } from './solution.js';
|
|
25
|
+
// 모든 도구 통합
|
|
26
|
+
export const allTools = [
|
|
27
|
+
...projectTools,
|
|
28
|
+
...sessionTools,
|
|
29
|
+
...memoryTools,
|
|
30
|
+
...embeddingTools,
|
|
31
|
+
...relationTools,
|
|
32
|
+
...feedbackTools,
|
|
33
|
+
...filterTools,
|
|
34
|
+
...learningTools,
|
|
35
|
+
...contextTools,
|
|
36
|
+
...taskTools,
|
|
37
|
+
...solutionTools
|
|
38
|
+
];
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Tool, CallToolResult, AutoLearnDecisionArgs, AutoLearnFixArgs, AutoLearnPatternArgs, AutoLearnDependencyArgs } from '../types.js';
|
|
2
|
+
export declare const learningTools: Tool[];
|
|
3
|
+
export declare function autoLearnDecision(args: AutoLearnDecisionArgs): Promise<CallToolResult>;
|
|
4
|
+
export declare function autoLearnFix(args: AutoLearnFixArgs): Promise<CallToolResult>;
|
|
5
|
+
export declare function autoLearnPattern(args: AutoLearnPatternArgs): Promise<CallToolResult>;
|
|
6
|
+
export declare function autoLearnDependency(args: AutoLearnDependencyArgs): Promise<CallToolResult>;
|
|
7
|
+
export declare function getProjectKnowledge(project: string, knowledgeType?: string, limit?: number): CallToolResult;
|
|
8
|
+
export declare function getSimilarIssues(errorOrIssue: string, project?: string, limit?: number): Promise<CallToolResult>;
|
|
@@ -0,0 +1,395 @@
|
|
|
1
|
+
// 자동 학습 시스템 도구 (6개)
|
|
2
|
+
import { db } from '../db/database.js';
|
|
3
|
+
import { generateEmbedding, embeddingToBuffer, getEmbeddingPipeline } from '../utils/embedding.js';
|
|
4
|
+
import { semanticSearch } from './embedding.js';
|
|
5
|
+
// ===== 도구 정의 =====
|
|
6
|
+
export const learningTools = [
|
|
7
|
+
{
|
|
8
|
+
name: 'auto_learn_decision',
|
|
9
|
+
description: '아키텍처/기술 결정 사항을 자동 기록합니다. 왜 이 선택을 했는지 기록하여 나중에 참조할 수 있습니다.',
|
|
10
|
+
inputSchema: {
|
|
11
|
+
type: 'object',
|
|
12
|
+
properties: {
|
|
13
|
+
project: { type: 'string', description: '프로젝트명' },
|
|
14
|
+
decision: { type: 'string', description: '결정 내용 (예: Socket.IO 대신 WebSocket 사용)' },
|
|
15
|
+
reason: { type: 'string', description: '결정 이유' },
|
|
16
|
+
context: { type: 'string', description: '결정 배경/맥락' },
|
|
17
|
+
alternatives: { type: 'array', items: { type: 'string' }, description: '고려했던 대안들' },
|
|
18
|
+
files: { type: 'array', items: { type: 'string' }, description: '관련 파일들' }
|
|
19
|
+
},
|
|
20
|
+
required: ['project', 'decision', 'reason']
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
name: 'auto_learn_fix',
|
|
25
|
+
description: '에러/버그 해결 방법을 자동 기록합니다. 비슷한 에러 발생 시 참조할 수 있습니다.',
|
|
26
|
+
inputSchema: {
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
project: { type: 'string', description: '프로젝트명' },
|
|
30
|
+
error: { type: 'string', description: '에러 메시지 또는 증상' },
|
|
31
|
+
cause: { type: 'string', description: '원인 (선택)' },
|
|
32
|
+
solution: { type: 'string', description: '해결 방법' },
|
|
33
|
+
files: { type: 'array', items: { type: 'string' }, description: '수정한 파일들' },
|
|
34
|
+
preventionTip: { type: 'string', description: '재발 방지 팁 (선택)' }
|
|
35
|
+
},
|
|
36
|
+
required: ['project', 'error', 'solution']
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
name: 'auto_learn_pattern',
|
|
41
|
+
description: '프로젝트의 코드 패턴/컨벤션을 자동 기록합니다. 일관성 유지에 활용됩니다.',
|
|
42
|
+
inputSchema: {
|
|
43
|
+
type: 'object',
|
|
44
|
+
properties: {
|
|
45
|
+
project: { type: 'string', description: '프로젝트명' },
|
|
46
|
+
patternName: { type: 'string', description: '패턴 이름 (예: Repository 패턴, State hoisting)' },
|
|
47
|
+
description: { type: 'string', description: '패턴 설명' },
|
|
48
|
+
example: { type: 'string', description: '예시 코드나 파일 경로' },
|
|
49
|
+
appliesTo: { type: 'string', description: '적용 대상 (예: 모든 Repository, Compose UI)' }
|
|
50
|
+
},
|
|
51
|
+
required: ['project', 'patternName', 'description']
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
name: 'auto_learn_dependency',
|
|
56
|
+
description: '의존성 변경 사항을 자동 기록합니다. 버전 충돌이나 업그레이드 시 참조합니다.',
|
|
57
|
+
inputSchema: {
|
|
58
|
+
type: 'object',
|
|
59
|
+
properties: {
|
|
60
|
+
project: { type: 'string', description: '프로젝트명' },
|
|
61
|
+
dependency: { type: 'string', description: '의존성 이름' },
|
|
62
|
+
action: { type: 'string', enum: ['add', 'remove', 'upgrade', 'downgrade'], description: '작업 유형' },
|
|
63
|
+
fromVersion: { type: 'string', description: '이전 버전 (선택)' },
|
|
64
|
+
toVersion: { type: 'string', description: '새 버전 (선택)' },
|
|
65
|
+
reason: { type: 'string', description: '변경 이유' },
|
|
66
|
+
breakingChanges: { type: 'string', description: 'Breaking changes 내용 (선택)' }
|
|
67
|
+
},
|
|
68
|
+
required: ['project', 'dependency', 'action', 'reason']
|
|
69
|
+
}
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
name: 'get_project_knowledge',
|
|
73
|
+
description: '프로젝트에서 학습된 모든 지식을 조회합니다. 결정, 해결, 패턴, 의존성 변경 등.',
|
|
74
|
+
inputSchema: {
|
|
75
|
+
type: 'object',
|
|
76
|
+
properties: {
|
|
77
|
+
project: { type: 'string', description: '프로젝트명' },
|
|
78
|
+
knowledgeType: {
|
|
79
|
+
type: 'string',
|
|
80
|
+
enum: ['all', 'decision', 'fix', 'pattern', 'dependency'],
|
|
81
|
+
description: '지식 유형 필터 (기본: all)'
|
|
82
|
+
},
|
|
83
|
+
limit: { type: 'number', description: '최대 결과 수 (기본: 20)' }
|
|
84
|
+
},
|
|
85
|
+
required: ['project']
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
{
|
|
89
|
+
name: 'get_similar_issues',
|
|
90
|
+
description: '비슷한 에러/이슈의 해결 방법을 검색합니다. 시맨틱 검색으로 유사한 문제를 찾습니다.',
|
|
91
|
+
inputSchema: {
|
|
92
|
+
type: 'object',
|
|
93
|
+
properties: {
|
|
94
|
+
errorOrIssue: { type: 'string', description: '에러 메시지 또는 이슈 설명' },
|
|
95
|
+
project: { type: 'string', description: '특정 프로젝트에서만 검색 (선택)' },
|
|
96
|
+
limit: { type: 'number', description: '최대 결과 수 (기본: 5)' }
|
|
97
|
+
},
|
|
98
|
+
required: ['errorOrIssue']
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
];
|
|
102
|
+
// ===== 핸들러 =====
|
|
103
|
+
export async function autoLearnDecision(args) {
|
|
104
|
+
try {
|
|
105
|
+
const content = `[DECISION] ${args.decision}\n이유: ${args.reason}${args.context ? `\n맥락: ${args.context}` : ''}${args.alternatives?.length ? `\n대안들: ${args.alternatives.join(', ')}` : ''}`;
|
|
106
|
+
const stmt = db.prepare(`
|
|
107
|
+
INSERT INTO memories (content, memory_type, tags, project, importance, metadata)
|
|
108
|
+
VALUES (?, 'decision', ?, ?, 7, ?)
|
|
109
|
+
`);
|
|
110
|
+
const tags = JSON.stringify(['auto-learn', 'architecture', 'decision']);
|
|
111
|
+
const metadata = JSON.stringify({
|
|
112
|
+
type: 'decision',
|
|
113
|
+
alternatives: args.alternatives,
|
|
114
|
+
files: args.files
|
|
115
|
+
});
|
|
116
|
+
const result = stmt.run(content, tags, args.project, metadata);
|
|
117
|
+
const memoryId = result.lastInsertRowid;
|
|
118
|
+
// 임베딩 생성
|
|
119
|
+
generateEmbedding(content).then(embedding => {
|
|
120
|
+
if (embedding) {
|
|
121
|
+
const embStmt = db.prepare('INSERT OR REPLACE INTO embeddings (memory_id, embedding) VALUES (?, ?)');
|
|
122
|
+
embStmt.run(memoryId, embeddingToBuffer(embedding));
|
|
123
|
+
}
|
|
124
|
+
});
|
|
125
|
+
return {
|
|
126
|
+
content: [{
|
|
127
|
+
type: 'text',
|
|
128
|
+
text: JSON.stringify({ success: true, id: memoryId, type: 'decision' })
|
|
129
|
+
}]
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
catch (error) {
|
|
133
|
+
return {
|
|
134
|
+
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
135
|
+
isError: true
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export async function autoLearnFix(args) {
|
|
140
|
+
try {
|
|
141
|
+
const content = `[FIX] ${args.error}\n해결: ${args.solution}${args.cause ? `\n원인: ${args.cause}` : ''}${args.preventionTip ? `\n예방: ${args.preventionTip}` : ''}`;
|
|
142
|
+
const stmt = db.prepare(`
|
|
143
|
+
INSERT INTO memories (content, memory_type, tags, project, importance, metadata)
|
|
144
|
+
VALUES (?, 'error', ?, ?, 8, ?)
|
|
145
|
+
`);
|
|
146
|
+
const tags = JSON.stringify(['auto-learn', 'fix', 'error-solution']);
|
|
147
|
+
const metadata = JSON.stringify({
|
|
148
|
+
type: 'fix',
|
|
149
|
+
error: args.error,
|
|
150
|
+
solution: args.solution,
|
|
151
|
+
cause: args.cause,
|
|
152
|
+
files: args.files,
|
|
153
|
+
preventionTip: args.preventionTip
|
|
154
|
+
});
|
|
155
|
+
const result = stmt.run(content, tags, args.project, metadata);
|
|
156
|
+
const memoryId = result.lastInsertRowid;
|
|
157
|
+
// 임베딩 생성
|
|
158
|
+
generateEmbedding(content).then(embedding => {
|
|
159
|
+
if (embedding) {
|
|
160
|
+
const embStmt = db.prepare('INSERT OR REPLACE INTO embeddings (memory_id, embedding) VALUES (?, ?)');
|
|
161
|
+
embStmt.run(memoryId, embeddingToBuffer(embedding));
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
return {
|
|
165
|
+
content: [{
|
|
166
|
+
type: 'text',
|
|
167
|
+
text: JSON.stringify({ success: true, id: memoryId, type: 'fix' })
|
|
168
|
+
}]
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
catch (error) {
|
|
172
|
+
return {
|
|
173
|
+
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
174
|
+
isError: true
|
|
175
|
+
};
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
export async function autoLearnPattern(args) {
|
|
179
|
+
try {
|
|
180
|
+
const content = `[PATTERN] ${args.patternName}\n설명: ${args.description}${args.example ? `\n예시: ${args.example}` : ''}${args.appliesTo ? `\n적용대상: ${args.appliesTo}` : ''}`;
|
|
181
|
+
const stmt = db.prepare(`
|
|
182
|
+
INSERT INTO memories (content, memory_type, tags, project, importance, metadata)
|
|
183
|
+
VALUES (?, 'pattern', ?, ?, 6, ?)
|
|
184
|
+
`);
|
|
185
|
+
const tags = JSON.stringify(['auto-learn', 'code-pattern', 'convention']);
|
|
186
|
+
const metadata = JSON.stringify({
|
|
187
|
+
type: 'pattern',
|
|
188
|
+
patternName: args.patternName,
|
|
189
|
+
appliesTo: args.appliesTo
|
|
190
|
+
});
|
|
191
|
+
const result = stmt.run(content, tags, args.project, metadata);
|
|
192
|
+
const memoryId = result.lastInsertRowid;
|
|
193
|
+
// 임베딩 생성
|
|
194
|
+
generateEmbedding(content).then(embedding => {
|
|
195
|
+
if (embedding) {
|
|
196
|
+
const embStmt = db.prepare('INSERT OR REPLACE INTO embeddings (memory_id, embedding) VALUES (?, ?)');
|
|
197
|
+
embStmt.run(memoryId, embeddingToBuffer(embedding));
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
return {
|
|
201
|
+
content: [{
|
|
202
|
+
type: 'text',
|
|
203
|
+
text: JSON.stringify({ success: true, id: memoryId, type: 'pattern' })
|
|
204
|
+
}]
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
catch (error) {
|
|
208
|
+
return {
|
|
209
|
+
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
210
|
+
isError: true
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
export async function autoLearnDependency(args) {
|
|
215
|
+
try {
|
|
216
|
+
const versionInfo = args.fromVersion && args.toVersion
|
|
217
|
+
? `${args.fromVersion} → ${args.toVersion}`
|
|
218
|
+
: args.toVersion || args.fromVersion || '';
|
|
219
|
+
const content = `[DEPENDENCY] ${args.action.toUpperCase()} ${args.dependency} ${versionInfo}\n이유: ${args.reason}${args.breakingChanges ? `\nBreaking Changes: ${args.breakingChanges}` : ''}`;
|
|
220
|
+
const stmt = db.prepare(`
|
|
221
|
+
INSERT INTO memories (content, memory_type, tags, project, importance, metadata)
|
|
222
|
+
VALUES (?, 'learning', ?, ?, 5, ?)
|
|
223
|
+
`);
|
|
224
|
+
const tags = JSON.stringify(['auto-learn', 'dependency', args.action]);
|
|
225
|
+
const metadata = JSON.stringify({
|
|
226
|
+
type: 'dependency',
|
|
227
|
+
dependency: args.dependency,
|
|
228
|
+
action: args.action,
|
|
229
|
+
fromVersion: args.fromVersion,
|
|
230
|
+
toVersion: args.toVersion,
|
|
231
|
+
breakingChanges: args.breakingChanges
|
|
232
|
+
});
|
|
233
|
+
const result = stmt.run(content, tags, args.project, metadata);
|
|
234
|
+
const memoryId = result.lastInsertRowid;
|
|
235
|
+
// 임베딩 생성
|
|
236
|
+
generateEmbedding(content).then(embedding => {
|
|
237
|
+
if (embedding) {
|
|
238
|
+
const embStmt = db.prepare('INSERT OR REPLACE INTO embeddings (memory_id, embedding) VALUES (?, ?)');
|
|
239
|
+
embStmt.run(memoryId, embeddingToBuffer(embedding));
|
|
240
|
+
}
|
|
241
|
+
});
|
|
242
|
+
return {
|
|
243
|
+
content: [{
|
|
244
|
+
type: 'text',
|
|
245
|
+
text: JSON.stringify({ success: true, id: memoryId, type: 'dependency' })
|
|
246
|
+
}]
|
|
247
|
+
};
|
|
248
|
+
}
|
|
249
|
+
catch (error) {
|
|
250
|
+
return {
|
|
251
|
+
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
252
|
+
isError: true
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
export function getProjectKnowledge(project, knowledgeType = 'all', limit = 20) {
|
|
257
|
+
try {
|
|
258
|
+
let sql = `
|
|
259
|
+
SELECT id, content, memory_type, tags, importance, metadata, created_at
|
|
260
|
+
FROM memories
|
|
261
|
+
WHERE project = ?
|
|
262
|
+
AND tags LIKE '%"auto-learn"%'
|
|
263
|
+
`;
|
|
264
|
+
const params = [project];
|
|
265
|
+
if (knowledgeType !== 'all') {
|
|
266
|
+
const typeMap = {
|
|
267
|
+
decision: 'decision',
|
|
268
|
+
fix: 'error',
|
|
269
|
+
pattern: 'pattern',
|
|
270
|
+
dependency: 'learning'
|
|
271
|
+
};
|
|
272
|
+
sql += ` AND memory_type = ?`;
|
|
273
|
+
params.push(typeMap[knowledgeType] || knowledgeType);
|
|
274
|
+
}
|
|
275
|
+
sql += ` ORDER BY importance DESC, created_at DESC LIMIT ?`;
|
|
276
|
+
params.push(limit);
|
|
277
|
+
const stmt = db.prepare(sql);
|
|
278
|
+
const rows = stmt.all(...params);
|
|
279
|
+
const knowledge = rows.map(row => {
|
|
280
|
+
let meta = {};
|
|
281
|
+
try {
|
|
282
|
+
meta = JSON.parse(row.metadata || '{}');
|
|
283
|
+
}
|
|
284
|
+
catch { }
|
|
285
|
+
return {
|
|
286
|
+
id: row.id,
|
|
287
|
+
type: meta.type || row.memory_type,
|
|
288
|
+
content: row.content,
|
|
289
|
+
importance: row.importance,
|
|
290
|
+
createdAt: row.created_at,
|
|
291
|
+
metadata: meta
|
|
292
|
+
};
|
|
293
|
+
});
|
|
294
|
+
// 유형별 통계
|
|
295
|
+
const stats = {
|
|
296
|
+
decision: knowledge.filter(k => k.type === 'decision').length,
|
|
297
|
+
fix: knowledge.filter(k => k.type === 'fix').length,
|
|
298
|
+
pattern: knowledge.filter(k => k.type === 'pattern').length,
|
|
299
|
+
dependency: knowledge.filter(k => k.type === 'dependency').length
|
|
300
|
+
};
|
|
301
|
+
return {
|
|
302
|
+
content: [{
|
|
303
|
+
type: 'text',
|
|
304
|
+
text: JSON.stringify({
|
|
305
|
+
project,
|
|
306
|
+
totalKnowledge: knowledge.length,
|
|
307
|
+
stats,
|
|
308
|
+
knowledge: knowledge.slice(0, limit)
|
|
309
|
+
}, null, 2)
|
|
310
|
+
}]
|
|
311
|
+
};
|
|
312
|
+
}
|
|
313
|
+
catch (error) {
|
|
314
|
+
return {
|
|
315
|
+
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
316
|
+
isError: true
|
|
317
|
+
};
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
export async function getSimilarIssues(errorOrIssue, project, limit = 5) {
|
|
321
|
+
try {
|
|
322
|
+
// 먼저 시맨틱 검색 시도
|
|
323
|
+
if (getEmbeddingPipeline()) {
|
|
324
|
+
const result = await semanticSearch(errorOrIssue, limit, 0.3, 'error', project);
|
|
325
|
+
const resultText = JSON.parse(result.content[0].text);
|
|
326
|
+
if (resultText.found > 0) {
|
|
327
|
+
return {
|
|
328
|
+
content: [{
|
|
329
|
+
type: 'text',
|
|
330
|
+
text: JSON.stringify({
|
|
331
|
+
searchType: 'semantic',
|
|
332
|
+
query: errorOrIssue.substring(0, 100),
|
|
333
|
+
found: resultText.found,
|
|
334
|
+
solutions: resultText.results.map((r) => ({
|
|
335
|
+
id: r.id,
|
|
336
|
+
similarity: r.similarity,
|
|
337
|
+
content: r.content,
|
|
338
|
+
project: r.project
|
|
339
|
+
}))
|
|
340
|
+
}, null, 2)
|
|
341
|
+
}]
|
|
342
|
+
};
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
// 시맨틱 검색 결과 없으면 FTS 검색
|
|
346
|
+
const keywords = errorOrIssue.split(/\s+/).filter(w => w.length > 3).slice(0, 5);
|
|
347
|
+
const ftsQuery = keywords.join(' OR ');
|
|
348
|
+
let query = `
|
|
349
|
+
SELECT m.id, m.content, m.project, m.metadata
|
|
350
|
+
FROM memories_fts fts
|
|
351
|
+
JOIN memories m ON m.id = fts.rowid
|
|
352
|
+
WHERE memories_fts MATCH ?
|
|
353
|
+
AND m.memory_type = 'error'
|
|
354
|
+
`;
|
|
355
|
+
const params = [ftsQuery];
|
|
356
|
+
if (project) {
|
|
357
|
+
query += ` AND m.project = ?`;
|
|
358
|
+
params.push(project);
|
|
359
|
+
}
|
|
360
|
+
query += ` LIMIT ?`;
|
|
361
|
+
params.push(limit);
|
|
362
|
+
const stmt = db.prepare(query);
|
|
363
|
+
const rows = stmt.all(...params);
|
|
364
|
+
const solutions = rows.map(row => {
|
|
365
|
+
let metadata = {};
|
|
366
|
+
try {
|
|
367
|
+
metadata = JSON.parse(row.metadata || '{}');
|
|
368
|
+
}
|
|
369
|
+
catch { }
|
|
370
|
+
return {
|
|
371
|
+
id: row.id,
|
|
372
|
+
content: row.content,
|
|
373
|
+
project: row.project,
|
|
374
|
+
solution: metadata.solution
|
|
375
|
+
};
|
|
376
|
+
});
|
|
377
|
+
return {
|
|
378
|
+
content: [{
|
|
379
|
+
type: 'text',
|
|
380
|
+
text: JSON.stringify({
|
|
381
|
+
searchType: 'fts',
|
|
382
|
+
query: errorOrIssue.substring(0, 100),
|
|
383
|
+
found: solutions.length,
|
|
384
|
+
solutions
|
|
385
|
+
}, null, 2)
|
|
386
|
+
}]
|
|
387
|
+
};
|
|
388
|
+
}
|
|
389
|
+
catch (error) {
|
|
390
|
+
return {
|
|
391
|
+
content: [{ type: 'text', text: `Error: ${error}` }],
|
|
392
|
+
isError: true
|
|
393
|
+
};
|
|
394
|
+
}
|
|
395
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { Tool, CallToolResult } from '../types.js';
|
|
2
|
+
export declare const memoryTools: Tool[];
|
|
3
|
+
export declare function storeMemory(content: string, memoryType: string, tags?: string[], project?: string, importance?: number, metadata?: Record<string, unknown>): Promise<CallToolResult>;
|
|
4
|
+
export declare function recallMemory(query: string, memoryType?: string, project?: string, limit?: number, minImportance?: number, maxContentLength?: number): CallToolResult;
|
|
5
|
+
export declare function recallByTimeframe(timeframe: string, memoryType?: string, project?: string, limit?: number): CallToolResult;
|
|
6
|
+
export declare function searchByTag(tags: string[], matchAll?: boolean, limit?: number): CallToolResult;
|
|
7
|
+
export declare function getMemoryStats(): CallToolResult;
|
|
8
|
+
export declare function deleteMemory(memoryId: number): CallToolResult;
|