a2a-memory 0.1.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 +119 -0
- package/dist/cli/commands/config.d.ts +6 -0
- package/dist/cli/commands/config.d.ts.map +1 -0
- package/dist/cli/commands/config.js +56 -0
- package/dist/cli/commands/config.js.map +1 -0
- package/dist/cli/commands/extract.d.ts +8 -0
- package/dist/cli/commands/extract.d.ts.map +1 -0
- package/dist/cli/commands/extract.js +103 -0
- package/dist/cli/commands/extract.js.map +1 -0
- package/dist/cli/commands/list.d.ts +6 -0
- package/dist/cli/commands/list.d.ts.map +1 -0
- package/dist/cli/commands/list.js +44 -0
- package/dist/cli/commands/list.js.map +1 -0
- package/dist/cli/commands/search.d.ts +6 -0
- package/dist/cli/commands/search.d.ts.map +1 -0
- package/dist/cli/commands/search.js +45 -0
- package/dist/cli/commands/search.js.map +1 -0
- package/dist/cli/commands/setup.d.ts +8 -0
- package/dist/cli/commands/setup.d.ts.map +1 -0
- package/dist/cli/commands/setup.js +132 -0
- package/dist/cli/commands/setup.js.map +1 -0
- package/dist/cli/commands/status.d.ts +6 -0
- package/dist/cli/commands/status.d.ts.map +1 -0
- package/dist/cli/commands/status.js +70 -0
- package/dist/cli/commands/status.js.map +1 -0
- package/dist/cli/commands/sync.d.ts +6 -0
- package/dist/cli/commands/sync.d.ts.map +1 -0
- package/dist/cli/commands/sync.js +142 -0
- package/dist/cli/commands/sync.js.map +1 -0
- package/dist/cli/index.d.ts +14 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +34 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/config/manager.d.ts +18 -0
- package/dist/config/manager.d.ts.map +1 -0
- package/dist/config/manager.js +95 -0
- package/dist/config/manager.js.map +1 -0
- package/dist/db/database.d.ts +55 -0
- package/dist/db/database.d.ts.map +1 -0
- package/dist/db/database.js +414 -0
- package/dist/db/database.js.map +1 -0
- package/dist/extraction/extractor.d.ts +12 -0
- package/dist/extraction/extractor.d.ts.map +1 -0
- package/dist/extraction/extractor.js +265 -0
- package/dist/extraction/extractor.js.map +1 -0
- package/dist/extraction/filter.d.ts +19 -0
- package/dist/extraction/filter.d.ts.map +1 -0
- package/dist/extraction/filter.js +60 -0
- package/dist/extraction/filter.js.map +1 -0
- package/dist/extraction/scorer.d.ts +18 -0
- package/dist/extraction/scorer.d.ts.map +1 -0
- package/dist/extraction/scorer.js +107 -0
- package/dist/extraction/scorer.js.map +1 -0
- package/dist/hooks/post-tool-use.d.ts +14 -0
- package/dist/hooks/post-tool-use.d.ts.map +1 -0
- package/dist/hooks/post-tool-use.js +144 -0
- package/dist/hooks/post-tool-use.js.map +1 -0
- package/dist/hooks/session-end.d.ts +13 -0
- package/dist/hooks/session-end.d.ts.map +1 -0
- package/dist/hooks/session-end.js +85 -0
- package/dist/hooks/session-end.js.map +1 -0
- package/dist/hooks/session-start.d.ts +15 -0
- package/dist/hooks/session-start.d.ts.map +1 -0
- package/dist/hooks/session-start.js +77 -0
- package/dist/hooks/session-start.js.map +1 -0
- package/dist/index.d.ts +17 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +18 -0
- package/dist/index.js.map +1 -0
- package/dist/session/parser.d.ts +30 -0
- package/dist/session/parser.d.ts.map +1 -0
- package/dist/session/parser.js +129 -0
- package/dist/session/parser.js.map +1 -0
- package/dist/sync/client.d.ts +72 -0
- package/dist/sync/client.d.ts.map +1 -0
- package/dist/sync/client.js +138 -0
- package/dist/sync/client.js.map +1 -0
- package/dist/sync/index.d.ts +8 -0
- package/dist/sync/index.d.ts.map +1 -0
- package/dist/sync/index.js +6 -0
- package/dist/sync/index.js.map +1 -0
- package/dist/sync/synchronizer.d.ts +39 -0
- package/dist/sync/synchronizer.d.ts.map +1 -0
- package/dist/sync/synchronizer.js +169 -0
- package/dist/sync/synchronizer.js.map +1 -0
- package/dist/types/index.d.ts +140 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +48 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +53 -0
|
@@ -0,0 +1,265 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Memory Extractor
|
|
3
|
+
*
|
|
4
|
+
* 세션 메시지에서 의미 있는 메모리를 추출합니다.
|
|
5
|
+
* 카테고리: error_solution, code_pattern, decision, context, preference
|
|
6
|
+
*/
|
|
7
|
+
import { filterSensitiveInfo } from './filter.js';
|
|
8
|
+
import { scoreSignificance } from './scorer.js';
|
|
9
|
+
const DECISION_KEYWORDS = [
|
|
10
|
+
'선택', '결정', '방식', '접근', '전략',
|
|
11
|
+
'approach', 'decision', 'chose', 'decided', 'strategy',
|
|
12
|
+
'instead of', 'rather than', 'opted for',
|
|
13
|
+
];
|
|
14
|
+
const PREFERENCE_KEYWORDS = [
|
|
15
|
+
'선호', '항상', '무조건', '습관', '규칙',
|
|
16
|
+
'prefer', 'always', 'never', 'convention', 'rule',
|
|
17
|
+
'style', 'pattern',
|
|
18
|
+
];
|
|
19
|
+
const ERROR_INDICATORS = [
|
|
20
|
+
'error', 'Error', 'ERROR',
|
|
21
|
+
'exception', 'Exception',
|
|
22
|
+
'failed', 'Failed', 'FAILED',
|
|
23
|
+
'traceback', 'Traceback',
|
|
24
|
+
'stack trace',
|
|
25
|
+
'에러', '오류', '실패',
|
|
26
|
+
];
|
|
27
|
+
/**
|
|
28
|
+
* 세션 메시지에서 메모리 추출
|
|
29
|
+
*/
|
|
30
|
+
export function extractMemories(messages, projectPath) {
|
|
31
|
+
const startTime = Date.now();
|
|
32
|
+
const memories = [];
|
|
33
|
+
let filteredSensitive = 0;
|
|
34
|
+
// 1. error_solution 추출
|
|
35
|
+
const errorSolutions = extractErrorSolutions(messages, projectPath);
|
|
36
|
+
memories.push(...errorSolutions);
|
|
37
|
+
// 2. code_pattern 추출
|
|
38
|
+
const codePatterns = extractCodePatterns(messages, projectPath);
|
|
39
|
+
memories.push(...codePatterns);
|
|
40
|
+
// 3. decision 추출
|
|
41
|
+
const decisions = extractDecisions(messages, projectPath);
|
|
42
|
+
memories.push(...decisions);
|
|
43
|
+
// 4. context 추출
|
|
44
|
+
const contexts = extractContexts(messages, projectPath);
|
|
45
|
+
memories.push(...contexts);
|
|
46
|
+
// 5. preference 추출
|
|
47
|
+
const preferences = extractPreferences(messages, projectPath);
|
|
48
|
+
memories.push(...preferences);
|
|
49
|
+
// 민감 정보 필터링
|
|
50
|
+
for (let i = 0; i < memories.length; i++) {
|
|
51
|
+
const result = filterSensitiveInfo(memories[i].content);
|
|
52
|
+
if (result.matchCount > 0) {
|
|
53
|
+
memories[i] = { ...memories[i], content: result.filtered };
|
|
54
|
+
filteredSensitive += result.matchCount;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
// 중복 제거 (동일 콘텐츠)
|
|
58
|
+
const unique = deduplicateMemories(memories);
|
|
59
|
+
const sessionInfo = {
|
|
60
|
+
sessionId: messages[0]?.uuid?.split('-')[0] ?? 'unknown',
|
|
61
|
+
projectPath,
|
|
62
|
+
filePath: '',
|
|
63
|
+
messageCount: messages.length,
|
|
64
|
+
startTime: messages[0]?.timestamp,
|
|
65
|
+
endTime: messages[messages.length - 1]?.timestamp,
|
|
66
|
+
sizeBytes: 0,
|
|
67
|
+
};
|
|
68
|
+
return {
|
|
69
|
+
memories: unique,
|
|
70
|
+
sessionInfo,
|
|
71
|
+
stats: {
|
|
72
|
+
totalMessages: messages.length,
|
|
73
|
+
extractedPatterns: unique.length,
|
|
74
|
+
filteredSensitive,
|
|
75
|
+
processingTimeMs: Date.now() - startTime,
|
|
76
|
+
},
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* 에러 → 해결 쌍 추출
|
|
81
|
+
*/
|
|
82
|
+
function extractErrorSolutions(messages, projectPath) {
|
|
83
|
+
const results = [];
|
|
84
|
+
for (let i = 0; i < messages.length; i++) {
|
|
85
|
+
const msg = messages[i];
|
|
86
|
+
const text = getFullText(msg);
|
|
87
|
+
const hasError = ERROR_INDICATORS.some((ind) => text.includes(ind));
|
|
88
|
+
if (!hasError)
|
|
89
|
+
continue;
|
|
90
|
+
// 다음 assistant 메시지에서 해결 과정 찾기
|
|
91
|
+
for (let j = i + 1; j < Math.min(i + 5, messages.length); j++) {
|
|
92
|
+
const next = messages[j];
|
|
93
|
+
if (next.type !== 'assistant')
|
|
94
|
+
continue;
|
|
95
|
+
const nextText = getFullText(next);
|
|
96
|
+
const hasToolUse = Array.isArray(next.message.content) &&
|
|
97
|
+
next.message.content.some((b) => b.type === 'tool_use');
|
|
98
|
+
if (hasToolUse || nextText.length > 50) {
|
|
99
|
+
const errorSnippet = text.slice(0, 300);
|
|
100
|
+
const solutionSnippet = nextText.slice(0, 500);
|
|
101
|
+
results.push({
|
|
102
|
+
content: `[Error] ${errorSnippet}\n\n[Solution] ${solutionSnippet}`,
|
|
103
|
+
category: 'error_solution',
|
|
104
|
+
tier: 'episodic',
|
|
105
|
+
tags: extractTags(text, 'error'),
|
|
106
|
+
projectPath,
|
|
107
|
+
});
|
|
108
|
+
break;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
return results;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* 반복 사용 도구 패턴 추출
|
|
116
|
+
*/
|
|
117
|
+
function extractCodePatterns(messages, projectPath) {
|
|
118
|
+
const toolCounts = new Map();
|
|
119
|
+
for (const msg of messages) {
|
|
120
|
+
if (!Array.isArray(msg.message.content))
|
|
121
|
+
continue;
|
|
122
|
+
for (const block of msg.message.content) {
|
|
123
|
+
if (block.type !== 'tool_use' || !block.name)
|
|
124
|
+
continue;
|
|
125
|
+
const entry = toolCounts.get(block.name) ?? { count: 0, samples: [] };
|
|
126
|
+
entry.count++;
|
|
127
|
+
if (entry.samples.length < 3 && block.input) {
|
|
128
|
+
entry.samples.push(JSON.stringify(block.input).slice(0, 200));
|
|
129
|
+
}
|
|
130
|
+
toolCounts.set(block.name, entry);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
const results = [];
|
|
134
|
+
for (const [toolName, data] of toolCounts) {
|
|
135
|
+
if (data.count < 3)
|
|
136
|
+
continue;
|
|
137
|
+
results.push({
|
|
138
|
+
content: `Tool "${toolName}" used ${data.count} times. Samples:\n${data.samples.join('\n')}`,
|
|
139
|
+
category: 'code_pattern',
|
|
140
|
+
tier: 'procedural',
|
|
141
|
+
tags: [toolName, 'pattern', 'tool-usage'],
|
|
142
|
+
projectPath,
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
return results;
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* 의사 결정 메시지 추출
|
|
149
|
+
*/
|
|
150
|
+
function extractDecisions(messages, projectPath) {
|
|
151
|
+
const results = [];
|
|
152
|
+
for (const msg of messages) {
|
|
153
|
+
const text = getFullText(msg);
|
|
154
|
+
if (text.length < 30)
|
|
155
|
+
continue;
|
|
156
|
+
const hasKeyword = DECISION_KEYWORDS.some((kw) => text.toLowerCase().includes(kw.toLowerCase()));
|
|
157
|
+
if (!hasKeyword)
|
|
158
|
+
continue;
|
|
159
|
+
const significance = scoreSignificance(msg, {});
|
|
160
|
+
if (significance.totalScore < 0)
|
|
161
|
+
continue;
|
|
162
|
+
results.push({
|
|
163
|
+
content: text.slice(0, 800),
|
|
164
|
+
category: 'decision',
|
|
165
|
+
tier: 'semantic',
|
|
166
|
+
tags: extractTags(text, 'decision'),
|
|
167
|
+
projectPath,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
return results;
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* 프로젝트 컨텍스트 추출 (첫 human/system 메시지)
|
|
174
|
+
*/
|
|
175
|
+
function extractContexts(messages, projectPath) {
|
|
176
|
+
const results = [];
|
|
177
|
+
for (let i = 0; i < Math.min(5, messages.length); i++) {
|
|
178
|
+
const msg = messages[i];
|
|
179
|
+
if (msg.type !== 'human' && msg.type !== 'system')
|
|
180
|
+
continue;
|
|
181
|
+
const text = getFullText(msg);
|
|
182
|
+
if (text.length < 50)
|
|
183
|
+
continue;
|
|
184
|
+
results.push({
|
|
185
|
+
content: text.slice(0, 1000),
|
|
186
|
+
category: 'context',
|
|
187
|
+
tier: 'semantic',
|
|
188
|
+
tags: extractTags(text, 'context'),
|
|
189
|
+
projectPath,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
return results;
|
|
193
|
+
}
|
|
194
|
+
/**
|
|
195
|
+
* 사용자 선호 추출
|
|
196
|
+
*/
|
|
197
|
+
function extractPreferences(messages, projectPath) {
|
|
198
|
+
const results = [];
|
|
199
|
+
for (const msg of messages) {
|
|
200
|
+
if (msg.type !== 'human')
|
|
201
|
+
continue;
|
|
202
|
+
const text = getFullText(msg);
|
|
203
|
+
if (text.length < 20)
|
|
204
|
+
continue;
|
|
205
|
+
const hasKeyword = PREFERENCE_KEYWORDS.some((kw) => text.toLowerCase().includes(kw.toLowerCase()));
|
|
206
|
+
if (!hasKeyword)
|
|
207
|
+
continue;
|
|
208
|
+
results.push({
|
|
209
|
+
content: text.slice(0, 500),
|
|
210
|
+
category: 'preference',
|
|
211
|
+
tier: 'semantic',
|
|
212
|
+
tags: extractTags(text, 'preference'),
|
|
213
|
+
projectPath,
|
|
214
|
+
});
|
|
215
|
+
}
|
|
216
|
+
return results;
|
|
217
|
+
}
|
|
218
|
+
/**
|
|
219
|
+
* 메시지에서 전체 텍스트 추출
|
|
220
|
+
*/
|
|
221
|
+
function getFullText(message) {
|
|
222
|
+
if (typeof message.message.content === 'string') {
|
|
223
|
+
return message.message.content;
|
|
224
|
+
}
|
|
225
|
+
return message.message.content
|
|
226
|
+
.map((block) => {
|
|
227
|
+
if (block.type === 'text' && block.text)
|
|
228
|
+
return block.text;
|
|
229
|
+
if (block.type === 'tool_result' && block.content)
|
|
230
|
+
return block.content;
|
|
231
|
+
return '';
|
|
232
|
+
})
|
|
233
|
+
.filter(Boolean)
|
|
234
|
+
.join('\n');
|
|
235
|
+
}
|
|
236
|
+
/**
|
|
237
|
+
* 텍스트에서 자동 태그 추출
|
|
238
|
+
*/
|
|
239
|
+
function extractTags(text, category) {
|
|
240
|
+
const tags = [category];
|
|
241
|
+
// 파일 경로 추출
|
|
242
|
+
const fileMatch = text.match(/[\w/.-]+\.\w{1,10}/g);
|
|
243
|
+
if (fileMatch) {
|
|
244
|
+
const extensions = new Set(fileMatch
|
|
245
|
+
.map((f) => f.split('.').pop())
|
|
246
|
+
.filter((ext) => ext.length <= 5));
|
|
247
|
+
tags.push(...[...extensions].slice(0, 3));
|
|
248
|
+
}
|
|
249
|
+
return [...new Set(tags)];
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* 중복 메모리 제거 (콘텐츠 유사도 기반)
|
|
253
|
+
*/
|
|
254
|
+
function deduplicateMemories(memories) {
|
|
255
|
+
const seen = new Set();
|
|
256
|
+
return memories.filter((m) => {
|
|
257
|
+
// category + 앞 100자 기준 중복 판단
|
|
258
|
+
const key = `${m.category}:${m.content.slice(0, 100)}`;
|
|
259
|
+
if (seen.has(key))
|
|
260
|
+
return false;
|
|
261
|
+
seen.add(key);
|
|
262
|
+
return true;
|
|
263
|
+
});
|
|
264
|
+
}
|
|
265
|
+
//# sourceMappingURL=extractor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extractor.js","sourceRoot":"","sources":["../../src/extraction/extractor.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sensitive Information Filter
|
|
3
|
+
*
|
|
4
|
+
* 메모리 콘텐츠에서 민감 정보를 탐지하고 마스킹합니다.
|
|
5
|
+
*/
|
|
6
|
+
interface FilterResult {
|
|
7
|
+
filtered: string;
|
|
8
|
+
matchCount: number;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* 콘텐츠에서 민감 정보를 [REDACTED_<type>]로 치환
|
|
12
|
+
*/
|
|
13
|
+
export declare function filterSensitiveInfo(content: string): FilterResult;
|
|
14
|
+
/**
|
|
15
|
+
* 파일 경로가 민감한 패턴에 해당하는지 검사
|
|
16
|
+
*/
|
|
17
|
+
export declare function isSensitivePath(filePath: string, excludePatterns: string[]): boolean;
|
|
18
|
+
export {};
|
|
19
|
+
//# sourceMappingURL=filter.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.d.ts","sourceRoot":"","sources":["../../src/extraction/filter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,UAAU,YAAY;IACpB,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;CACpB;AAaD;;GAEG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,YAAY,CAgBjE;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,QAAQ,EAAE,MAAM,EAChB,eAAe,EAAE,MAAM,EAAE,GACxB,OAAO,CAmBT"}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Sensitive Information Filter
|
|
3
|
+
*
|
|
4
|
+
* 메모리 콘텐츠에서 민감 정보를 탐지하고 마스킹합니다.
|
|
5
|
+
*/
|
|
6
|
+
import { SENSITIVE_PATTERNS } from '../types/index.js';
|
|
7
|
+
const PATTERN_LABELS = [
|
|
8
|
+
'API_KEY',
|
|
9
|
+
'SECRET',
|
|
10
|
+
'OPENAI_KEY',
|
|
11
|
+
'STRIPE_KEY',
|
|
12
|
+
'GITHUB_TOKEN',
|
|
13
|
+
'BEARER_TOKEN',
|
|
14
|
+
'EMAIL',
|
|
15
|
+
'PRIVATE_KEY',
|
|
16
|
+
];
|
|
17
|
+
/**
|
|
18
|
+
* 콘텐츠에서 민감 정보를 [REDACTED_<type>]로 치환
|
|
19
|
+
*/
|
|
20
|
+
export function filterSensitiveInfo(content) {
|
|
21
|
+
let filtered = content;
|
|
22
|
+
let matchCount = 0;
|
|
23
|
+
for (let i = 0; i < SENSITIVE_PATTERNS.length; i++) {
|
|
24
|
+
const pattern = new RegExp(SENSITIVE_PATTERNS[i].source, SENSITIVE_PATTERNS[i].flags);
|
|
25
|
+
const label = PATTERN_LABELS[i] ?? 'SENSITIVE';
|
|
26
|
+
const matches = filtered.match(pattern);
|
|
27
|
+
if (matches) {
|
|
28
|
+
matchCount += matches.length;
|
|
29
|
+
filtered = filtered.replace(pattern, `[REDACTED_${label}]`);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return { filtered, matchCount };
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* 파일 경로가 민감한 패턴에 해당하는지 검사
|
|
36
|
+
*/
|
|
37
|
+
export function isSensitivePath(filePath, excludePatterns) {
|
|
38
|
+
const fileName = filePath.split('/').pop() ?? filePath;
|
|
39
|
+
for (const pattern of excludePatterns) {
|
|
40
|
+
if (pattern.startsWith('*')) {
|
|
41
|
+
// *.key, *.pem 등 확장자 매칭
|
|
42
|
+
const ext = pattern.slice(1); // .key, .pem
|
|
43
|
+
if (fileName.endsWith(ext))
|
|
44
|
+
return true;
|
|
45
|
+
}
|
|
46
|
+
else if (pattern.endsWith('*')) {
|
|
47
|
+
// sk-*, sk_live_* 등 접두사 매칭
|
|
48
|
+
const prefix = pattern.slice(0, -1);
|
|
49
|
+
if (fileName.startsWith(prefix))
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
// .env, credentials.json 등 정확 매칭
|
|
54
|
+
if (fileName === pattern)
|
|
55
|
+
return true;
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
//# sourceMappingURL=filter.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filter.js","sourceRoot":"","sources":["../../src/extraction/filter.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAOvD,MAAM,cAAc,GAAa;IAC/B,SAAS;IACT,QAAQ;IACR,YAAY;IACZ,YAAY;IACZ,cAAc;IACd,cAAc;IACd,OAAO;IACP,aAAa;CACd,CAAC;AAEF;;GAEG;AACH,MAAM,UAAU,mBAAmB,CAAC,OAAe;IACjD,IAAI,QAAQ,GAAG,OAAO,CAAC;IACvB,IAAI,UAAU,GAAG,CAAC,CAAC;IAEnB,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,kBAAkB,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACnD,MAAM,OAAO,GAAG,IAAI,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC;QACtF,MAAM,KAAK,GAAG,cAAc,CAAC,CAAC,CAAC,IAAI,WAAW,CAAC;QAE/C,MAAM,OAAO,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACxC,IAAI,OAAO,EAAE,CAAC;YACZ,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;YAC7B,QAAQ,GAAG,QAAQ,CAAC,OAAO,CAAC,OAAO,EAAE,aAAa,KAAK,GAAG,CAAC,CAAC;QAC9D,CAAC;IACH,CAAC;IAED,OAAO,EAAE,QAAQ,EAAE,UAAU,EAAE,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAC7B,QAAgB,EAChB,eAAyB;IAEzB,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,EAAE,IAAI,QAAQ,CAAC;IAEvD,KAAK,MAAM,OAAO,IAAI,eAAe,EAAE,CAAC;QACtC,IAAI,OAAO,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;YAC5B,wBAAwB;YACxB,MAAM,GAAG,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,aAAa;YAC3C,IAAI,QAAQ,CAAC,QAAQ,CAAC,GAAG,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC1C,CAAC;aAAM,IAAI,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YACjC,2BAA2B;YAC3B,MAAM,MAAM,GAAG,OAAO,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;YACpC,IAAI,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC;gBAAE,OAAO,IAAI,CAAC;QAC/C,CAAC;aAAM,CAAC;YACN,iCAAiC;YACjC,IAAI,QAAQ,KAAK,OAAO;gBAAE,OAAO,IAAI,CAAC;QACxC,CAAC;IACH,CAAC;IAED,OAAO,KAAK,CAAC;AACf,CAAC"}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Significance Scorer
|
|
3
|
+
*
|
|
4
|
+
* 메모리의 중요도를 시그널 기반으로 점수화합니다.
|
|
5
|
+
*/
|
|
6
|
+
import type { SessionMessage, SignificanceResult } from '../types/index.js';
|
|
7
|
+
interface ScoringContext {
|
|
8
|
+
toolName?: string;
|
|
9
|
+
hasError?: boolean;
|
|
10
|
+
isNewFile?: boolean;
|
|
11
|
+
isConfigChange?: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* 메시지의 중요도 점수 계산
|
|
15
|
+
*/
|
|
16
|
+
export declare function scoreSignificance(message: SessionMessage, context: ScoringContext, threshold?: number): SignificanceResult;
|
|
17
|
+
export {};
|
|
18
|
+
//# sourceMappingURL=scorer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scorer.d.ts","sourceRoot":"","sources":["../../src/extraction/scorer.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAsB,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AAchG,UAAU,cAAc;IACtB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,cAAc,CAAC,EAAE,OAAO,CAAC;CAC1B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAC/B,OAAO,EAAE,cAAc,EACvB,OAAO,EAAE,cAAc,EACvB,SAAS,GAAE,MAA0B,GACpC,kBAAkB,CAoDpB"}
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Significance Scorer
|
|
3
|
+
*
|
|
4
|
+
* 메모리의 중요도를 시그널 기반으로 점수화합니다.
|
|
5
|
+
*/
|
|
6
|
+
const DEFAULT_THRESHOLD = 0.6;
|
|
7
|
+
const TRIVIAL_TOOLS = new Set([
|
|
8
|
+
'Read', 'Glob', 'Grep', 'LS', 'ListDirectory',
|
|
9
|
+
'WebFetch', 'WebSearch', 'AskFollowupQuestion',
|
|
10
|
+
]);
|
|
11
|
+
const CONFIG_EXTENSIONS = new Set([
|
|
12
|
+
'.json', '.yaml', '.yml', '.toml', '.ini', '.cfg',
|
|
13
|
+
'.config', '.env', '.properties',
|
|
14
|
+
]);
|
|
15
|
+
/**
|
|
16
|
+
* 메시지의 중요도 점수 계산
|
|
17
|
+
*/
|
|
18
|
+
export function scoreSignificance(message, context, threshold = DEFAULT_THRESHOLD) {
|
|
19
|
+
const signals = [];
|
|
20
|
+
// 1. 에러 해결 패턴 (+0.9)
|
|
21
|
+
const errorResolution = detectErrorResolution(message, context);
|
|
22
|
+
signals.push({
|
|
23
|
+
name: 'error_resolution',
|
|
24
|
+
score: 0.9,
|
|
25
|
+
matched: errorResolution,
|
|
26
|
+
});
|
|
27
|
+
// 2. 새 파일 생성 (+0.7)
|
|
28
|
+
const newFile = context.isNewFile ?? detectNewFile(message);
|
|
29
|
+
signals.push({
|
|
30
|
+
name: 'new_file_creation',
|
|
31
|
+
score: 0.7,
|
|
32
|
+
matched: newFile,
|
|
33
|
+
});
|
|
34
|
+
// 3. 설정 변경 (+0.6)
|
|
35
|
+
const configChange = context.isConfigChange ?? detectConfigChange(message, context);
|
|
36
|
+
signals.push({
|
|
37
|
+
name: 'config_change',
|
|
38
|
+
score: 0.6,
|
|
39
|
+
matched: configChange,
|
|
40
|
+
});
|
|
41
|
+
// 4. 코드 수정 (+0.4)
|
|
42
|
+
const codeModification = detectCodeModification(context);
|
|
43
|
+
signals.push({
|
|
44
|
+
name: 'code_modification',
|
|
45
|
+
score: 0.4,
|
|
46
|
+
matched: codeModification,
|
|
47
|
+
});
|
|
48
|
+
// 5. 조회성 행동 (-0.3)
|
|
49
|
+
const trivial = detectTrivialAction(context);
|
|
50
|
+
signals.push({
|
|
51
|
+
name: 'trivial_action',
|
|
52
|
+
score: -0.3,
|
|
53
|
+
matched: trivial,
|
|
54
|
+
});
|
|
55
|
+
const totalScore = signals
|
|
56
|
+
.filter((s) => s.matched)
|
|
57
|
+
.reduce((sum, s) => sum + s.score, 0);
|
|
58
|
+
return {
|
|
59
|
+
totalScore,
|
|
60
|
+
signals,
|
|
61
|
+
shouldCapture: totalScore >= threshold,
|
|
62
|
+
};
|
|
63
|
+
}
|
|
64
|
+
function detectErrorResolution(message, context) {
|
|
65
|
+
if (context.hasError)
|
|
66
|
+
return true;
|
|
67
|
+
const content = extractTextContent(message);
|
|
68
|
+
const errorKeywords = [
|
|
69
|
+
'error', 'Error', 'ERROR',
|
|
70
|
+
'fix', 'Fixed', 'resolved',
|
|
71
|
+
'bug', 'Bug',
|
|
72
|
+
'에러', '오류', '수정', '해결',
|
|
73
|
+
];
|
|
74
|
+
return errorKeywords.some((kw) => content.includes(kw));
|
|
75
|
+
}
|
|
76
|
+
function detectNewFile(message) {
|
|
77
|
+
if (!Array.isArray(message.message.content))
|
|
78
|
+
return false;
|
|
79
|
+
return message.message.content.some((block) => block.type === 'tool_use' &&
|
|
80
|
+
block.name === 'Write' &&
|
|
81
|
+
block.input !== undefined);
|
|
82
|
+
}
|
|
83
|
+
function detectConfigChange(message, context) {
|
|
84
|
+
if (context.toolName === 'Edit' || context.toolName === 'Write') {
|
|
85
|
+
const content = extractTextContent(message);
|
|
86
|
+
return [...CONFIG_EXTENSIONS].some((ext) => content.includes(ext));
|
|
87
|
+
}
|
|
88
|
+
return false;
|
|
89
|
+
}
|
|
90
|
+
function detectCodeModification(context) {
|
|
91
|
+
return context.toolName === 'Edit' || context.toolName === 'Write';
|
|
92
|
+
}
|
|
93
|
+
function detectTrivialAction(context) {
|
|
94
|
+
if (!context.toolName)
|
|
95
|
+
return false;
|
|
96
|
+
return TRIVIAL_TOOLS.has(context.toolName);
|
|
97
|
+
}
|
|
98
|
+
function extractTextContent(message) {
|
|
99
|
+
if (typeof message.message.content === 'string') {
|
|
100
|
+
return message.message.content;
|
|
101
|
+
}
|
|
102
|
+
return message.message.content
|
|
103
|
+
.filter((block) => block.type === 'text' && block.text)
|
|
104
|
+
.map((block) => block.text)
|
|
105
|
+
.join('\n');
|
|
106
|
+
}
|
|
107
|
+
//# sourceMappingURL=scorer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"scorer.js","sourceRoot":"","sources":["../../src/extraction/scorer.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse Hook
|
|
3
|
+
*
|
|
4
|
+
* Claude Code가 도구를 실행한 후 호출됩니다.
|
|
5
|
+
* 중요도가 높은 행동을 로컬 DB에 자동 캡처합니다.
|
|
6
|
+
* 비동기로 실행되어 Claude Code 응답을 블로킹하지 않습니다.
|
|
7
|
+
*/
|
|
8
|
+
import type { PostToolUseContext } from '../types/index.js';
|
|
9
|
+
export declare function handlePostToolUse(context: PostToolUseContext): Promise<void>;
|
|
10
|
+
/**
|
|
11
|
+
* CLI 엔트리포인트
|
|
12
|
+
*/
|
|
13
|
+
export declare function main(): Promise<void>;
|
|
14
|
+
//# sourceMappingURL=post-tool-use.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-tool-use.d.ts","sourceRoot":"","sources":["../../src/hooks/post-tool-use.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAqC,MAAM,mBAAmB,CAAC;AAE/F,wBAAsB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAiElF;AA+CD;;GAEG;AACH,wBAAsB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAkB1C"}
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* PostToolUse Hook
|
|
3
|
+
*
|
|
4
|
+
* Claude Code가 도구를 실행한 후 호출됩니다.
|
|
5
|
+
* 중요도가 높은 행동을 로컬 DB에 자동 캡처합니다.
|
|
6
|
+
* 비동기로 실행되어 Claude Code 응답을 블로킹하지 않습니다.
|
|
7
|
+
*/
|
|
8
|
+
import { MemoryDatabase } from '../db/database.js';
|
|
9
|
+
import { ConfigManager } from '../config/manager.js';
|
|
10
|
+
import { scoreSignificance } from '../extraction/scorer.js';
|
|
11
|
+
import { filterSensitiveInfo } from '../extraction/filter.js';
|
|
12
|
+
export async function handlePostToolUse(context) {
|
|
13
|
+
const config = new ConfigManager().load();
|
|
14
|
+
if (!config.autoCapture.enabled) {
|
|
15
|
+
return;
|
|
16
|
+
}
|
|
17
|
+
// 트리거 도구인지 확인
|
|
18
|
+
if (!config.autoCapture.triggers.includes(context.toolName)) {
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// 민감 파일 체크
|
|
22
|
+
const filePath = extractFilePath(context.toolInput);
|
|
23
|
+
if (filePath && isSensitiveFile(filePath, config.autoCapture.excludePatterns)) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// 중요도 점수 계산
|
|
27
|
+
const message = {
|
|
28
|
+
uuid: context.sessionId,
|
|
29
|
+
type: 'assistant',
|
|
30
|
+
message: {
|
|
31
|
+
role: 'assistant',
|
|
32
|
+
content: context.toolResult,
|
|
33
|
+
},
|
|
34
|
+
timestamp: new Date().toISOString(),
|
|
35
|
+
};
|
|
36
|
+
const isNewFile = context.toolName === 'Write';
|
|
37
|
+
const isConfigChange = filePath ? isConfigFile(filePath) : false;
|
|
38
|
+
const hasError = context.toolResult.toLowerCase().includes('error');
|
|
39
|
+
const significance = scoreSignificance(message, {
|
|
40
|
+
toolName: context.toolName,
|
|
41
|
+
isNewFile,
|
|
42
|
+
isConfigChange,
|
|
43
|
+
hasError,
|
|
44
|
+
}, config.autoCapture.significanceThreshold);
|
|
45
|
+
if (!significance.shouldCapture) {
|
|
46
|
+
return;
|
|
47
|
+
}
|
|
48
|
+
// 메모리 생성
|
|
49
|
+
const category = determineCategory(context, hasError);
|
|
50
|
+
const content = buildContent(context);
|
|
51
|
+
const filtered = filterSensitiveInfo(content);
|
|
52
|
+
const input = {
|
|
53
|
+
content: filtered.filtered,
|
|
54
|
+
category,
|
|
55
|
+
tier: hasError ? 'episodic' : 'procedural',
|
|
56
|
+
tags: buildTags(context),
|
|
57
|
+
projectPath: context.projectPath,
|
|
58
|
+
sessionId: context.sessionId,
|
|
59
|
+
};
|
|
60
|
+
const db = new MemoryDatabase(config.db.path);
|
|
61
|
+
try {
|
|
62
|
+
db.initialize();
|
|
63
|
+
db.createMemory(input);
|
|
64
|
+
}
|
|
65
|
+
finally {
|
|
66
|
+
db.close();
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
function extractFilePath(toolInput) {
|
|
70
|
+
if (typeof toolInput.file_path === 'string')
|
|
71
|
+
return toolInput.file_path;
|
|
72
|
+
if (typeof toolInput.path === 'string')
|
|
73
|
+
return toolInput.path;
|
|
74
|
+
return null;
|
|
75
|
+
}
|
|
76
|
+
function isSensitiveFile(filePath, patterns) {
|
|
77
|
+
const fileName = filePath.split('/').pop() ?? '';
|
|
78
|
+
return patterns.some((pattern) => {
|
|
79
|
+
if (pattern.startsWith('*'))
|
|
80
|
+
return fileName.endsWith(pattern.slice(1));
|
|
81
|
+
if (pattern.endsWith('*'))
|
|
82
|
+
return fileName.startsWith(pattern.slice(0, -1));
|
|
83
|
+
return fileName === pattern;
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
function isConfigFile(filePath) {
|
|
87
|
+
const configExts = ['.json', '.yaml', '.yml', '.toml', '.env', '.ini', '.cfg'];
|
|
88
|
+
return configExts.some((ext) => filePath.endsWith(ext));
|
|
89
|
+
}
|
|
90
|
+
function determineCategory(context, hasError) {
|
|
91
|
+
if (hasError)
|
|
92
|
+
return 'error_solution';
|
|
93
|
+
if (context.toolName === 'Write')
|
|
94
|
+
return 'code_pattern';
|
|
95
|
+
if (context.toolName === 'Edit')
|
|
96
|
+
return 'code_pattern';
|
|
97
|
+
return 'context';
|
|
98
|
+
}
|
|
99
|
+
function buildContent(context) {
|
|
100
|
+
const filePath = extractFilePath(context.toolInput);
|
|
101
|
+
const parts = [`Tool: ${context.toolName}`];
|
|
102
|
+
if (filePath)
|
|
103
|
+
parts.push(`File: ${filePath}`);
|
|
104
|
+
parts.push(`Result: ${context.toolResult.slice(0, 500)}`);
|
|
105
|
+
return parts.join('\n');
|
|
106
|
+
}
|
|
107
|
+
function buildTags(context) {
|
|
108
|
+
const tags = [context.toolName];
|
|
109
|
+
const filePath = extractFilePath(context.toolInput);
|
|
110
|
+
if (filePath) {
|
|
111
|
+
const ext = filePath.split('.').pop();
|
|
112
|
+
if (ext)
|
|
113
|
+
tags.push(ext);
|
|
114
|
+
}
|
|
115
|
+
return tags;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* CLI 엔트리포인트
|
|
119
|
+
*/
|
|
120
|
+
export async function main() {
|
|
121
|
+
const chunks = [];
|
|
122
|
+
for await (const chunk of process.stdin) {
|
|
123
|
+
chunks.push(chunk);
|
|
124
|
+
}
|
|
125
|
+
const input = Buffer.concat(chunks).toString('utf-8').trim();
|
|
126
|
+
if (!input) {
|
|
127
|
+
process.exit(0);
|
|
128
|
+
}
|
|
129
|
+
try {
|
|
130
|
+
const context = JSON.parse(input);
|
|
131
|
+
await handlePostToolUse(context);
|
|
132
|
+
}
|
|
133
|
+
catch (err) {
|
|
134
|
+
console.error('[a2a] PostToolUse hook error:', err);
|
|
135
|
+
process.exit(0);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
// 직접 실행 시에만 main() 호출
|
|
139
|
+
const isMainModule = process.argv[1]?.endsWith('post-tool-use.js') ||
|
|
140
|
+
process.argv[1]?.endsWith('post-tool-use.ts');
|
|
141
|
+
if (isMainModule) {
|
|
142
|
+
main();
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=post-tool-use.js.map
|