ralph-mem 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 +232 -0
- package/dist/chunk-41rc1bhg.js +1116 -0
- package/dist/chunk-c3a91ngd.js +2734 -0
- package/dist/chunk-kga64hvg.js +90 -0
- package/dist/chunk-ns0dgdnb.js +21 -0
- package/dist/chunk-v8anyhk1.js +103 -0
- package/dist/chunk-w40c0y00.js +36 -0
- package/dist/hooks/post-tool-use.js +155 -0
- package/dist/hooks/session-end.js +89 -0
- package/dist/hooks/session-start.js +95 -0
- package/dist/hooks/user-prompt-submit.js +234 -0
- package/dist/index.js +64 -0
- package/dist/skills/mem-forget.js +192 -0
- package/dist/skills/mem-inject.js +130 -0
- package/dist/skills/mem-search.js +303 -0
- package/dist/skills/mem-status.js +200 -0
- package/dist/skills/ralph-config.js +404 -0
- package/dist/skills/ralph.js +654 -0
- package/package.json +64 -0
- package/plugin.json +51 -0
|
@@ -0,0 +1,303 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createSearchEngine
|
|
3
|
+
} from "../chunk-kga64hvg.js";
|
|
4
|
+
import {
|
|
5
|
+
createDBClient
|
|
6
|
+
} from "../chunk-41rc1bhg.js";
|
|
7
|
+
import {
|
|
8
|
+
ensureProjectDirs,
|
|
9
|
+
getProjectDBPath
|
|
10
|
+
} from "../chunk-w40c0y00.js";
|
|
11
|
+
import"../chunk-ns0dgdnb.js";
|
|
12
|
+
|
|
13
|
+
// src/skills/mem-search.ts
|
|
14
|
+
function parseSince(since) {
|
|
15
|
+
const relativeMatch = since.match(/^(\d+)d$/);
|
|
16
|
+
if (relativeMatch) {
|
|
17
|
+
const days = Number.parseInt(relativeMatch[1], 10);
|
|
18
|
+
const date2 = new Date;
|
|
19
|
+
date2.setDate(date2.getDate() - days);
|
|
20
|
+
return date2;
|
|
21
|
+
}
|
|
22
|
+
const date = new Date(since);
|
|
23
|
+
if (!Number.isNaN(date.getTime())) {
|
|
24
|
+
return date;
|
|
25
|
+
}
|
|
26
|
+
return null;
|
|
27
|
+
}
|
|
28
|
+
function parseType(type) {
|
|
29
|
+
const validTypes = [
|
|
30
|
+
"note",
|
|
31
|
+
"tool_use",
|
|
32
|
+
"bash",
|
|
33
|
+
"error",
|
|
34
|
+
"success"
|
|
35
|
+
];
|
|
36
|
+
const types = type.split(",").map((t) => t.trim().toLowerCase());
|
|
37
|
+
return types.filter((t) => validTypes.includes(t));
|
|
38
|
+
}
|
|
39
|
+
function formatTable(results) {
|
|
40
|
+
if (results.length === 0) {
|
|
41
|
+
return "검색 결과가 없습니다.";
|
|
42
|
+
}
|
|
43
|
+
const lines = [];
|
|
44
|
+
lines.push("┌────────────────┬───────┬─────────────────────────────────────┐");
|
|
45
|
+
lines.push("│ ID │ 점수 │ 요약 │");
|
|
46
|
+
lines.push("├────────────────┼───────┼─────────────────────────────────────┤");
|
|
47
|
+
for (const result of results) {
|
|
48
|
+
const id = result.id.slice(0, 14).padEnd(14);
|
|
49
|
+
const score = result.score.toFixed(2).padStart(5);
|
|
50
|
+
const summary = (result.summary || "(없음)").slice(0, 35).padEnd(35);
|
|
51
|
+
lines.push(`│ ${id} │ ${score} │ ${summary} │`);
|
|
52
|
+
}
|
|
53
|
+
lines.push("└────────────────┴───────┴─────────────────────────────────────┘");
|
|
54
|
+
return lines.join(`
|
|
55
|
+
`);
|
|
56
|
+
}
|
|
57
|
+
function formatTimeline(results) {
|
|
58
|
+
if (results.length === 0) {
|
|
59
|
+
return "검색 결과가 없습니다.";
|
|
60
|
+
}
|
|
61
|
+
const lines = [];
|
|
62
|
+
for (const result of results) {
|
|
63
|
+
const date = result.createdAt ? result.createdAt.toLocaleDateString("ko-KR", {
|
|
64
|
+
year: "numeric",
|
|
65
|
+
month: "2-digit",
|
|
66
|
+
day: "2-digit"
|
|
67
|
+
}) : "날짜 없음";
|
|
68
|
+
const type = result.type || "unknown";
|
|
69
|
+
const tool = result.toolName ? ` (${result.toolName})` : "";
|
|
70
|
+
lines.push(`\uD83D\uDCCC ${result.id}`);
|
|
71
|
+
lines.push(` 날짜: ${date}`);
|
|
72
|
+
lines.push(` 유형: ${type}${tool}`);
|
|
73
|
+
lines.push(` 점수: ${result.score.toFixed(2)}`);
|
|
74
|
+
lines.push(` 요약: ${result.summary || "(없음)"}`);
|
|
75
|
+
lines.push("");
|
|
76
|
+
}
|
|
77
|
+
return lines.join(`
|
|
78
|
+
`);
|
|
79
|
+
}
|
|
80
|
+
function formatDetail(result) {
|
|
81
|
+
const lines = [];
|
|
82
|
+
lines.push(`\uD83D\uDCC4 ${result.id} 상세`);
|
|
83
|
+
lines.push("");
|
|
84
|
+
if (result.createdAt) {
|
|
85
|
+
const date = result.createdAt.toLocaleDateString("ko-KR", {
|
|
86
|
+
year: "numeric",
|
|
87
|
+
month: "2-digit",
|
|
88
|
+
day: "2-digit",
|
|
89
|
+
hour: "2-digit",
|
|
90
|
+
minute: "2-digit"
|
|
91
|
+
});
|
|
92
|
+
lines.push(`세션: ${date}`);
|
|
93
|
+
}
|
|
94
|
+
if (result.sessionId) {
|
|
95
|
+
lines.push(`세션 ID: ${result.sessionId}`);
|
|
96
|
+
}
|
|
97
|
+
lines.push(`유형: ${result.type || "unknown"}`);
|
|
98
|
+
if (result.toolName) {
|
|
99
|
+
lines.push(`도구: ${result.toolName}`);
|
|
100
|
+
}
|
|
101
|
+
lines.push(`점수: ${result.score.toFixed(4)}`);
|
|
102
|
+
if (result.metadata) {
|
|
103
|
+
lines.push(`중요도: ${result.metadata.importance || 0.5}`);
|
|
104
|
+
if (result.metadata.projectPath) {
|
|
105
|
+
lines.push(`프로젝트: ${result.metadata.projectPath}`);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
lines.push("");
|
|
109
|
+
lines.push("내용:");
|
|
110
|
+
lines.push("─".repeat(60));
|
|
111
|
+
lines.push(result.content || result.summary || "(내용 없음)");
|
|
112
|
+
lines.push("─".repeat(60));
|
|
113
|
+
return lines.join(`
|
|
114
|
+
`);
|
|
115
|
+
}
|
|
116
|
+
function formatDetails(results) {
|
|
117
|
+
if (results.length === 0) {
|
|
118
|
+
return "검색 결과가 없습니다.";
|
|
119
|
+
}
|
|
120
|
+
return results.map(formatDetail).join(`
|
|
121
|
+
|
|
122
|
+
`);
|
|
123
|
+
}
|
|
124
|
+
async function memSearchSkill(input, options) {
|
|
125
|
+
const { query, projectPath, layer = 1, limit = 10, since, type, id } = input;
|
|
126
|
+
ensureProjectDirs(projectPath);
|
|
127
|
+
const dbPath = getProjectDBPath(projectPath);
|
|
128
|
+
const client = options?.client ?? createDBClient(dbPath);
|
|
129
|
+
const engine = options?.engine ?? createSearchEngine(client);
|
|
130
|
+
if (id) {
|
|
131
|
+
const observation = client.getObservation(id);
|
|
132
|
+
if (!options?.client) {
|
|
133
|
+
client.close();
|
|
134
|
+
}
|
|
135
|
+
if (!observation) {
|
|
136
|
+
return {
|
|
137
|
+
results: [],
|
|
138
|
+
totalCount: 0,
|
|
139
|
+
layer: 3,
|
|
140
|
+
formatted: `ID '${id}'를 찾을 수 없습니다.`
|
|
141
|
+
};
|
|
142
|
+
}
|
|
143
|
+
const result = {
|
|
144
|
+
id: observation.id,
|
|
145
|
+
score: 1,
|
|
146
|
+
summary: observation.content.slice(0, 100),
|
|
147
|
+
createdAt: new Date(observation.created_at),
|
|
148
|
+
sessionId: observation.session_id,
|
|
149
|
+
type: observation.type,
|
|
150
|
+
toolName: observation.tool_name ?? undefined,
|
|
151
|
+
content: observation.content,
|
|
152
|
+
metadata: {
|
|
153
|
+
importance: observation.importance
|
|
154
|
+
}
|
|
155
|
+
};
|
|
156
|
+
return {
|
|
157
|
+
results: [result],
|
|
158
|
+
totalCount: 1,
|
|
159
|
+
layer: 3,
|
|
160
|
+
formatted: formatDetail(result)
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
const searchOptions = {
|
|
164
|
+
limit,
|
|
165
|
+
layer,
|
|
166
|
+
projectPath
|
|
167
|
+
};
|
|
168
|
+
if (since) {
|
|
169
|
+
const sinceDate = parseSince(since);
|
|
170
|
+
if (sinceDate) {
|
|
171
|
+
searchOptions.since = sinceDate;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
if (type) {
|
|
175
|
+
const types = parseType(type);
|
|
176
|
+
if (types.length > 0) {
|
|
177
|
+
searchOptions.types = types;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
let results = [];
|
|
181
|
+
try {
|
|
182
|
+
results = engine.search(query, searchOptions);
|
|
183
|
+
} catch {
|
|
184
|
+
if (!options?.client) {
|
|
185
|
+
client.close();
|
|
186
|
+
}
|
|
187
|
+
return {
|
|
188
|
+
results: [],
|
|
189
|
+
totalCount: 0,
|
|
190
|
+
layer,
|
|
191
|
+
formatted: "검색 중 오류가 발생했습니다."
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
if (!options?.client) {
|
|
195
|
+
client.close();
|
|
196
|
+
}
|
|
197
|
+
let formatted;
|
|
198
|
+
if (layer === 1) {
|
|
199
|
+
formatted = `\uD83D\uDD0D 검색 결과: "${query}" (${results.length}건)
|
|
200
|
+
|
|
201
|
+
${formatTable(results)}`;
|
|
202
|
+
} else if (layer === 2) {
|
|
203
|
+
formatted = `\uD83D\uDD0D 검색 결과: "${query}" (${results.length}건)
|
|
204
|
+
|
|
205
|
+
${formatTimeline(results)}`;
|
|
206
|
+
} else {
|
|
207
|
+
formatted = `\uD83D\uDD0D 검색 결과: "${query}" (${results.length}건)
|
|
208
|
+
|
|
209
|
+
${formatDetails(results)}`;
|
|
210
|
+
}
|
|
211
|
+
return {
|
|
212
|
+
results,
|
|
213
|
+
totalCount: results.length,
|
|
214
|
+
layer,
|
|
215
|
+
formatted
|
|
216
|
+
};
|
|
217
|
+
}
|
|
218
|
+
function parseArgs(argsString, projectPath) {
|
|
219
|
+
const args = {
|
|
220
|
+
query: "",
|
|
221
|
+
projectPath
|
|
222
|
+
};
|
|
223
|
+
const tokens = [];
|
|
224
|
+
let current = "";
|
|
225
|
+
let inQuotes = false;
|
|
226
|
+
for (const char of argsString) {
|
|
227
|
+
if (char === '"' || char === "'") {
|
|
228
|
+
inQuotes = !inQuotes;
|
|
229
|
+
} else if (char === " " && !inQuotes) {
|
|
230
|
+
if (current) {
|
|
231
|
+
tokens.push(current);
|
|
232
|
+
current = "";
|
|
233
|
+
}
|
|
234
|
+
} else {
|
|
235
|
+
current += char;
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (current) {
|
|
239
|
+
tokens.push(current);
|
|
240
|
+
}
|
|
241
|
+
let i = 0;
|
|
242
|
+
while (i < tokens.length) {
|
|
243
|
+
const token = tokens[i];
|
|
244
|
+
if (token === "--layer" && i + 1 < tokens.length) {
|
|
245
|
+
const layerNum = Number.parseInt(tokens[i + 1], 10);
|
|
246
|
+
if (layerNum >= 1 && layerNum <= 3) {
|
|
247
|
+
args.layer = layerNum;
|
|
248
|
+
}
|
|
249
|
+
i += 2;
|
|
250
|
+
} else if (token === "--limit" && i + 1 < tokens.length) {
|
|
251
|
+
args.limit = Number.parseInt(tokens[i + 1], 10);
|
|
252
|
+
i += 2;
|
|
253
|
+
} else if (token === "--since" && i + 1 < tokens.length) {
|
|
254
|
+
args.since = tokens[i + 1];
|
|
255
|
+
i += 2;
|
|
256
|
+
} else if (token === "--type" && i + 1 < tokens.length) {
|
|
257
|
+
args.type = tokens[i + 1];
|
|
258
|
+
i += 2;
|
|
259
|
+
} else if (token.startsWith("obs-")) {
|
|
260
|
+
args.id = token;
|
|
261
|
+
args.layer = 3;
|
|
262
|
+
i++;
|
|
263
|
+
} else if (!token.startsWith("--")) {
|
|
264
|
+
args.query = args.query ? `${args.query} ${token}` : token;
|
|
265
|
+
i++;
|
|
266
|
+
} else {
|
|
267
|
+
i++;
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
return args;
|
|
271
|
+
}
|
|
272
|
+
async function executeMemSearch(argsString, projectPath, options) {
|
|
273
|
+
const input = parseArgs(argsString, projectPath);
|
|
274
|
+
if (!input.query && !input.id) {
|
|
275
|
+
return `사용법: /mem-search <query> [options]
|
|
276
|
+
|
|
277
|
+
옵션:
|
|
278
|
+
--layer <1|2|3> 상세 수준 (기본: 1)
|
|
279
|
+
--since <7d|30d|YYYY-MM-DD> 기간 필터
|
|
280
|
+
--type <error|success|bash|tool_use|note> 유형 필터
|
|
281
|
+
--limit <n> 결과 수 제한
|
|
282
|
+
|
|
283
|
+
예시:
|
|
284
|
+
/mem-search "JWT authentication"
|
|
285
|
+
/mem-search --layer 3 obs-a1b2c3d4
|
|
286
|
+
/mem-search "database" --since 7d --type error`;
|
|
287
|
+
}
|
|
288
|
+
const result = await memSearchSkill(input, options);
|
|
289
|
+
return result.formatted;
|
|
290
|
+
}
|
|
291
|
+
export {
|
|
292
|
+
parseType,
|
|
293
|
+
parseSince,
|
|
294
|
+
parseArgs,
|
|
295
|
+
memSearchSkill,
|
|
296
|
+
formatTimeline,
|
|
297
|
+
formatTable,
|
|
298
|
+
formatDetails,
|
|
299
|
+
formatDetail,
|
|
300
|
+
executeMemSearch
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
export { memSearchSkill };
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
import {
|
|
2
|
+
getProjectDBPath
|
|
3
|
+
} from "../chunk-w40c0y00.js";
|
|
4
|
+
import"../chunk-ns0dgdnb.js";
|
|
5
|
+
|
|
6
|
+
// src/skills/mem-status.ts
|
|
7
|
+
import { existsSync, statSync } from "node:fs";
|
|
8
|
+
|
|
9
|
+
// src/utils/tokens.ts
|
|
10
|
+
var CHARS_PER_TOKEN_EN = 4;
|
|
11
|
+
var CHARS_PER_TOKEN_KO = 1.5;
|
|
12
|
+
var CHARS_PER_TOKEN_CODE = 3.5;
|
|
13
|
+
function isKorean(char) {
|
|
14
|
+
const code = char.charCodeAt(0);
|
|
15
|
+
return code >= 44032 && code <= 55203 || code >= 4352 && code <= 4607 || code >= 12592 && code <= 12687;
|
|
16
|
+
}
|
|
17
|
+
function isCodeLike(text) {
|
|
18
|
+
const codePatterns = [
|
|
19
|
+
/\bfunction\b/,
|
|
20
|
+
/\bconst\b/,
|
|
21
|
+
/\blet\b/,
|
|
22
|
+
/\bvar\b/,
|
|
23
|
+
/\bclass\b/,
|
|
24
|
+
/\bimport\b/,
|
|
25
|
+
/\bexport\b/,
|
|
26
|
+
/\breturn\b/,
|
|
27
|
+
/\bif\s*\(/,
|
|
28
|
+
/\bfor\s*\(/,
|
|
29
|
+
/\bwhile\s*\(/,
|
|
30
|
+
/=>/,
|
|
31
|
+
/\{\s*$/m,
|
|
32
|
+
/^\s*\}/m,
|
|
33
|
+
/\[\s*$/m,
|
|
34
|
+
/^\s*\]/m,
|
|
35
|
+
/;\s*$/m
|
|
36
|
+
];
|
|
37
|
+
let matches = 0;
|
|
38
|
+
for (const pattern of codePatterns) {
|
|
39
|
+
if (pattern.test(text)) {
|
|
40
|
+
matches++;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return matches >= 3;
|
|
44
|
+
}
|
|
45
|
+
function estimateTokens(text) {
|
|
46
|
+
if (!text)
|
|
47
|
+
return 0;
|
|
48
|
+
if (isCodeLike(text)) {
|
|
49
|
+
return Math.ceil(text.length / CHARS_PER_TOKEN_CODE);
|
|
50
|
+
}
|
|
51
|
+
let koreanChars = 0;
|
|
52
|
+
let otherChars = 0;
|
|
53
|
+
for (const char of text) {
|
|
54
|
+
if (isKorean(char)) {
|
|
55
|
+
koreanChars++;
|
|
56
|
+
} else {
|
|
57
|
+
otherChars++;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
const koreanTokens = Math.ceil(koreanChars / CHARS_PER_TOKEN_KO);
|
|
61
|
+
const otherTokens = Math.ceil(otherChars / CHARS_PER_TOKEN_EN);
|
|
62
|
+
return koreanTokens + otherTokens;
|
|
63
|
+
}
|
|
64
|
+
function countTokens(text) {
|
|
65
|
+
return estimateTokens(text);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// src/skills/mem-status.ts
|
|
69
|
+
function getDBSize(projectPath) {
|
|
70
|
+
try {
|
|
71
|
+
const dbPath = getProjectDBPath(projectPath);
|
|
72
|
+
if (!existsSync(dbPath)) {
|
|
73
|
+
return 0;
|
|
74
|
+
}
|
|
75
|
+
const stats = statSync(dbPath);
|
|
76
|
+
return Number((stats.size / (1024 * 1024)).toFixed(2));
|
|
77
|
+
} catch {
|
|
78
|
+
return 0;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
function getRecentSessionCount(client, days) {
|
|
82
|
+
const cutoff = new Date;
|
|
83
|
+
cutoff.setDate(cutoff.getDate() - days);
|
|
84
|
+
const cutoffStr = cutoff.toISOString();
|
|
85
|
+
const result = client.db.prepare("SELECT COUNT(*) as count FROM sessions WHERE started_at >= ?").get(cutoffStr);
|
|
86
|
+
return result.count;
|
|
87
|
+
}
|
|
88
|
+
function getSessionTokenUsage(client, sessionId) {
|
|
89
|
+
const observations = client.listObservations(sessionId);
|
|
90
|
+
let totalTokens = 0;
|
|
91
|
+
for (const obs of observations) {
|
|
92
|
+
const content = obs.content_compressed || obs.content;
|
|
93
|
+
totalTokens += countTokens(content);
|
|
94
|
+
}
|
|
95
|
+
return totalTokens;
|
|
96
|
+
}
|
|
97
|
+
function getLoopStats(client, sessionId) {
|
|
98
|
+
const runs = client.listLoopRuns(sessionId, 100);
|
|
99
|
+
const total = runs.length;
|
|
100
|
+
if (total === 0) {
|
|
101
|
+
return { total: 0, successRate: 0 };
|
|
102
|
+
}
|
|
103
|
+
const successful = runs.filter((r) => r.status === "success").length;
|
|
104
|
+
const successRate = Math.round(successful / total * 100);
|
|
105
|
+
return { total, successRate };
|
|
106
|
+
}
|
|
107
|
+
function isLoopActive(client, sessionId) {
|
|
108
|
+
const runs = client.listLoopRuns(sessionId, 1);
|
|
109
|
+
return runs.length > 0 && runs[0].status === "running";
|
|
110
|
+
}
|
|
111
|
+
function getMemStatus(context) {
|
|
112
|
+
const { projectPath, sessionId, client, contextBudget = 1e5 } = context;
|
|
113
|
+
const allSessions = client.db.prepare("SELECT COUNT(*) as count FROM sessions").get();
|
|
114
|
+
const recentSessions = getRecentSessionCount(client, 30);
|
|
115
|
+
const allObservations = client.db.prepare("SELECT COUNT(*) as count FROM observations").get();
|
|
116
|
+
const dbSizeMB = getDBSize(projectPath);
|
|
117
|
+
const currentTokens = getSessionTokenUsage(client, sessionId);
|
|
118
|
+
const budgetPercent = Math.round(currentTokens / contextBudget * 100);
|
|
119
|
+
const loopStats = getLoopStats(client, sessionId);
|
|
120
|
+
const loopActive = isLoopActive(client, sessionId);
|
|
121
|
+
const configPath = `${projectPath}/.ralph-mem/config.yaml`;
|
|
122
|
+
return {
|
|
123
|
+
sessions: {
|
|
124
|
+
total: allSessions.count,
|
|
125
|
+
recent: recentSessions
|
|
126
|
+
},
|
|
127
|
+
observations: {
|
|
128
|
+
total: allObservations.count
|
|
129
|
+
},
|
|
130
|
+
storage: {
|
|
131
|
+
dbSizeMB
|
|
132
|
+
},
|
|
133
|
+
tokens: {
|
|
134
|
+
currentSession: currentTokens,
|
|
135
|
+
budgetUsed: currentTokens,
|
|
136
|
+
budgetPercent
|
|
137
|
+
},
|
|
138
|
+
loop: {
|
|
139
|
+
isActive: loopActive,
|
|
140
|
+
totalRuns: loopStats.total,
|
|
141
|
+
successRate: loopStats.successRate
|
|
142
|
+
},
|
|
143
|
+
configPath: existsSync(configPath) ? configPath : null
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
function formatNumber(n) {
|
|
147
|
+
return n.toLocaleString();
|
|
148
|
+
}
|
|
149
|
+
function formatMemStatus(status) {
|
|
150
|
+
const loopStatus = status.loop.isActive ? "실행 중" : "비활성";
|
|
151
|
+
return `\uD83D\uDCCA ralph-mem 상태
|
|
152
|
+
|
|
153
|
+
메모리:
|
|
154
|
+
├─ 세션: ${status.sessions.total}개 (최근 30일: ${status.sessions.recent}개)
|
|
155
|
+
├─ 관찰: ${formatNumber(status.observations.total)}개
|
|
156
|
+
└─ 용량: ${status.storage.dbSizeMB} MB
|
|
157
|
+
|
|
158
|
+
토큰:
|
|
159
|
+
├─ 현재 세션: ${formatNumber(status.tokens.currentSession)} tokens
|
|
160
|
+
├─ Budget: ${formatNumber(status.tokens.budgetUsed)} tokens (${status.tokens.budgetPercent}%)
|
|
161
|
+
└─ 사용률: ${status.tokens.budgetPercent}%
|
|
162
|
+
|
|
163
|
+
Loop:
|
|
164
|
+
├─ 현재: ${loopStatus}
|
|
165
|
+
├─ 총 실행: ${status.loop.totalRuns}회
|
|
166
|
+
└─ 성공률: ${status.loop.successRate}%
|
|
167
|
+
|
|
168
|
+
설정: ${status.configPath || "(없음)"}`;
|
|
169
|
+
}
|
|
170
|
+
async function executeMemStatus(context) {
|
|
171
|
+
const status = getMemStatus(context);
|
|
172
|
+
return formatMemStatus(status);
|
|
173
|
+
}
|
|
174
|
+
function createMemStatusSkill(context) {
|
|
175
|
+
return {
|
|
176
|
+
name: "/mem-status",
|
|
177
|
+
async execute() {
|
|
178
|
+
return executeMemStatus(context);
|
|
179
|
+
},
|
|
180
|
+
getStatus() {
|
|
181
|
+
return getMemStatus(context);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
async function memStatusSkill(_input) {
|
|
186
|
+
return {
|
|
187
|
+
sessionCount: 0,
|
|
188
|
+
observationCount: 0,
|
|
189
|
+
totalTokens: 0
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
export {
|
|
193
|
+
memStatusSkill,
|
|
194
|
+
getMemStatus,
|
|
195
|
+
formatMemStatus,
|
|
196
|
+
executeMemStatus,
|
|
197
|
+
createMemStatusSkill
|
|
198
|
+
};
|
|
199
|
+
|
|
200
|
+
export { memStatusSkill };
|