leerness 1.9.62 → 1.9.64
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 +45 -0
- package/README.md +4 -2
- package/bin/harness.js +27 -2
- package/package.json +1 -1
- package/scripts/e2e.js +37 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,50 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.64 — 2026-05-19
|
|
4
|
+
|
|
5
|
+
**`leerness install <skill>` 별칭 + 성능 벤치마크 1차 실측**.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **`leerness install <SKILL.md path or URL>`** — `skill install` 별칭:
|
|
9
|
+
- 자주 쓰는 명령 단축 (agentskills.io 컨벤션 맞춤)
|
|
10
|
+
- 디렉토리만 주면 init 의도로 친절 안내 (`leerness init` 권장)
|
|
11
|
+
- 인자 없으면 사용법 안내
|
|
12
|
+
|
|
13
|
+
### 📊 성능 벤치마크 1차 (stress-v10)
|
|
14
|
+
|
|
15
|
+
10회 평균 latency (Node.js spawnSync cold start 포함):
|
|
16
|
+
|
|
17
|
+
| 명령 | median | p95 |
|
|
18
|
+
|---|---:|---:|
|
|
19
|
+
| status | 1330ms | 1426ms |
|
|
20
|
+
| handoff --compact | 1378ms | 2500ms |
|
|
21
|
+
| drift check | 1303ms | 1782ms |
|
|
22
|
+
| audit | 1159ms | 1806ms |
|
|
23
|
+
| skill list | 1526ms | 2503ms |
|
|
24
|
+
| handoff (100 task) | 1176ms | - |
|
|
25
|
+
| task export (100 task) | 2163ms | - |
|
|
26
|
+
| skill suggest (30 task) | 1075ms | - |
|
|
27
|
+
|
|
28
|
+
### 1.9.65+ 성능 최적화 후보 (벤치마크에서 도출)
|
|
29
|
+
- `.harness/cache/usage-stats.json` 파일 I/O 캐싱
|
|
30
|
+
- handoff의 lessons fuzzy 매칭 워크스페이스 크기에 비례 → 키워드 캐시
|
|
31
|
+
|
|
32
|
+
## 1.9.63 — 2026-05-19
|
|
33
|
+
|
|
34
|
+
**`leerness audit --strict` — CI 친화 옵션 (warnings → failures 승격)**.
|
|
35
|
+
|
|
36
|
+
### Added
|
|
37
|
+
- **`--strict [--threshold N]`** — warnings ≥ N (기본 1) 시 failures 승격 → exit 1
|
|
38
|
+
- CI 환경에서 audit warning 무시 방지
|
|
39
|
+
|
|
40
|
+
### 검증 (stress-v10 + 누적 회귀)
|
|
41
|
+
- EE1-EE3 (audit --strict) 3/3 PASS
|
|
42
|
+
- FF1-FF3 (install 별칭) 3/3 PASS
|
|
43
|
+
- GG1-GG5 (성능 벤치마크 10회 평균) 5/5 PASS
|
|
44
|
+
- HH1-HH3 (큰 워크스페이스 100 task) 3/3 PASS
|
|
45
|
+
- II1-II3 (1.9.43~62 회귀) 3/3 PASS
|
|
46
|
+
- **stress-v10: 17/17 PASS**, e2e: **219/219 PASS**
|
|
47
|
+
|
|
3
48
|
## 1.9.62 — 2026-05-19
|
|
4
49
|
|
|
5
50
|
**`leerness audit`에 npm CVE 자동 감지 통합**.
|
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.64 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.64** — `leerness install <SKILL.md>` 별칭 (skill install 단축) · **성능 벤치마크 1차 실측** (status/handoff/drift/audit/skill list 평균 1.2~1.5초).
|
|
437
|
+
- **1.9.63** — `leerness audit --strict [--threshold N]` — CI 친화 옵션 (warnings → failures 승격).
|
|
436
438
|
- **1.9.62** — `leerness audit` npm CVE 자동 감지 (npm audit --json 통합, OFFLINE/no-npm-audit 스킵).
|
|
437
439
|
- **1.9.61** — MCP server cursor 기반 페이지네이션 — `nextCursor` + `_chunkSize` override.
|
|
438
440
|
- **1.9.60** — `leerness task export` — progress-tracker → TodoWrite JSON 형식. **양방향 sync 완성** (1.9.38 sync + 1.9.60 export).
|
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.64';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -1451,7 +1451,15 @@ function audit(root) {
|
|
|
1451
1451
|
}
|
|
1452
1452
|
} catch {}
|
|
1453
1453
|
}
|
|
1454
|
-
|
|
1454
|
+
// 1.9.63: --strict — warnings ≥ threshold 시 failures로 승격 (CI 친화)
|
|
1455
|
+
if (has('--strict')) {
|
|
1456
|
+
const threshold = parseInt(arg('--threshold', '1'), 10);
|
|
1457
|
+
if (warnings >= threshold) {
|
|
1458
|
+
failures++;
|
|
1459
|
+
warn(`--strict 활성: warnings ${warnings} ≥ threshold ${threshold} → failures 승격`);
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
log(`Audit summary: warnings=${warnings} failures=${failures}${fix ? ` fixed=${fixed}` : ''}${has('--strict') ? ` strict-threshold=${arg('--threshold', '1')}` : ''}`);
|
|
1455
1463
|
if (failures) process.exitCode = 1;
|
|
1456
1464
|
}
|
|
1457
1465
|
|
|
@@ -7185,6 +7193,23 @@ async function main() {
|
|
|
7185
7193
|
} catch {}
|
|
7186
7194
|
}
|
|
7187
7195
|
if (cmd === 'init') return await install(args[1] || process.cwd(), { force:false, dry:false, migration:false });
|
|
7196
|
+
// 1.9.64: install <skill-id-or-url> 별칭 (= skill install). 자주 쓰는 명령 단축형.
|
|
7197
|
+
// 단, init이 leerness install . 같은 형태로도 동작하던 옛 호환은 유지 — args[1]이 디렉토리면 init으로 라우팅.
|
|
7198
|
+
if (cmd === 'install') {
|
|
7199
|
+
const arg1 = args[1];
|
|
7200
|
+
// skill source는 .md 파일 또는 URL 또는 skill id. 디렉토리면 init으로.
|
|
7201
|
+
if (!arg1) { fail('사용법: leerness install <skill SKILL.md path or URL>'); return process.exit(1); }
|
|
7202
|
+
if (/^https?:\/\//.test(arg1) || /\.md$/.test(arg1) || exists(path.join(arg1, 'SKILL.md'))) {
|
|
7203
|
+
return await skillInstallCmd(absRoot(arg('--path', process.cwd())), arg1);
|
|
7204
|
+
}
|
|
7205
|
+
// 디렉토리면 안내
|
|
7206
|
+
if (exists(arg1) && fs.statSync(arg1).isDirectory() && !exists(path.join(arg1, 'SKILL.md'))) {
|
|
7207
|
+
fail(`디렉토리에 SKILL.md 없음: ${arg1}\n init 의도였다면: leerness init "${arg1}"`);
|
|
7208
|
+
return process.exit(1);
|
|
7209
|
+
}
|
|
7210
|
+
fail(`알 수 없는 install 대상: ${arg1}\n SKILL.md 파일/URL/SKILL.md 포함 디렉토리 필요`);
|
|
7211
|
+
return process.exit(1);
|
|
7212
|
+
}
|
|
7188
7213
|
if (cmd === 'migrate') return await install(args[1] || process.cwd(), { force:has('--force'), dry:has('--dry-run'), migration:true });
|
|
7189
7214
|
if (cmd === 'update') return await updateCmd(args[1] || process.cwd(), { checkOnly: has('--check'), yes: has('--yes'), force: has('--force') });
|
|
7190
7215
|
if (cmd === 'auto-update' && args[1] === 'install') return autoUpdateInstall(args[2] || process.cwd());
|
package/package.json
CHANGED
package/scripts/e2e.js
CHANGED
|
@@ -950,6 +950,43 @@ total++;
|
|
|
950
950
|
if (!ok) { failed++; console.log(r.stdout.slice(0, 800)); }
|
|
951
951
|
}
|
|
952
952
|
|
|
953
|
+
// 1.9.63/64 회귀
|
|
954
|
+
total++;
|
|
955
|
+
{
|
|
956
|
+
// audit --strict
|
|
957
|
+
const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-st-'));
|
|
958
|
+
cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
|
|
959
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'audit', tmpC, '--strict'], { encoding: 'utf8', timeout: 15000 });
|
|
960
|
+
const ok = r.status !== 0 && /strict.*승격|failures=1/.test(r.stdout);
|
|
961
|
+
console.log(ok ? '✓ B(1.9.63) audit --strict: warnings → failures 승격' : `✗ --strict 실패`);
|
|
962
|
+
if (!ok) { failed++; console.log(r.stdout.slice(0, 400)); }
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
total++;
|
|
966
|
+
{
|
|
967
|
+
// audit --strict --threshold 10 → exit 0
|
|
968
|
+
const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-st2-'));
|
|
969
|
+
cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
|
|
970
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'audit', tmpC, '--strict', '--threshold', '10'], { encoding: 'utf8', timeout: 15000 });
|
|
971
|
+
const ok = r.status === 0;
|
|
972
|
+
console.log(ok ? '✓ B(1.9.63) audit --strict --threshold 10: warnings 적으면 exit 0' : `✗ threshold 실패`);
|
|
973
|
+
if (!ok) { failed++; console.log(r.stdout.slice(-300)); }
|
|
974
|
+
}
|
|
975
|
+
|
|
976
|
+
total++;
|
|
977
|
+
{
|
|
978
|
+
// install 별칭
|
|
979
|
+
const tmpC = fs.mkdtempSync(path.join(os.tmpdir(), 'leerness-iv-'));
|
|
980
|
+
cp.spawnSync(process.execPath, [CLI, 'init', tmpC, '--yes', '--no-banner', '--no-stale-check', '--language', 'ko', '--skills', 'recommended'], { stdio: 'ignore', timeout: 30000 });
|
|
981
|
+
const src = path.join(tmpC, 'sk.md');
|
|
982
|
+
fs.writeFileSync(src, '---\nname: install-alias-test\ndescription: 별칭 검증\n---\n# Body\n', 'utf8');
|
|
983
|
+
const r = cp.spawnSync(process.execPath, [CLI, 'install', src, '--path', tmpC], { encoding: 'utf8', timeout: 15000 });
|
|
984
|
+
const f = path.join(tmpC, '.harness', 'skills', 'install-alias-test', 'SKILL.md');
|
|
985
|
+
const ok = r.status === 0 && fs.existsSync(f);
|
|
986
|
+
console.log(ok ? '✓ B(1.9.64) install <SKILL.md>: skill install 별칭 동작' : `✗ install 별칭 실패`);
|
|
987
|
+
if (!ok) { failed++; console.log(r.stdout.slice(0, 300)); }
|
|
988
|
+
}
|
|
989
|
+
|
|
953
990
|
// 1.9.60/61/62 회귀
|
|
954
991
|
total++;
|
|
955
992
|
{
|