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 +32 -0
- package/README.md +4 -2
- package/bin/harness.js +21 -8
- package/package.json +1 -1
- package/scripts/e2e.js +38 -0
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
|
-
[](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.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.
|
|
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 .
|
|
282
|
-
#
|
|
283
|
-
|
|
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('## ') &&
|
|
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 .
|
|
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
|
-
|
|
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
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
|
{
|