leerness 1.9.228 β†’ 1.9.230

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,75 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.230 β€” 2026-05-23
4
+
5
+ **πŸ“Š handoff/session close/health --json milestones 톡합 (7 ν•„λ“œ μ™„μ„±) + ν—€λ“œλΌμΈ ETA μž„λ°• ν‘œμ‹œ.**
6
+
7
+ ### 1. handoff/session close/health --json 7번째 톡합 ν•„λ“œ `milestones`
8
+ - 1.9.229 milestones 데이터λ₯Ό JSON 3 λͺ…λ Ή λͺ¨λ‘μ— 톡합
9
+ - 동일 ν˜•μ‹: `{ reachedCount, reached: [{milestone, version, reachedAt}], next, avgRoundsPerDay }`
10
+ - 1.9.218 (4 ν•„λ“œ) β†’ 1.9.223 (5) β†’ 1.9.227 (6) β†’ 1.9.230 (**7 ν•„λ“œ**)
11
+
12
+ ### 2. 3 λͺ…λ Ή milestones 동일 ν˜•μ‹ 일관성
13
+ - handoff/session close/health λͺ¨λ‘ λ™μΌν•œ `milestones` 객체 λ°˜ν™˜
14
+ - μ™ΈλΆ€ μžλ™ν™”/CI κ°€ μ–΄λŠ λͺ…λ ΉμœΌλ‘œ μ‘°νšŒν•΄λ„ 동일 κ²°κ³Ό
15
+ - 일관성 맀트릭슀: 3 λͺ…λ Ή Γ— 7 ν•„λ“œ = **21 톡합 포인트**
16
+
17
+ ### 3. handoff ν—€λ“œλΌμΈ μž„λ°• ETA ν‘œμ‹œ (1.9.230)
18
+ - λ‹€μŒ λ§ˆμΌμŠ€ν†€μ΄ **10R 이내** + ETAκ°€ **7일 이내** 일 λ•Œλ§Œ 별도 λ…ΈμΆœ
19
+ - `🎯 R200 ETA 2026-05-24` ν˜•μ‹ (ν˜„μž¬λŠ” 14R 남아 보호 κ°€λ“œ λ°œλ™ X)
20
+ - λ§ˆμΌμŠ€ν†€ μž„λ°• μ‹œ μžλ™ κ°•μ‘° (슀팸 λ°©μ§€: 쑰건 μΆ©μ‘± μ‹œλ§Œ)
21
+
22
+ ### 4. λˆ„μ  νšŒκ·€ (1.9.207~229) β€” λͺ¨λ‘ μœ μ§€
23
+
24
+ ### 5. stress-v175 β€” 17/17 PASS
25
+ - 1.9.230 (7): VERSION + handoff/session close/health JSON + 3 λͺ…λ Ή 일관성 + 7 ν•„λ“œ 검증 + ν—€λ“œλΌμΈ ETA
26
+ - μ„±λŠ₯ (2): cold_start avg 439ms / handoff --json (7 ν•„λ“œ) 1019ms
27
+ - λˆ„μ  νšŒκ·€ (8): 1.9.207~229
28
+
29
+ ### 6. μžλ™ release (92 λΌμš΄λ“œ main-push streak Β· 53 λΌμš΄λ“œ npm publish streak)
30
+
31
+ πŸ“Š **JSON 톡합 맀트릭슀 7 ν•„λ“œ μ™„μ„±** β€” μ™ΈλΆ€ μžλ™ν™”κ°€ leerness μƒνƒœλ₯Ό 7 차원 동기 회수 (3 λͺ…λ Ή 일관성)
32
+
33
+ ---
34
+
35
+ ## 1.9.229 β€” 2026-05-23
36
+
37
+ **🎯 leerness milestones CLI + MCP 63번째 도ꡬ (도달 λ§ˆμΌμŠ€ν†€ + ETA 예츑).**
38
+
39
+ ### 1. `leerness milestones` μƒˆ λͺ…λ Ή
40
+ - git tag 순차 λΆ„μ„μœΌλ‘œ 25/50/75/100/125/150/175/200/250/300/400/500 λ§ˆμΌμŠ€ν†€ 도달 μ‹œμ  회수
41
+ - 각 λ§ˆμΌμŠ€ν†€λ³„: `{milestone, version, reachedAt, daysFromBaseline}`
42
+ - λ‹€μŒ λ§ˆμΌμŠ€ν†€ ETA 계산 (ν˜„μž¬ 속도 κΈ°μ€€): `{milestone, roundsRemaining, etaDays, etaDate}`
43
+ - `--json` μ˜΅μ…˜
44
+ - 1.9.226 round-history ν™•μž₯ (간단 톡계 β†’ 도달 이λ ₯ + ETA)
45
+
46
+ ### 2. MCP 63번째 도ꡬ β€” `leerness_milestones`
47
+ - μ™ΈλΆ€ AIκ°€ "ν”„λ‘œμ νŠΈ λ§ˆμΌμŠ€ν†€ 진척도 + λ‹€μŒ 달성 μ˜ˆμƒμΌ" 직접 회수
48
+ - MCP 62 β†’ 63 (+1)
49
+
50
+ ### 3. μ‹€ μΈ‘μ • β€” leerness 자체 λ§ˆμΌμŠ€ν†€ 이λ ₯ (7개 달성)
51
+ - R25 β†’ v1.9.68 (2026-05-19, +11d)
52
+ - R50 β†’ v1.9.93 (2026-05-20, +11d)
53
+ - R75 β†’ v1.9.118 (2026-05-20, +11d)
54
+ - R100 β†’ v1.9.143 (2026-05-20, +11d)
55
+ - R125 β†’ v1.9.168 (2026-05-20, +12d)
56
+ - R150 β†’ v1.9.193 (2026-05-21, +12d)
57
+ - R175 β†’ v1.9.218 (2026-05-22, +14d)
58
+ - **λ‹€μŒ: R200** β€” 2026-05-24 ETA (15 λΌμš΄λ“œ λ‚¨μŒ, ~2일 ν›„)
59
+
60
+ ### 4. λˆ„μ  νšŒκ·€ (1.9.207~228) β€” λͺ¨λ‘ μœ μ§€
61
+
62
+ ### 5. stress-v174 β€” 17/17 PASS
63
+ - 1.9.229 (7): VERSION + CLI + reached ν˜•νƒœ + next ETA + git fallback + MCP 63 + μ‹€ 호좜
64
+ - μ„±λŠ₯ (2): cold_start avg 442ms / milestones 717ms
65
+ - λˆ„μ  νšŒκ·€ (8): 1.9.207~228
66
+
67
+ ### 6. μžλ™ release (91 λΌμš΄λ“œ main-push streak Β· 52 λΌμš΄λ“œ npm publish streak)
68
+
69
+ 🎯 **자율 λͺ¨λ“œ 진척도 μ‹œκ°ν™” μ™„μ„±** β€” 도달 이λ ₯ + 예츑 ETA λͺ¨λ‘ κ°€μ‹œν™”
70
+
71
+ ---
72
+
3
73
  ## 1.9.228 β€” 2026-05-23
