leerness 1.9.72 → 1.9.74

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,39 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.74 — 2026-05-20
4
+
5
+ **`session close` 마감 시 누적 회고 통계 강화** (1.9.70 MCP + 1.9.68 history 결합).
6
+
7
+ ### Improved — session close --suggest 블록 확장
8
+ - 기존: skill suggest 후보 / drift 상태 / 가장 많이 쓴 명령 top 3.
9
+ - **신규** 라인:
10
+ - `🔌 MCP 호출 (1.9.74): 총 N회, top: tool(n), ...` + `💡 드물게 호출된 MCP: ...` (1.9.70 통계 연동).
11
+ - `📒 skill match query 누적 (1.9.74): 총 N회 / 종류 M개` + top 3 query 표시 (1.9.68 rolling history 집계).
12
+ - AI 에이전트가 한 세션의 사용 패턴을 한눈에 파악 가능.
13
+
14
+ ### Verified
15
+ - stress-v20 — session close 회고 통계 + 1.9.43~73 누적 회귀.
16
+ - e2e 219/219 PASS 유지.
17
+
18
+ ---
19
+
20
+ ## 1.9.73 — 2026-05-20
21
+
22
+ **MCP server 14번째 도구 `leerness_env_check` 추가 (1.9.71 env 보안을 외부 AI에 노출)**.
23
+
24
+ ### Added — MCP 14번째 도구
25
+ - `leerness_env_check` — 워크스페이스 `.env` ↔ `.env.example` 동기화 검사를 외부 AI 에이전트에 노출.
26
+ - inputSchema: `{ path: string }`
27
+ - 응답: 1.9.71의 `env check --json` 결과 그대로 (envPath/examplePath/envKeys/exKeys/inEnvOnly/inExampleOnly).
28
+ - 외부 AI가 워크스페이스 보안 자동 점검 가능 (Claude Code/Cursor 등에서 호출).
29
+ - MCP server 도구 카운트: 13 → **14**.
30
+
31
+ ### Verified
32
+ - stress-v19 — MCP 14 도구 + env_check JSON 응답 + 1.9.43~72 누적 회귀 + 성능.
33
+ - e2e 회귀: 219/219 PASS 유지.
34
+
35
+ ---
36
+
3
37
  ## 1.9.72 — 2026-05-20
4
38
 
5
39
  **`leerness brainstorm`에 skill-suggestions.md history + task-log 실패 라인 통합**.
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.72-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.74-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.72 AI Agent Reliability Harness ║
15
+ ║ v1.9.74 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.74** — **`session close` 마감 시 누적 회고 통계 강화** — MCP tools/call 카운트 + skill match query top + drift 통계를 자동 요약 (1.9.70 + 1.9.68 결합).
437
+ - **1.9.73** — **MCP server 14번째 도구 `leerness_env_check`** — 1.9.71 env 보안 검사를 외부 AI에 노출 (외부 워크스페이스 .env/.env.example 동기화 자동 점검).
436
438
  - **1.9.72** — **`leerness brainstorm`에 skill-suggestions.md history + task-log 실패 라인 통합** — 누적 컨텍스트 기반 brainstorm 강화 (이전 매칭 이력 + task-log 실패 회수).
437
439
  - **1.9.71** — **`.env` / `.env.example` 자동 동기화** — `leerness env check` / `env sync` 명령 + `audit` 통합 (`--fix`로 누락 키 자동 추가). 보안 정책: 실제 값 절대 노출 안 함 (키만 추가, 값은 빈 문자열).
438
440
  - **1.9.70** — **MCP server `tools/call` 자동 사용 통계** — 도구별 호출 카운트 (`.harness/cache/usage-stats.json#mcp.tools`) + `leerness usage stats` 출력에 MCP 섹션 + 드물게 호출되는 도구 식별.
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.72';
9
+ const VERSION = '1.9.74';
10
10
  const MARK = '<!-- leerness:managed -->';
11
11
  const README_START = '<!-- leerness:project-readme:start -->';
