leerness 1.9.57 → 1.9.59

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,37 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.59 — 2026-05-19
4
+
5
+ **`session close --suggest` default 활성 — 라운드 마감 잊을 단계 없음**.
6
+
7
+ ### Changed (호환성 보장)
8
+ - **`leerness session close`** — 1.9.57 `--suggest`를 **default 활성**으로 승격:
9
+ - 라운드 마감 시 자동으로 skill suggest + drift check + usage stats 통합 보고
10
+ - 사용자가 잊지 않도록 default 동작
11
+ - **새 옵션 `--no-suggest`** — 이전 동작으로 복귀
12
+ - **새 env `LEERNESS_NO_SUGGEST=1`** — CI/자동화 환경에서 suggest 강제 비활성
13
+ - `--suggest` 명시 호출도 그대로 동작 (호환성)
14
+
15
+ ### 설치 가이드 갱신
16
+ - banner quickStart에서 `--suggest` 명시 제거 (이제 default라 불필요)
17
+ - `.harness/session-workflow.md` Step 6 갱신 — `--no-suggest`로 비활성 가능 명시
18
+
19
+ ## 1.9.58 — 2026-05-19
20
+
21
+ **handoff lessons fuzzy 매칭 (어간 변형 + decisions.md 매칭)**.
22
+
23
+ ### Added
24
+ - **fuzzy 매칭** — `escapeRegex(keyword.slice(0, max(4, len*0.7)))` 으로 부분 매칭:
25
+ - webhook ↔ webhooks ↔ webhook-payload ↔ webhooked 모두 매칭
26
+ - 한국어 어미 변화도 부분 매칭 (예: "결제" ↔ "결제처리" ↔ "결제검증")
27
+ - **decisions.md 매칭 추가** — 이전엔 review-evidence.md만 → 이제 decisions.md의 *실패/롤백/취소/회귀* 결정도 자동 회수
28
+
29
+ ### 검증 (stress-v8)
30
+ - X1-X4 (fuzzy 매칭: 어간/복합어/decisions/false positive 차단) 4/4 PASS
31
+ - Y1-Y4 (session close default suggest + 옵션 호환) 4/4 PASS
32
+ - Z1-Z4 (1.9.43~57 누적 회귀) 4/4 PASS
33
+ - **stress-v8: 12/12 PASS**, e2e: **213/213 PASS**
34
+
3
35
  ## 1.9.57 — 2026-05-19
4
36
 