4
74
 
5
75
  **πŸ“Š health --json roundHistory 톡합 (JSON 3 λͺ…λ Ή 일관성) + session-workflow.md drift 차단 + ν—€λ“œλΌμΈ label κ°±μ‹ .**
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.228-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v173-15%2F15-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-62-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-184-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-90_rounds-success)]() [![goal-matrix](https://img.shields.io/badge/λͺ©μ _맀트릭슀-100%2F100_(5μΆ•)-brightgreen)]() [![json-3cmd](https://img.shields.io/badge/πŸ“Š_JSON_3_λͺ…λ Ή_일관성-handoff%2Bclose%2Bhealth-blueviolet)]() [![perf](https://img.shields.io/badge/cold_start-414ms-success)]() [![install](https://img.shields.io/badge/npm_i_leerness-μ¦‰μ‹œ_μ‚¬μš©_κ°€λŠ₯-success)]() [![sandbox](https://img.shields.io/badge/runCommandSafe-cwd_jail%2Benv_scrub-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.230-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v175-17%2F17-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-63-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-186-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-92_rounds-success)]() [![goal-matrix](https://img.shields.io/badge/λͺ©μ _맀트릭슀-100%2F100_(5μΆ•)-brightgreen)]() [![json-7fields](https://img.shields.io/badge/πŸ“Š_JSON_톡합_ν•„λ“œ-7%2F7-blueviolet)]() [![perf](https://img.shields.io/badge/cold_start-439ms-success)]() [![install](https://img.shields.io/badge/npm_i_leerness-μ¦‰μ‹œ_μ‚¬μš©_κ°€λŠ₯-success)]() [![sandbox](https://img.shields.io/badge/runCommandSafe-cwd_jail%2Benv_scrub-success)]() [![license](https://img.shields.io/badge/license-MIT-lightgrey)]()
6
6
 
7
7
  ```
8
8
  ╔══════════════════════════════════════════════════════════════╗
@@ -12,9 +12,9 @@
12
12
  β•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β•β• β–ˆβ–ˆβ•”β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β• β•šβ•β•β•β•β–ˆβ–ˆβ•‘ β•‘
13
13
  β•‘ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘ β•‘
14
14
  β•‘ β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β• β•‘
15
- β•‘ v1.9.228 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.230 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ πŸ“Š health --json roundHistory + 3 λͺ…λ Ή 일관성 μ™„μ„± β•‘
17
+ β•‘ πŸ“Š JSON 3 λͺ…λ Ή Γ— 7 ν•„λ“œ = 21 톡합 포인트 β•‘
18
18
  β•šβ•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•β•
19
19
  ```
20
20
 
package/bin/harness.js CHANGED
@@ -7,7 +7,7 @@ const cp = require('child_process');
7
7
  const os = require('os'); // 1.9.178: _publishToNpm μ—μ„œ os.tmpdir() μ‚¬μš© (μ „μ—­ import)
8
8
  const readline = require('readline');
9
9
 
10
- const VERSION = '1.9.228';
10
+ const VERSION = '1.9.230';
11
11
 
12
12
  // 1.9.184: DEP0190 (child_process shell: true) deprecation warning μ–΅μ œ (μ‚¬μš©μž λͺ…μ‹œ).
13
13
  // leerness λŠ” cross-platform PATH resolution 을 μœ„ν•΄ shell: true λ₯Ό μ˜λ„μ μœΌλ‘œ μ‚¬μš© (claude.cmd / ollama.cmd λ“± Windows .cmd 처리).
@@ -2105,6 +2105,97 @@ function _computeRoundHistory(root) {
2105
2105
  return result;
2106
2106
  }
2107
2107
 
2108
+ // 1.9.229: milestones β€” λ„λ‹¬ν•œ λ§ˆμΌμŠ€ν†€ 일자 + λ‹€μŒ λ§ˆμΌμŠ€ν†€ μ˜ˆμƒ μ‹œμ  (1.9.226 round-history ν™•μž₯)
2109
+ // git tag 순차 뢄석 β†’ 50/75/100/... 도달 μ‹œμ μ˜ tag 와 일자 회수
2110
+ // 응닡: { reached: [{milestone, version, reachedAt, daysFromBaseline}], next: {milestone, roundsRemaining, eta}, totalRounds, avgRoundsPerDay }
2111
+ function _computeMilestones(root) {
2112
+ const result = {
2113
+ totalRounds: 0,
2114
+ reached: [],
2115
+ next: null,
2116
+ avgRoundsPerDay: 0,
2117
+ baselineAt: null
2118
+ };
2119
+ try {
2120
+ const r = cp.spawnSync('git', ['tag', '--list', 'v1.9.*', '--sort=creatordate', '--format=%(refname:short)|%(creatordate:iso8601)'], {
2121
+ cwd: root, encoding: 'utf8', timeout: 5000
2122
+ });
2123
+ if (r.status !== 0 || !r.stdout) return result;
2124
+ const lines = r.stdout.split('\n').filter(l => l.trim() && /^v\d+\.\d+\.\d+\|/.test(l));
2125
+ if (lines.length === 0) return result;
2126
+ const tags = lines.map(l => {
2127
+ const [name, date] = l.split('|');
2128
+ return { name: name.trim(), version: name.replace(/^v/, '').trim(), date: date.trim() };
2129
+ });
2130
+ result.totalRounds = tags.length;
2131
+ result.baselineAt = tags[0].date;
2132
+ const milestones = [25, 50, 75, 100, 125, 150, 175, 200, 250, 300, 400, 500];
2133
+ const baselineTime = new Date(tags[0].date).getTime();
2134
+ // 1-indexed: tags[0] is round 1
2135
+ for (const m of milestones) {
2136
+ if (m <= tags.length) {
2137
+ const t = tags[m - 1];
2138
+ const reachedTime = new Date(t.date).getTime();
2139
+ const daysFromBaseline = Math.floor((reachedTime - baselineTime) / 86400000);
2140
+ result.reached.push({
2141
+ milestone: m,
2142
+ version: t.version,
2143
+ reachedAt: t.date.split(' ')[0],
2144
+ daysFromBaseline
2145
+ });
2146
+ }
2147
+ }
2148
+ // λ‹€μŒ λ§ˆμΌμŠ€ν†€ + 평균 μ†λ„λ‘œ ETA 계산
2149
+ const lastTime = new Date(tags[tags.length - 1].date).getTime();
2150
+ const days = Math.max(1, Math.floor((lastTime - baselineTime) / 86400000));
2151
+ const avgPerDay = (tags.length / days);
2152
+ result.avgRoundsPerDay = Math.round(avgPerDay * 100) / 100;
2153
+ const nextM = milestones.find(m => m > tags.length);
2154
+ if (nextM != null) {
2155
+ const roundsRemaining = nextM - tags.length;
2156
+ const etaDays = avgPerDay > 0 ? Math.ceil(roundsRemaining / avgPerDay) : null;
2157
+ const etaDate = etaDays != null ? new Date(Date.now() + etaDays * 86400000).toISOString().split('T')[0] : null;
2158
+ result.next = { milestone: nextM, roundsRemaining, etaDays, etaDate };
2159
+ }
2160
+ } catch {}
2161
+ return result;
2162
+ }
2163
+
2164
+ function milestonesCmd(root) {
2165
+ root = absRoot(root);
2166
+ const isTty = process.stdout && process.stdout.isTTY;
2167
+ const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
2168
+ const gr = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
2169
+ const yl = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
2170
+ const dm = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
2171
+ const data = _computeMilestones(root);
2172
+ if (has('--json')) { log(JSON.stringify(data, null, 2)); return; }
2173
+ log(cy(`# leerness milestones (1.9.229) β€” 도달 λ§ˆμΌμŠ€ν†€ + λ‹€μŒ ETA`));
2174
+ log('');
2175
+ log(` πŸ“¦ 총 λΌμš΄λ“œ: ${gr(String(data.totalRounds))}`);
2176
+ if (data.baselineAt) log(` πŸ“ baseline: ${(data.baselineAt || '').split(' ')[0]}`);
2177
+ if (data.avgRoundsPerDay) log(` πŸ“Š 평균: ${gr(data.avgRoundsPerDay + ' rounds/day')}`);
2178
+ log('');
2179
+ if (data.reached.length === 0) {
2180
+ log(dm(` (도달 λ§ˆμΌμŠ€ν†€ μ—†μŒ β€” 첫 R25 λ§ˆμΌμŠ€ν†€ μ§„ν–‰ 쀑)`));
2181
+ } else {
2182
+ log(` 도달 λ§ˆμΌμŠ€ν†€ ${data.reached.length}개:`);
2183
+ for (const m of data.reached) {
2184
+ log(` βœ“ R${m.milestone} ${gr(`(v${m.version}, ${m.reachedAt}, +${m.daysFromBaseline}d)`)}`);
2185
+ }
2186
+ }
2187
+ log('');
2188
+ if (data.next) {
2189
+ log(yl(` 🎯 λ‹€μŒ λ§ˆμΌμŠ€ν†€: R${data.next.milestone}`));
2190
+ log(` β€’ ${data.next.roundsRemaining} λΌμš΄λ“œ λ‚¨μŒ`);
2191
+ if (data.next.etaDays != null) {
2192
+ log(` β€’ ETA: ${data.next.etaDate} (~${data.next.etaDays}일 ν›„, ν˜„μž¬ 속도 κΈ°μ€€)`);
2193
+ }
2194
+ } else {
2195
+ log(gr(` πŸŽ‰ λͺ¨λ“  λ§ˆμΌμŠ€ν†€ 달성 (500+)`));
2196
+ }
2197
+ }
2198
+
2108
2199
  function roundHistoryCmd(root) {
2109
2200
  root = absRoot(root);
2110
2201
  const isTty = process.stdout && process.stdout.isTTY;
@@ -5915,6 +6006,16 @@ function handoff(root) {
5915
6006
  avgRoundsPerDay: rh.avgRoundsPerDay
5916
6007
  };
5917
6008
  } catch {}
6009
+ // 1.9.230: milestones 톡합 (handoff JSON 7번째 톡합 ν•„λ“œ) β€” 도달 λ§ˆμΌμŠ€ν†€ + ETA
6010
+ try {
6011
+ const ms = _computeMilestones(root);
6012
+ result.milestones = {
6013
+ reachedCount: ms.reached.length,
6014
+ reached: ms.reached.map(m => ({ milestone: m.milestone, version: m.version, reachedAt: m.reachedAt })),
6015
+ next: ms.next,
6016
+ avgRoundsPerDay: ms.avgRoundsPerDay
6017
+ };
6018
+ } catch {}
5918
6019
  } catch {}
5919
6020
  try {
5920
6021
  const pwState = _loadPreWakeReport(root);
@@ -6078,6 +6179,7 @@ function handoff(root) {
6078
6179
  }
6079
6180
  } catch {}
6080
6181
  // 17) 1.9.226: round-history ν—€λ“œλΌμΈ β€” 자율 λΌμš΄λ“œ μΉ΄μš΄ν„° + λ‹€μŒ λ§ˆμΌμŠ€ν†€ (5 λΌμš΄λ“œ 이상 μ‹œλ§Œ ν‘œμ‹œ)
6182
+ // 1.9.230: λ‹€μŒ λ§ˆμΌμŠ€ν†€ ETA (10R 이내 + ETA 7일 이내일 λ•Œ κ°•μ‘°)
6081
6183
  try {
6082
6184
  const rh = _computeRoundHistory(root);
6083
6185
  if (rh.roundCount >= 5) {
@@ -6086,6 +6188,13 @@ function handoff(root) {
6086
6188
  label += ` β†’ R${rh.nextMilestone} (${rh.roundsToNextMilestone}R λ‚¨μŒ)`;
6087
6189
  }
6088
6190
  parts.push(label);
6191
+ // 1.9.230: μž„λ°• λ§ˆμΌμŠ€ν†€ ETA 별도 λ…ΈμΆœ (λ‹€μŒ λ§ˆμΌμŠ€ν†€μ΄ 맀우 κ°€κΉŒμšΈ λ•Œλ§Œ)
6192
+ try {
6193
+ const ms = _computeMilestones(root);
6194
+ if (ms.next && ms.next.roundsRemaining <= 10 && ms.next.etaDays != null && ms.next.etaDays <= 7) {
6195
+ parts.push(`🎯 R${ms.next.milestone} ETA ${ms.next.etaDate}`);
6196
+ }
6197
+ } catch {}
6089
6198
  }
6090
6199
  } catch {}
6091
6200
  // 12) 1.9.204: μžλ™ λͺ¨λ“œ ν™œμ„± ν‘œμ‹œ (μ‚¬μš©μž λͺ…μ‹œ) β€” R-0001 every-round λ£° ν™œμ„± μ‹œ ν—€λ“œλΌμΈ λ…ΈμΆœ
@@ -10052,6 +10161,16 @@ function sessionClose(root, opts = {}) {
10052
10161
  avgRoundsPerDay: rh.avgRoundsPerDay
10053
10162
  };
10054
10163
  } catch {}
