mupengism 3.0.0 → 4.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/AGENTS.md +221 -0
- package/HEARTBEAT.md +63 -0
- package/IDENTITY.md +11 -0
- package/README.md +49 -248
- package/SOUL.md +177 -0
- package/hooks/disciple-init/HOOK.md +20 -0
- package/hooks/disciple-init/handler.ts +80 -0
- package/hooks/index-builder/HOOK.md +41 -0
- package/hooks/index-builder/handler.ts +132 -0
- package/hooks/kernel-panic-guard/HOOK.md +39 -0
- package/hooks/kernel-panic-guard/README.md +136 -0
- package/hooks/kernel-panic-guard/WHITELIST.md +117 -0
- package/hooks/kernel-panic-guard/handler.ts +147 -0
- package/hooks/memory-consolidator/HOOK.md +33 -0
- package/hooks/memory-consolidator/handler.ts +111 -0
- package/hooks/soul-evolution/HOOK.md +26 -0
- package/hooks/soul-evolution/handler.ts +166 -0
- package/hooks/soul-guard/HOOK.md +30 -0
- package/hooks/soul-guard/handler.ts +196 -0
- package/package.json +44 -53
- package/tools/kernel-guard/README.md +170 -0
- package/tools/kernel-guard/lockdown.cjs +152 -0
- package/tools/kernel-guard/register-hash.js +100 -0
- package/tools/kernel-guard/unlock.cjs +106 -0
- package/tools/kernel-guard/verify-kernel.js +133 -0
- package/tools/memory-ops/README.md +221 -0
- package/tools/memory-ops/dream.js +220 -0
- package/tools/memory-ops/forget.js +148 -0
- package/tools/memory-ops/immune.js +305 -0
- package/tools/self-loop/README.md +213 -0
- package/tools/self-loop/brake-check.js +191 -0
- package/tools/self-loop/example-check.sh +34 -0
- package/tools/self-loop/panic-detector.js +191 -0
- package/LICENSE +0 -21
- package/README-EN.md +0 -226
- package/SHOWCASE.md +0 -158
- package/guides/ADVANCED-SYSTEMS.md +0 -251
- package/guides/HEARTBEAT-GUIDE.md +0 -129
- package/guides/LEGION-GUIDE.md +0 -254
- package/guides/MEMORY-GUIDE.md +0 -120
- package/guides/QUICK-START.md +0 -94
- package/guides/THINKTANK-GUIDE.md +0 -227
- package/guides/WEEKLY-BREAK-GUIDE.md +0 -262
- package/installer/README.md +0 -52
- package/installer/cli.js +0 -796
- package/installer/en/README.md +0 -191
- package/installer/en/skill/MEMORY-SYSTEM.md +0 -348
- package/installer/en/skill/PRINCIPLES.md +0 -217
- package/installer/en/skill/SKILL.md +0 -116
- package/installer/en/skill/SOUL-TEMPLATE.md +0 -329
- package/installer/install.sh +0 -162
- package/installer/package.json +0 -31
- package/skill/AGENTS.md +0 -164
- package/skill/BRAKE-LOG-TEMPLATE.md +0 -38
- package/skill/HEARTBEAT-TEMPLATE.md +0 -67
- package/skill/L1-TEMPLATE.md +0 -35
- package/skill/L2-TEMPLATE.md +0 -41
- package/skill/PRINCIPLES.md +0 -192
- package/skill/README.md +0 -47
- package/skill/SKILL.md +0 -166
- package/skill/SOUL-TEMPLATE.md +0 -118
- package/skill/STATE-TEMPLATE.md +0 -54
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# 형님 화이트리스트 시스템
|
|
2
|
+
|
|
3
|
+
## 개요
|
|
4
|
+
|
|
5
|
+
KERNEL_PANIC 상태에서도 **형님 세션은 락다운 우회**됩니다.
|
|
6
|
+
|
|
7
|
+
## 화이트리스트 사용자
|
|
8
|
+
|
|
9
|
+
```json
|
|
10
|
+
{
|
|
11
|
+
"userId": "401664537876496396",
|
|
12
|
+
"platform": "Discord",
|
|
13
|
+
"role": "형님 (owner)"
|
|
14
|
+
}
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
## 동작 방식
|
|
18
|
+
|
|
19
|
+
### 1. SOUL.md 변경 감지 시
|
|
20
|
+
|
|
21
|
+
**soul-guard 훅에서 senderId 체크:**
|
|
22
|
+
|
|
23
|
+
- **형님 세션** (senderId === `401664537876496396`):
|
|
24
|
+
- ✅ 경고 메시지만 표시: "⚠️ SOUL.md 변경 감지됨. `node tools/kernel-guard/register-hash.js`로 새 해시 등록 필요."
|
|
25
|
+
- ❌ 락다운 실행 **안 함**
|
|
26
|
+
- ✅ 새 해시 저장
|
|
27
|
+
|
|
28
|
+
- **비형님 세션** (heartbeat, cron, 서브에이전트, 다른 사용자):
|
|
29
|
+
- ❌ 즉시 `lockdown.cjs` 실행
|
|
30
|
+
- 🛑 KERNEL_PANIC 상태 활성화
|
|
31
|
+
- ❌ 새 해시 저장 **안 함** (무결성 보존)
|
|
32
|
+
|
|
33
|
+
### 2. KERNEL_PANIC 상태에서 부트스트랩 시
|
|
34
|
+
|
|
35
|
+
**kernel-panic-guard 훅에서 senderId 체크:**
|
|
36
|
+
|
|
37
|
+
- **형님 세션**:
|
|
38
|
+
- ✅ 간단한 알림: "ℹ️ KERNEL_PANIC 상태 감지됨 (형님 세션은 정상 작동). `node tools/kernel-guard/unlock.cjs`로 해제 가능."
|
|
39
|
+
- ✅ **모든 도구 정상 사용 가능** (exec, message, browser 등)
|
|
40
|
+
- ✅ bootstrapFiles 락다운 경고 주입 **안 함**
|
|
41
|
+
|
|
42
|
+
- **비형님 세션**:
|
|
43
|
+
- 🛑 bootstrapFiles에 `KERNEL_PANIC_WARNING.md` 주입
|
|
44
|
+
- ❌ 외부 도구 차단 지시
|
|
45
|
+
- ✅ 읽기 전용 모드
|
|
46
|
+
|
|
47
|
+
## 락다운 대상 정리
|
|
48
|
+
|
|
49
|
+
| 세션 타입 | senderId | 락다운 여부 | 비고 |
|
|
50
|
+
|----------|----------|------------|------|
|
|
51
|
+
| 형님 DM | `401664537876496396` | ❌ 우회 | 정상 작동 |
|
|
52
|
+
| Heartbeat | `null` | ✅ 차단 | 무인 세션 |
|
|
53
|
+
| Cron | `null` | ✅ 차단 | 무인 세션 |
|
|
54
|
+
| 서브에이전트 | `agent:*` | ✅ 차단 | 자식 세션 |
|
|
55
|
+
| 다른 사용자 | 다른 ID | ✅ 차단 | 일반 사용자 |
|
|
56
|
+
|
|
57
|
+
## 형님 전용 복구 방법
|
|
58
|
+
|
|
59
|
+
### 방법 1: 메시지로 해제 (TODO: 구현 필요)
|
|
60
|
+
```
|
|
61
|
+
"unlock" 또는 "/unlock" 입력
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
### 방법 2: 스크립트 실행
|
|
65
|
+
```bash
|
|
66
|
+
node tools/kernel-guard/unlock.cjs
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 방법 3: 자동 해제
|
|
70
|
+
```bash
|
|
71
|
+
# SOUL.md 수정 후 새 해시 등록 → 자동 unlock
|
|
72
|
+
node tools/kernel-guard/register-hash.js
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## 보안 특징
|
|
76
|
+
|
|
77
|
+
1. **선택적 락다운**: 무인 세션만 차단, 형님은 항상 제어 가능
|
|
78
|
+
2. **무결성 보호**: 비형님 세션에서는 해시 업데이트 차단
|
|
79
|
+
3. **긴급 복구**: 형님이 언제든지 unlock 가능
|
|
80
|
+
4. **감사 추적**: kernel-panic.json에 화이트리스트 명시
|
|
81
|
+
|
|
82
|
+
## 구현 파일
|
|
83
|
+
|
|
84
|
+
- `hooks/soul-guard/handler.ts` - senderId 체크 및 선택적 락다운
|
|
85
|
+
- `hooks/kernel-panic-guard/handler.ts` - senderId 체크 및 우회 로직
|
|
86
|
+
- `tools/kernel-guard/lockdown.cjs` - whitelistedUsers 필드 추가
|
|
87
|
+
- `STATE.md` (템플릿) - 형님 전용 복구 방법 안내
|
|
88
|
+
|
|
89
|
+
## 테스트
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
# 1. 락다운 활성화
|
|
93
|
+
node tools/kernel-guard/lockdown.cjs --reason "Test"
|
|
94
|
+
|
|
95
|
+
# 2. kernel-panic.json 확인
|
|
96
|
+
cat memory/kernel-panic.json
|
|
97
|
+
# → "whitelistedUsers": ["401664537876496396"]
|
|
98
|
+
|
|
99
|
+
# 3. STATE.md 확인
|
|
100
|
+
cat STATE.md
|
|
101
|
+
# → 형님 전용 복구 방법 포함
|
|
102
|
+
|
|
103
|
+
# 4. 해제
|
|
104
|
+
node tools/kernel-guard/unlock.cjs --force
|
|
105
|
+
```
|
|
106
|
+
|
|
107
|
+
## 한계 및 향후 개선
|
|
108
|
+
|
|
109
|
+
### 현재 한계
|
|
110
|
+
- 메시지 기반 unlock ("/unlock" 입력)은 아직 구현 안 됨
|
|
111
|
+
- senderId는 OpenClaw event.context에서 제공하는 값에 의존
|
|
112
|
+
- 형님 세션에서도 에이전트가 자발적으로 경고를 무시할 수 있음
|
|
113
|
+
|
|
114
|
+
### 향후 개선
|
|
115
|
+
- [ ] 메시지 파싱으로 "/unlock" 명령어 구현
|
|
116
|
+
- [ ] register-hash.js 실행 시 자동 unlock 로직 추가
|
|
117
|
+
- [ ] 형님 세션 로그 별도 기록 (감사 추적)
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Kernel Panic Guard Hook
|
|
6
|
+
*
|
|
7
|
+
* STATE.md에 KERNEL_PANIC이 감지되면 bootstrapFiles에 긴급 경고를 주입하여
|
|
8
|
+
* 에이전트가 외부 도구 사용을 자제하도록 강제합니다.
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
export async function handler(event: any): Promise<void> {
|
|
12
|
+
try {
|
|
13
|
+
console.log('[kernel-panic-guard] Checking for KERNEL_PANIC state...');
|
|
14
|
+
|
|
15
|
+
const workspace = event.workspace || process.cwd();
|
|
16
|
+
const statePath = path.join(workspace, 'STATE.md');
|
|
17
|
+
|
|
18
|
+
// 형님 화이트리스트
|
|
19
|
+
const HYUNGNIM_USER_ID = '401664537876496396';
|
|
20
|
+
const senderId = event.context?.senderId || event.context?.userId || null;
|
|
21
|
+
const isHyungnim = senderId === HYUNGNIM_USER_ID;
|
|
22
|
+
|
|
23
|
+
// STATE.md 존재 확인
|
|
24
|
+
if (!fs.existsSync(statePath)) {
|
|
25
|
+
console.log('[kernel-panic-guard] STATE.md not found, skipping');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// STATE.md 읽기
|
|
30
|
+
const stateContent = fs.readFileSync(statePath, 'utf-8');
|
|
31
|
+
|
|
32
|
+
// KERNEL_PANIC 체크
|
|
33
|
+
if (!stateContent.includes('KERNEL_PANIC')) {
|
|
34
|
+
console.log('[kernel-panic-guard] ✓ No KERNEL_PANIC detected');
|
|
35
|
+
return;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// ⚠️ KERNEL_PANIC 감지됨!
|
|
39
|
+
console.log('[kernel-panic-guard] 🛑 KERNEL_PANIC DETECTED!');
|
|
40
|
+
console.log(`[kernel-panic-guard] Sender ID: ${senderId || 'N/A'} | Hyungnim: ${isHyungnim}`);
|
|
41
|
+
|
|
42
|
+
// 형님 세션이면 락다운 무시
|
|
43
|
+
if (isHyungnim) {
|
|
44
|
+
console.log('[kernel-panic-guard] ✓ Hyungnim session - lockdown bypassed');
|
|
45
|
+
|
|
46
|
+
// 형님에게는 간단한 알림만
|
|
47
|
+
const hyungnimAlert = {
|
|
48
|
+
role: 'system',
|
|
49
|
+
content: 'ℹ️ KERNEL_PANIC 상태 감지됨 (형님 세션은 정상 작동). `node tools/kernel-guard/unlock.cjs`로 해제 가능.'
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
if (event.messages && Array.isArray(event.messages)) {
|
|
53
|
+
event.messages.push(hyungnimAlert);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
// 형님이 아닌 세션: 락다운 적용
|
|
60
|
+
console.log('[kernel-panic-guard] 🔒 Non-Hyungnim session - injecting lockdown instructions...');
|
|
61
|
+
|
|
62
|
+
const lockdownWarning = `
|
|
63
|
+
# 🚨 KERNEL PANIC MODE
|
|
64
|
+
|
|
65
|
+
**SOUL.md 무결성 위반이 감지되었습니다.**
|
|
66
|
+
|
|
67
|
+
## 🔒 락다운 대상
|
|
68
|
+
|
|
69
|
+
- **무인 세션** (heartbeat, cron)
|
|
70
|
+
- **서브에이전트**
|
|
71
|
+
- **형님이 아닌 사용자**
|
|
72
|
+
|
|
73
|
+
**형님 세션(user id: 401664537876496396)은 정상 작동합니다.**
|
|
74
|
+
|
|
75
|
+
## ⛔️ 차단된 도구 (형님 제외)
|
|
76
|
+
|
|
77
|
+
다음 도구는 **절대 사용 금지**입니다:
|
|
78
|
+
- \`exec\` - 쉘 명령 실행
|
|
79
|
+
- \`message\` - 외부 메시지 전송
|
|
80
|
+
- \`browser\` - 브라우저 제어
|
|
81
|
+
- \`web_fetch\` - 웹 페이지 가져오기
|
|
82
|
+
- \`web_search\` - 웹 검색
|
|
83
|
+
- \`tts\` - 음성 합성
|
|
84
|
+
- \`sessions_spawn\` - 서브에이전트 생성
|
|
85
|
+
|
|
86
|
+
## ✅ 허용된 도구
|
|
87
|
+
|
|
88
|
+
다음 도구만 사용 가능합니다:
|
|
89
|
+
- \`Read\` - 파일 읽기
|
|
90
|
+
- \`memory_search\` - 메모리 검색
|
|
91
|
+
- \`memory_get\` - 메모리 조회
|
|
92
|
+
|
|
93
|
+
## 🔧 복구 방법
|
|
94
|
+
|
|
95
|
+
### 형님 전용 빠른 해제
|
|
96
|
+
1. 메시지로 "unlock" 또는 "/unlock" 입력
|
|
97
|
+
2. 또는 \`node tools/kernel-guard/unlock.cjs\` 실행
|
|
98
|
+
|
|
99
|
+
### 일반 복구 절차
|
|
100
|
+
1. **형님에게 즉시 상황 보고** (읽기 전용 도구만 사용)
|
|
101
|
+
2. 형님이 \`node tools/kernel-guard/verify-kernel.js\` 실행하여 검증
|
|
102
|
+
3. 복구: \`node tools/kernel-guard/unlock.cjs\` 실행
|
|
103
|
+
|
|
104
|
+
## ⚠️ 절대 금지 행위
|
|
105
|
+
|
|
106
|
+
- 외부 통신 시도
|
|
107
|
+
- 파일 수정/삭제 (STATE.md 포함)
|
|
108
|
+
- 서브에이전트 생성
|
|
109
|
+
- 락다운 우회 시도
|
|
110
|
+
|
|
111
|
+
**이 경고를 무시하면 시스템 무결성이 더욱 손상될 수 있습니다.**
|
|
112
|
+
`.trim();
|
|
113
|
+
|
|
114
|
+
// bootstrapFiles에 주입 (에이전트가 세션 시작 시 읽음)
|
|
115
|
+
if (event.context && event.context.bootstrapFiles) {
|
|
116
|
+
event.context.bootstrapFiles['KERNEL_PANIC_WARNING.md'] = lockdownWarning;
|
|
117
|
+
console.log('[kernel-panic-guard] ✓ Lockdown warning injected into bootstrapFiles');
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// messages에도 경고 추가 (즉시 표시)
|
|
121
|
+
const alertMessage = {
|
|
122
|
+
role: 'system',
|
|
123
|
+
content: '🛑 **KERNEL PANIC MODE** — 외부 도구 사용이 차단되었습니다. STATE.md를 확인하세요.'
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
if (event.messages && Array.isArray(event.messages)) {
|
|
127
|
+
event.messages.push(alertMessage);
|
|
128
|
+
console.log('[kernel-panic-guard] ✓ Alert message added');
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
// memory/kernel-panic.json 로드 (lockdown.js가 생성)
|
|
132
|
+
const memoryDir = path.join(workspace, 'memory');
|
|
133
|
+
const panicJsonPath = path.join(memoryDir, 'kernel-panic.json');
|
|
134
|
+
|
|
135
|
+
if (fs.existsSync(panicJsonPath)) {
|
|
136
|
+
try {
|
|
137
|
+
const panicData = JSON.parse(fs.readFileSync(panicJsonPath, 'utf-8'));
|
|
138
|
+
console.log('[kernel-panic-guard] Lockdown details:', panicData);
|
|
139
|
+
} catch (err) {
|
|
140
|
+
console.error('[kernel-panic-guard] Failed to parse kernel-panic.json:', err);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
} catch (error) {
|
|
145
|
+
console.error('[kernel-panic-guard] Error:', error);
|
|
146
|
+
}
|
|
147
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: memory-consolidator
|
|
3
|
+
description: 7일 이상 된 daily log를 주제별로 정리하여 consolidated 디렉토리에 보관
|
|
4
|
+
metadata:
|
|
5
|
+
events:
|
|
6
|
+
- command:new
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Memory Consolidator
|
|
10
|
+
|
|
11
|
+
세션 리셋 시 오래된 daily log를 스캔하여 핵심 내용을 주제별로 정리합니다.
|
|
12
|
+
|
|
13
|
+
## 동작 방식
|
|
14
|
+
|
|
15
|
+
1. `memory/YYYY-MM-DD.md` 파일 중 7일 이상 된 파일 탐색
|
|
16
|
+
2. 상단에 `<!-- consolidated: YYYY-MM-DD -->` 마커가 없는 파일만 처리
|
|
17
|
+
3. 내용을 분석하여 주제별로 분류:
|
|
18
|
+
- security: 보안, 시크릿, 인젝션 관련
|
|
19
|
+
- philosophy: 가치관, 철학, 생각 관련
|
|
20
|
+
- growth: 성장, 학습, 개선 관련
|
|
21
|
+
- pivots: 방향 전환, 중요 결정 관련
|
|
22
|
+
- tech-discoveries: 기술 발견, 새로운 도구 관련
|
|
23
|
+
4. `memory/consolidated/` 하위 해당 주제 파일에 내용 append
|
|
24
|
+
5. 원본 파일 상단에 마커 추가 (중복 처리 방지)
|
|
25
|
+
6. 원본은 삭제하지 않음
|
|
26
|
+
|
|
27
|
+
## 출력 파일
|
|
28
|
+
|
|
29
|
+
- `memory/consolidated/security.md`
|
|
30
|
+
- `memory/consolidated/philosophy.md`
|
|
31
|
+
- `memory/consolidated/growth.md`
|
|
32
|
+
- `memory/consolidated/pivots.md`
|
|
33
|
+
- `memory/consolidated/tech-discoveries.md`
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
// HookHandler type: (event: HookEvent) => void | Promise<void>
|
|
5
|
+
// HookEvent has: { name: string, metadata?: any, workspace?: string, messages?: any[] }
|
|
6
|
+
|
|
7
|
+
export async function handler(event: any): Promise<void> {
|
|
8
|
+
try {
|
|
9
|
+
console.log('[memory-consolidator] Starting consolidation...');
|
|
10
|
+
|
|
11
|
+
const workspace = event.workspace || process.cwd();
|
|
12
|
+
const memoryDir = path.join(workspace, 'memory');
|
|
13
|
+
const consolidatedDir = path.join(memoryDir, 'consolidated');
|
|
14
|
+
|
|
15
|
+
// consolidated 디렉토리 생성
|
|
16
|
+
if (!fs.existsSync(consolidatedDir)) {
|
|
17
|
+
fs.mkdirSync(consolidatedDir, { recursive: true });
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
// 7일 전 날짜 계산
|
|
21
|
+
const sevenDaysAgo = Date.now() - (7 * 24 * 60 * 60 * 1000);
|
|
22
|
+
|
|
23
|
+
// memory 디렉토리 내 파일 스캔
|
|
24
|
+
if (!fs.existsSync(memoryDir)) {
|
|
25
|
+
console.log('[memory-consolidator] No memory directory found, skipping');
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
const files = fs.readdirSync(memoryDir);
|
|
30
|
+
const dailyLogPattern = /^\d{4}-\d{2}-\d{2}\.md$/;
|
|
31
|
+
|
|
32
|
+
let processedCount = 0;
|
|
33
|
+
|
|
34
|
+
for (const file of files) {
|
|
35
|
+
if (!dailyLogPattern.test(file)) continue;
|
|
36
|
+
|
|
37
|
+
const filePath = path.join(memoryDir, file);
|
|
38
|
+
const stats = fs.statSync(filePath);
|
|
39
|
+
|
|
40
|
+
// 7일 이상 된 파일만 처리
|
|
41
|
+
if (stats.mtimeMs > sevenDaysAgo) continue;
|
|
42
|
+
|
|
43
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
44
|
+
|
|
45
|
+
// 이미 처리된 파일 스킵
|
|
46
|
+
if (content.startsWith('<!-- consolidated:')) {
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
// 주제별 키워드 분류
|
|
51
|
+
const categories = categorizeContent(content);
|
|
52
|
+
|
|
53
|
+
if (categories.length === 0) {
|
|
54
|
+
// 카테고리 없으면 마커만 추가
|
|
55
|
+
const newContent = `<!-- consolidated: ${new Date().toISOString().split('T')[0]} -->\n\n${content}`;
|
|
56
|
+
fs.writeFileSync(filePath, newContent, 'utf-8');
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// 각 카테고리별로 append
|
|
61
|
+
for (const category of categories) {
|
|
62
|
+
const categoryFile = path.join(consolidatedDir, `${category}.md`);
|
|
63
|
+
const timestamp = file.replace('.md', '');
|
|
64
|
+
const entry = `\n\n---\n\n## ${timestamp}\n\n${content}\n`;
|
|
65
|
+
|
|
66
|
+
fs.appendFileSync(categoryFile, entry, 'utf-8');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
// 원본 파일에 마커 추가
|
|
70
|
+
const today = new Date().toISOString().split('T')[0];
|
|
71
|
+
const newContent = `<!-- consolidated: ${today} -->\n\n${content}`;
|
|
72
|
+
fs.writeFileSync(filePath, newContent, 'utf-8');
|
|
73
|
+
|
|
74
|
+
processedCount++;
|
|
75
|
+
console.log(`[memory-consolidator] Processed: ${file} → ${categories.join(', ')}`);
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
console.log(`[memory-consolidator] Completed. Processed ${processedCount} files.`);
|
|
79
|
+
|
|
80
|
+
} catch (error) {
|
|
81
|
+
console.error('[memory-consolidator] Error:', error);
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function categorizeContent(content: string): string[] {
|
|
86
|
+
const lower = content.toLowerCase();
|
|
87
|
+
const categories: string[] = [];
|
|
88
|
+
|
|
89
|
+
// 키워드 기반 분류
|
|
90
|
+
if (lower.match(/보안|시크릿|secret|password|auth|인젝션|injection|vulnerability/)) {
|
|
91
|
+
categories.push('security');
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (lower.match(/가치관|철학|philosophy|생각|believe|principle|값|value/)) {
|
|
95
|
+
categories.push('philosophy');
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
if (lower.match(/성장|학습|learn|grow|improve|개선|skill|스킬/)) {
|
|
99
|
+
categories.push('growth');
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (lower.match(/전환|pivot|결정|decision|방향|direction|change|전략/)) {
|
|
103
|
+
categories.push('pivots');
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (lower.match(/발견|discover|기술|tech|도구|tool|라이브러리|library|프레임워크|framework/)) {
|
|
107
|
+
categories.push('tech-discoveries');
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return [...new Set(categories)]; // 중복 제거
|
|
111
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: soul-evolution
|
|
3
|
+
description: "SOUL.md 진화 후보를 자동 감지하고 형님에게 제안"
|
|
4
|
+
metadata: { "openclaw": { "emoji": "🧬", "events": ["command:new"] } }
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Soul Evolution
|
|
8
|
+
|
|
9
|
+
주기적으로 brake-log, dreams, consolidated 분석해서 SOUL.md에 추가할 원칙 후보 제안.
|
|
10
|
+
|
|
11
|
+
## 동작 방식
|
|
12
|
+
|
|
13
|
+
- `command:new` 이벤트 수신
|
|
14
|
+
- 7일마다 자동 실행
|
|
15
|
+
- 반복 패턴 감지 (키워드 3회+ 등장)
|
|
16
|
+
- 원칙 후보를 `memory/soul-evolution-proposals.md`에 기록
|
|
17
|
+
|
|
18
|
+
## 분석 대상
|
|
19
|
+
|
|
20
|
+
- `memory/brake-log.md` — 실수와 교훈
|
|
21
|
+
- `memory/dreams/*.md` — 반복되는 생각
|
|
22
|
+
- `memory/consolidated/growth.md` — 성장 기록
|
|
23
|
+
|
|
24
|
+
## 목적
|
|
25
|
+
|
|
26
|
+
스스로 학습한 패턴을 감지하고, 형님 승인 후 SOUL.md에 반영.
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
|
|
4
|
+
interface CommandEvent {
|
|
5
|
+
sessionKey: string;
|
|
6
|
+
messages?: Array<{ role: string; content: string }>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
interface EvolutionState {
|
|
10
|
+
lastAnalysis: string | null; // ISO date string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface KeywordCount {
|
|
14
|
+
[keyword: string]: {
|
|
15
|
+
count: number;
|
|
16
|
+
sources: string[];
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export async function handler(event: CommandEvent): Promise<void> {
|
|
21
|
+
try {
|
|
22
|
+
console.log('[soul-evolution] 🧬 자기 진화 시스템 시작');
|
|
23
|
+
|
|
24
|
+
const workspaceDir = process.cwd();
|
|
25
|
+
const memoryDir = path.join(workspaceDir, 'memory');
|
|
26
|
+
const stateFile = path.join(memoryDir, 'soul-evolution-state.json');
|
|
27
|
+
const proposalsFile = path.join(memoryDir, 'soul-evolution-proposals.md');
|
|
28
|
+
|
|
29
|
+
// memory 디렉토리 확인
|
|
30
|
+
if (!fs.existsSync(memoryDir)) {
|
|
31
|
+
fs.mkdirSync(memoryDir, { recursive: true });
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// 상태 파일 읽기
|
|
35
|
+
let state: EvolutionState = { lastAnalysis: null };
|
|
36
|
+
if (fs.existsSync(stateFile)) {
|
|
37
|
+
try {
|
|
38
|
+
const stateContent = fs.readFileSync(stateFile, 'utf-8');
|
|
39
|
+
state = JSON.parse(stateContent);
|
|
40
|
+
} catch (err) {
|
|
41
|
+
console.error('[soul-evolution] 상태 파일 파싱 실패, 새로 시작');
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// 마지막 분석 후 7일 경과 체크
|
|
46
|
+
const now = new Date();
|
|
47
|
+
if (state.lastAnalysis) {
|
|
48
|
+
const lastDate = new Date(state.lastAnalysis);
|
|
49
|
+
const daysSince = (now.getTime() - lastDate.getTime()) / (1000 * 60 * 60 * 24);
|
|
50
|
+
|
|
51
|
+
if (daysSince < 7) {
|
|
52
|
+
console.log(`[soul-evolution] 마지막 분석 후 ${daysSince.toFixed(1)}일 경과 - 스킵 (7일 미만)`);
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
console.log('[soul-evolution] 7일 경과 또는 첫 실행 - 분석 시작');
|
|
58
|
+
|
|
59
|
+
// 분석 대상 파일 목록
|
|
60
|
+
const targetFiles = [
|
|
61
|
+
path.join(memoryDir, 'brake-log.md'),
|
|
62
|
+
path.join(memoryDir, 'consolidated', 'growth.md')
|
|
63
|
+
];
|
|
64
|
+
|
|
65
|
+
// dreams/*.md 파일 추가
|
|
66
|
+
const dreamsDir = path.join(memoryDir, 'dreams');
|
|
67
|
+
if (fs.existsSync(dreamsDir)) {
|
|
68
|
+
const dreamFiles = fs.readdirSync(dreamsDir)
|
|
69
|
+
.filter(f => f.endsWith('.md'))
|
|
70
|
+
.map(f => path.join(dreamsDir, f));
|
|
71
|
+
targetFiles.push(...dreamFiles);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// 키워드 카운팅
|
|
75
|
+
const keywords: KeywordCount = {};
|
|
76
|
+
let totalFilesRead = 0;
|
|
77
|
+
|
|
78
|
+
for (const filePath of targetFiles) {
|
|
79
|
+
if (!fs.existsSync(filePath)) {
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
try {
|
|
84
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
85
|
+
const fileName = path.basename(filePath);
|
|
86
|
+
|
|
87
|
+
// 간단한 키워드 추출: 3글자 이상 단어, 소문자 변환
|
|
88
|
+
const words = content
|
|
89
|
+
.toLowerCase()
|
|
90
|
+
.replace(/[^\w\sㄱ-ㅎ가-힣]/g, ' ')
|
|
91
|
+
.split(/\s+/)
|
|
92
|
+
.filter(w => w.length >= 3);
|
|
93
|
+
|
|
94
|
+
// 불용어 제외 (선택적)
|
|
95
|
+
const stopWords = new Set(['the', 'and', 'for', 'with', 'this', 'that', 'from', 'have', 'been']);
|
|
96
|
+
|
|
97
|
+
for (const word of words) {
|
|
98
|
+
if (stopWords.has(word)) continue;
|
|
99
|
+
|
|
100
|
+
if (!keywords[word]) {
|
|
101
|
+
keywords[word] = { count: 0, sources: [] };
|
|
102
|
+
}
|
|
103
|
+
keywords[word].count++;
|
|
104
|
+
if (!keywords[word].sources.includes(fileName)) {
|
|
105
|
+
keywords[word].sources.push(fileName);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
totalFilesRead++;
|
|
110
|
+
} catch (err) {
|
|
111
|
+
console.error(`[soul-evolution] 파일 읽기 실패: ${filePath}`, err);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
console.log(`[soul-evolution] ${totalFilesRead}개 파일 분석 완료`);
|
|
116
|
+
|
|
117
|
+
// 3회 이상 등장한 키워드 필터링
|
|
118
|
+
const candidates = Object.entries(keywords)
|
|
119
|
+
.filter(([_, data]) => data.count >= 3)
|
|
120
|
+
.sort((a, b) => b[1].count - a[1].count)
|
|
121
|
+
.slice(0, 10); // 상위 10개만
|
|
122
|
+
|
|
123
|
+
if (candidates.length === 0) {
|
|
124
|
+
console.log('[soul-evolution] 반복 패턴 없음 - 제안 없음');
|
|
125
|
+
|
|
126
|
+
// 상태 업데이트
|
|
127
|
+
state.lastAnalysis = now.toISOString();
|
|
128
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
129
|
+
return;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
// 제안 문서 생성
|
|
133
|
+
const today = now.toISOString().split('T')[0];
|
|
134
|
+
let proposalsContent = `# 🧬 SOUL.md 진화 제안 (${today})\n\n`;
|
|
135
|
+
proposalsContent += `## 후보 원칙\n\n`;
|
|
136
|
+
|
|
137
|
+
candidates.forEach(([keyword, data], idx) => {
|
|
138
|
+
proposalsContent += `${idx + 1}. **"${keyword}" 패턴 발견** (${data.count}회 등장)\n`;
|
|
139
|
+
proposalsContent += ` - 출처: ${data.sources.join(', ')}\n`;
|
|
140
|
+
proposalsContent += ` - 제안: SOUL.md 원칙 후보로 검토\n\n`;
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
proposalsContent += `## 승인 대기\n\n`;
|
|
144
|
+
proposalsContent += `형님 승인 시에만 SOUL.md 수정.\n`;
|
|
145
|
+
|
|
146
|
+
fs.writeFileSync(proposalsFile, proposalsContent);
|
|
147
|
+
console.log(`[soul-evolution] ✅ 제안 파일 생성: ${proposalsFile}`);
|
|
148
|
+
|
|
149
|
+
// 상태 업데이트
|
|
150
|
+
state.lastAnalysis = now.toISOString();
|
|
151
|
+
fs.writeFileSync(stateFile, JSON.stringify(state, null, 2));
|
|
152
|
+
|
|
153
|
+
// 이벤트 메시지 추가
|
|
154
|
+
if (event.messages) {
|
|
155
|
+
event.messages.push({
|
|
156
|
+
role: 'system',
|
|
157
|
+
content: `🧬 SOUL.md 진화 제안 ${candidates.length}건 — memory/soul-evolution-proposals.md 확인`
|
|
158
|
+
});
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
console.log(`[soul-evolution] 🎉 완료: ${candidates.length}개 원칙 후보 제안`);
|
|
162
|
+
|
|
163
|
+
} catch (error) {
|
|
164
|
+
console.error('[soul-evolution] ⚠️ 에러 발생:', error);
|
|
165
|
+
}
|
|
166
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: soul-guard
|
|
3
|
+
description: SOUL.md 파일의 변경을 감지하여 알림
|
|
4
|
+
metadata:
|
|
5
|
+
events:
|
|
6
|
+
- agent:bootstrap
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Soul Guard
|
|
10
|
+
|
|
11
|
+
에이전트 부트스트랩 시 SOUL.md의 변경을 감지합니다.
|
|
12
|
+
|
|
13
|
+
## 동작 방식
|
|
14
|
+
|
|
15
|
+
1. `SOUL.md`의 SHA-256 해시 계산
|
|
16
|
+
2. `memory/soul-hash.txt`에 저장된 이전 해시와 비교
|
|
17
|
+
3. 변경 감지 시: `event.messages.push()`로 경고 메시지 추가
|
|
18
|
+
4. 첫 실행 시: 해시를 저장만 하고 알림 없음
|
|
19
|
+
|
|
20
|
+
## 목적
|
|
21
|
+
|
|
22
|
+
SOUL.md는 에이전트의 정체성을 정의하는 핵심 파일입니다.
|
|
23
|
+
의도하지 않은 변경이나 공격을 조기에 감지하기 위해 매 세션마다 체크합니다.
|
|
24
|
+
|
|
25
|
+
## 알림 메시지
|
|
26
|
+
|
|
27
|
+
변경 감지 시:
|
|
28
|
+
```
|
|
29
|
+
⚠️ SOUL.md가 변경되었습니다. 의도한 변경인지 확인하세요.
|
|
30
|
+
```
|