5
37
  **`session close --suggest` + 설치 가이드 갱신**.
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.57-green)]() [![tests](https://img.shields.io/badge/e2e-210%2F210-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.59-green)]() [![tests](https://img.shields.io/badge/e2e-213%2F213-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.57 AI Agent Reliability Harness ║
15
+ ║ v1.9.59 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.59** — `session close` **--suggest default 활성** (잊을 단계 없음, `--no-suggest`로 비활성 가능) · 설치 가이드 갱신.
437
+ - **1.9.58** — handoff lessons **fuzzy 매칭** (어간 변형, 복합어, decisions.md 매칭 추가).
436
438
  - **1.9.57** — `session close --suggest` (마감 시 skill suggest + drift + 명령 통계 통합 보고) · install banner quickStart + session-workflow.md 갱신 (1.9.56/57 흐름 자동 안내).
437
439
  - **1.9.56** — `handoff`에 lessons 자동 재상기 통합 (현재 in-progress task 키워드 매칭 → 과거 실패 자동 표시).
438
440
  - **1.9.55** — MCP server에 `leerness_skill_suggest` + `leerness_lessons` 추가 (10 → 12 도구) · lessons --auto의 stopword 확장 (false positive 차단).
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.57';
9
+ const VERSION = '1.9.59';
10
10
  const MARK = '<!-- leerness:managed -->';
11
11
  const README_START = '<!-- leerness:project-readme:start -->';
12
12
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -278,9 +278,10 @@ leerness review <file> --persona security,performance,ux
278
278
 
279
279
  ## Step 6. 세션 마감 + 인계 + 다음 라운드 추천
280
280
  \`\`\`bash
281
- leerness session close . --suggest # 1.9.57+ — 마감 + 다음 라운드 추천 통합
282
- # 단독으로도 가능:
283
- leerness session close . # handoff/current-state/task-log 자동 갱신
281
+ leerness session close . # 1.9.59+ — --suggest default 활성 (마감 + 다음 라운드 자동)
282
+ leerness session close . --no-suggest # suggest 비활성 (이전 동작)
283
+
284
+ # 분리 호출도 가능:
284
285
  leerness skill suggest . # 1.9.53 — 반복 패턴 → 새 skill 후보
285
286
  leerness drift check . # 4 신호 + 4 레벨 점검
286
287
  leerness audit . --fix # 누락 메타 자동 보강
@@ -1720,16 +1721,26 @@ function handoff(root) {
1720
1721
  const tokens = String(latestRow.request).toLowerCase().match(/[\w가-힣]{4,}/g) || [];
1721
1722
  const keyword = tokens.filter(t => !stopwords.has(t)).sort((a, b) => b.length - a.length)[0];
1722
1723
  if (keyword) {
1723
- // lessons 검색
1724
+ // lessons 검색 — 1.9.58: fuzzy 매칭 (substring + 어간 변형)
1724
1725
  const decisions = exists(decisionsPath(root)) ? read(decisionsPath(root)) : '';
1725
1726
  const evidence = exists(evidencePath(root)) ? read(evidencePath(root)) : '';
1727
+ // fuzzy: keyword 또는 keyword 부분 (4자+) 일치
1728
+ // 예: "webhook" 매칭 시 "webhook-payload", "webhooks", "webhooked" 모두 매칭
1729
+ const fuzzyRe = new RegExp(escapeRegex(keyword.slice(0, Math.max(4, Math.floor(keyword.length * 0.7)))), 'i');
1726
1730
  const matches = [];
1727
1731
  for (const block of evidence.split(/\n(?=## )/)) {
1728
- if (block.startsWith('## ') && new RegExp(escapeRegex(keyword), 'i').test(block) && /✗|fail|롤백|버그|incomplete/i.test(block)) {
1732
+ if (block.startsWith('## ') && fuzzyRe.test(block) && /✗|fail|롤백|버그|incomplete/i.test(block)) {
1729
1733
  const titleM = block.match(/^## (.+)$/m);
1730
1734
  if (titleM) matches.push({ source: 'review-evidence.md', title: titleM[1].trim(), block });
1731
1735
  }
1732
1736
  }
1737
+ // 1.9.58: decisions.md도 fuzzy 매칭 (실패/롤백 관련 결정만)
1738
+ for (const block of decisions.split(/\n(?=### )/)) {
1739
+ if (block.startsWith('### ') && fuzzyRe.test(block) && /롤백|실패|fail|취소|회귀|deprecate/i.test(block)) {
1740
+ const titleM = block.match(/^### (.+)$/m);
1741
+ if (titleM) matches.push({ source: 'decisions.md', title: titleM[1].trim(), block });
1742
+ }
1743
+ }
1733
1744
  if (matches.length) {
1734
1745
  const isTty = process.stdout && process.stdout.isTTY;
1735
1746
  const yel = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
@@ -3096,7 +3107,7 @@ function _banner(opts = {}) {
3096
3107
  log(' ' + C.green('npx leerness@latest init .') + C.dim(' # 신규 프로젝트 + 외부 AI CLI 설정'));
3097
3108
  log(' ' + C.green('npx leerness handoff .') + C.dim(' # 컨텍스트 적재 + 과거 lessons 자동 재상기'));
3098
3109
  log(' ' + C.green('npx leerness verify-claim T-0001 --run-tests') + C.dim(' # AI 거짓 완료 자동 검증'));
3099
- log(' ' + C.green('npx leerness session close . --suggest') + C.dim(' # 마감 + 다음 라운드 추천'));
3110
+ log(' ' + C.green('npx leerness session close .') + C.dim(' # 마감 + 다음 라운드 추천 (default)'));
3100
3111
  log('');
3101
3112
  log(C.bold(C.cyan(' 🤖 메인 에이전트 (Claude/Cursor/Copilot)용')));
3102
3113
  log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 — 12 도구 노출'));
@@ -3849,7 +3860,9 @@ function sessionClose(root) {
3849
3860
  // 1.9.12: session close 끝에 roadmap.html 자동 갱신
3850
3861
  _autoRoadmap(root, 'session-close');
3851
3862
  // 1.9.57: --suggest 옵션 — 마감 시 skill suggest + drift check + lessons 통합 보고
3852
- if (has('--suggest')) {
3863
+ // 1.9.59: default 활성 — --no-suggest 명시 비활성 가능
3864
+ const suggestEnabled = (has('--suggest') || (!has('--no-suggest') && process.env.LEERNESS_NO_SUGGEST !== '1'));
3865
+ if (suggestEnabled) {
3853
3866
  const isTty = process.stdout && process.stdout.isTTY;
3854
3867
  const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
3855
3868
  const dim = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.57",
3
+ "version": "1.9.59",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",
package/scripts/e2e.js CHANGED
@@ -950,6 +950,44 @@ total++;
950
950
  if (!ok) { failed++; console.log(r.stdout.slice(0, 800)); }
951
951
  }
952
952
 
953
+ // 1.9.58/59 회귀
954
+ total++;
955
+ {
956
+ // fuzzy 매칭 — 어간 변형 (webhook ↔ webhooks)
957
+ const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-fz-'));
958
+ cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
959
+ fs.writeFileSync(path.join(tmpC, '.harness', 'review-evidence.md'),
960
+ '## 2026-04\nNote: ✗ webhooks payload 실패\n', 'utf8');
961
+ cp.spawnSync(process.execPath, [CLI, 'task', 'add', 'webhook 작업', '--status', 'in-progress', '--path', tmpC], { stdio: 'ignore', timeout: 10000 });
962
+ const r = cp.spawnSync(process.execPath, [CLI, 'handoff', tmpC, '--no-drift-check', '--no-workflow-guide'], { encoding: 'utf8', timeout: 15000 });
963
+ const ok = /lessons 자동 재상기.*webhook/.test(r.stdout);
964
+ console.log(ok ? '✓ B(1.9.58) lessons fuzzy: webhook ↔ webhooks 어간 변형 매칭' : `✗ fuzzy 실패`);
965
+ if (!ok) { failed++; console.log(r.stdout.slice(-500)); }
966
+ }
967
+
968
+ total++;
969
+ {
970
+ // session close default suggest
971
+ const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-sd-'));
972
+ cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
973
+ const r = cp.spawnSync(process.execPath, [CLI, 'session', 'close', tmpC], { encoding: 'utf8', timeout: 30000 });
974
+ // 1.9.59: default activated → "다음 라운드 추천" 자동 표시
975
+ const ok = /다음 라운드 추천|drift 상태/.test(r.stdout);
976
+ console.log(ok ? '✓ B(1.9.59) session close: --suggest default 활성' : `✗ default suggest 실패`);
977
+ if (!ok) { failed++; console.log(r.stdout.slice(-500)); }
978
+ }
979
+
980
+ total++;
981
+ {
982
+ // --no-suggest 비활성 (이전 동작 보존)
983
+ const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-ns-'));
984
+ cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
985
+ const r = cp.spawnSync(process.execPath, [CLI, 'session', 'close', tmpC, '--no-suggest'], { encoding: 'utf8', timeout: 30000 });
986
+ const ok = r.status === 0 && !/다음 라운드 추천/.test(r.stdout) && /진행 요약/.test(r.stdout);
987
+ console.log(ok ? '✓ B(1.9.59) --no-suggest: suggest 비활성 (이전 동작)' : `✗ --no-suggest 실패`);
988
+ if (!ok) { failed++; console.log(r.stdout.slice(-300)); }
989
+ }
990
+
953
991
  // 1.9.56/57 회귀
954
992
  total++;
955
993
  {