leerness 1.9.69 → 1.9.70
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 +34 -0
- package/README.md +3 -2
- package/bin/harness.js +38 -3
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,39 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.70 — 2026-05-19
|
|
4
|
+
|
|
5
|
+
**MCP server `tools/call` 자동 사용 통계** (1.9.65 usage-stats 확장).
|
|
6
|
+
|
|
7
|
+
### Added — MCP 도구별 호출 카운트
|
|
8
|
+
- MCP server가 `tools/call` 요청을 받을 때마다 도구 이름별 카운트를 `.harness/cache/usage-stats.json`의 `mcp.tools` 섹션에 기록.
|
|
9
|
+
- 별도 mtime 캐시 invalidation (1.9.65 _USAGE_CACHE 재활용).
|
|
10
|
+
- `leerness usage stats` 출력에 MCP 섹션 자동 노출:
|
|
11
|
+
```
|
|
12
|
+
## 🔌 MCP tools/call 통계 (1.9.70) — last: <ISO>
|
|
13
|
+
| MCP 도구 | 호출 수 |
|
|
14
|
+
| leerness_handoff | 8 |
|
|
15
|
+
| ... |
|
|
16
|
+
💡 드물게 호출된 도구 (≤N): leerness_xxx, ...
|
|
17
|
+
```
|
|
18
|
+
- 드물게 호출되는 도구 (전체 5% 미만)를 자동 식별 — AI 에이전트가 안 쓰는 도구가 있다는 가시화.
|
|
19
|
+
|
|
20
|
+
### Internal
|
|
21
|
+
- 새 헬퍼: `_bumpMcpUsage(root, toolName)` — atomic write + 캐시 invalidation.
|
|
22
|
+
- usage-stats.json 스키마 확장:
|
|
23
|
+
```json
|
|
24
|
+
{
|
|
25
|
+
"commands": {...},
|
|
26
|
+
"drift": {...},
|
|
27
|
+
"mcp": { "tools": {...}, "lastTool": "...", "lastAt": "..." }
|
|
28
|
+
}
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
### Verified
|
|
32
|
+
- stress-v16 — MCP 카운트 정합성 + 13 도구 종합 호출 + 1.9.43~69 누적 회귀 + 성능.
|
|
33
|
+
- e2e 회귀: 219/219 PASS 유지.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
3
37
|
## 1.9.69 — 2026-05-19
|
|
4
38
|
|
|
5
39
|
**handoff에 skill-suggestions.md rolling history hit 노출 (1.9.67 + 1.9.68 결합)**.
|
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.70 AI Agent Reliability Harness ║
|
|
16
16
|
║ verify · remember · orchestrate · audit · prevent drift ║
|
|
17
17
|
╚══════════════════════════════════════════════════════════════╝
|
|
18
18
|
```
|
|
@@ -433,6 +433,7 @@ npm test # = node ./scripts/e2e.js
|
|
|
433
433
|
|
|
434
434
|
## 변경 이력 (최근)
|
|
435
435
|
|
|
436
|
+
- **1.9.70** — **MCP server `tools/call` 자동 사용 통계** — 도구별 호출 카운트 (`.harness/cache/usage-stats.json#mcp.tools`) + `leerness usage stats` 출력에 MCP 섹션 + 드물게 호출되는 도구 식별.
|
|
436
437
|
- **1.9.69** — **handoff에 skill-suggestions.md history hit 노출** (fuzzy 매칭, 최근 2건 + top 2 매치). AI가 이전 세션 결정을 일관 유지. mtime 기반 캐시 (1.9.65/66/67 캐시 패밀리 연속).
|
|
437
438
|
- **1.9.68** — **`skill match` 결과 → `.harness/skill-suggestions.md` rolling history 자동 누적** (AI가 다음 세션에 이전 추천 참조 가능). `--no-save` / `LEERNESS_NO_SKILL_HISTORY=1`로 끄기.
|
|
438
439
|
- **1.9.67** — **handoff 자동 skill 추천 default ON** (jaccard 매칭) + lessons 인덱스에 task-log.md 실패 라인 통합 (회수 범위 확장).
|
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.70';
|
|
10
10
|
const MARK = '<!-- leerness:managed -->';
|
|
11
11
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
12
12
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -3216,7 +3216,7 @@ function _banner(opts = {}) {
|
|
|
3216
3216
|
lines.push('');
|
|
3217
3217
|
for (const ln of lines) log(ln);
|
|
3218
3218
|
if (opts.quickStart) {
|
|
3219
|
-
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.
|
|
3219
|
+
log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.70+ 워크플로)')));
|
|
3220
3220
|
log(' ' + C.green('npx leerness@latest init .') + C.dim(' # 신규 프로젝트 + 외부 AI CLI 설정'));
|
|
3221
3221
|
log(' ' + C.green('npx leerness handoff .') + C.dim(' # 컨텍스트 + lessons + 매칭 skill + 이전 history hit (1.9.69)'));
|
|
3222
3222
|
log(' ' + C.green('npx leerness skill match "<query>"') + C.dim(' # 매칭 skill + rolling history 자동 누적 (1.9.68)'));
|
|
@@ -3224,7 +3224,7 @@ function _banner(opts = {}) {
|
|
|
3224
3224
|
log(' ' + C.green('npx leerness session close .') + C.dim(' # 마감 + 다음 라운드 추천 (default)'));
|
|
3225
3225
|
log('');
|
|
3226
3226
|
log(C.bold(C.cyan(' 🤖 메인 에이전트 (Claude/Cursor/Copilot)용')));
|
|
3227
|
-
log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 — 13 도구
|
|
3227
|
+
log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 — 13 도구 + tools/call 자동 통계 (1.9.70)'));
|
|
3228
3228
|
log(' ' + C.green('npx leerness agents bench "<task>"') + C.dim(' # 3 CLI 동시 비교'));
|
|
3229
3229
|
log('');
|
|
3230
3230
|
}
|
|
@@ -6319,6 +6319,23 @@ function _bumpUsage(root, cmdName) {
|
|
|
6319
6319
|
} catch {}
|
|
6320
6320
|
}
|
|
6321
6321
|
|
|
6322
|
+
// 1.9.70: MCP tools/call 자동 사용 통계 — 도구별 호출 카운트
|
|
6323
|
+
function _bumpMcpUsage(root, toolName) {
|
|
6324
|
+
try {
|
|
6325
|
+
const stats = _readUsageStats(root);
|
|
6326
|
+
if (!stats.mcp) stats.mcp = { tools: {} };
|
|
6327
|
+
if (!stats.mcp.tools) stats.mcp.tools = {};
|
|
6328
|
+
stats.mcp.tools[toolName] = (stats.mcp.tools[toolName] || 0) + 1;
|
|
6329
|
+
stats.mcp.lastTool = toolName;
|
|
6330
|
+
stats.mcp.lastAt = new Date().toISOString();
|
|
6331
|
+
if (!stats.since) stats.since = today();
|
|
6332
|
+
const p = _usageStatsPath(root);
|
|
6333
|
+
mkdirp(path.dirname(p));
|
|
6334
|
+
writeUtf8(p, JSON.stringify(stats, null, 2) + '\n');
|
|
6335
|
+
try { _USAGE_CACHE.set(p, { stats, mtime: fs.statSync(p).mtimeMs }); } catch {}
|
|
6336
|
+
} catch {}
|
|
6337
|
+
}
|
|
6338
|
+
|
|
6322
6339
|
// 1.9.41: CHANGELOG.md를 파싱하여 from → to 사이 버전 차분 추출
|
|
6323
6340
|
// 반환: [{ version, date, body, newCommands, newFlags, newFiles }]
|
|
6324
6341
|
function _parseChangelogBetween(changelogText, fromV, toV) {
|
|
@@ -6993,6 +7010,8 @@ function mcpServeCmd(root) {
|
|
|
6993
7010
|
} else if (req.method === 'tools/call') {
|
|
6994
7011
|
const { name, arguments: args = {} } = req.params || {};
|
|
6995
7012
|
const targetPath = args.path || root;
|
|
7013
|
+
// 1.9.70: MCP tools/call 자동 사용 통계 — 어떤 도구가 자주/드물게 호출되는지 가시화
|
|
7014
|
+
try { _bumpMcpUsage(targetPath, name); } catch {}
|
|
6996
7015
|
let cliArgs;
|
|
6997
7016
|
try {
|
|
6998
7017
|
switch (name) {
|
|
@@ -7144,6 +7163,22 @@ function usageStatsCmd(root) {
|
|
|
7144
7163
|
log(`💡 drift 경고 ${stats.drift.skipped}회 스킵 → 1.9.38 학습: 임계 자동 완화 (--no-drift-check 빈도 ≥5)`);
|
|
7145
7164
|
}
|
|
7146
7165
|
}
|
|
7166
|
+
// 1.9.70: MCP tools/call 자동 사용 통계 — 어떤 도구가 자주/드물게 호출되는지
|
|
7167
|
+
if (stats.mcp && stats.mcp.tools && Object.keys(stats.mcp.tools).length) {
|
|
7168
|
+
const mcpEntries = Object.entries(stats.mcp.tools).sort((a, b) => b[1] - a[1]);
|
|
7169
|
+
const mcpTotal = mcpEntries.reduce((s, [, n]) => s + n, 0);
|
|
7170
|
+
log('');
|
|
7171
|
+
log(`## 🔌 MCP tools/call 통계 (1.9.70) — last: ${stats.mcp.lastAt || '(none)'}`);
|
|
7172
|
+
log(`| MCP 도구 | 호출 수 |`);
|
|
7173
|
+
log(`|---|---:|`);
|
|
7174
|
+
for (const [tool, n] of mcpEntries) log(`| ${tool} | ${n} |`);
|
|
7175
|
+
log('');
|
|
7176
|
+
log(`총 ${mcpTotal} 회 MCP 호출 · 도구 ${mcpEntries.length} 가지 사용`);
|
|
7177
|
+
// 드물게 호출되는 도구 식별 (전체의 5% 미만 호출)
|
|
7178
|
+
const threshold = Math.max(1, Math.floor(mcpTotal * 0.05));
|
|
7179
|
+
const rare = mcpEntries.filter(([, n]) => n <= threshold).map(([t]) => t);
|
|
7180
|
+
if (rare.length) log(`💡 드물게 호출된 도구 (≤${threshold}): ${rare.slice(0, 6).join(', ')}`);
|
|
7181
|
+
}
|
|
7147
7182
|
}
|
|
7148
7183
|
|
|
7149
7184
|
// 1.9.38: task sync — TodoWrite/외부 JSON에서 leerness task로 mirror
|