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 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
- [![npm](https://img.shields.io/badge/npm-leerness-blue)](https://www.npmjs.com/package/leerness) [![version](https://img.shields.io/badge/version-1.9.79-green)]() [![tests](https://img.shields.io/badge/e2e-219%2F219-success)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
5
+ [![npm](https://img.shields.io/badge/npm-leerness-blue)](https://www.npmjs.com/package/leerness) [![version](https://img.shields.io/badge/version-1.9.81-green)]() [![tests](https://img.shields.io/badge/e2e-219%2F219-success)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
6
6
 
7
7
  ```
8
8
  ╔══════════════════════════════════════════════════════════════╗
@@ -12,7 +12,7 @@
12
12
  ║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
13
13
  ║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
14
14
  ║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
15
- ║ v1.9.79 AI Agent Reliability Harness ║
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.79';
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.79+ 워크플로)')));
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)'));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.79",
3
+ "version": "1.9.81",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",