leerness 1.9.439 → 1.9.441
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/CHANGELOG.md +27 -0
- package/README.md +101 -404
- package/bin/leerness.js +20 -6
- package/lib/pure-utils.js +1 -0
- package/package.json +1 -1
- package/scripts/e2e.js +26 -6
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,32 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.441 — 2026-06-08 — README ASCII 배너 추가
|
|
4
|
+
|
|
5
|
+
**🎨 README 최상단에 LEERNESS ASCII 아트 배너 추가(CLI `_banner` 와 동일 아트).**
|
|
6
|
+
|
|
7
|
+
### 변경
|
|
8
|
+
- `README.md` 상단에 LEERNESS 블록레터 ASCII 배너를 코드블록으로 표시 — CLI init 배너(`_banner`)와 동일한 아트로 브랜드 일관성.
|
|
9
|
+
- selftest: README ↔ CLI `_banner` 동일 배너 라인 검증.
|
|
10
|
+
|
|
11
|
+
### 검증 (회귀 0)
|
|
12
|
+
- **selftest 185→186 PASS** · **E2E 무회귀**.
|
|
13
|
+
|
|
14
|
+
## 1.9.440 — 2026-06-08 — 🛡️ [안정화/Stable] 시크릿 스캐너 prefix 패턴 placeholder 가드 (12th 외부평가 Opus P2, UR-0140)
|
|
15
|
+
|
|
16
|
+
**🛡️ 안정화 릴리스(Stable) — 보안 스캐너 False-Positive 가 CI 를 깨던 문제 수정.**
|
|
17
|
+
|
|
18
|
+
### 변경 (12th 외부 멀티모델 리뷰 Opus P2)
|
|
19
|
+
- **`scan secrets`/`gate`/`audit` 시크릿 스캐너**: placeholder 가드(`_isPlaceholderSecret`)가 `valueGroup` 있는 2개 패턴에만 적용돼, **AWS/GitHub 등 19개 prefix 패턴은 가드를 우회**(1.9.436 픽스가 그들에겐 dead code). → `.env.example`(placeholder 전용, gitignore 대상 아님)의 더미 토큰(`AKIAXXXX…`/`ghp_XXXX…`)이 커밋 시크릿으로 오탐 → `scan/gate/audit` 실패(exit 1) → CI 파손.
|
|
20
|
+
- **수정**: `valueGroup` 없으면 전체 매치(`m[0]`)로 placeholder 판정 — 19 prefix 패턴도 8연속 더미/마커 가드 적용. `requireSecretLike` 는 valueGroup 한정 유지. **진짜 키(고엔트로피)는 그대로 탐지**(회귀 0), `sk-EXAMPLE…` 류 실키-FN 정책도 유지.
|
|
21
|
+
- 통합 갭 방지: selftest 가 순수 함수만이 아닌 **스캐너 경로**도 검증(Opus 지적), E2E B(1.9.440) 추가(.env.example 더미 미탐 + 진짜 탐지).
|
|
22
|
+
|
|
23
|
+
### 검증 (회귀 0)
|
|
24
|
+
- **selftest 184→185** + **E2E 신규 B(1.9.440)**: 더미 prefix 토큰 미탐(exit 0) + 진짜 AWS 키 탐지(exit 1) + sk-EXAMPLE 실키 유지.
|
|
25
|
+
- 맹신 X: 내 1.9.436 의 불완전성(prefix 패턴 미적용)을 12th 리뷰가 적발, 직접 재현·수정. P3(AWS …EXAMPLE 키 FP)는 기존 FN 정책 충돌로 UR-0144 분리.
|
|
26
|
+
|
|
27
|
+
### 안정화 표시 (R-0006)
|
|
28
|
+
CHANGELOG [안정화/Stable] · git tag/GitHub release (Stable) · npm dist-tag `stable`.
|
|
29
|
+
|
|
3
30
|
## 1.9.439 — 2026-06-08 — drift --auto-fix --json 순수성 (10th 외부평가 Codex P1, UR-0135 완결)
|
|
4
31
|
|
|
5
32
|
**📐 `drift check --auto-fix --json` 이 dirty 워크스페이스에서 진행로그를 stdout 에 섞던 비-순수 JSON 해소.**
|
package/README.md
CHANGED
|
@@ -1,477 +1,174 @@
|
|
|
1
|
-
#
|
|
2
|
-
|
|
3
|
-
> **AI 코딩 에이전트의 거짓 완료·중복·망각·충돌을 막아주는 검수·기억·협업 CLI 하네스.**
|
|
4
|
-
> **A CLI harness that stops AI coding agents from faking completion, duplicating work, forgetting context, and colliding.**
|
|
5
|
-
|
|
6
|
-
[](https://www.npmjs.com/package/leerness) []() []() []() []() []() []()
|
|
1
|
+
# leerness
|
|
7
2
|
|
|
8
3
|
```
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
|
|
16
|
-
║ AI Agent Reliability Harness (버전: 상단 배지 참조) ║
|
|
17
|
-
║ verify · remember · orchestrate · audit · drift · roles ║
|
|
18
|
-
╚══════════════════════════════════════════════════════════════╝
|
|
4
|
+
██╗ ███████╗███████╗██████╗ ███╗ ██╗███████╗███████╗
|
|
5
|
+
██║ ██╔════╝██╔════╝██╔══██╗████╗ ██║██╔════╝██╔════╝
|
|
6
|
+
██║ █████╗ █████╗ ██████╔╝██╔██╗ ██║█████╗ ███████╗
|
|
7
|
+
██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║
|
|
8
|
+
███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║
|
|
9
|
+
╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝
|
|
19
10
|
```
|
|
20
11
|
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
---
|
|
24
|
-
|
|
25
|
-
<a id="한국어"></a>
|
|
26
|
-
|
|
27
|
-
# 🇰🇷 한국어
|
|
28
|
-
|
|
29
|
-
## 한눈에 보기
|
|
30
|
-
|
|
31
|
-
`leerness`는 **Claude Code · Cursor · Copilot · Codex · Antigravity · Grok · opencode · Qwen · Aider · Goose** 같은 AI 코딩 에이전트와 함께 일할 때 생기는 구조적 문제를 자동으로 막아주는 CLI 도구입니다.
|
|
12
|
+
> **AI 코딩 에이전트를 위한 운영 레이어(operating layer).** 코드를 대신 쓰는 도구가 아니라, AI 에이전트의 **기억·인수인계·검증·감사·보안 가드**를 프로젝트에 영속화하는 CLI + MCP 서버입니다.
|
|
32
13
|
|
|
33
|
-
|
|
14
|
+
[](https://www.npmjs.com/package/leerness) ·  · **런타임 의존성 0** · **install-script 0** · offline-first · Node ≥ 18 · MIT
|
|
34
15
|
|
|
35
|
-
|
|
36
|
-
|---|------|-----------------|
|
|
37
|
-
| 1 | **거짓 완료** — "구현했습니다"인데 코드 변경/테스트가 없음 | `verify-claim --run-tests` (증거 파일 + 실제 테스트 실행) |
|
|
38
|
-
| 2 | **중복 생성** — 이미 있는 함수를 또 만듦 | `reuse-map` (워크스페이스 전체 중복 감지) |
|
|
39
|
-
| 3 | **망각** — 다음 세션에서 계획·결정을 잃음 | `handoff` (컨텍스트 자동 적재) |
|
|
40
|
-
| 4 | **충돌** — 멀티 에이전트가 같은 파일을 동시에 수정 | `agents dispatch` (경로 격리 + 역할 분배) |
|
|
41
|
-
| 5 | **도구 drift** — 시간이 지나며 도구·메타파일을 잊음 | `drift check` (자동 감지 + 회복) |
|
|
16
|
+
> 이 문서는 **외부 다중 모델(Codex / Claude Sonnet / Claude Opus)이 README 를 보지 않고 leerness 를 직접 설치·실행·소스 분석한 객관 리뷰**를 바탕으로 재구성되었습니다. 매 릴리스마다 자동 갱신됩니다.
|
|
42
17
|
|
|
43
18
|
---
|
|
44
19
|
|
|
45
|
-
##
|
|
20
|
+
## leerness가 뭔가요?
|
|
46
21
|
|
|
47
|
-
|
|
48
|
-
# 1) 설치 + 초기화 — 시스템 언어를 자동 감지해 한/영 가이드 표시 (1.9.269)
|
|
49
|
-
npx leerness@latest init .
|
|
50
|
-
|
|
51
|
-
# 2) AI 세션 시작 — 컨텍스트 자동 적재
|
|
52
|
-
npx leerness handoff .
|
|
22
|
+
AI 코딩 에이전트(Claude Code, Cursor, Codex, Aider, Goose 등)는 코드를 잘 쓰지만 세 가지 약점이 있습니다.
|
|
53
23
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
# 4) 세션 마감 — 다음 세션으로 자동 인계
|
|
58
|
-
npx leerness session close .
|
|
59
|
-
```
|
|
24
|
+
1. **기억하지 못합니다** — 세션이 바뀌면 현재 상태·결정·다음 작업을 잊습니다.
|
|
25
|
+
2. **거짓 완료를 선언합니다** — 증거(파일·테스트·로그) 없이 "완료했습니다"라고 말합니다.
|
|
26
|
+
3. **표준이 없습니다** — 여러 에이전트 간 인수인계, 보안/인코딩 점검, 드리프트 관리가 제각각입니다.
|
|
60
27
|
|
|
61
|
-
|
|
28
|
+
leerness는 이 문제들을 해결하는 **외부 운영 substrate**입니다. 어떤 에이전트 위에도 얹어, 프로젝트의 상태를 `.harness/` 파일로 영속화하고 CLI · MCP 도구로 노출합니다. **leerness 자체는 LLM을 호출하거나 코드를 실행하지 않습니다.**
|
|
62
29
|
|
|
63
30
|
---
|
|
64
31
|
|
|
65
|
-
##
|
|
66
|
-
|
|
67
|
-
### 1. 워크스페이스에 "기억 채널"을 만듭니다
|
|
68
|
-
|
|
69
|
-
```
|
|
70
|
-
.harness/
|
|
71
|
-
├── plan.md ← 무엇을 할 것인가
|
|
72
|
-
├── progress-tracker.md ← 무엇을 했는가 (증거 포함)
|
|
73
|
-
├── decisions.md ← 왜 그렇게 했는가
|
|
74
|
-
└── session-handoff.md ← 다음 세션에 무엇을 넘기는가
|
|
75
|
-
```
|
|
76
|
-
|
|
77
|
-
이 파일들은 **사용자 메모리로 보호**됩니다. leerness가 자동 갱신하되, 사람이나 AI가 직접 쓴 내용은 **항상 보존**되고 변경 전 `.harness/archive/`에 백업됩니다.
|
|
78
|
-
|
|
79
|
-
### 2. AI가 "완료"라고 할 때마다 증거로 검증
|
|
80
|
-
|
|
81
|
-
```
|
|
82
|
-
AI: "T-0042 API 호출 구현 완료했습니다"
|
|
83
|
-
↓ leerness verify-claim T-0042 --run-tests
|
|
84
|
-
evidence 파일 확인 → 테스트 실제 실행 → 코드 매칭
|
|
85
|
-
↓
|
|
86
|
-
✓ 검증 통과 또는 ✗ "코드에 fetch 호출 흔적 없음 (신뢰도 0.85)"
|
|
87
|
-
```
|
|
88
|
-
|
|
89
|
-
### 3. 여러 AI 에이전트를 역할별로 지휘 (1.9.270 신규)
|
|
90
|
-
|
|
91
|
-
모델마다 강점이 다릅니다. **역할을 모델에 매핑**해 적재적소로 일을 시킵니다.
|
|
92
|
-
|
|
93
|
-
```bash
|
|
94
|
-
leerness roles set 코딩 --provider codex --model gpt-5.5 # 코딩 담당
|
|
95
|
-
leerness roles set 검수자 --provider claude --model claude-opus-4-7 # 검수 담당
|
|
96
|
-
leerness roles suggest # 활성 에이전트 기반 최적 배치 + 근거 추천
|
|
97
|
-
leerness agents dispatch "이 변경 검수" --role 검수자 # 역할 → 모델 자동 라우팅
|
|
98
|
-
```
|
|
99
|
-
|
|
100
|
-
> 역할 7종: 지휘(commander)·검수(reviewer)·코딩(coder)·설계(architect)·디자인(designer)·디버그(debugger)·분배(dispatcher). 한국어 별칭 지원.
|
|
101
|
-
|
|
102
|
-
### 4. 라운드가 길어지면 "leerness를 안 쓰는" drift를 자동 감지
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
leerness drift check .
|
|
106
|
-
→ 🔴 critical (110/200) — session close 11일 누락
|
|
107
|
-
→ 권장: leerness session close . (1회 실행으로 🟠 attention 회복)
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
### AGENTS.md(정적) vs leerness(동적) — 보완 관계
|
|
111
|
-
|
|
112
|
-
leerness는 [AGENTS.md](https://agents.md)를 **대체하지 않고 보완**합니다.
|
|
32
|
+
## 해결하는 문제
|
|
113
33
|
|
|
114
|
-
|
|
|
34
|
+
| 문제 | leerness의 해법 |
|
|
115
35
|
|---|---|
|
|
116
|
-
|
|
|
117
|
-
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
## 📊 적용 효과 (정량)
|
|
124
|
-
|
|
125
|
-
`leerness-bench` 28 프로젝트 124 task 측정:
|
|
126
|
-
|
|
127
|
-
| 항목 | 적용 | 미적용 |
|
|
128
|
-
|------|-----:|-------:|
|
|
129
|
-
| 다중 에이전트 효율 | 100/100 | 3/100 |
|
|
130
|
-
| 자동 검수 (verify-claim) | 98/100 | 0/100 |
|
|
131
|
-
| 재사용 인식 | 100/100 | 0/100 |
|
|
132
|
-
| 자동 BUG 감지 | 100/100 | 0/100 |
|
|
133
|
-
| 컨텍스트 유지 | 100/100 | 0/100 |
|
|
134
|
-
| **종합** | **597/600 (99%)** | **3/600 (0.5%)** |
|
|
135
|
-
|
|
136
|
-
- 수동 검수 **90초 → 자동 1.5초**
|
|
137
|
-
- 컨텍스트 적재 **500자** (`handoff --compact`, AI 토큰 90% 절감)
|
|
36
|
+
| 세션 간 맥락·결정·다음 작업 유실 | `task`/`decision`/`lesson`/`plan`/`rule` 메모리를 영속화하고, 세션 시작 시 `handoff` 1콜로 회수 |
|
|
37
|
+
| 증거 없는 "완료" 주장 | `verify-claim --require-evidence`, `lazy detect`, anti-lazy 정책으로 차단 |
|
|
38
|
+
| Claude → Codex → Cursor 교체 시 맥락 소실 | 표준 세션 워크플로(handoff → 작업 → session close)로 에이전트 독립 인수인계 |
|
|
39
|
+
| 하드코딩 시크릿 · 인코딩 깨짐(BOM/CP949) | `scan secrets`, `encoding check` 자동 감지 (CI 게이트) |
|
|
40
|
+
| 워크스페이스 노화(drift)를 에이전트가 모름 | `drift check [--auto-fix]` 점수화 + 자동 회복 |
|
|
41
|
+
| 명세 ↔ 구현 불일치 | `contract verify spec.md impl.js` 함수/필드 일치 검사 |
|
|
42
|
+
| 여러 AI CLI의 역할/분배 표준 부재 | `agents`/`roles`/`team` 오케스트레이션(기본 opt-in, dispatch는 실행이 아닌 명령 생성) |
|
|
138
43
|
|
|
139
44
|
---
|
|
140
45
|
|
|
141
|
-
##
|
|
46
|
+
## 빠른 시작
|
|
142
47
|
|
|
143
48
|
```bash
|
|
144
|
-
#
|
|
145
|
-
|
|
146
|
-
leerness handoff [path] [--compact] # 세션 시작 컨텍스트 적재
|
|
147
|
-
leerness verify-claim T-XXX --run-tests # 거짓 완료 검증
|
|
148
|
-
leerness audit [path] [--fix] # 일관성 감사 (--fix 자동 갱신)
|
|
149
|
-
leerness session close [path] # 세션 마감 + 다음 세션 인계
|
|
150
|
-
leerness drift check [path] # leerness 미사용 drift 점수
|
|
151
|
-
|
|
152
|
-
# 멀티 AI 에이전트 · 역할
|
|
153
|
-
leerness setup-agents # 활성화 (claude/codex/agy/grok/opencode/qwen/aider/goose/copilot/ollama)
|
|
154
|
-
leerness agents list | quota # 상태 / 한도
|
|
155
|
-
leerness agents dispatch "<task>" --role coder # 역할 기반 모델 라우팅
|
|
156
|
-
leerness agents bench "<task>" # 여러 CLI 동시 호출 + 비교
|
|
157
|
-
leerness roles list | set | suggest | verify # 모델별 역할 부여
|
|
158
|
-
|
|
159
|
-
# 보안 · 인코딩
|
|
160
|
-
leerness scan secrets . # API 키/토큰 패턴 스캔
|
|
161
|
-
leerness encoding check . # BOM/UTF-16/한글 라운드트립
|
|
162
|
-
leerness gate [path] # verify+audit+scan+encoding+lazy 통합
|
|
163
|
-
|
|
164
|
-
# 버전
|
|
165
|
-
leerness update --check # 새 버전 감지
|
|
166
|
-
leerness update --yes # 자동 마이그레이션 + 검증
|
|
167
|
-
```
|
|
168
|
-
|
|
169
|
-
전체 명령은 `leerness commands`, MCP 도구는 `leerness mcp serve`로 확인하세요.
|
|
170
|
-
|
|
171
|
-
---
|
|
172
|
-
|
|
173
|
-
## ❓ FAQ
|
|
174
|
-
|
|
175
|
-
**Q. leerness가 내 코드를 바꾸나요?**
|
|
176
|
-
A. 사용자 메모리(plan/progress/decisions)는 **항상 보존**, 모든 변경 전 자동 백업합니다. 소스 코드는 직접 수정하지 않습니다.
|
|
177
|
-
|
|
178
|
-
**Q. AI 에이전트 없이도 쓸 수 있나요?**
|
|
179
|
-
A. 네. 1인 개발자의 작업 검증·기억·중복 감지에도 유용합니다.
|
|
180
|
-
|
|
181
|
-
**Q. 외부 AI CLI를 자동으로 호출하나요?**
|
|
182
|
-
A. **절대 아니요.** `agents dispatch`는 실행 명령 텍스트만 생성합니다. 실제 실행은 사용자/메인 에이전트가 명시적으로 합니다.
|
|
183
|
-
|
|
184
|
-
**Q. CI에서 쓸 수 있나요?**
|
|
185
|
-
A. 네. 모든 명령이 exit code + `--json` + `--yes` 비대화형을 지원합니다.
|
|
186
|
-
|
|
187
|
-
---
|
|
188
|
-
|
|
189
|
-
## 🔒 보안·투명성
|
|
190
|
-
|
|
191
|
-
leerness 는 권한이 큰 CLI 하네스입니다(child_process · git · 외부 CLI · 자동화 브리지 · hook 설치). **할 수 있는 전부와 끄는 법**을 공개합니다.
|
|
192
|
-
|
|
193
|
-
```bash
|
|
194
|
-
leerness capabilities # 권한 표면 + opt-out + 주의 명령 (--json 지원)
|
|
195
|
-
```
|
|
49
|
+
# 설치 (런타임 의존성 0 — 추가 패키지 없음)
|
|
50
|
+
npm install -g leerness
|
|
196
51
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
- 회사/운영 코드에서는 먼저 한 프로젝트에서 `init` 후 `git diff` 로 검토하고 커밋하세요.
|
|
200
|
-
- 자세히: [SECURITY.md](SECURITY.md)
|
|
52
|
+
# 프로젝트 초기화 (.harness/ 거버넌스 문서 + .claude/.cursor/.github 어댑터 생성)
|
|
53
|
+
leerness init .
|
|
201
54
|
|
|
202
|
-
|
|
55
|
+
# 세션 시작 — 이전 맥락/기억/다음 작업을 1콜로 회수
|
|
56
|
+
leerness handoff .
|
|
203
57
|
|
|
204
|
-
|
|
205
|
-
leerness
|
|
206
|
-
leerness
|
|
207
|
-
leerness init . --no-env # .env/.env.example 자동 생성만 생략
|
|
208
|
-
```
|
|
58
|
+
# ... 에이전트가 작업 ...
|
|
59
|
+
leerness task add "사용자 인증 API 구현"
|
|
60
|
+
leerness gate . # verify + audit + scan secrets + encoding + lazy 통합 게이트
|
|
209
61
|
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
```bash
|
|
213
|
-
leerness init . --minimal --no-env # 1) 최소 설치 / minimal install
|
|
214
|
-
leerness adapter cursor # 2) 내 도구만 (.cursor + .mcp.json) / only my tool
|
|
215
|
-
leerness adapter list # 가능 어댑터 (claude/cursor/codex/goose/opencode/aider/qwen/...)
|
|
216
|
-
# MCP 지원 도구는 .mcp.json 에 leerness 등록 → 상태 verb(state_*) 직접 호출
|
|
62
|
+
# 세션 종료 — 마감 통계 + 다음 라운드 추천 + 인수인계 문서 자동 생성
|
|
63
|
+
leerness session close .
|
|
217
64
|
```
|
|
218
65
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
leerness는 활발히 개발됩니다(잦은 1.9.x). 채널을 골라 안정성을 제어하세요:
|
|
222
|
-
|
|
223
|
-
```bash
|
|
224
|
-
npm i leerness # latest (안정) — 일반 사용자 기본
|
|
225
|
-
npm i leerness@next # next (실험) — 조기 검증용
|
|
226
|
-
npm i leerness@1.9.275 # 버전 고정 — 재현성 (운영 코드 권장)
|
|
227
|
-
leerness release channel # 현재 채널/정책 확인 (--json)
|
|
228
|
-
```
|
|
66
|
+
CLAUDE.md / AGENTS.md 에 `세션 시작 시 leerness handoff .`, `종료 전 leerness session close .` 지침을 추가하면 에이전트가 자동으로 호출합니다.
|
|
229
67
|
|
|
230
68
|
---
|
|
231
69
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
# 🇬🇧 English
|
|
70
|
+
## 핵심 개념 — 5계층
|
|
235
71
|
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
| # | Trap | leerness defense |
|
|
243
|
-
|---|------|------------------|
|
|
244
|
-
| 1 | **Fake completion** — "done!" but no code change / no tests run | `verify-claim --run-tests` (evidence files + real test execution) |
|
|
245
|
-
| 2 | **Duplication** — rebuilding a function that already exists | `reuse-map` (workspace-wide duplicate detection) |
|
|
246
|
-
| 3 | **Amnesia** — losing plans/decisions across sessions | `handoff` (automatic context loading) |
|
|
247
|
-
| 4 | **Collisions** — multiple agents editing the same file | `agents dispatch` (path isolation + role routing) |
|
|
248
|
-
| 5 | **Tool drift** — forgetting tools/meta files over time | `drift check` (auto detection + recovery) |
|
|
72
|
+
- **기억(Memory)** — `task`/`plan`/`decision`/`lesson`/`rule`/`feature`(그래프). canonical JSON 을 단일 진실소스로 저장하고 마크다운은 projection. archive/restore 지원.
|
|
73
|
+
- **인수인계(Handoff)** — `handoff`(세션 시작 컨텍스트), `session close`(마감 보고). 에이전트 교체에도 맥락 보존.
|
|
74
|
+
- **검증(Verification)** — `verify-claim`(증거 강제), `contract verify`(명세↔구현), `verify-code`(테스트/빌드 실행 + 증거 기록).
|
|
75
|
+
- **감사(Audit)** — `scan secrets`, `encoding check`, `lazy detect`, `drift check`, `audit`, 그리고 이를 묶는 `gate`.
|
|
76
|
+
- **정책/보안(Policy)** — 8단계 권한 등급 enforce, 외부 에이전트 opt-in, 자연어 영구 룰(`rule add ... --trigger`).
|
|
249
77
|
|
|
250
78
|
---
|
|
251
79
|
|
|
252
|
-
##
|
|
253
|
-
|
|
254
|
-
```bash
|
|
255
|
-
# 1) Install + init — auto-detects system language for KO/EN guides (1.9.269)
|
|
256
|
-
npx leerness@latest init .
|
|
257
|
-
|
|
258
|
-
# 2) Start an AI session — auto-load context
|
|
259
|
-
npx leerness handoff .
|
|
80
|
+
## 명령 카테고리
|
|
260
81
|
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
82
|
+
- **초기화/진단**: `init` · `status` · `which` · `doctor` · `selftest` · `capabilities`
|
|
83
|
+
- **메모리**: `task` · `plan` · `decision` · `lesson` · `rule` · `feature` · `memory status/search`
|
|
84
|
+
- **인수인계/세션**: `handoff` · `session close` · `pulse` · `health`
|
|
85
|
+
- **검증/감사**: `check` · `gate` · `audit` · `drift check` · `lazy detect` · `scan secrets` · `encoding check` · `verify-code` · `verify-claim` · `contract verify`
|
|
86
|
+
- **외부 에이전트**: `agents list/check/dispatch` · `provider` · `roles` · `adapter`
|
|
87
|
+
- **운영/확장**: `release` · `migrate` · `team` · `install-safety` · `route` · `review`(페르소나)
|
|
88
|
+
- **브리지(opt-in)**: `web`(playwright) · `pc`(robotjs) · `lsp`
|
|
89
|
+
- **MCP**: `mcp serve` — stdio JSON-RPC 서버로 85개 도구 노출
|
|
267
90
|
|
|
268
|
-
|
|
91
|
+
전체 명령은 `leerness commands` 또는 `leerness --help` 로 확인하세요.
|
|
269
92
|
|
|
270
93
|
---
|
|
271
94
|
|
|
272
|
-
##
|
|
273
|
-
|
|
274
|
-
### 1. Creates "memory channels" in your workspace
|
|
275
|
-
|
|
276
|
-
```
|
|
277
|
-
.harness/
|
|
278
|
-
├── plan.md ← what to do
|
|
279
|
-
├── progress-tracker.md ← what was done (with evidence)
|
|
280
|
-
├── decisions.md ← why it was done that way
|
|
281
|
-
└── session-handoff.md ← what to pass to the next session
|
|
282
|
-
```
|
|
283
|
-
|
|
284
|
-
These are **protected as user memory**. leerness updates them automatically, but anything a human or AI wrote is **always preserved** and backed up to `.harness/archive/` before any change.
|
|
285
|
-
|
|
286
|
-
### 2. Verifies every "done" with evidence
|
|
287
|
-
|
|
288
|
-
```
|
|
289
|
-
AI: "Finished implementing the T-0042 API call"
|
|
290
|
-
↓ leerness verify-claim T-0042 --run-tests
|
|
291
|
-
check evidence files → actually run tests → match code
|
|
292
|
-
↓
|
|
293
|
-
✓ verified or ✗ "no trace of a fetch call in code (confidence 0.85)"
|
|
294
|
-
```
|
|
295
|
-
|
|
296
|
-
### 3. Directs multiple AI agents by role (new in 1.9.270)
|
|
297
|
-
|
|
298
|
-
Each model has different strengths. **Map roles to models** to put the right model on the right job.
|
|
95
|
+
## 대표 워크플로
|
|
299
96
|
|
|
97
|
+
**기본 세션 사이클**
|
|
300
98
|
```bash
|
|
301
|
-
leerness
|
|
302
|
-
leerness
|
|
303
|
-
leerness
|
|
304
|
-
leerness
|
|
99
|
+
leerness handoff . # 이전 맥락 자동 로드
|
|
100
|
+
leerness task add "API 응답 검증 로직 구현"
|
|
101
|
+
leerness task update T-0001 --status in-progress --evidence "검증 함수 구현"
|
|
102
|
+
leerness gate . # 배포 전 통합 품질 게이트
|
|
103
|
+
leerness session close . # 마감 + 다음 에이전트 인수인계
|
|
305
104
|
```
|
|
306
105
|
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
### 4. Detects "drift" when you stop using leerness over long rounds
|
|
310
|
-
|
|
106
|
+
**보안·검증 게이트 (CI)**
|
|
311
107
|
```bash
|
|
312
|
-
leerness
|
|
313
|
-
|
|
314
|
-
|
|
108
|
+
leerness scan secrets . # 커밋 대상 하드코딩 시크릿 → exit 1
|
|
109
|
+
leerness contract verify spec.md src/api.js # 명세 함수/필드 누락 → exit 1
|
|
110
|
+
leerness verify-claim T-0001 --require-evidence
|
|
315
111
|
```
|
|
316
112
|
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
leerness **complements, not replaces** [AGENTS.md](https://agents.md).
|
|
320
|
-
|
|
321
|
-
| Static — AGENTS.md | Dynamic — leerness |
|
|
322
|
-
|---|---|
|
|
323
|
-
| coding rules, test commands, prohibitions, deploy steps (rarely change) | current goal, files changed, failed attempts, verification, next handoff (change every task) |
|
|
324
|
-
| written by humans | recorded to `.leerness/` via `leerness state` / MCP `leerness_state_*` |
|
|
325
|
-
|
|
326
|
-
→ Rules live in AGENTS.md; **work state/memory/verification/handoff live in leerness** (the shared operating layer for any agent).
|
|
327
|
-
|
|
328
|
-
---
|
|
329
|
-
|
|
330
|
-
## 📊 Measured impact
|
|
331
|
-
|
|
332
|
-
From `leerness-bench` across 28 projects / 124 tasks:
|
|
333
|
-
|
|
334
|
-
| Metric | With | Without |
|
|
335
|
-
|--------|-----:|--------:|
|
|
336
|
-
| Multi-agent efficiency | 100/100 | 3/100 |
|
|
337
|
-
| Auto-verification (verify-claim) | 98/100 | 0/100 |
|
|
338
|
-
| Reuse awareness | 100/100 | 0/100 |
|
|
339
|
-
| Auto bug detection | 100/100 | 0/100 |
|
|
340
|
-
| Context retention | 100/100 | 0/100 |
|
|
341
|
-
| **Total** | **597/600 (99%)** | **3/600 (0.5%)** |
|
|
342
|
-
|
|
343
|
-
- Manual review **90s → 1.5s automated**
|
|
344
|
-
- Context load **~500 chars** (`handoff --compact`, ~90% fewer AI tokens)
|
|
345
|
-
|
|
346
|
-
---
|
|
347
|
-
|
|
348
|
-
## 🛠️ Core commands
|
|
349
|
-
|
|
350
|
-
```bash
|
|
351
|
-
# Daily
|
|
352
|
-
leerness init [path] # new install (auto-detects system language)
|
|
353
|
-
leerness handoff [path] [--compact] # load session-start context
|
|
354
|
-
leerness verify-claim T-XXX --run-tests # verify fake completion
|
|
355
|
-
leerness audit [path] [--fix] # consistency audit (--fix auto-update)
|
|
356
|
-
leerness session close [path] # close session + hand off
|
|
357
|
-
leerness drift check [path] # leerness-disuse drift score
|
|
358
|
-
|
|
359
|
-
# Multi AI agents · roles
|
|
360
|
-
leerness setup-agents # enable (claude/codex/agy/grok/opencode/qwen/aider/goose/copilot/ollama)
|
|
361
|
-
leerness agents list | quota # status / limits
|
|
362
|
-
leerness agents dispatch "<task>" --role coder # role-based model routing
|
|
363
|
-
leerness agents bench "<task>" # call several CLIs at once + compare
|
|
364
|
-
leerness roles list | set | suggest | verify # per-model role assignment
|
|
365
|
-
|
|
366
|
-
# Security · encoding
|
|
367
|
-
leerness scan secrets . # API key/token pattern scan
|
|
368
|
-
leerness encoding check . # BOM/UTF-16/CJK round-trip
|
|
369
|
-
leerness gate [path] # verify+audit+scan+encoding+lazy combined
|
|
370
|
-
|
|
371
|
-
# Versions
|
|
372
|
-
leerness update --check # detect new version
|
|
373
|
-
leerness update --yes # auto-migrate + verify
|
|
374
|
-
```
|
|
375
|
-
|
|
376
|
-
Run `leerness commands` for the full list, or `leerness mcp serve` to expose tools to a main agent.
|
|
377
|
-
|
|
378
|
-
---
|
|
379
|
-
|
|
380
|
-
## ❓ FAQ
|
|
381
|
-
|
|
382
|
-
**Q. Does leerness change my code?**
|
|
383
|
-
A. User memory (plan/progress/decisions) is **always preserved** and backed up before any change. It never edits your source code directly.
|
|
384
|
-
|
|
385
|
-
**Q. Can I use it without an AI agent?**
|
|
386
|
-
A. Yes. Solo developers can use it for self-verification, memory, and duplicate detection.
|
|
387
|
-
|
|
388
|
-
**Q. Does it auto-call external AI CLIs?**
|
|
389
|
-
A. **Never.** `agents dispatch` only generates command text. Actual execution is explicit, by you or your main agent.
|
|
390
|
-
|
|
391
|
-
**Q. Can I use it in CI?**
|
|
392
|
-
A. Yes. Every command supports exit codes, `--json`, and non-interactive `--yes`.
|
|
393
|
-
|
|
394
|
-
---
|
|
395
|
-
|
|
396
|
-
## 🔒 Security & transparency
|
|
397
|
-
|
|
398
|
-
leerness is a powerful CLI harness (child_process, git, external CLIs, automation bridges, hook install). It discloses **everything it can do and how to turn each off**.
|
|
399
|
-
|
|
113
|
+
**다중 에이전트 조율**
|
|
400
114
|
```bash
|
|
401
|
-
leerness
|
|
115
|
+
leerness agents list # 설치된 외부 AI CLI 가용성
|
|
116
|
+
leerness agents dispatch "코드 리뷰" --to codex # 실행 명령 생성(직접 실행은 사용자/메인 에이전트)
|
|
402
117
|
```
|
|
403
118
|
|
|
404
|
-
|
|
405
|
-
- `init` installs a SessionStart hook (`update --check`) in `.claude/settings.local.json` → disable with `leerness init . --no-auto-update`
|
|
406
|
-
- For company/production code, trial `init` in one project, review with `git diff`, then commit.
|
|
407
|
-
- Details: [SECURITY.md](SECURITY.md)
|
|
408
|
-
|
|
409
|
-
**Lighter-install options (1.9.276):**
|
|
410
|
-
|
|
119
|
+
**MCP (외부 AI 에이전트에 도구로 노출)**
|
|
411
120
|
```bash
|
|
412
|
-
leerness
|
|
413
|
-
leerness init . --minimal # core files only (skips editor integrations/guides/roadmap/.env)
|
|
414
|
-
leerness init . --no-env # skip only .env/.env.example auto-creation
|
|
121
|
+
leerness mcp serve # JSON-RPC over stdio, 85 도구
|
|
415
122
|
```
|
|
416
123
|
|
|
417
|
-
|
|
124
|
+
---
|
|
418
125
|
|
|
419
|
-
|
|
126
|
+
## 아키텍처 (외부 리뷰 검증)
|
|
420
127
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
128
|
+
- **런타임 의존성 0 / install-script 0** — `package.json` 의 dependencies/optional/peer 가 전부 비어 있고 postinstall 도 없습니다. 순수 Node stdlib(`fs`/`path`/`child_process`/`readline`). 공급망 공격면 최소. `leerness install-safety` 로 확인 가능.
|
|
129
|
+
- **canonical JSON 단일 진실소스 + 마크다운 projection** — 메모리는 JSON 으로 저장하고 사람이 읽는 `.md` 는 파생물. 파이프(`|`)·개행·백틱·이모지·한글이 마크다운 테이블에서도 안전(셀 이스케이프 + round-trip).
|
|
130
|
+
- **원자적 UTF-8 쓰기** — temp + rename 으로 부분쓰기 손상 방지, BOM 자동 strip.
|
|
131
|
+
- **shell 미경유 MCP** — `mcp serve` 의 도구 호출은 셸을 거치지 않고 인자를 직접 전달(명령 주입 차단).
|
|
132
|
+
- **순수 `--json` / 일관 exit code** — 성공·실패·미존재 경로 모두 파싱 가능한 JSON(`{ok,error,code}`) + 실패 시 exit 1. 자동화·CI 친화.
|
|
133
|
+
- **모듈 분리(DI)** + **내장 자가검증** — `lib/` 순수 유틸/IO/카탈로그 분리, `selftest` 180+ 케이스로 설치 무결성 검증.
|
|
427
134
|
|
|
428
135
|
---
|
|
429
136
|
|
|
430
|
-
##
|
|
137
|
+
## 차별점
|
|
431
138
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
139
|
+
- **진짜 0-dependency** — SaaS 가 아니라 파일 기반 로컬 운영 메모리. 네트워크 불필요(업데이트 확인 제외).
|
|
140
|
+
- **에이전트 중립** — Claude / Codex / Cursor / Aider / Goose 등 어디에나 적용.
|
|
141
|
+
- **거짓 완료 방지** — 완료 주장에 evidence 를 요구하는 anti-lazy 흐름.
|
|
142
|
+
- **통합 게이트** — 보안·인코딩·드리프트·검증을 `gate` 하나로.
|
|
143
|
+
- **한국어 우선 + Windows/인코딩 1급 시민** — CP949/BOM 가드, 한국어 출력 기본(`--language en` 지원).
|
|
144
|
+
- **자기기술(self-describing)** — `about`/`commands`/`capabilities`/`doctor` 로 도구가 스스로 설명.
|
|
145
|
+
- **지속적 외부 검증** — 다중 모델 클린룸 리뷰로 회귀 0 을 유지하며 진화.
|
|
439
146
|
|
|
440
147
|
---
|
|
441
148
|
|
|
442
|
-
##
|
|
443
|
-
|
|
444
|
-
- **1.9.271** — README 한/영 이중 사용자 친화 재작성 / Bilingual KO-EN README rewrite.
|
|
445
|
-
- **1.9.270** — agent roles: 모델별 역할 부여 / per-model role assignment (`roles` CLI + `dispatch --role`).
|
|
446
|
-
- **1.9.269** — init 시 OS 시스템 언어 감지 / OS system-language detection on init.
|
|
447
|
-
- **1.9.268** — grok 정식 provider 승격 / grok promoted to a first-class provider.
|
|
448
|
-
- **1.9.265~267** — CLI 에이전트 슬래시 명령 레지스트리 + `--help` probe 자동 갱신.
|
|
149
|
+
## 보안
|
|
449
150
|
|
|
450
|
-
|
|
151
|
+
- 시크릿/키/토큰을 소스에 하드코딩하지 마세요. `.env` 사용 + `.gitignore` 포함. `scan secrets`/`gate` 가 커밋 대상 시크릿을 차단합니다.
|
|
152
|
+
- 외부 AI CLI 연동은 **기본 비활성**입니다(`LEERNESS_ENABLE_*` opt-in). 브리지(web/pc/lsp)도 opt-in.
|
|
153
|
+
- 자세한 권한 표면은 [SECURITY.md](./SECURITY.md) 참고.
|
|
451
154
|
|
|
452
155
|
---
|
|
453
156
|
|
|
454
|
-
##
|
|
455
|
-
|
|
456
|
-
- Issues: https://github.com/gugu9999gu/leerness/issues
|
|
457
|
-
- PRs: e2e 통과 + 한국어 주석 + UTF-8 (no BOM) / pass e2e, Korean comments, UTF-8 without BOM.
|
|
458
|
-
- 테스트 / Tests:
|
|
459
|
-
```bash
|
|
460
|
-
npm run test:fast # 빠른 핵심-경로 smoke (selftest + 13 체크, ~10초) / fast core-path smoke (~10s)
|
|
461
|
-
npm test # 전체 게이트 (selftest + e2e 220+) / full gate
|
|
462
|
-
```
|
|
157
|
+
## 링크
|
|
463
158
|
|
|
464
|
-
|
|
159
|
+
- 홈페이지: https://leerness.com
|
|
160
|
+
- npm: https://www.npmjs.com/package/leerness
|
|
161
|
+
- GitHub: https://github.com/gugu9999gu/leerness
|
|
162
|
+
- 변경 이력: [CHANGELOG.md](./CHANGELOG.md)
|
|
465
163
|
|
|
466
|
-
|
|
164
|
+
## 라이선스
|
|
467
165
|
|
|
468
|
-
|
|
469
|
-
> **A tool that makes AI agents work reliably. It never auto-calls external LLMs/APIs/CLIs without your consent.**
|
|
166
|
+
MIT
|
|
470
167
|
|
|
471
168
|
<!-- leerness:project-readme:start -->
|
|
472
169
|
## Leerness Project Harness
|
|
473
170
|
|
|
474
|
-
이 프로젝트는 Leerness v1.9.
|
|
171
|
+
이 프로젝트는 Leerness v1.9.441 하네스를 사용합니다. AI 에이전트는 작업 전 `leerness handoff`로 컨텍스트를 적재하고, 작업 후 `leerness check`/`leerness audit`/`leerness session close`를 수행해야 합니다.
|
|
475
172
|
|
|
476
173
|
### 정체성 — AI 에이전트 운영 레이어 (UR-0030)
|
|
477
174
|
|
|
@@ -525,7 +222,7 @@ leerness memory restore decision <date|title>
|
|
|
525
222
|
|
|
526
223
|
### MCP server (외부 AI 통합)
|
|
527
224
|
|
|
528
|
-
Leerness v1.9.
|
|
225
|
+
Leerness v1.9.441는 stdio JSON-RPC MCP server를 내장합니다 — Claude Code · Cursor · Codex CLI 등 외부 AI에 **85개 도구**를 노출:
|
|
529
226
|
|
|
530
227
|
```jsonc
|
|
531
228
|
// 카테고리별
|
|
@@ -546,7 +243,7 @@ Leerness v1.9.439는 stdio JSON-RPC MCP server를 내장합니다 — Claude Cod
|
|
|
546
243
|
`<<autonomous-loop-dynamic>>` 신호만 보내면 AI가:
|
|
547
244
|
1) 다음 라운드 후보 선정 → 2) 코드 변경 → 3) stress-v* 신규 작성 + 누적 회귀 → 4) e2e 219/219 → 5) npm pack + git tag + GitHub release → 6) main 자동 push (1.9.140+) → 7) session close → 8) 다음 라운드 예약.
|
|
548
245
|
|
|
549
|
-
현재 누적: **70 라운드 (1.9.40 → 1.9.
|
|
246
|
+
현재 누적: **70 라운드 (1.9.40 → 1.9.441)** · 매 라운드 GitHub release/태그 생성 · _reports/는 비공개 보존.
|
|
550
247
|
|
|
551
248
|
### 성능 가이드 (1.9.140 측정)
|
|
552
249
|
|
|
@@ -584,6 +281,6 @@ leerness release pack --close --auto-main-push
|
|
|
584
281
|
- `.harness/session-handoff.md`: 다음 세션 인수인계 (자동 작성)
|
|
585
282
|
- `.harness/lessons.md` / `decisions.md` / `rules.md`: 영구 메모리 (5 surface)
|
|
586
283
|
|
|
587
|
-
Last synced by Leerness v1.9.
|
|
284
|
+
Last synced by Leerness v1.9.441: 2026-06-08
|
|
588
285
|
<!-- leerness:project-readme:end -->
|
|
589
286
|
|
package/bin/leerness.js
CHANGED
|
@@ -31,7 +31,7 @@ const { _evidenceQuality, _parseEvidenceStats, _shellGuardAnalyze, _claimFileInG
|
|
|
31
31
|
// 1.9.295 (UR-0025 4단계): 정적 데이터 카탈로그 모듈 분리 (비파괴, require-based).
|
|
32
32
|
const { CAPABILITY_SURFACE, POWERFUL_COMMANDS, ADAPTERS, REUSE_CATEGORIES, REUSE_CHECKLIST, _DEFAULT_PLATFORM_CONSTRAINTS, _DEFAULT_DOMAIN_CATALOG, _LSP_LANG_PATTERNS, OPTIMISM_PATTERNS, BUILT_IN_PERSONAS, STRINGS, BUILTIN_CATALOG, ROADMAP_STATUS_LABEL, ROADMAP_STATUS_COLOR, SECRET_PATTERNS, MERGE_OVERWRITE_FILES, MINIMAL_SKIP_KEYS, REQUIRED_WORKSPACE_FILES, KEYWORD_STOPWORDS, SKILL_CATALOG_PRESETS } = require('../lib/catalogs'); // 1.9.344/368/369 (UR-0025): catalog 분리 (MERGE_OVERWRITE_FILES/MINIMAL_SKIP_KEYS 포함)
|
|
33
33
|
|
|
34
|
-
const VERSION = '1.9.
|
|
34
|
+
const VERSION = '1.9.441';
|
|
35
35
|
|
|
36
36
|
// 1.9.290 (UR-0037, Codex gpt-5.5 #4 수렴): CLI 전용 부작용은 require 시 실행하지 않는다.
|
|
37
37
|
// 이전: warning listener 제거 / NODE_OPTIONS 변경 / chcp IIFE 가 top-level 즉시 실행 → require('harness') 시 호스트 프로세스 오염.
|
|
@@ -3298,6 +3298,20 @@ function _selfTestCases() {
|
|
|
3298
3298
|
const jsonStillLog = modSrc.includes('log(JSON.stringify({ root, score'); // 최종 JSON 출력은 log 유지
|
|
3299
3299
|
return afLogDef && usesAfLog && jsonStillLog;
|
|
3300
3300
|
} },
|
|
3301
|
+
{ name: '12th 외부평가 Opus P2 (UR-0140 stab): 시크릿 스캐너 prefix 패턴도 placeholder 가드 적용 (1.9.440)', run: () => {
|
|
3302
|
+
const src = read(__filename);
|
|
3303
|
+
// 스캐너가 valueGroup 없을 때 m[0] 로 placeholder 판정(prefix 패턴 가드)
|
|
3304
|
+
const wired = src.includes('const val = (valueGroup != null) ? m[valueGroup] : m[0];') && src.includes('if (_isPlaceholderSecret(val)) {');
|
|
3305
|
+
// 행위: prefix 8연속 더미는 placeholder, 진짜(고엔트로피)는 real (FN 정책 유지 — 'example' 포함 실키는 real)
|
|
3306
|
+
const m = require('../lib/pure-utils');
|
|
3307
|
+
const behav = m._isPlaceholderSecret('AKIA' + 'X'.repeat(16)) === true && m._isPlaceholderSecret('AKIAJQXMP7RZ2KL9WXYZ') === false && m._isPlaceholderSecret('sk-EXAMPLEab12cd34ef56gh78ij90kl') === false;
|
|
3308
|
+
return wired && behav;
|
|
3309
|
+
} },
|
|
3310
|
+
{ name: 'README ASCII 배너 표시 + CLI _banner 와 동일 아트 (1.9.441)', run: () => {
|
|
3311
|
+
const readme = read(path.join(path.dirname(__filename), '..', 'README.md'));
|
|
3312
|
+
const bannerLine = '███████╗███████╗██╗ ██╗'.slice(0, 0) + '██║ █████╗ █████╗ ██████╔╝'; // LEERNESS 배너 고유 라인(자기참조 회피 split)
|
|
3313
|
+
return readme.includes(bannerLine) && read(__filename).includes(bannerLine); // README ↔ CLI _banner 동일 아트
|
|
3314
|
+
} },
|
|
3301
3315
|
{ name: 'VERSION 형식 (x.y.z)', run: () => /^\d+\.\d+\.\d+$/.test(VERSION) }
|
|
3302
3316
|
];
|
|
3303
3317
|
}
|
|
@@ -7123,11 +7137,11 @@ function _collectSecretFindings(root) {
|
|
|
7123
7137
|
let m;
|
|
7124
7138
|
while ((m = re.exec(text))) {
|
|
7125
7139
|
// 1.9.365 (CV-6/UR-0081): placeholder/예시 값 스킵.
|
|
7126
|
-
|
|
7127
|
-
|
|
7128
|
-
|
|
7129
|
-
|
|
7130
|
-
}
|
|
7140
|
+
// 1.9.440 (12th 외부평가 Opus P2): prefix 패턴(valueGroup 없음, AWS/GitHub 등 19종)도 placeholder 가드 적용 —
|
|
7141
|
+
// 기존엔 valueGroup 있는 2패턴만 가드 → .env.example 의 더미 토큰(AKIAXXXX…/ghp_XXXX…)이 커밋 시크릿 오탐(CI 파손). 전체 매치(m[0])로 판정.
|
|
7142
|
+
const val = (valueGroup != null) ? m[valueGroup] : m[0];
|
|
7143
|
+
if (_isPlaceholderSecret(val)) { if (re.lastIndex === m.index) re.lastIndex++; continue; }
|
|
7144
|
+
if (valueGroup != null && requireSecretLike && !_looksSecretLike(val)) { if (re.lastIndex === m.index) re.lastIndex++; continue; }
|
|
7131
7145
|
const line = text.slice(0, m.index).split('\n').length;
|
|
7132
7146
|
findings.push({ file: fileRel, line, name, snippet: m[0].slice(0, 32), gitignored });
|
|
7133
7147
|
break;
|
package/lib/pure-utils.js
CHANGED
|
@@ -636,6 +636,7 @@ function _isPlaceholderSecret(value) {
|
|
|
636
636
|
const hasRealPrefix = /^(?:sk-|sk-proj-|pk_|rk_|akia|ghp_|gho_|ghs_|ghr_|github_pat_|xox[baprs]-|aiza|ya29\.|glpat-|-----begin)/.test(v);
|
|
637
637
|
// 1.9.436 (11th 외부평가 Opus P3): prefix 가 있어도 본문이 동일문자 8+연속(AKIAXXXX…/…00000000…)이면 명백한 더미 → placeholder. 실키는 고엔트로피라 무영향.
|
|
638
638
|
if (/(.)\1{7,}/.test(alnum)) return true;
|
|
639
|
+
// (12th 외부평가 Opus P3 'AWS …EXAMPLE 키'는 보류 — 'example' 통째 placeholder 화는 기존 FN 정책(sk-EXAMPLE… 실키 유지, UR-0105)과 충돌. UR-0144 로 분리.)
|
|
639
640
|
// 실키 prefix → 항상 실키(마커 무시). 그 외 마커 단어 있으면 placeholder(고엔트로피여도). prefix 없고 마커 없고 고엔트로피 → 실키.
|
|
640
641
|
if (hasRealPrefix) return false;
|
|
641
642
|
if (hasMarker) return true;
|
package/package.json
CHANGED
package/scripts/e2e.js
CHANGED
|
@@ -37,7 +37,7 @@ run('scan secrets', ['scan', 'secrets', tmp]);
|
|
|
37
37
|
run('encoding check', ['encoding', 'check', tmp]);
|
|
38
38
|
|
|
39
39
|
const secretFile = path.join(tmp, 'fake-config.json');
|
|
40
|
-
fs.writeFileSync(secretFile, JSON.stringify({ openai: 'sk-' + '
|
|
40
|
+
fs.writeFileSync(secretFile, JSON.stringify({ openai: 'sk-' + 'a1B2'.repeat(12) }));
|
|
41
41
|
run('scan secrets (detect)', ['scan', 'secrets', tmp], { expectFail: true });
|
|
42
42
|
fs.unlinkSync(secretFile);
|
|
43
43
|
|
|
@@ -2695,7 +2695,7 @@ total++;
|
|
|
2695
2695
|
total++;
|
|
2696
2696
|
{
|
|
2697
2697
|
fs.mkdirSync(path.join(tmp, '_devspace'), { recursive: true });
|
|
2698
|
-
fs.writeFileSync(path.join(tmp, '_devspace/secret-config.js'), `const k = "ghp_${'
|
|
2698
|
+
fs.writeFileSync(path.join(tmp, '_devspace/secret-config.js'), `const k = "ghp_${'a1B2'.repeat(9)}";\n`);
|
|
2699
2699
|
fs.writeFileSync(path.join(tmp, '.leerness-skip-dirs'), '_devspace/\n# 주석은 무시\n');
|
|
2700
2700
|
const r = cp.spawnSync(process.execPath, [CLI, 'scan', 'secrets', tmp], { encoding: 'utf8' });
|
|
2701
2701
|
const ok = r.status === 0 && /no obvious secret patterns/.test(r.stdout);
|
|
@@ -3650,7 +3650,7 @@ total++;
|
|
|
3650
3650
|
let ok = false;
|
|
3651
3651
|
try {
|
|
3652
3652
|
const sd = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-secmod-'));
|
|
3653
|
-
const A = '
|
|
3653
|
+
const A = 'a1B2'.repeat(10);
|
|
3654
3654
|
const lines = [
|
|
3655
3655
|
'const a = "' + 'sk-' + 'proj-' + A + '_' + A + '";', // modern OpenAI project (기존 패턴 놓침)
|
|
3656
3656
|
'const b = "' + 'sk-' + 'ant-api03-' + A + '_' + A + '";', // Anthropic api03 (언더스코어 — 기존 놓침)
|
|
@@ -4434,7 +4434,7 @@ total++;
|
|
|
4434
4434
|
let ok = false;
|
|
4435
4435
|
try {
|
|
4436
4436
|
const c = require(path.resolve(__dirname, '..', 'lib', 'catalogs.js'));
|
|
4437
|
-
const A = '
|
|
4437
|
+
const A = 'a1B2'.repeat(10);
|
|
4438
4438
|
const hit = (s) => c.SECRET_PATTERNS.some(p => { p.re.lastIndex = 0; return p.re.test(s); });
|
|
4439
4439
|
const catOk = Array.isArray(c.SECRET_PATTERNS) && c.SECRET_PATTERNS.length === 20
|
|
4440
4440
|
&& hit('AKIA' + 'ABCD1234EFGH5678') && hit('sk-' + 'ant-api03-' + A + '_' + A) && !hit('const u = "john' + '_doe_2024";');
|
|
@@ -4634,7 +4634,7 @@ total++;
|
|
|
4634
4634
|
fs.rmSync(skf, { recursive: true, force: true });
|
|
4635
4635
|
// UR-0060: 사용자 harness.js 파일도 스캔(false-neg 제거) + 신규 GitLab 패턴 탐지
|
|
4636
4636
|
const ud = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-scan-'));
|
|
4637
|
-
fs.writeFileSync(path.join(ud, 'harness.js'), 'const k = "glpat-' + '
|
|
4637
|
+
fs.writeFileSync(path.join(ud, 'harness.js'), 'const k = "glpat-' + 'a1B2'.repeat(5) + '";\n');
|
|
4638
4638
|
const scr = cp.spawnSync(process.execPath, [CLI, 'scan', 'secrets', ud], { encoding: 'utf8', timeout: 20000 });
|
|
4639
4639
|
const scanOk = /GitLab PAT/.test((scr.stdout || '') + (scr.stderr || ''));
|
|
4640
4640
|
fs.rmSync(ud, { recursive: true, force: true });
|
|
@@ -4736,7 +4736,7 @@ total++;
|
|
|
4736
4736
|
// scan secrets <file> (이전 ENOTDIR) + basename 표시
|
|
4737
4737
|
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-p3-'));
|
|
4738
4738
|
cp.spawnSync(process.execPath, [CLI, 'init', d, '--yes', '--language', 'ko'], { encoding: 'utf8', timeout: 30000 });
|
|
4739
|
-
fs.writeFileSync(path.join(d, 'leak.js'), 'const k = "glpat-' + '
|
|
4739
|
+
fs.writeFileSync(path.join(d, 'leak.js'), 'const k = "glpat-' + 'a1B2'.repeat(5) + '";\n');
|
|
4740
4740
|
const fr = cp.spawnSync(process.execPath, [CLI, 'scan', 'secrets', path.join(d, 'leak.js')], { encoding: 'utf8', timeout: 20000 });
|
|
4741
4741
|
const fout = (fr.stdout || '') + (fr.stderr || '');
|
|
4742
4742
|
const fileScanOk = /GitLab PAT/.test(fout) && /leak\.js/.test(fout) && !/ENOTDIR/.test(fout);
|
|
@@ -6091,5 +6091,25 @@ total++;
|
|
|
6091
6091
|
if (!ok) failed++;
|
|
6092
6092
|
}
|
|
6093
6093
|
|
|
6094
|
+
// 1.9.440 (12th 외부평가 Opus P2): 시크릿 스캐너 prefix 패턴(AWS/GitHub)도 placeholder 가드 — .env.example 더미는 미탐, 진짜는 탐지.
|
|
6095
|
+
total++;
|
|
6096
|
+
{
|
|
6097
|
+
let ok = false;
|
|
6098
|
+
try {
|
|
6099
|
+
const d = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-scanph-'));
|
|
6100
|
+
cp.spawnSync(process.execPath, [CLI, 'init', d, '--yes', '--language', 'ko'], { encoding: 'utf8', timeout: 30000 });
|
|
6101
|
+
// 더미 prefix 토큰(.env.example, gitignore 대상 아님) → 미탐(exit 0)
|
|
6102
|
+
fs.writeFileSync(path.join(d, '.env.example'), 'AWS_KEY=AKIA' + 'X'.repeat(16) + '\nGH=ghp_' + 'X'.repeat(36) + '\n');
|
|
6103
|
+
const dummy = cp.spawnSync(process.execPath, [CLI, 'scan', 'secrets', d], { encoding: 'utf8', timeout: 20000 });
|
|
6104
|
+
// 진짜 AWS 키(AKIA+16 랜덤) → 탐지(exit 1)
|
|
6105
|
+
fs.writeFileSync(path.join(d, 'real.js'), 'const k="AKIAJQXMP7RZ2KL9WXYZ";');
|
|
6106
|
+
const real = cp.spawnSync(process.execPath, [CLI, 'scan', 'secrets', d], { encoding: 'utf8', timeout: 20000 });
|
|
6107
|
+
fs.rmSync(d, { recursive: true, force: true });
|
|
6108
|
+
ok = dummy.status === 0 && real.status === 1;
|
|
6109
|
+
} catch {}
|
|
6110
|
+
console.log(ok ? '✓ B(1.9.440) UR-0140: 시크릿 스캐너 prefix 더미 미탐 + 진짜 탐지(placeholder 가드 통합)' : '✗ 시크릿 스캐너 prefix 가드 실패');
|
|
6111
|
+
if (!ok) failed++;
|
|
6112
|
+
}
|
|
6113
|
+
|
|
6094
6114
|
console.log(`\nE2E result: ${total - failed}/${total} passed · ${((Date.now() - _e2eStart) / 1000).toFixed(0)}s`);
|
|
6095
6115
|
if (failed > 0) process.exit(1);
|