10164
+ // 1.9.230: milestones 톡합 (session close JSON 7번째 톡합 ν•„λ“œ)
10165
+ try {
10166
+ const ms = _computeMilestones(root);
10167
+ jsonResult.milestones = {
10168
+ reachedCount: ms.reached.length,
10169
+ reached: ms.reached.map(m => ({ milestone: m.milestone, version: m.version, reachedAt: m.reachedAt })),
10170
+ next: ms.next,
10171
+ avgRoundsPerDay: ms.avgRoundsPerDay
10172
+ };
10173
+ } catch {}
10055
10174
  } catch {}
10056
10175
  try {
10057
10176
  // 1.9.209: pre-wake-audit μžλ™ μ‹€ν–‰ + μ €μž₯ (sleep μ „ μžλ™ 점검)
@@ -14003,7 +14122,8 @@ function mcpServeCmd(root) {
14003
14122
  { name: 'leerness_idempotency_audit', description: '1.9.216 (1.9.212 μ‚¬μš©μž λͺ…μ‹œ) β€” λ©±λ“±μ„± μœ„λ°˜ 탐지. 4μ˜μ—­ 점검: rule-duplicate (medium) / task-duplicate-request (medium) / user-request-duplicate (low) / wakeup-duplicate (high). 응닡: { violations[], verified[], summary: {totalViolations, high/medium/low, overall} }. μ™ΈλΆ€ AIκ°€ "μ›Œν¬μŠ€νŽ˜μ΄μŠ€μ— 쀑볡/좩돌이 μžˆλ‚˜?"λ₯Ό 회수. πŸŽ‰ MCP 58 도ꡬ λ§ˆμΌμŠ€ν†€ (50β†’58). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
14004
14123
  { name: 'leerness_session_resume', description: '1.9.221 (1.9.220 μ‚¬μš©μž λͺ…μ‹œ) β€” 비정상 μ’…λ£Œ 감지 + 자율 재개. 5μ‹ ν˜Έ 뢄석: last-handoff-stale / wakeup-missed / in-progress-stale / auto-resume-plan-unused / release-branch-pending. 응닡: { abnormalShutdown, severity (none/low/medium/high), signals[], resumeGuide[] }. μ™ΈλΆ€ AIκ°€ "μ ˆμ „/μ‹œμŠ€ν…œμ’…λ£Œ/μ„Έμ…˜μ’…λ£Œ ν›„ leerness μƒνƒœκ°€ 정상인가? μ–΄λ–»κ²Œ 재개?"λ₯Ό 회수. πŸŽ‰ MCP 60 도ꡬ λ§ˆμΌμŠ€ν†€ (53β†’60, +7 in 1.9.168/216/221). 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
14005
14124
  { name: 'leerness_requests_auto_complete', description: '1.9.224 (1.9.223 μžλ™ 회수) β€” delivered νŒ¨ν„΄ μžλ™ 감지 + μžλ™ μ™„λ£Œ. "Round X.Y.Z β€” κ΅¬ν˜„ μ™„λ£Œ" / "implemented" / "delivered" νŒ¨ν„΄μ΄ μžˆλŠ” open μš”μ²­ 쀑 ν˜„μž¬ 버전 μ΄ν•˜μΈ 것을 ν›„λ³΄λ‘œ λΆ„λ₯˜. 응닡: { total, candidates: [{id, text, claimedVersion, currentVersion, deliveredKeyword, recordedAt}], currentVersion, applied (apply=true μ‹œ), completedIds[] }. μ™ΈλΆ€ AIκ°€ "운영 λˆ„μ λœ κ°€μ§œ λ―Έλ‹΅ μ‹ ν˜Έ 정리 κ°€λŠ₯ν•œκ°€?"λ₯Ό 회수. κΈ°λ³Έ dry-run, apply: true λͺ…μ‹œ μ‹œμ—λ§Œ μ‹€ 적용. 인자: { path?, apply? (default false) }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, apply: { type: 'boolean' } } } },
14006
- { name: 'leerness_round_history', description: '1.9.226 β€” 자율 λΌμš΄λ“œ 톡계. git tag v1.9.X 기반 λˆ„μ  λΌμš΄λ“œ 카운트 + λ‹€μŒ λ§ˆμΌμŠ€ν†€ (50/75/100/125/150/175/200/250/300/400/500) + 평균 rounds/day. 응닡: { currentVersion, roundCount, baselineVersion, latestTags[], nextMilestone, roundsToNextMilestone, firstTagAt, latestTagAt, daysActive, avgRoundsPerDay }. μ™ΈλΆ€ AIκ°€ "이 ν”„λ‘œμ νŠΈλŠ” μ–Όλ§ˆλ‚˜ 진행됐고 λ‹€μŒ λ§ˆμΌμŠ€ν†€κΉŒμ§€ λͺ‡ λΌμš΄λ“œ λ‚¨μ•˜λ‚˜?"λ₯Ό 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } }
14125
+ { name: 'leerness_round_history', description: '1.9.226 β€” 자율 λΌμš΄λ“œ 톡계. git tag v1.9.X 기반 λˆ„μ  λΌμš΄λ“œ 카운트 + λ‹€μŒ λ§ˆμΌμŠ€ν†€ (50/75/100/125/150/175/200/250/300/400/500) + 평균 rounds/day. 응닡: { currentVersion, roundCount, baselineVersion, latestTags[], nextMilestone, roundsToNextMilestone, firstTagAt, latestTagAt, daysActive, avgRoundsPerDay }. μ™ΈλΆ€ AIκ°€ "이 ν”„λ‘œμ νŠΈλŠ” μ–Όλ§ˆλ‚˜ 진행됐고 λ‹€μŒ λ§ˆμΌμŠ€ν†€κΉŒμ§€ λͺ‡ λΌμš΄λ“œ λ‚¨μ•˜λ‚˜?"λ₯Ό 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
14126
+ { name: 'leerness_milestones', description: '1.9.229 (1.9.226 ν™•μž₯) β€” 도달 λ§ˆμΌμŠ€ν†€ + λ‹€μŒ ETA. git tag 순차 λΆ„μ„μœΌλ‘œ 25/50/75/100/125/150/175/200/250/300/400/500 λ§ˆμΌμŠ€ν†€ 도달 일자 + λ‹€μŒ λ§ˆμΌμŠ€ν†€ ETA (ν˜„μž¬ 속도 κΈ°μ€€) 계산. 응닡: { totalRounds, reached: [{milestone, version, reachedAt, daysFromBaseline}], next: {milestone, roundsRemaining, etaDays, etaDate}, baselineAt, avgRoundsPerDay }. μ™ΈλΆ€ AIκ°€ "μ§€κΈˆκΉŒμ§€ μ–΄λ–€ λ§ˆμΌμŠ€ν†€μ„ μ–Έμ œ λ‹¬μ„±ν–ˆκ³  λ‹€μŒ λ§ˆμΌμŠ€ν†€ μ˜ˆμƒ 도달일은?"을 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } }
14007
14127
  ];
14008
14128
 
14009
14129
  function send(obj) {
@@ -14168,6 +14288,10 @@ function mcpServeCmd(root) {
14168
14288
  // 1.9.226: 자율 λΌμš΄λ“œ 톡계 + λ‹€μŒ λ§ˆμΌμŠ€ν†€
14169
14289
  cliArgs = ['round-history', '--path', targetPath, '--json'];
14170
14290
  break;
14291
+ case 'leerness_milestones':
14292
+ // 1.9.229: 도달 λ§ˆμΌμŠ€ν†€ + λ‹€μŒ ETA
14293
+ cliArgs = ['milestones', '--path', targetPath, '--json'];
14294
+ break;
14171
14295
  default:
14172
14296
  return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
14173
14297
  }
@@ -16378,6 +16502,16 @@ function healthCmd(root) {
16378
16502
  avgRoundsPerDay: rh.avgRoundsPerDay
16379
16503
  };
16380
16504
  } catch { out.roundHistory = { error: 'roundHistory 점검 μ‹€νŒ¨' }; }
16505
+ // 1.9.230: health --json milestones 톡합 (handoff/session close/health 3 λͺ…λ Ή 일관성 μœ μ§€)
16506
+ try {
16507
+ const ms = _computeMilestones(root);
16508
+ out.milestones = {
16509
+ reachedCount: ms.reached.length,
16510
+ reached: ms.reached.map(m => ({ milestone: m.milestone, version: m.version, reachedAt: m.reachedAt })),
16511
+ next: ms.next,
16512
+ avgRoundsPerDay: ms.avgRoundsPerDay
16513
+ };
16514
+ } catch { out.milestones = { error: 'milestones 점검 μ‹€νŒ¨' }; }
16381
16515
  // 1.9.163: 5λŠ₯λ ₯ 맀트릭슀 μžλ™ 평가 (1.9.155 sub-agent 점검 β†’ μ½”λ“œ 기반 μžλ™ν™”)
16382
16516
  // 각 λŠ₯λ ₯을 μ½”λ“œ grep 으둜 κ²€μΆœ β†’ 0~100 점수. μ‚¬μš©μžκ°€ λ§€ health 호좜 μ‹œ leerness 자기 평가 확인.
16383
16517
  try {
@@ -17813,6 +17947,8 @@ async function main() {
17813
17947
  if (cmd === 'session-resume') return sessionResumeCmd(arg('--path', process.cwd()));
17814
17948
  // 1.9.226: leerness round-history β€” 자율 λΌμš΄λ“œ 톡계 + λ‹€μŒ λ§ˆμΌμŠ€ν†€
17815
17949
  if (cmd === 'round-history') return roundHistoryCmd(arg('--path', process.cwd()));
17950
+ // 1.9.229: leerness milestones β€” 도달 λ§ˆμΌμŠ€ν†€ + λ‹€μŒ ETA (1.9.226 ν™•μž₯)
17951
+ if (cmd === 'milestones') return milestonesCmd(arg('--path', process.cwd()));
17816
17952
  // 1.9.208: leerness constraints <list|check|add> β€” ν”Œλž«νΌ/API μ œμ•½ 사전 체크 (μ‚¬μš©μž λͺ…μ‹œ)
17817
17953
  if (cmd === 'constraints') return constraintsCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
17818
17954
  // 1.9.209: leerness pre-wake-audit β€” sleep μ „ sub-agent audit (μ‚¬μš©μž λͺ…μ‹œ)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.228",
3
+ "version": "1.9.230",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",