ralph-mem 0.1.0 → 0.1.3
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/.claude-plugin/plugin.json +11 -0
- package/README.md +233 -3
- package/commands/mem-forget.md +17 -0
- package/commands/mem-inject.md +23 -0
- package/commands/mem-search.md +24 -0
- package/commands/mem-status.md +23 -0
- package/commands/ralph.md +26 -0
- package/dist/{chunk-v8anyhk1.js → chunk-gy7xx0jv.js} +1 -1
- package/dist/{chunk-41rc1bhg.js → chunk-jz140n5a.js} +1 -1
- package/dist/{chunk-kga64hvg.js → chunk-rcnembz5.js} +1 -1
- package/dist/hooks/post-tool-use.js +25 -1
- package/dist/hooks/session-end.js +2 -3
- package/dist/hooks/session-start.js +2 -3
- package/dist/hooks/stop.js +61 -0
- package/dist/hooks/user-prompt-submit.js +3 -4
- package/dist/index.js +283 -15
- package/dist/skills/mem-search.js +2 -2
- package/dist/skills/mem-status.js +0 -1
- package/dist/skills/ralph.js +1 -2
- package/hooks/hooks.json +26 -0
- package/package.json +5 -2
- package/skills/mem-forget/SKILL.md +25 -0
- package/skills/mem-inject/SKILL.md +22 -0
- package/skills/mem-search/SKILL.md +27 -0
- package/skills/mem-status/SKILL.md +30 -0
- package/skills/ralph/SKILL.md +43 -0
- package/plugin.json +0 -51
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ralph-mem",
|
|
3
|
+
"description": "Ralph Loop 기반 반복 실행 및 세션 간 컨텍스트 영속성 관리",
|
|
4
|
+
"version": "0.1.0",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "roboco-io"
|
|
7
|
+
},
|
|
8
|
+
"homepage": "https://github.com/roboco-io/ralph-mem",
|
|
9
|
+
"repository": "https://github.com/roboco-io/ralph-mem",
|
|
10
|
+
"license": "MIT"
|
|
11
|
+
}
|
package/README.md
CHANGED
|
@@ -1,10 +1,17 @@
|
|
|
1
1
|
# ralph-mem
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/ralph-mem)
|
|
4
|
+
[](https://opensource.org/licenses/MIT)
|
|
5
|
+
[](https://www.typescriptlang.org/)
|
|
6
|
+
[](https://bun.sh/)
|
|
7
|
+
|
|
3
8
|
Claude Code를 위한 Ralph Loop 기반 지속적 컨텍스트 관리 플러그인
|
|
4
9
|
|
|
5
10
|
## 개요
|
|
6
11
|
|
|
7
|
-
ralph-mem은 [Ralph Loop](https://ghuntley.com/ralph/)의
|
|
12
|
+
ralph-mem은 [Geoffrey Huntley](https://ghuntley.com/)의 [Ralph Loop](https://ghuntley.com/ralph/)와 [thedotmack](https://github.com/thedotmack)의 [claude-mem](https://github.com/thedotmack/claude-mem)에서 영감을 받아 시작된 프로젝트입니다.
|
|
13
|
+
|
|
14
|
+
Ralph Loop의 "성공할 때까지 반복" 철학과 claude-mem의 "지능적 컨텍스트 관리"를 결합하여 Claude Code를 위한 지속적 메모리 관리 플러그인을 구현했습니다.
|
|
8
15
|
|
|
9
16
|
### 해결하는 문제
|
|
10
17
|
|
|
@@ -60,6 +67,7 @@ flowchart TB
|
|
|
60
67
|
|
|
61
68
|
- `SessionStart` - 관련 메모리 자동 주입
|
|
62
69
|
- `PostToolUse` - 도구 사용 결과 기록
|
|
70
|
+
- `Stop` - 세션 강제 종료 시 정리 작업
|
|
63
71
|
- `SessionEnd` - 세션 요약 생성 및 저장
|
|
64
72
|
|
|
65
73
|
### 3. Progressive Disclosure
|
|
@@ -79,11 +87,63 @@ flowchart TB
|
|
|
79
87
|
|
|
80
88
|
## 설치
|
|
81
89
|
|
|
90
|
+
### npm
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
npm install ralph-mem
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
### yarn
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
yarn add ralph-mem
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
### pnpm
|
|
103
|
+
|
|
82
104
|
```bash
|
|
83
|
-
|
|
84
|
-
|
|
105
|
+
pnpm add ralph-mem
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### bun
|
|
109
|
+
|
|
110
|
+
```bash
|
|
111
|
+
bun add ralph-mem
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
### Claude Code 플러그인
|
|
115
|
+
|
|
116
|
+
Claude Code에서 플러그인으로 사용하려면 [roboco-io/plugins](https://github.com/roboco-io/plugins) 마켓플레이스를 통해 설치합니다:
|
|
117
|
+
|
|
118
|
+
1. 마켓플레이스 추가
|
|
119
|
+
```
|
|
120
|
+
/plugin marketplace add roboco-io/plugins
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
2. 플러그인 설치
|
|
124
|
+
```
|
|
125
|
+
/plugin install ralph-mem@roboco-plugins
|
|
85
126
|
```
|
|
86
127
|
|
|
128
|
+
또는 `/plugin` 명령으로 플러그인 매니저를 열어 UI에서 설치할 수 있습니다.
|
|
129
|
+
|
|
130
|
+
### 플러그인 업데이트
|
|
131
|
+
|
|
132
|
+
방법 1: CLI 명령으로 업데이트 (터미널에서 실행)
|
|
133
|
+
```
|
|
134
|
+
claude plugin update ralph-mem@roboco-plugins
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
방법 2: 재설치 (세션 내에서 실행)
|
|
138
|
+
```
|
|
139
|
+
/plugin uninstall ralph-mem
|
|
140
|
+
```
|
|
141
|
+
```
|
|
142
|
+
/plugin install ralph-mem@roboco-plugins
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
업데이트 후 Claude Code를 재시작하면 변경 사항이 적용됩니다.
|
|
146
|
+
|
|
87
147
|
## 사용법
|
|
88
148
|
|
|
89
149
|
### Ralph Loop
|
|
@@ -128,6 +188,38 @@ claude plugins install ralph-mem
|
|
|
128
188
|
/mem-forget <observation-id>
|
|
129
189
|
```
|
|
130
190
|
|
|
191
|
+
### 4. Privacy 기능
|
|
192
|
+
|
|
193
|
+
민감한 정보를 메모리에서 제외합니다.
|
|
194
|
+
|
|
195
|
+
**`<private>` 태그:**
|
|
196
|
+
|
|
197
|
+
```bash
|
|
198
|
+
# 태그로 감싼 내용은 저장되지 않습니다
|
|
199
|
+
My API key is <private>sk-1234567890</private>
|
|
200
|
+
# 저장됨: My API key is [PRIVATE]
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
**설정 기반 제외:**
|
|
204
|
+
|
|
205
|
+
```yaml
|
|
206
|
+
privacy:
|
|
207
|
+
exclude_patterns:
|
|
208
|
+
- "*.env"
|
|
209
|
+
- "*password*"
|
|
210
|
+
- "*secret*"
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
### 5. MCP 도구
|
|
214
|
+
|
|
215
|
+
스킬 외에 MCP(Model Context Protocol) 도구로도 메모리에 접근할 수 있습니다.
|
|
216
|
+
|
|
217
|
+
| 도구 | 설명 |
|
|
218
|
+
|------|------|
|
|
219
|
+
| `ralph_mem_search` | Progressive Disclosure 기반 검색 |
|
|
220
|
+
| `ralph_mem_timeline` | 특정 관찰 주변 시간순 컨텍스트 |
|
|
221
|
+
| `ralph_mem_get` | 관찰 ID로 전체 상세 조회 |
|
|
222
|
+
|
|
131
223
|
## 설정
|
|
132
224
|
|
|
133
225
|
`~/.config/ralph-mem/config.yaml`:
|
|
@@ -153,6 +245,144 @@ privacy:
|
|
|
153
245
|
- "*secret*"
|
|
154
246
|
```
|
|
155
247
|
|
|
248
|
+
## 동작 원리
|
|
249
|
+
|
|
250
|
+
ralph-mem은 크게 두 가지 모드로 동작합니다:
|
|
251
|
+
|
|
252
|
+
1. **자동 모드 (Lifecycle Hooks)**: 사용자 개입 없이 백그라운드에서 동작
|
|
253
|
+
2. **명시적 모드 (Skills/Commands)**: 사용자가 슬래시 명령어로 직접 제어
|
|
254
|
+
|
|
255
|
+
### Lifecycle Hooks
|
|
256
|
+
|
|
257
|
+
플러그인이 설치되면 Claude Code의 lifecycle에 자동으로 연결되어 동작합니다.
|
|
258
|
+
|
|
259
|
+
```mermaid
|
|
260
|
+
sequenceDiagram
|
|
261
|
+
participant CC as Claude Code
|
|
262
|
+
participant Hook as Hook Layer
|
|
263
|
+
participant Core as Core Layer
|
|
264
|
+
participant DB as SQLite
|
|
265
|
+
|
|
266
|
+
CC->>Hook: SessionStart
|
|
267
|
+
Hook->>Core: 관련 메모리 검색
|
|
268
|
+
Core->>DB: FTS5 + Embedding 검색
|
|
269
|
+
DB-->>Core: 이전 컨텍스트
|
|
270
|
+
Core-->>Hook: 검색 결과
|
|
271
|
+
Hook-->>CC: 컨텍스트 자동 주입
|
|
272
|
+
|
|
273
|
+
CC->>Hook: UserPromptSubmit
|
|
274
|
+
Hook->>Core: 쿼리 관련 검색
|
|
275
|
+
Core-->>Hook: 관련 메모리 알림
|
|
276
|
+
Hook-->>CC: 알림 표시 (주입 X)
|
|
277
|
+
|
|
278
|
+
CC->>Hook: PostToolUse
|
|
279
|
+
Hook->>Core: 도구 사용 결과 기록
|
|
280
|
+
Core->>DB: Observation 저장
|
|
281
|
+
|
|
282
|
+
CC->>Hook: SessionEnd
|
|
283
|
+
Hook->>Core: 세션 요약 생성
|
|
284
|
+
Core->>DB: 요약 저장
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
| Hook | 시점 | 동작 |
|
|
288
|
+
|------|------|------|
|
|
289
|
+
| `SessionStart` | 세션 시작 | 프로젝트 관련 이전 컨텍스트 자동 주입 |
|
|
290
|
+
| `UserPromptSubmit` | 프롬프트 제출 | 관련 메모리 알림 (토큰 절약을 위해 주입하지 않음) |
|
|
291
|
+
| `PostToolUse` | 도구 사용 후 | 쓰기 도구, Bash 명령 결과를 Observation으로 기록 |
|
|
292
|
+
| `SessionEnd` | 세션 종료 | 세션 요약 생성 및 저장 |
|
|
293
|
+
|
|
294
|
+
### Ralph Loop 동작
|
|
295
|
+
|
|
296
|
+
`/ralph start` 명령으로 활성화되며, 성공 기준 달성까지 자동 반복합니다.
|
|
297
|
+
|
|
298
|
+
```mermaid
|
|
299
|
+
flowchart LR
|
|
300
|
+
A[Task + Context] --> B[Claude 실행]
|
|
301
|
+
B --> C{성공 판단}
|
|
302
|
+
C -->|YES| D[완료]
|
|
303
|
+
C -->|NO| E[결과 추가]
|
|
304
|
+
E --> F{중단 조건?}
|
|
305
|
+
F -->|NO| A
|
|
306
|
+
F -->|YES| G[실패 + 롤백 안내]
|
|
307
|
+
```
|
|
308
|
+
|
|
309
|
+
**성공 판단**: Claude가 테스트/빌드 출력을 분석하여 성공 여부를 판단합니다.
|
|
310
|
+
|
|
311
|
+
**Overbaking 방지**: 무한 반복을 방지하기 위한 중단 조건:
|
|
312
|
+
|
|
313
|
+
| 조건 | 기본값 | 설명 |
|
|
314
|
+
|------|--------|------|
|
|
315
|
+
| `maxIterations` | 10 | 최대 반복 횟수 |
|
|
316
|
+
| `maxDurationMs` | 30분 | 최대 실행 시간 |
|
|
317
|
+
| `noProgressThreshold` | 3회 | 진척 없음 허용 횟수 |
|
|
318
|
+
|
|
319
|
+
**스냅샷**: Loop 시작 시 변경 파일을 스냅샷으로 저장하여 실패 시 롤백 가능.
|
|
320
|
+
|
|
321
|
+
### 검색 엔진
|
|
322
|
+
|
|
323
|
+
2단계 검색으로 최적의 결과를 반환합니다:
|
|
324
|
+
|
|
325
|
+
1. **FTS5 전문 검색** (기본): SQLite FTS5를 사용한 빠른 텍스트 검색
|
|
326
|
+
2. **Embedding 유사도** (폴백): FTS5 결과가 부족할 때 의미 기반 검색
|
|
327
|
+
|
|
328
|
+
**Embedding 모델**: `paraphrase-multilingual-MiniLM-L12-v2`
|
|
329
|
+
- 로컬 실행 (API 호출 없음)
|
|
330
|
+
- 50+ 언어 지원 (한국어, 영어 포함)
|
|
331
|
+
- 384차원, ~278MB
|
|
332
|
+
|
|
333
|
+
### 데이터 흐름
|
|
334
|
+
|
|
335
|
+
```mermaid
|
|
336
|
+
flowchart TB
|
|
337
|
+
subgraph Input["입력"]
|
|
338
|
+
Tool[도구 사용 결과]
|
|
339
|
+
Prompt[사용자 프롬프트]
|
|
340
|
+
end
|
|
341
|
+
|
|
342
|
+
subgraph Process["처리"]
|
|
343
|
+
Privacy[Privacy 필터]
|
|
344
|
+
Compress[압축기]
|
|
345
|
+
Embed[Embedding 생성]
|
|
346
|
+
end
|
|
347
|
+
|
|
348
|
+
subgraph Storage["저장"]
|
|
349
|
+
Obs[(Observations)]
|
|
350
|
+
Session[(Sessions)]
|
|
351
|
+
FTS[(FTS5 Index)]
|
|
352
|
+
Vec[(Embedding)]
|
|
353
|
+
end
|
|
354
|
+
|
|
355
|
+
Tool --> Privacy
|
|
356
|
+
Privacy --> Compress
|
|
357
|
+
Compress --> Obs
|
|
358
|
+
Obs --> FTS
|
|
359
|
+
Obs --> Embed
|
|
360
|
+
Embed --> Vec
|
|
361
|
+
|
|
362
|
+
Prompt --> FTS
|
|
363
|
+
Prompt --> Vec
|
|
364
|
+
FTS --> Result[검색 결과]
|
|
365
|
+
Vec --> Result
|
|
366
|
+
```
|
|
367
|
+
|
|
368
|
+
### Observation 타입
|
|
369
|
+
|
|
370
|
+
도구 사용 결과는 타입별로 분류되어 저장됩니다:
|
|
371
|
+
|
|
372
|
+
| 타입 | 설명 | 기록 대상 |
|
|
373
|
+
|------|------|----------|
|
|
374
|
+
| `tool_use` | 도구 사용 결과 | Edit, Write 등 쓰기 도구 |
|
|
375
|
+
| `bash` | 명령 실행 결과 | Bash 명령어 |
|
|
376
|
+
| `error` | 에러 발생 | 모든 에러 (높은 중요도) |
|
|
377
|
+
| `success` | 성공 기록 | 테스트 통과, 빌드 성공 |
|
|
378
|
+
| `note` | 수동 메모 | `/mem-inject`로 주입된 내용 |
|
|
379
|
+
|
|
380
|
+
**중요도 자동 산정**:
|
|
381
|
+
- 에러 발생: 1.0 (최고)
|
|
382
|
+
- 테스트 통과/실패: 0.9
|
|
383
|
+
- 파일 생성/수정: 0.7
|
|
384
|
+
- 일반 명령: 0.5
|
|
385
|
+
|
|
156
386
|
## 아키텍처
|
|
157
387
|
|
|
158
388
|
```mermaid
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 특정 메모리 항목 삭제
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Memory Forget
|
|
6
|
+
|
|
7
|
+
특정 메모리 항목을 삭제합니다.
|
|
8
|
+
|
|
9
|
+
## 사용법
|
|
10
|
+
|
|
11
|
+
- `obs-id` - 특정 관찰 삭제
|
|
12
|
+
- `--session sess-id` - 특정 세션의 모든 관찰 삭제
|
|
13
|
+
- `--before 30d` - 30일 이전의 관찰 삭제
|
|
14
|
+
- `--type error` - 특정 타입의 관찰만 삭제
|
|
15
|
+
- `--dry-run` - 실제 삭제 없이 대상 확인
|
|
16
|
+
|
|
17
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 수동으로 컨텍스트를 메모리에 주입
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Memory Inject
|
|
6
|
+
|
|
7
|
+
수동으로 컨텍스트를 메모리에 저장합니다.
|
|
8
|
+
|
|
9
|
+
## 사용법
|
|
10
|
+
|
|
11
|
+
- `"context"` - 컨텍스트 저장
|
|
12
|
+
- `"context" --type note` - 타입 지정하여 저장
|
|
13
|
+
- `"context" --tags tag1,tag2` - 태그와 함께 저장
|
|
14
|
+
|
|
15
|
+
## 관찰 타입
|
|
16
|
+
|
|
17
|
+
- `note` - 일반 메모 (기본값)
|
|
18
|
+
- `tool_use` - 도구 사용 기록
|
|
19
|
+
- `bash` - 명령어 실행 기록
|
|
20
|
+
- `error` - 에러 기록
|
|
21
|
+
- `success` - 성공 기록
|
|
22
|
+
|
|
23
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 저장된 메모리 검색 (Progressive disclosure로 토큰 효율적 사용)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Memory Search
|
|
6
|
+
|
|
7
|
+
저장된 관찰 기록과 세션 정보를 검색합니다.
|
|
8
|
+
|
|
9
|
+
## 사용법
|
|
10
|
+
|
|
11
|
+
- `"keyword"` - 키워드로 검색 (Layer 1)
|
|
12
|
+
- `"keyword" --layer 2` - 타임라인 컨텍스트 포함
|
|
13
|
+
- `--layer 3 obs-id` - 특정 관찰의 전체 상세 정보
|
|
14
|
+
- `"keyword" --since 7d` - 최근 7일 내 검색
|
|
15
|
+
|
|
16
|
+
## 검색 레이어
|
|
17
|
+
|
|
18
|
+
| Layer | 내용 | 토큰 |
|
|
19
|
+
|-------|------|------|
|
|
20
|
+
| 1 | Index (ID + 점수) | 50-100/결과 |
|
|
21
|
+
| 2 | Timeline (시간순 컨텍스트) | 200-300/결과 |
|
|
22
|
+
| 3 | Full Details | 500-1000/결과 |
|
|
23
|
+
|
|
24
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: 메모리 상태와 통계 확인
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Memory Status
|
|
6
|
+
|
|
7
|
+
메모리 시스템의 현재 상태를 확인합니다.
|
|
8
|
+
|
|
9
|
+
## 사용법
|
|
10
|
+
|
|
11
|
+
- (인수 없음) - 기본 상태 표시
|
|
12
|
+
- `--detailed` - 상세 통계 표시
|
|
13
|
+
- `--json` - JSON 형식으로 출력
|
|
14
|
+
|
|
15
|
+
## 출력 정보
|
|
16
|
+
|
|
17
|
+
- 총 세션 수
|
|
18
|
+
- 총 관찰 수
|
|
19
|
+
- 타입별 관찰 분포
|
|
20
|
+
- 스토리지 사용량
|
|
21
|
+
- 최근 활동
|
|
22
|
+
|
|
23
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
description: Ralph Loop 제어 - 성공할 때까지 반복 실행 (start, stop, status, config)
|
|
3
|
+
---
|
|
4
|
+
|
|
5
|
+
# Ralph Loop
|
|
6
|
+
|
|
7
|
+
성공 기준을 달성할 때까지 작업을 자동으로 반복 실행합니다.
|
|
8
|
+
|
|
9
|
+
## 사용법
|
|
10
|
+
|
|
11
|
+
- `start "task"` - Loop 시작
|
|
12
|
+
- `start "task" --criteria lint_clean` - 커스텀 성공 기준으로 시작
|
|
13
|
+
- `stop` - Loop 중단
|
|
14
|
+
- `stop --rollback` - 변경사항 롤백하며 중단
|
|
15
|
+
- `status` - 현재 상태 확인
|
|
16
|
+
- `config` - 설정 확인/변경
|
|
17
|
+
|
|
18
|
+
## 성공 기준
|
|
19
|
+
|
|
20
|
+
- `test_pass` - 테스트 통과 (기본값)
|
|
21
|
+
- `build_success` - 빌드 성공
|
|
22
|
+
- `lint_clean` - Lint 오류 없음
|
|
23
|
+
- `type_check` - 타입 체크 통과
|
|
24
|
+
- `custom` - 사용자 정의 명령
|
|
25
|
+
|
|
26
|
+
$ARGUMENTS
|
|
@@ -69,7 +69,7 @@ var require_file_uri_to_path = __commonJS((exports, module) => {
|
|
|
69
69
|
|
|
70
70
|
// node_modules/bindings/bindings.js
|
|
71
71
|
var require_bindings = __commonJS((exports, module) => {
|
|
72
|
-
var __filename = "/
|
|
72
|
+
var __filename = "/home/runner/work/ralph-mem/ralph-mem/node_modules/bindings/bindings.js";
|
|
73
73
|
var fs = __require("fs");
|
|
74
74
|
var path = __require("path");
|
|
75
75
|
var fileURLToPath = require_file_uri_to_path();
|
|
@@ -3,13 +3,28 @@ import {
|
|
|
3
3
|
} from "../chunk-c3a91ngd.js";
|
|
4
4
|
import {
|
|
5
5
|
createDBClient
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-jz140n5a.js";
|
|
7
7
|
import {
|
|
8
8
|
ensureProjectDirs,
|
|
9
9
|
getProjectDBPath
|
|
10
10
|
} from "../chunk-w40c0y00.js";
|
|
11
11
|
import"../chunk-ns0dgdnb.js";
|
|
12
12
|
|
|
13
|
+
// src/utils/privacy.ts
|
|
14
|
+
function stripPrivateTags(content) {
|
|
15
|
+
const privateTagRegex = /<private>[\s\S]*?<\/private>/gi;
|
|
16
|
+
return content.replace(privateTagRegex, "[PRIVATE]");
|
|
17
|
+
}
|
|
18
|
+
function isEntirelyPrivate(content) {
|
|
19
|
+
const trimmed = content.trim();
|
|
20
|
+
const fullPrivateRegex = /^<private>[\s\S]*<\/private>$/i;
|
|
21
|
+
if (fullPrivateRegex.test(trimmed)) {
|
|
22
|
+
const stripped = stripPrivateTags(trimmed);
|
|
23
|
+
return stripped.trim() === "[PRIVATE]";
|
|
24
|
+
}
|
|
25
|
+
return false;
|
|
26
|
+
}
|
|
27
|
+
|
|
13
28
|
// src/hooks/post-tool-use.ts
|
|
14
29
|
var RECORDABLE_TOOLS = new Set([
|
|
15
30
|
"Edit",
|
|
@@ -97,6 +112,15 @@ async function postToolUseHook(context, options) {
|
|
|
97
112
|
content = `Error: ${context.error}
|
|
98
113
|
${content}`;
|
|
99
114
|
}
|
|
115
|
+
if (isEntirelyPrivate(content)) {
|
|
116
|
+
return {
|
|
117
|
+
observationId: null,
|
|
118
|
+
recorded: false,
|
|
119
|
+
type: null,
|
|
120
|
+
importance: 0
|
|
121
|
+
};
|
|
122
|
+
}
|
|
123
|
+
content = stripPrivateTags(content);
|
|
100
124
|
if (shouldExclude(content, config.privacy.exclude_patterns)) {
|
|
101
125
|
return {
|
|
102
126
|
observationId: null,
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import {
|
|
2
2
|
createMemoryStore
|
|
3
|
-
} from "../chunk-
|
|
3
|
+
} from "../chunk-gy7xx0jv.js";
|
|
4
4
|
import {
|
|
5
5
|
createDBClient
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-jz140n5a.js";
|
|
7
7
|
import {
|
|
8
8
|
ensureProjectDirs,
|
|
9
9
|
getProjectDBPath
|
|
@@ -86,4 +86,3 @@ export {
|
|
|
86
86
|
sessionEndHook,
|
|
87
87
|
generateSummary
|
|
88
88
|
};
|
|
89
|
-
export { sessionEndHook };
|
|
@@ -4,10 +4,10 @@ import {
|
|
|
4
4
|
import {
|
|
5
5
|
createMemoryStore,
|
|
6
6
|
estimateTokens
|
|
7
|
-
} from "../chunk-
|
|
7
|
+
} from "../chunk-gy7xx0jv.js";
|
|
8
8
|
import {
|
|
9
9
|
createDBClient
|
|
10
|
-
} from "../chunk-
|
|
10
|
+
} from "../chunk-jz140n5a.js";
|
|
11
11
|
import {
|
|
12
12
|
ensureProjectDirs,
|
|
13
13
|
getProjectDBPath
|
|
@@ -92,4 +92,3 @@ export {
|
|
|
92
92
|
formatSessionContext,
|
|
93
93
|
backupDatabase
|
|
94
94
|
};
|
|
95
|
-
export { sessionStartHook };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createDBClient
|
|
3
|
+
} from "../chunk-jz140n5a.js";
|
|
4
|
+
import {
|
|
5
|
+
ensureProjectDirs,
|
|
6
|
+
getProjectDBPath
|
|
7
|
+
} from "../chunk-w40c0y00.js";
|
|
8
|
+
import"../chunk-ns0dgdnb.js";
|
|
9
|
+
|
|
10
|
+
// src/hooks/stop.ts
|
|
11
|
+
async function stopHook(context, options) {
|
|
12
|
+
const {
|
|
13
|
+
sessionId,
|
|
14
|
+
projectPath,
|
|
15
|
+
signal = "SIGINT",
|
|
16
|
+
activeLoopRunId
|
|
17
|
+
} = context;
|
|
18
|
+
let client;
|
|
19
|
+
if (options?.client) {
|
|
20
|
+
client = options.client;
|
|
21
|
+
} else {
|
|
22
|
+
ensureProjectDirs(projectPath);
|
|
23
|
+
const dbPath = getProjectDBPath(projectPath);
|
|
24
|
+
client = createDBClient(dbPath);
|
|
25
|
+
}
|
|
26
|
+
try {
|
|
27
|
+
const session = client.getSession(sessionId);
|
|
28
|
+
if (!session || session.ended_at) {
|
|
29
|
+
return {
|
|
30
|
+
sessionEnded: false,
|
|
31
|
+
loopStopped: false,
|
|
32
|
+
summary: "세션이 이미 종료되었거나 존재하지 않습니다."
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
let loopStopped = false;
|
|
36
|
+
if (activeLoopRunId) {
|
|
37
|
+
client.db.prepare(`
|
|
38
|
+
UPDATE loop_runs
|
|
39
|
+
SET status = 'stopped', ended_at = datetime('now')
|
|
40
|
+
WHERE id = ? AND status = 'running'
|
|
41
|
+
`).run(activeLoopRunId);
|
|
42
|
+
loopStopped = true;
|
|
43
|
+
}
|
|
44
|
+
const observations = client.listObservations(sessionId, 1000);
|
|
45
|
+
const summary = `[${signal}] 세션 강제 종료. 작업 ${observations.length}건 기록됨.`;
|
|
46
|
+
client.endSession(sessionId, summary);
|
|
47
|
+
return {
|
|
48
|
+
sessionEnded: true,
|
|
49
|
+
loopStopped,
|
|
50
|
+
summary
|
|
51
|
+
};
|
|
52
|
+
} finally {
|
|
53
|
+
if (!options?.client) {
|
|
54
|
+
client.close();
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
export {
|
|
59
|
+
stopHook
|
|
60
|
+
};
|
|
61
|
+
|
|
@@ -3,13 +3,13 @@ import {
|
|
|
3
3
|
} from "../chunk-c3a91ngd.js";
|
|
4
4
|
import {
|
|
5
5
|
estimateTokens
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-gy7xx0jv.js";
|
|
7
7
|
import {
|
|
8
8
|
createSearchEngine
|
|
9
|
-
} from "../chunk-
|
|
9
|
+
} from "../chunk-rcnembz5.js";
|
|
10
10
|
import {
|
|
11
11
|
createDBClient
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-jz140n5a.js";
|
|
13
13
|
import {
|
|
14
14
|
ensureProjectDirs,
|
|
15
15
|
getProjectDBPath
|
|
@@ -231,4 +231,3 @@ export {
|
|
|
231
231
|
extractKeywords
|
|
232
232
|
};
|
|
233
233
|
|
|
234
|
-
export { userPromptSubmitHook };
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
import"./chunk-c3a91ngd.js";
|
|
2
|
+
import {
|
|
3
|
+
createMemoryStore,
|
|
4
|
+
estimateTokens
|
|
5
|
+
} from "./chunk-gy7xx0jv.js";
|
|
6
|
+
import {
|
|
7
|
+
createSearchEngine
|
|
8
|
+
} from "./chunk-rcnembz5.js";
|
|
9
|
+
import {
|
|
10
|
+
createDBClient
|
|
11
|
+
} from "./chunk-jz140n5a.js";
|
|
12
|
+
import {
|
|
13
|
+
ensureProjectDirs,
|
|
14
|
+
getProjectDBPath
|
|
15
|
+
} from "./chunk-w40c0y00.js";
|
|
16
|
+
import"./chunk-ns0dgdnb.js";
|
|
17
|
+
import {
|
|
18
|
+
memInjectSkill
|
|
19
|
+
} from "./skills/mem-inject.js";
|
|
1
20
|
import {
|
|
2
21
|
ralphSkill
|
|
3
22
|
} from "./skills/ralph.js";
|
|
@@ -7,36 +26,282 @@ import {
|
|
|
7
26
|
import {
|
|
8
27
|
userPromptSubmitHook
|
|
9
28
|
} from "./hooks/user-prompt-submit.js";
|
|
29
|
+
import {
|
|
30
|
+
stopHook
|
|
31
|
+
} from "./hooks/stop.js";
|
|
10
32
|
import {
|
|
11
33
|
postToolUseHook
|
|
12
34
|
} from "./hooks/post-tool-use.js";
|
|
13
35
|
import {
|
|
14
36
|
sessionStartHook
|
|
15
37
|
} from "./hooks/session-start.js";
|
|
16
|
-
import"./chunk-c3a91ngd.js";
|
|
17
38
|
import {
|
|
18
39
|
sessionEndHook
|
|
19
40
|
} from "./hooks/session-end.js";
|
|
20
|
-
import {
|
|
21
|
-
createMemoryStore,
|
|
22
|
-
estimateTokens
|
|
23
|
-
} from "./chunk-v8anyhk1.js";
|
|
24
41
|
import {
|
|
25
42
|
memForgetSkill
|
|
26
43
|
} from "./skills/mem-forget.js";
|
|
27
|
-
import {
|
|
28
|
-
memInjectSkill
|
|
29
|
-
} from "./skills/mem-inject.js";
|
|
30
44
|
import {
|
|
31
45
|
memSearchSkill
|
|
32
46
|
} from "./skills/mem-search.js";
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
47
|
+
// src/mcp/server.ts
|
|
48
|
+
var MCP_TOOLS = [
|
|
49
|
+
{
|
|
50
|
+
name: "ralph_mem_search",
|
|
51
|
+
description: "Search memories using Progressive Disclosure. Layer 1 returns compact results (~50-100 tokens), Layer 2 adds context (~200-300 tokens), Layer 3 returns full details (~500-1000 tokens).",
|
|
52
|
+
inputSchema: {
|
|
53
|
+
type: "object",
|
|
54
|
+
properties: {
|
|
55
|
+
query: {
|
|
56
|
+
type: "string",
|
|
57
|
+
description: "Search query"
|
|
58
|
+
},
|
|
59
|
+
layer: {
|
|
60
|
+
type: "number",
|
|
61
|
+
description: "Detail level (1=index, 2=context, 3=full)",
|
|
62
|
+
enum: [1, 2, 3],
|
|
63
|
+
default: 1
|
|
64
|
+
},
|
|
65
|
+
limit: {
|
|
66
|
+
type: "number",
|
|
67
|
+
description: "Maximum results to return",
|
|
68
|
+
default: 10
|
|
69
|
+
},
|
|
70
|
+
since: {
|
|
71
|
+
type: "string",
|
|
72
|
+
description: "Filter results after this date (ISO format or relative like '7d')"
|
|
73
|
+
},
|
|
74
|
+
types: {
|
|
75
|
+
type: "array",
|
|
76
|
+
items: { type: "string" },
|
|
77
|
+
description: "Filter by observation types (tool_use, bash, error, success, note)"
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
required: ["query"]
|
|
81
|
+
}
|
|
82
|
+
},
|
|
83
|
+
{
|
|
84
|
+
name: "ralph_mem_timeline",
|
|
85
|
+
description: "Get time-ordered observations around a specific observation. Useful for understanding context.",
|
|
86
|
+
inputSchema: {
|
|
87
|
+
type: "object",
|
|
88
|
+
properties: {
|
|
89
|
+
observationId: {
|
|
90
|
+
type: "string",
|
|
91
|
+
description: "The observation ID to center the timeline around"
|
|
92
|
+
},
|
|
93
|
+
before: {
|
|
94
|
+
type: "number",
|
|
95
|
+
description: "Number of observations before",
|
|
96
|
+
default: 3
|
|
97
|
+
},
|
|
98
|
+
after: {
|
|
99
|
+
type: "number",
|
|
100
|
+
description: "Number of observations after",
|
|
101
|
+
default: 3
|
|
102
|
+
}
|
|
103
|
+
},
|
|
104
|
+
required: ["observationId"]
|
|
105
|
+
}
|
|
106
|
+
},
|
|
107
|
+
{
|
|
108
|
+
name: "ralph_mem_get",
|
|
109
|
+
description: "Get full details of a specific observation by ID.",
|
|
110
|
+
inputSchema: {
|
|
111
|
+
type: "object",
|
|
112
|
+
properties: {
|
|
113
|
+
id: {
|
|
114
|
+
type: "string",
|
|
115
|
+
description: "Observation ID"
|
|
116
|
+
}
|
|
117
|
+
},
|
|
118
|
+
required: ["id"]
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
];
|
|
122
|
+
function parseRelativeDate(str) {
|
|
123
|
+
const match = str.match(/^(\d+)([dhm])$/);
|
|
124
|
+
if (!match)
|
|
125
|
+
return null;
|
|
126
|
+
const value = Number.parseInt(match[1], 10);
|
|
127
|
+
const unit = match[2];
|
|
128
|
+
const now = new Date;
|
|
129
|
+
switch (unit) {
|
|
130
|
+
case "d":
|
|
131
|
+
return new Date(now.getTime() - value * 24 * 60 * 60 * 1000);
|
|
132
|
+
case "h":
|
|
133
|
+
return new Date(now.getTime() - value * 60 * 60 * 1000);
|
|
134
|
+
case "m":
|
|
135
|
+
return new Date(now.getTime() - value * 60 * 1000);
|
|
136
|
+
default:
|
|
137
|
+
return null;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function handleToolCall(call, projectPath) {
|
|
141
|
+
try {
|
|
142
|
+
ensureProjectDirs(projectPath);
|
|
143
|
+
const dbPath = getProjectDBPath(projectPath);
|
|
144
|
+
switch (call.name) {
|
|
145
|
+
case "ralph_mem_search":
|
|
146
|
+
return handleSearch(call.arguments, dbPath);
|
|
147
|
+
case "ralph_mem_timeline":
|
|
148
|
+
return handleTimeline(call.arguments, dbPath);
|
|
149
|
+
case "ralph_mem_get":
|
|
150
|
+
return handleGet(call.arguments, dbPath);
|
|
151
|
+
default:
|
|
152
|
+
return {
|
|
153
|
+
content: [{ type: "text", text: `Unknown tool: ${call.name}` }],
|
|
154
|
+
isError: true
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
} catch (error) {
|
|
158
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
159
|
+
return {
|
|
160
|
+
content: [{ type: "text", text: `Error: ${message}` }],
|
|
161
|
+
isError: true
|
|
162
|
+
};
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
function handleSearch(args, dbPath) {
|
|
166
|
+
const query = args.query;
|
|
167
|
+
const layer = args.layer ?? 1;
|
|
168
|
+
const limit = args.limit ?? 10;
|
|
169
|
+
const sinceStr = args.since;
|
|
170
|
+
const types = args.types;
|
|
171
|
+
const options = { layer, limit, types };
|
|
172
|
+
if (sinceStr) {
|
|
173
|
+
const relativeDate = parseRelativeDate(sinceStr);
|
|
174
|
+
if (relativeDate) {
|
|
175
|
+
options.since = relativeDate;
|
|
176
|
+
} else {
|
|
177
|
+
const isoDate = new Date(sinceStr);
|
|
178
|
+
if (!Number.isNaN(isoDate.getTime())) {
|
|
179
|
+
options.since = isoDate;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
const engine = createSearchEngine(dbPath);
|
|
184
|
+
try {
|
|
185
|
+
const results = engine.search(query, options);
|
|
186
|
+
if (results.length === 0) {
|
|
187
|
+
return {
|
|
188
|
+
content: [{ type: "text", text: "No results found." }]
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
const formatted = results.map((r, i) => {
|
|
192
|
+
const parts = [`${i + 1}. [${r.id}] (score: ${r.score.toFixed(2)})`];
|
|
193
|
+
if (r.summary) {
|
|
194
|
+
parts.push(` ${r.summary}`);
|
|
195
|
+
}
|
|
196
|
+
if (layer >= 2 && r.createdAt) {
|
|
197
|
+
parts.push(` Time: ${r.createdAt.toISOString()}`);
|
|
198
|
+
if (r.type)
|
|
199
|
+
parts.push(` Type: ${r.type}`);
|
|
200
|
+
if (r.toolName)
|
|
201
|
+
parts.push(` Tool: ${r.toolName}`);
|
|
202
|
+
}
|
|
203
|
+
if (layer >= 3 && r.content) {
|
|
204
|
+
parts.push(` Content: ${r.content}`);
|
|
205
|
+
}
|
|
206
|
+
return parts.join(`
|
|
207
|
+
`);
|
|
208
|
+
});
|
|
209
|
+
return {
|
|
210
|
+
content: [
|
|
211
|
+
{
|
|
212
|
+
type: "text",
|
|
213
|
+
text: `Found ${results.length} results:
|
|
214
|
+
|
|
215
|
+
${formatted.join(`
|
|
39
216
|
|
|
217
|
+
`)}`
|
|
218
|
+
}
|
|
219
|
+
]
|
|
220
|
+
};
|
|
221
|
+
} finally {
|
|
222
|
+
engine.close();
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
function handleTimeline(args, dbPath) {
|
|
226
|
+
const observationId = args.observationId;
|
|
227
|
+
const before = args.before ?? 3;
|
|
228
|
+
const after = args.after ?? 3;
|
|
229
|
+
const client = createDBClient(dbPath);
|
|
230
|
+
try {
|
|
231
|
+
const target = client.getObservation(observationId);
|
|
232
|
+
if (!target) {
|
|
233
|
+
return {
|
|
234
|
+
content: [
|
|
235
|
+
{ type: "text", text: `Observation not found: ${observationId}` }
|
|
236
|
+
],
|
|
237
|
+
isError: true
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
const beforeObs = client.db.prepare(`
|
|
241
|
+
SELECT * FROM observations
|
|
242
|
+
WHERE session_id = ? AND created_at < ?
|
|
243
|
+
ORDER BY created_at DESC
|
|
244
|
+
LIMIT ?
|
|
245
|
+
`).all(target.session_id, target.created_at, before);
|
|
246
|
+
const afterObs = client.db.prepare(`
|
|
247
|
+
SELECT * FROM observations
|
|
248
|
+
WHERE session_id = ? AND created_at > ?
|
|
249
|
+
ORDER BY created_at ASC
|
|
250
|
+
LIMIT ?
|
|
251
|
+
`).all(target.session_id, target.created_at, after);
|
|
252
|
+
const formatObs = (o, marker = "") => {
|
|
253
|
+
const summary = o.content.slice(0, 100) + (o.content.length > 100 ? "..." : "");
|
|
254
|
+
return `${marker}[${o.id}] ${o.created_at}
|
|
255
|
+
Type: ${o.type}${o.tool_name ? `, Tool: ${o.tool_name}` : ""}
|
|
256
|
+
${summary}`;
|
|
257
|
+
};
|
|
258
|
+
const lines = [
|
|
259
|
+
"=== Timeline ===",
|
|
260
|
+
"",
|
|
261
|
+
...beforeObs.reverse().map((o) => formatObs(o)),
|
|
262
|
+
"",
|
|
263
|
+
formatObs(target, ">>> "),
|
|
264
|
+
"",
|
|
265
|
+
...afterObs.map((o) => formatObs(o))
|
|
266
|
+
];
|
|
267
|
+
return {
|
|
268
|
+
content: [{ type: "text", text: lines.join(`
|
|
269
|
+
`) }]
|
|
270
|
+
};
|
|
271
|
+
} finally {
|
|
272
|
+
client.close();
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
function handleGet(args, dbPath) {
|
|
276
|
+
const id = args.id;
|
|
277
|
+
const client = createDBClient(dbPath);
|
|
278
|
+
try {
|
|
279
|
+
const obs = client.getObservation(id);
|
|
280
|
+
if (!obs) {
|
|
281
|
+
return {
|
|
282
|
+
content: [{ type: "text", text: `Observation not found: ${id}` }],
|
|
283
|
+
isError: true
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
const details = [
|
|
287
|
+
`ID: ${obs.id}`,
|
|
288
|
+
`Session: ${obs.session_id}`,
|
|
289
|
+
`Type: ${obs.type}`,
|
|
290
|
+
`Tool: ${obs.tool_name ?? "N/A"}`,
|
|
291
|
+
`Importance: ${obs.importance}`,
|
|
292
|
+
`Created: ${obs.created_at}`,
|
|
293
|
+
"",
|
|
294
|
+
"Content:",
|
|
295
|
+
obs.content
|
|
296
|
+
];
|
|
297
|
+
return {
|
|
298
|
+
content: [{ type: "text", text: details.join(`
|
|
299
|
+
`) }]
|
|
300
|
+
};
|
|
301
|
+
} finally {
|
|
302
|
+
client.close();
|
|
303
|
+
}
|
|
304
|
+
}
|
|
40
305
|
// src/index.ts
|
|
41
306
|
var VERSION = "0.1.0";
|
|
42
307
|
async function activate() {
|
|
@@ -47,6 +312,7 @@ async function deactivate() {
|
|
|
47
312
|
}
|
|
48
313
|
export {
|
|
49
314
|
userPromptSubmitHook,
|
|
315
|
+
stopHook,
|
|
50
316
|
sessionStartHook,
|
|
51
317
|
sessionEndHook,
|
|
52
318
|
ralphSkill,
|
|
@@ -55,10 +321,12 @@ export {
|
|
|
55
321
|
memSearchSkill,
|
|
56
322
|
memInjectSkill,
|
|
57
323
|
memForgetSkill,
|
|
324
|
+
handleToolCall,
|
|
58
325
|
estimateTokens,
|
|
59
326
|
deactivate,
|
|
60
327
|
createSearchEngine,
|
|
61
328
|
createMemoryStore,
|
|
62
329
|
activate,
|
|
63
|
-
VERSION
|
|
330
|
+
VERSION,
|
|
331
|
+
MCP_TOOLS
|
|
64
332
|
};
|
package/dist/skills/ralph.js
CHANGED
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "../chunk-c3a91ngd.js";
|
|
4
4
|
import {
|
|
5
5
|
createDBClient
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-jz140n5a.js";
|
|
7
7
|
import {
|
|
8
8
|
ensureProjectDirs,
|
|
9
9
|
getProjectDBPath,
|
|
@@ -651,4 +651,3 @@ export {
|
|
|
651
651
|
createRalphSkill
|
|
652
652
|
};
|
|
653
653
|
|
|
654
|
-
export { ralphSkill };
|
package/hooks/hooks.json
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
{
|
|
2
|
+
"hooks": {
|
|
3
|
+
"PostToolUse": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": ".*",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node $CLAUDE_PLUGIN_ROOT/dist/hooks/post-tool-use.js"
|
|
10
|
+
}
|
|
11
|
+
]
|
|
12
|
+
}
|
|
13
|
+
],
|
|
14
|
+
"Stop": [
|
|
15
|
+
{
|
|
16
|
+
"matcher": ".*",
|
|
17
|
+
"hooks": [
|
|
18
|
+
{
|
|
19
|
+
"type": "command",
|
|
20
|
+
"command": "node $CLAUDE_PLUGIN_ROOT/dist/hooks/stop.js"
|
|
21
|
+
}
|
|
22
|
+
]
|
|
23
|
+
}
|
|
24
|
+
]
|
|
25
|
+
}
|
|
26
|
+
}
|
package/package.json
CHANGED
|
@@ -1,13 +1,16 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ralph-mem",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "Persistent context management plugin for Claude Code with Ralph Loop",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"files": [
|
|
9
9
|
"dist",
|
|
10
|
-
"plugin
|
|
10
|
+
".claude-plugin",
|
|
11
|
+
"commands",
|
|
12
|
+
"skills",
|
|
13
|
+
"hooks",
|
|
11
14
|
"prompts"
|
|
12
15
|
],
|
|
13
16
|
"repository": {
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mem-forget
|
|
3
|
+
description: 특정 메모리 항목을 삭제합니다. 더 이상 필요 없는 컨텍스트를 정리할 때 사용합니다.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory Forget
|
|
7
|
+
|
|
8
|
+
특정 메모리 항목을 삭제합니다.
|
|
9
|
+
|
|
10
|
+
## 사용법
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/mem-forget obs-a1b2c3d4
|
|
14
|
+
/mem-forget --session sess-xyz123
|
|
15
|
+
/mem-forget --before 30d
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 옵션
|
|
19
|
+
|
|
20
|
+
- `--session <id>` - 특정 세션의 모든 관찰 삭제
|
|
21
|
+
- `--before <duration>` - 지정 기간 이전의 관찰 삭제
|
|
22
|
+
- `--type <type>` - 특정 타입의 관찰만 삭제
|
|
23
|
+
- `--dry-run` - 실제 삭제 없이 대상 확인
|
|
24
|
+
|
|
25
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mem-inject
|
|
3
|
+
description: 수동으로 컨텍스트를 메모리에 주입합니다. 중요한 정보를 영구 저장할 때 사용합니다.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory Inject
|
|
7
|
+
|
|
8
|
+
수동으로 컨텍스트를 메모리에 저장합니다.
|
|
9
|
+
|
|
10
|
+
## 사용법
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/mem-inject "이 프로젝트는 Express + Prisma 기반"
|
|
14
|
+
/mem-inject "API 엔드포인트는 /api/v1 prefix 사용" --type note
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 옵션
|
|
18
|
+
|
|
19
|
+
- `--type <type>` - 관찰 타입 (note, tool_use, bash, error, success)
|
|
20
|
+
- `--tags <tags>` - 태그 (쉼표로 구분)
|
|
21
|
+
|
|
22
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mem-search
|
|
3
|
+
description: 저장된 메모리를 검색합니다. Progressive disclosure로 토큰을 효율적으로 사용합니다.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory Search
|
|
7
|
+
|
|
8
|
+
저장된 관찰 기록과 세션 정보를 검색합니다.
|
|
9
|
+
|
|
10
|
+
## 사용법
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/mem-search "authentication error"
|
|
14
|
+
/mem-search "JWT" --since 7d
|
|
15
|
+
/mem-search --layer 3 obs-a1b2c3d4
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
## 옵션
|
|
19
|
+
|
|
20
|
+
- `--layer <1|2|3>` - 검색 레이어 지정
|
|
21
|
+
- Layer 1: Index (ID + 점수) - 50-100 토큰/결과
|
|
22
|
+
- Layer 2: Timeline (시간순 컨텍스트) - 200-300 토큰/결과
|
|
23
|
+
- Layer 3: Full Details - 500-1000 토큰/결과
|
|
24
|
+
- `--since <duration>` - 시간 범위 (예: 7d, 24h)
|
|
25
|
+
- `--limit <n>` - 최대 결과 수
|
|
26
|
+
|
|
27
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: mem-status
|
|
3
|
+
description: 메모리 상태와 통계를 확인합니다. 저장된 세션 수, 관찰 수, 스토리지 사용량 등을 표시합니다.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Memory Status
|
|
7
|
+
|
|
8
|
+
메모리 시스템의 현재 상태를 확인합니다.
|
|
9
|
+
|
|
10
|
+
## 사용법
|
|
11
|
+
|
|
12
|
+
```
|
|
13
|
+
/mem-status
|
|
14
|
+
/mem-status --detailed
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 출력 정보
|
|
18
|
+
|
|
19
|
+
- 총 세션 수
|
|
20
|
+
- 총 관찰 수
|
|
21
|
+
- 타입별 관찰 분포
|
|
22
|
+
- 스토리지 사용량
|
|
23
|
+
- 최근 활동
|
|
24
|
+
|
|
25
|
+
## 옵션
|
|
26
|
+
|
|
27
|
+
- `--detailed` - 상세 통계 표시
|
|
28
|
+
- `--json` - JSON 형식으로 출력
|
|
29
|
+
|
|
30
|
+
$ARGUMENTS
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ralph
|
|
3
|
+
description: Ralph Loop 제어 - 성공할 때까지 반복 실행합니다. start, stop, status, config 서브커맨드를 지원합니다.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Ralph Loop
|
|
7
|
+
|
|
8
|
+
성공 기준을 달성할 때까지 작업을 자동으로 반복 실행합니다.
|
|
9
|
+
|
|
10
|
+
## 사용법
|
|
11
|
+
|
|
12
|
+
### 시작
|
|
13
|
+
```
|
|
14
|
+
/ralph start "Implement feature X"
|
|
15
|
+
/ralph start "Fix lint errors" --criteria lint_clean
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
### 상태 확인
|
|
19
|
+
```
|
|
20
|
+
/ralph status
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
### 중단
|
|
24
|
+
```
|
|
25
|
+
/ralph stop
|
|
26
|
+
/ralph stop --rollback # 변경사항 롤백
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
### 설정
|
|
30
|
+
```
|
|
31
|
+
/ralph config
|
|
32
|
+
/ralph config --max-iterations 15
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## 성공 기준
|
|
36
|
+
|
|
37
|
+
- `test_pass` - 테스트 통과 (기본값)
|
|
38
|
+
- `build_success` - 빌드 성공
|
|
39
|
+
- `lint_clean` - Lint 오류 없음
|
|
40
|
+
- `type_check` - 타입 체크 통과
|
|
41
|
+
- `custom` - 사용자 정의 명령
|
|
42
|
+
|
|
43
|
+
$ARGUMENTS
|
package/plugin.json
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "ralph-mem",
|
|
3
|
-
"version": "0.1.0",
|
|
4
|
-
"description": "Persistent context management with Ralph Loop",
|
|
5
|
-
"main": "dist/index.js",
|
|
6
|
-
"hooks": [
|
|
7
|
-
{
|
|
8
|
-
"event": "SessionStart",
|
|
9
|
-
"handler": "dist/hooks/session-start.js"
|
|
10
|
-
},
|
|
11
|
-
{
|
|
12
|
-
"event": "SessionEnd",
|
|
13
|
-
"handler": "dist/hooks/session-end.js"
|
|
14
|
-
},
|
|
15
|
-
{
|
|
16
|
-
"event": "UserPromptSubmit",
|
|
17
|
-
"handler": "dist/hooks/user-prompt-submit.js"
|
|
18
|
-
},
|
|
19
|
-
{
|
|
20
|
-
"event": "PostToolUse",
|
|
21
|
-
"handler": "dist/hooks/post-tool-use.js"
|
|
22
|
-
}
|
|
23
|
-
],
|
|
24
|
-
"skills": [
|
|
25
|
-
{
|
|
26
|
-
"name": "ralph",
|
|
27
|
-
"description": "Ralph Loop control - start, stop, status",
|
|
28
|
-
"handler": "dist/skills/ralph.js"
|
|
29
|
-
},
|
|
30
|
-
{
|
|
31
|
-
"name": "mem-search",
|
|
32
|
-
"description": "Search memory with progressive disclosure",
|
|
33
|
-
"handler": "dist/skills/mem-search.js"
|
|
34
|
-
},
|
|
35
|
-
{
|
|
36
|
-
"name": "mem-inject",
|
|
37
|
-
"description": "Manually inject context into memory",
|
|
38
|
-
"handler": "dist/skills/mem-inject.js"
|
|
39
|
-
},
|
|
40
|
-
{
|
|
41
|
-
"name": "mem-forget",
|
|
42
|
-
"description": "Remove specific memory entries",
|
|
43
|
-
"handler": "dist/skills/mem-forget.js"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"name": "mem-status",
|
|
47
|
-
"description": "View memory status and statistics",
|
|
48
|
-
"handler": "dist/skills/mem-status.js"
|
|
49
|
-
}
|
|
50
|
-
]
|
|
51
|
-
}
|