leerness 1.9.79 → 1.9.81
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 +49 -0
- package/README.md +4 -2
- package/bin/harness.js +77 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,54 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.81 — 2026-05-20
|
|
4
|
+
|
|
5
|
+
**`handoff` "통합 헤드라인" 한 줄 요약** (drift + 보안 + skill + MCP).
|
|
6
|
+
|
|
7
|
+
### Added — handoff 헤드라인
|
|
8
|
+
- Date / Project 라인 직후에 한 줄 요약 자동 노출:
|
|
9
|
+
```
|
|
10
|
+
📊 헤드라인 (1.9.81): drift healthy (0) · 🔒 보안 OK · 🔌 MCP 8회 · 📒 skill query 4회 · 📚 12 skills
|
|
11
|
+
```
|
|
12
|
+
- 표시 요소:
|
|
13
|
+
- `drift <level> (<score>)` — 1.9.78 5신호 결과
|
|
14
|
+
- `🔒 보안 OK` 또는 `🚨 보안 위험` — 1.9.76 보안 요약 압축
|
|
15
|
+
- `🔌 MCP N회` — 1.9.70 MCP 누적 카운트
|
|
16
|
+
- `📒 skill query N회` — 1.9.68 rolling history 누적
|
|
17
|
+
- `📚 N skills` — 설치된 skill 총 수
|
|
18
|
+
- 데이터 없는 항목은 자동 생략 (잡음 방지).
|
|
19
|
+
- 끄기: `--no-headline` 또는 `--compact`.
|
|
20
|
+
|
|
21
|
+
### Use Case
|
|
22
|
+
- AI 에이전트가 한 줄로 워크스페이스 상태 즉시 인지 → "drift attention인데 MCP 0회면 도구 안 쓰고 있다" 같은 빠른 판단.
|
|
23
|
+
|
|
24
|
+
### Verified
|
|
25
|
+
- stress-v27 — 헤드라인 노출 / --no-headline / 누적 회귀.
|
|
26
|
+
- e2e 219/219 PASS 유지.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 1.9.80 — 2026-05-20
|
|
31
|
+
|
|
32
|
+
**handoff에서 `.env` 보안 critical 시 자동 회복 옵션** (1.9.76 보안 요약 + 1.9.75 audit --fix 결합).
|
|
33
|
+
|
|
34
|
+
### Added — 보안 critical 자동 회복
|
|
35
|
+
- 1.9.76 handoff 보안 요약 블록 확장:
|
|
36
|
+
- `.env` 가 `.gitignore` 에 없으면 **🚨 CRITICAL** 경고.
|
|
37
|
+
- 즉시 `leerness audit --fix` 권장 안내.
|
|
38
|
+
- **자동 실행 옵션**: `LEERNESS_AUTO_SECURITY_FIX=1` 환경변수 활성 시 handoff에서 `audit --fix` 자동 실행.
|
|
39
|
+
- 시크릿 노출 위험 즉시 회복.
|
|
40
|
+
- 성공 시 `✓ 자동 회복 (LEERNESS_AUTO_SECURITY_FIX=1)` 메시지.
|
|
41
|
+
|
|
42
|
+
### Use Case
|
|
43
|
+
- 사용자가 `.env` 를 무심코 만들었지만 `.gitignore` 에 추가 안 한 상태 → 다음 handoff에서 즉시 인지 + 옵션 활성 시 자동 회복.
|
|
44
|
+
- 1.9.78 drift 보안 신호 + 1.9.76 handoff 요약 + 1.9.80 자동 회복 = **3중 보안 가드** 완성.
|
|
45
|
+
|
|
46
|
+
### Verified
|
|
47
|
+
- stress-v26 — handoff CRITICAL 메시지 / 자동 회복 / 누적 회귀.
|
|
48
|
+
- e2e 219/219 PASS 유지.
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
3
52
|
## 1.9.79 — 2026-05-20
|
|
4
53
|
|
|
5
54
|
**`leerness skill suggest` 알고리즘 강화** (1.9.68 rolling history 빈도 활용 — Hermes-style 학습 신호 보강).
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **AI 코딩 에이전트의 거짓 완료·중복·망각·충돌을 막아주는 검수·기억·협업 CLI 하네스.**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/leerness) [](https://www.npmjs.com/package/leerness) []() []() []()
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
╔══════════════════════════════════════════════════════════════╗
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
|
|
13
13
|
║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
|
|
14
14
|
║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
|
|
15
|
-
║ v1.9.
|
|
15
|
+
║ v1.9.81 AI Agent Reliability Harness ║
|
|
16
16
|
║ verify · remember · orchestrate · audit · prevent drift ║
|
|
17
17
|
╚══════════════════════════════════════════════════════════════╝
|
|
18
18
|
```
|
|
@@ -433,6 +433,8 @@ npm test # = node ./scripts/e2e.js
|
|
|
433
433
|
|
|
434
434
|
## 변경 이력 (최근)
|
|
435
435
|
|
|
436
|
+
- **1.9.81** — **`handoff` 통합 헤드라인** — drift level + 보안 상태 + MCP 활동 + skill query + 설치 skill 수를 한 줄로 압축. AI가 세션 시작 즉시 워크스페이스 상태 인지.
|
|
437
|
+
- **1.9.80** — **handoff 보안 critical 자동 회복** — `.env` 가 `.gitignore` 에 없으면 🚨 CRITICAL 경고 + `LEERNESS_AUTO_SECURITY_FIX=1` 시 `audit --fix` 자동 실행.
|
|
436
438
|
- **1.9.79** — **`leerness skill suggest` 알고리즘 강화** — 1.9.68 rolling history 빈도 (×2 가중) 추가. 반복 검색된 키워드를 신규 skill 후보로 자동 식별 (Hermes-style 학습).
|
|
437
439
|
- **1.9.78** — **`drift check` 5번째 신호** — `.env` + `.gitignore` 보안 누락을 drift score에 가중 (`.env` 자체 누락 시 +30, 동기화 누락 +15). 보안 위험이 drift critical 승격 가능.
|
|
438
440
|
- **1.9.77** — **MCP server 15번째 도구 `leerness_brainstorm`** — 1.9.72 brainstorm (decisions + skills + tasks + rules + evidence + lessons + skillHistory + taskLogFails)을 외부 AI에 노출. AI가 새 작업 시작 전 누적 컨텍스트 자동 회수.
|
package/bin/harness.js
CHANGED
|
@@ -6,7 +6,7 @@ const path = require('path');
|
|
|
6
6
|
const cp = require('child_process');
|
|
7
7
|
const readline = require('readline');
|
|
8
8
|
|
|
9
|
-
const VERSION = '1.9.
|
|
9
|
+
const VERSION = '1.9.81';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -1815,6 +1815,56 @@ function handoff(root) {
|
|
|
1815
1815
|
log('# Session Start Context');
|
|
1816
1816
|
log(`Date: ${today()}`);
|
|
1817
1817
|
log(`Project: ${detectProjectName(root)}`);
|
|
1818
|
+
// 1.9.81: 통합 헤드라인 — drift level + 보안 상태 + skill 추천 + MCP 활동을 한 줄로 압축
|
|
1819
|
+
// AI 에이전트가 매 세션 시작 즉시 컨텍스트 인지. --no-headline 또는 --compact로 끄기.
|
|
1820
|
+
if (!has('--no-headline') && !has('--compact')) {
|
|
1821
|
+
try {
|
|
1822
|
+
const parts = [];
|
|
1823
|
+
// 1) drift level (가벼운 check)
|
|
1824
|
+
try {
|
|
1825
|
+
const r = cp.spawnSync(process.execPath, [__filename, 'drift', 'check', root, '--json'],
|
|
1826
|
+
{ encoding: 'utf8', timeout: 8000, env: { ...process.env, LEERNESS_NO_PROMPT: '1', LEERNESS_NO_DRIFT_CHECK: '0' } });
|
|
1827
|
+
const j = JSON.parse(r.stdout.trim());
|
|
1828
|
+
if (j.level) parts.push(`drift ${j.level.replace(/^[^\w]+/, '')} (${j.score})`);
|
|
1829
|
+
} catch {}
|
|
1830
|
+
// 2) 보안 상태
|
|
1831
|
+
try {
|
|
1832
|
+
const envPath = path.join(root, '.env');
|
|
1833
|
+
if (exists(envPath)) {
|
|
1834
|
+
const giText = exists(path.join(root, '.gitignore')) ? read(path.join(root, '.gitignore')) : '';
|
|
1835
|
+
const giLines = giText.split('\n').map(l => l.trim());
|
|
1836
|
+
if (giLines.includes('.env') || giLines.includes('/.env')) parts.push('🔒 보안 OK');
|
|
1837
|
+
else parts.push('🚨 보안 위험');
|
|
1838
|
+
}
|
|
1839
|
+
} catch {}
|
|
1840
|
+
// 3) MCP 활동 누적
|
|
1841
|
+
try {
|
|
1842
|
+
const stats = _readUsageStats(root);
|
|
1843
|
+
const mcpTotal = stats.mcp?.tools ? Object.values(stats.mcp.tools).reduce((s, n) => s + n, 0) : 0;
|
|
1844
|
+
if (mcpTotal > 0) parts.push(`🔌 MCP ${mcpTotal}회`);
|
|
1845
|
+
} catch {}
|
|
1846
|
+
// 4) skill match history 누적
|
|
1847
|
+
try {
|
|
1848
|
+
const histPath = path.join(root, '.harness', 'skill-suggestions.md');
|
|
1849
|
+
if (exists(histPath)) {
|
|
1850
|
+
const txt = read(histPath);
|
|
1851
|
+
const cnt = (txt.match(/^## [\d-]+ [\d:]+ — query/gm) || []).length;
|
|
1852
|
+
if (cnt > 0) parts.push(`📒 skill query ${cnt}회`);
|
|
1853
|
+
}
|
|
1854
|
+
} catch {}
|
|
1855
|
+
// 5) 설치된 skill 수
|
|
1856
|
+
try {
|
|
1857
|
+
const all = listAllSkills(root);
|
|
1858
|
+
const skillCnt = Object.keys(all).length;
|
|
1859
|
+
if (skillCnt > 0) parts.push(`📚 ${skillCnt} skills`);
|
|
1860
|
+
} catch {}
|
|
1861
|
+
if (parts.length) {
|
|
1862
|
+
const isTty = process.stdout && process.stdout.isTTY;
|
|
1863
|
+
const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
|
|
1864
|
+
log(cy(`📊 헤드라인 (1.9.81): ${parts.join(' · ')}`));
|
|
1865
|
+
}
|
|
1866
|
+
} catch {}
|
|
1867
|
+
}
|
|
1818
1868
|
// 1.9.8: active rules 자동 노출 (매 세션 시작 시 AI에게 보임)
|
|
1819
1869
|
const activeRules = readRules(root).filter(r => r.status === 'active');
|
|
1820
1870
|
if (activeRules.length) {
|
|
@@ -1955,10 +2005,35 @@ function handoff(root) {
|
|
|
1955
2005
|
const isTty = process.stdout && process.stdout.isTTY;
|
|
1956
2006
|
const red = s => isTty ? `\x1b[31m${s}\x1b[0m` : s;
|
|
1957
2007
|
const dim = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
2008
|
+
const yel = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
|
|
1958
2009
|
log('');
|
|
1959
2010
|
log(red(`## 🔒 보안 요약 (1.9.76) — ${issues.length}건 주의`));
|
|
1960
2011
|
for (const i of issues) log(dim(` ⚠ ${i}`));
|
|
1961
2012
|
log(dim(` → 자동 수정: leerness audit --fix · 상세: leerness env check / leerness audit`));
|
|
2013
|
+
// 1.9.80: critical 수준 (.gitignore에 .env 자체 누락) 시 자동 회복 옵션
|
|
2014
|
+
const giText = exists(path.join(root, '.gitignore')) ? read(path.join(root, '.gitignore')) : '';
|
|
2015
|
+
const giLines = giText.split('\n').map(l => l.trim());
|
|
2016
|
+
const envInGitignore = giLines.includes('.env') || giLines.includes('/.env');
|
|
2017
|
+
if (!envInGitignore) {
|
|
2018
|
+
// .env 자체 누락 → 최우선 위험
|
|
2019
|
+
log(yel(` 🚨 CRITICAL (1.9.80): .env가 .gitignore에 없습니다! 시크릿 노출 위험 — 즉시 \`leerness audit --fix\` 권장.`));
|
|
2020
|
+
// LEERNESS_AUTO_SECURITY_FIX=1 자동 실행 옵션
|
|
2021
|
+
if (process.env.LEERNESS_AUTO_SECURITY_FIX === '1') {
|
|
2022
|
+
try {
|
|
2023
|
+
const r = cp.spawnSync(process.execPath, [__filename, 'audit', root, '--fix'],
|
|
2024
|
+
{ encoding: 'utf8', timeout: 15000, env: { ...process.env, LEERNESS_NO_PROMPT: '1', LEERNESS_NO_DRIFT_CHECK: '1' } });
|
|
2025
|
+
if (r.status === 0) {
|
|
2026
|
+
log(dim(` ✓ 자동 회복 (LEERNESS_AUTO_SECURITY_FIX=1): audit --fix 완료`));
|
|
2027
|
+
} else {
|
|
2028
|
+
log(dim(` ⚠ 자동 회복 실패 (exit ${r.status}) — 수동 \`leerness audit --fix\` 권장`));
|
|
2029
|
+
}
|
|
2030
|
+
} catch (e) {
|
|
2031
|
+
log(dim(` ⚠ 자동 회복 예외: ${e.message}`));
|
|
2032
|
+
}
|
|
2033
|
+
} else {
|
|
2034
|
+
log(dim(` 💡 자동 실행 옵션: LEERNESS_AUTO_SECURITY_FIX=1 leerness handoff .`));
|
|
2035
|
+
}
|
|
2036
|
+
}
|
|
1962
2037
|
log('');
|
|
1963
2038
|
}
|
|
1964
2039
|
}
|
|
@@ -3309,7 +3384,7 @@ function _banner(opts = {}) {
|
|
|
3309
3384
|
lines.push('');
|
|
3310
3385
|
for (const ln of lines) log(ln);
|
|
3311
3386
|
if (opts.quickStart) {
|
|
3312
|
-
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.
|
|
3387
|
+
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.81+ 워크플로)')));
|
|
3313
3388
|
log(' ' + C.green('npx leerness@latest init .') + C.dim(' # 신규 프로젝트 + 외부 AI CLI 설정'));
|
|
3314
3389
|
log(' ' + C.green('npx leerness handoff .') + C.dim(' # 컨텍스트 + lessons + 매칭 skill + 이전 history hit (1.9.69)'));
|
|
3315
3390
|
log(' ' + C.green('npx leerness skill match "<query>"') + C.dim(' # 매칭 skill + rolling history 자동 누적 (1.9.68)'));
|