12
12
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -3242,7 +3242,7 @@ function _banner(opts = {}) {
3242
3242
  lines.push('');
3243
3243
  for (const ln of lines) log(ln);
3244
3244
  if (opts.quickStart) {
3245
- log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.72+ 워크플로)')));
3245
+ log(C.bold(C.cyan(' ✨ 빠른 시작 (1.9.74+ 워크플로)')));
3246
3246
  log(' ' + C.green('npx leerness@latest init .') + C.dim(' # 신규 프로젝트 + 외부 AI CLI 설정'));
3247
3247
  log(' ' + C.green('npx leerness handoff .') + C.dim(' # 컨텍스트 + lessons + 매칭 skill + 이전 history hit (1.9.69)'));
3248
3248
  log(' ' + C.green('npx leerness skill match "<query>"') + C.dim(' # 매칭 skill + rolling history 자동 누적 (1.9.68)'));
@@ -3251,7 +3251,7 @@ function _banner(opts = {}) {
3251
3251
  log(' ' + C.green('npx leerness session close .') + C.dim(' # 마감 + 다음 라운드 추천 (default)'));
3252
3252
  log('');
3253
3253
  log(C.bold(C.cyan(' 🤖 메인 에이전트 (Claude/Cursor/Copilot)용')));
3254
- log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 — 13 도구 + tools/call 자동 통계 (1.9.70)'));
3254
+ log(' ' + C.green('npx leerness mcp serve') + C.dim(' # MCP 서버 — 14 도구 (env_check 포함) + tools/call 통계'));
3255
3255
  log(' ' + C.green('npx leerness agents bench "<task>"') + C.dim(' # 3 CLI 동시 비교'));
3256
3256
  log('');
3257
3257
  }
@@ -4036,6 +4036,37 @@ function sessionClose(root) {
4036
4036
  if (entries.length) {
4037
4037
  log(dim(` 📊 가장 많이 쓴 명령: ${entries.map(([c, n]) => `${c}(${n})`).join(', ')}`));
4038
4038
  }
4039
+ // 1.9.74: MCP tools/call 통계 + rare 도구 노출
4040
+ if (stats.mcp && stats.mcp.tools) {
4041
+ const mcpEntries = Object.entries(stats.mcp.tools).sort((a, b) => b[1] - a[1]);
4042
+ if (mcpEntries.length) {
4043
+ const mcpTotal = mcpEntries.reduce((s, [, n]) => s + n, 0);
4044
+ log(dim(` 🔌 MCP 호출 (1.9.74): 총 ${mcpTotal}회, top: ${mcpEntries.slice(0, 3).map(([t, n]) => `${t}(${n})`).join(', ')}`));
4045
+ const threshold = Math.max(1, Math.floor(mcpTotal * 0.05));
4046
+ const rare = mcpEntries.filter(([, n]) => n <= threshold).map(([t]) => t);
4047
+ if (rare.length && mcpTotal >= 5) log(dim(` 💡 드물게 호출된 MCP: ${rare.slice(0, 4).join(', ')}`));
4048
+ }
4049
+ }
4050
+ } catch {}
4051
+ // 1.9.74: skill match query top (skill-suggestions.md 누적)
4052
+ try {
4053
+ const histPath = path.join(root, '.harness', 'skill-suggestions.md');
4054
+ if (exists(histPath)) {
4055
+ const histTxt = read(histPath);
4056
+ const queries = [];
4057
+ for (const block of histTxt.split(/\n(?=## )/)) {
4058
+ const h = block.match(/^## ([\d-]+ [\d:]+) — query "([^"]+)"/);
4059
+ if (h) queries.push(h[2]);
4060
+ }
4061
+ if (queries.length) {
4062
+ // 같은 query 개수 카운트
4063
+ const counts = {};
4064
+ for (const q of queries) counts[q] = (counts[q] || 0) + 1;
4065
+ const topQueries = Object.entries(counts).sort((a, b) => b[1] - a[1]).slice(0, 3);
4066
+ log(dim(` 📒 skill match query 누적 (1.9.74): 총 ${queries.length}회 / 종류 ${Object.keys(counts).length}개`));
4067
+ for (const [q, n] of topQueries) log(dim(` • "${q.slice(0, 50)}"${n > 1 ? ` (${n}회)` : ''}`));
4068
+ }
4069
+ }
4039
4070
  } catch {}
4040
4071
  log('');
4041
4072
  }
@@ -7075,7 +7106,8 @@ function mcpServeCmd(root) {
7075
7106
  { name: 'leerness_session_close', description: '세션 마감 — handoff/current-state/task-log 자동 갱신', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
7076
7107
  { name: 'leerness_skill_suggest', description: '1.9.53 — 사용 패턴 자동 분석 → 새 skill 후보 제안 (Hermes-style 자동 학습)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, min: { type: 'number' }, days: { type: 'number' } } } },
7077
7108
  { name: 'leerness_lessons', description: '1.9.7/54 — 과거 결정·실수 자동 회수 (--auto: 현재 task 키워드 자동 추출)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, query: { type: 'string' }, auto: { type: 'boolean' }, limit: { type: 'number' } } } },
7078
- { name: 'leerness_task_export', description: '1.9.60/66 — leerness task → Claude Code TodoWrite 호환 JSON (외부 AI 양방향 sync)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, to: { type: 'string' } } } }
7109
+ { name: 'leerness_task_export', description: '1.9.60/66 — leerness task → Claude Code TodoWrite 호환 JSON (외부 AI 양방향 sync)', inputSchema: { type: 'object', properties: { path: { type: 'string' }, to: { type: 'string' } } } },
7110
+ { name: 'leerness_env_check', description: '1.9.71/73 — .env vs .env.example 동기화 검사 (보안: 키만, 값 미노출). exit 1 if 누락 키 있음', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } }
7079
7111
  ];
7080
7112
 
7081
7113
  function send(obj) {
@@ -7120,6 +7152,7 @@ function mcpServeCmd(root) {
7120
7152
  case 'leerness_skill_suggest': cliArgs = ['skill', 'suggest', '--path', targetPath, '--json', ...(args.min ? ['--min', String(args.min)] : []), ...(args.days ? ['--days', String(args.days)] : [])]; break;
7121
7153
  case 'leerness_lessons': cliArgs = ['lessons', '--path', targetPath, ...(args.auto ? ['--auto'] : []), ...(args.query ? ['--query', args.query] : []), ...(args.limit ? ['--limit', String(args.limit)] : [])]; break;
7122
7154
  case 'leerness_task_export': cliArgs = ['task', 'export', '--path', targetPath, ...(args.to ? ['--to', args.to] : ['--json'])]; break;
7155
+ case 'leerness_env_check': cliArgs = ['env', 'check', targetPath, '--json']; break;
7123
7156
  default:
7124
7157
  return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
7125
7158
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.72",
3
+ "version": "1.9.74",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",