leerness 1.9.222 β†’ 1.9.224

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,83 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.224 β€” 2026-05-22
4
+
5
+ **πŸ”Œ MCP 61 도ꡬ (leerness_requests_auto_complete) + handoff λ³Έλ¬Έ + session close --auto-apply-delivered.**
6
+
7
+ ### 1. MCP 61번째 도ꡬ β€” `leerness_requests_auto_complete`
8
+ - 1.9.223 delivered νŒ¨ν„΄ μžλ™ 감지λ₯Ό μ™ΈλΆ€ AI에 λ…ΈμΆœ
9
+ - 인자: `{ path?, apply? (default false) }`
10
+ - 응닡: `{ total, candidates: [{id, text, claimedVersion, currentVersion, deliveredKeyword, recordedAt}], currentVersion, applied, completedIds[] }`
11
+ - MCP 60 (1.9.221) β†’ 61 도ꡬ (+1)
12
+
13
+ ### 2. handoff λ³Έλ¬Έ μžλ™ λ…ΈμΆœ
14
+ - delivered 후보 β‰₯ 1건 μ‹œ: `## πŸ“₯ μ‚¬μš©μž μš”μ²­ μžλ™ μ™„λ£Œ κ°€λŠ₯ (1.9.224, N건)` λ³Έλ¬Έ μ„Ήμ…˜
15
+ - top 5 후보 λ…ΈμΆœ (overflowλŠ” `... +N건 더`)
16
+ - μžλ™ ꢌμž₯: `β†’ leerness requests auto-complete --apply`
17
+ - 1.9.222 비정상 μ’…λ£Œ λ³Έλ¬Έ λ‹€μŒ, ν™œμ„± λ£° 이전 μœ„μΉ˜
18
+
19
+ ### 3. `session close --auto-apply-delivered` ν”Œλž˜κ·Έ
20
+ - 마감 μ‹œ delivered 후보 μžλ™ μ™„λ£Œ (μ•ˆμ „: νŒ¨ν„΄ λ§€μΉ­ + 버전 κ°€λ“œ)
21
+ - `autoCompleteReason: 'session-close-auto-apply-1.9.224'` 좔적
22
+ - ν”Œλž˜κ·Έ μ—†μœΌλ©΄ κΈ°μ‘΄ λ™μž‘ (μ•ˆλ‚΄λ§Œ)
23
+
24
+ ### 4. λˆ„μ  νšŒκ·€ (1.9.207~223) β€” λͺ¨λ‘ μœ μ§€
25
+
26
+ ### 5. stress-v169 β€” 16/16 PASS
27
+ - 1.9.224 (7): VERSION + MCP 61 + μ‹€ 호좜 + handoff λ³Έλ¬Έ + λ³Έλ¬Έ λ―Έλ…ΈμΆœ + session close auto-apply + κΈ°λ³Έ 보쑴
28
+ - μ„±λŠ₯ (2): cold_start avg 556ms / MCP 61 tools/list 417ms
29
+ - λˆ„μ  νšŒκ·€ (7): 1.9.207~223
30
+
31
+ ### 6. μžλ™ release (86 λΌμš΄λ“œ main-push streak Β· 47 λΌμš΄λ“œ npm publish streak)
32
+
33
+ πŸ”Œ **μ‚¬μš©μž μš”μ²­ μžλ™ μ™„λ£Œ μ‹œμŠ€ν…œ μ™„μ„±** (3 λΌμš΄λ“œ λˆ„μ ):
34
+ - 1.9.223 CLI auto-complete
35
+ - 1.9.224 MCP 61 + handoff λ³Έλ¬Έ + session close μžλ™ 적용
36
+
37
+ ---
38
+
39
+ ## 1.9.223 β€” 2026-05-22
40
+
41
+ **πŸ“₯ μ‚¬μš©μž μš”μ²­ auto-complete (delivered νŒ¨ν„΄ μžλ™ 감지) + handoff/session close 톡합.**
42
+
43
+ ### 1. `leerness requests auto-complete` β€” delivered νŒ¨ν„΄ μžλ™ 정리
44
+ - 1.9.207 μ‹œμŠ€ν…œ 운영 쀑 λˆ„μ λœ "Round X.Y.Z β€” κ΅¬ν˜„ μ™„λ£Œ" νŒ¨ν„΄μ˜ 자기-기둝 μš”μ²­μ„ μžλ™ 감지
45
+ - μ •κ·œμ‹: `(Round\s+)?\d+\.\d+\.\d+` + `(κ΅¬ν˜„ μ™„λ£Œ|implemented|delivered|shipped|배포 μ™„λ£Œ)`
46
+ - ν˜„μž¬ package.json 버전 μ΄ν•˜λ§Œ 후보 (미래 버전 λ¬΄μ‹œ) β€” μ•ˆμ „ κ°€λ“œ
47
+ - κΈ°λ³Έ dry-run (λͺ©λ‘λ§Œ), `--apply` λͺ…μ‹œ μ‹œμ—λ§Œ μ‹€μ œ μƒνƒœ λ³€κ²½
48
+ - μƒνƒœ λ³€κ²½ μ‹œ `autoCompletedAt` + `autoCompleteReason: 'delivered-pattern-1.9.223'` 좔적 기둝
49
+
50
+ ### 2. handoff ν—€λ“œλΌμΈ μžλ™ λ…ΈμΆœ
51
+ - delivered 후보 β‰₯ 1건 μ‹œ: `πŸ“₯ μžλ™μ™„λ£Œκ°€λŠ₯ N건 (1.9.223)` β€” λ―Έλ‹΅ μš”μ²­ μ‹ ν˜Έ λŒ€μ‹  μš°μ„  λ…ΈμΆœ
52
+ - 운영 쀑 λˆ„μ λœ κ°€μ§œ λ―Έλ‹΅ μ‹ ν˜Έ (μ‹€μ œλŠ” λͺ¨λ‘ 처리됨) μžλ™ 정리 ꢌμž₯
53
+
54
+ ### 3. handoff/session close JSON 5번째 톡합 ν•„λ“œ β€” `deliveredRequests`
55
+ - `{ candidates, currentVersion, autoCompleteAvailable, ids[] }`
56
+ - 1.9.218 4 ν•„λ“œ (userRequests/preWake/idempotency/abnormalShutdown) β†’ **5 ν•„λ“œ μ™„μ„±**
57
+ - μ™ΈλΆ€ λͺ¨λ‹ˆν„°λ§/CI μžλ™ 감지
58
+
59
+ ### 4. session close μžλ™ 톡합 λ³Έλ¬Έ λ…ΈμΆœ
60
+ - 1.9.207/209/212/220 톡합 νŒ¨ν„΄μ— 1.9.223 μΆ”κ°€
61
+ - delivered 후보 μ‹œ: `πŸ“₯ delivered νŒ¨ν„΄ N건 (1.9.223) β€” μžλ™ μ™„λ£Œ κ°€λŠ₯`
62
+ - `β†’ leerness requests auto-complete --apply` κ°€μ΄λ“œ μžλ™
63
+
64
+ ### 5. μ‹€ 운영 검증 β€” λˆ„μ  8건 정리 μ™„λ£Œ
65
+ - UR-0004~UR-0011 (1.9.208~213 λΌμš΄λ“œ 자기-기둝) β€” λͺ¨λ‘ μˆ˜λ™ 정리 μ™„λ£Œ
66
+ - 1.9.223 μ‹œμŠ€ν…œμ€ ν–₯ν›„ 재발 μ‹œ 1쀄 λͺ…λ ΉμœΌλ‘œ μ¦‰μ‹œ 정리
67
+
68
+ ### 6. λˆ„μ  νšŒκ·€ (1.9.207~222) β€” λͺ¨λ‘ μœ μ§€
69
+
70
+ ### 7. stress-v168 β€” 15/15 PASS
71
+ - 1.9.223 (6): VERSION + μžλ™ 감지 + --apply + dry-run + handoff JSON + session close JSON
72
+ - μ„±λŠ₯ (2): cold_start avg 401ms / auto-complete 432ms
73
+ - λˆ„μ  νšŒκ·€ (7): 1.9.207~222
74
+
75
+ ### 8. μžλ™ release (85 λΌμš΄λ“œ main-push streak Β· 46 λΌμš΄λ“œ npm publish streak)
76
+
77
+ πŸ“₯ **μ‚¬μš©μž μš”μ²­ 라이프사이클 μ™„μ„±**: 1.9.207 (μˆ˜λ™ audit) β†’ 1.9.217 (마감 μžλ™) β†’ **1.9.223 (μžλ™ μ™„λ£Œ)** β€” 3 λΌμš΄λ“œ λˆ„μ 
78
+
79
+ ---
80
+
3
81
  ## 1.9.222 β€” 2026-05-22
4
82
 
5
83
  **πŸ›‘ session-resume --auto-fix μ•ˆμ „ 회볡 + handoff λ³Έλ¬Έ 비정상 μ’…λ£Œ μžλ™ λ…ΈμΆœ.**
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.222-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v167-14%2F14-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-60_πŸŽ‰-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-152-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-84_rounds-success)]() [![goal-matrix](https://img.shields.io/badge/λͺ©μ _맀트릭슀-100%2F100_(5μΆ•)-brightgreen)]() [![auto-fix](https://img.shields.io/badge/πŸ›‘_session--resume-auto--fix-blueviolet)]() [![perf](https://img.shields.io/badge/cold_start-408ms-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.224-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v169-16%2F16-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-61-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-154-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-86_rounds-success)]() [![goal-matrix](https://img.shields.io/badge/λͺ©μ _맀트릭슀-100%2F100_(5μΆ•)-brightgreen)]() [![auto-complete](https://img.shields.io/badge/πŸ“₯_requests-auto--complete-blueviolet)]() [![perf](https://img.shields.io/badge/cold_start-556ms-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.222 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.224 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ πŸ›‘ session-resume --auto-fix Β· handoff λ³Έλ¬Έ 비정상 μ’…λ£Œ μžλ™ β•‘
17
+ β•‘ πŸ”Œ MCP 61 Β· handoff λ³Έλ¬Έ + session close --auto-apply β•‘
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.222';
10
+ const VERSION = '1.9.224';
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 처리).
@@ -2006,6 +2006,54 @@ function _auditUserRequests(root) {
2006
2006
  };
2007
2007
  }
2008
2008
 
2009
+ // 1.9.223: delivered-by-pattern 감지 β€” μ‚¬μš©μž μš”μ²­ 본문에 "Round X.Y.Z β€” κ΅¬ν˜„ μ™„λ£Œ" 같은 자기-기둝 νŒ¨ν„΄μ΄ 있고
2010
+ // ν•΄λ‹Ή 버전이 이미 μΆœμ‹œλœ 경우, μžλ™ μ™„λ£Œ ν›„λ³΄λ‘œ λΆ„λ₯˜. 운영 쀑 λˆ„μ λœ β—― μ‹ ν˜Έ μ •λ¦¬μš©.
2011
+ // μ•ˆμ „ 원칙: κΈ°λ³Έ dry-run (λͺ©λ‘λ§Œ), --apply μ‹œμ—λ§Œ μ‹€μ œ μƒνƒœ λ³€κ²½.
2012
+ function _detectDeliveredRequests(root) {
2013
+ const state = _loadUserRequests(root);
2014
+ const open = state.requests.filter(r => r.status === 'open' || r.status === 'in-progress');
2015
+ // νŒ¨ν„΄: "Round 1.9.123" λ˜λŠ” "1.9.123" + ("κ΅¬ν˜„ μ™„λ£Œ" | "implemented" | "delivered" | "shipped" | "ROUND … μ™„λ£Œ")
2016
+ const versionRe = /(?:Round\s+)?(\d+\.\d+\.\d+)/i;
2017
+ const doneRe = /(κ΅¬ν˜„\s*μ™„λ£Œ|κ΅¬ν˜„μ™„λ£Œ|implemented|delivered|shipped|ship\s*μ™„λ£Œ|배포\s*μ™„λ£Œ)/i;
2018
+ let currentMajor = '0.0.0';
2019
+ try {
2020
+ const pkg = JSON.parse(read(path.join(root, 'package.json')));
2021
+ if (pkg && pkg.version) currentMajor = pkg.version;
2022
+ } catch {}
2023
+ const candidates = [];
2024
+ open.forEach(req => {
2025
+ const text = req.text || '';
2026
+ const vMatch = text.match(versionRe);
2027
+ const dMatch = text.match(doneRe);
2028
+ if (vMatch && dMatch) {
2029
+ const claimedVer = vMatch[1];
2030
+ // ν˜„μž¬ 버전 μ΄ν•˜μΈμ§€ (자기-기둝 μ‹œμ μ˜ 버전이 이미 μΆœμ‹œλ¨)
2031
+ const cmp = _compareSemver(claimedVer, currentMajor);
2032
+ if (cmp <= 0) {
2033
+ candidates.push({
2034
+ id: req.id,
2035
+ text: text.slice(0, 120),
2036
+ claimedVersion: claimedVer,
2037
+ currentVersion: currentMajor,
2038
+ deliveredKeyword: dMatch[0],
2039
+ recordedAt: req.recordedAt
2040
+ });
2041
+ }
2042
+ }
2043
+ });
2044
+ return { total: open.length, candidates, currentVersion: currentMajor };
2045
+ }
2046
+
2047
+ function _compareSemver(a, b) {
2048
+ const pa = String(a).split('.').map(n => parseInt(n, 10) || 0);
2049
+ const pb = String(b).split('.').map(n => parseInt(n, 10) || 0);
2050
+ for (let i = 0; i < 3; i++) {
2051
+ if ((pa[i] || 0) < (pb[i] || 0)) return -1;
2052
+ if ((pa[i] || 0) > (pb[i] || 0)) return 1;
2053
+ }
2054
+ return 0;
2055
+ }
2056
+
2009
2057
  // 1.9.208: ν”Œλž«νΌ/API μ œμ•½ 사전 체크 (μ‚¬μš©μž λͺ…μ‹œ)
2010
2058
  // "μ‚¬μš©μžμ˜ λͺ…령을 λ°›μœΌλ©΄, κ³ λ €ν•΄μ•Όν•˜λŠ” 뢀뢄을 λ¨Όμ € ν™•μΈν•˜κ³  μ§„ν–‰ν•˜κ²Œ ν•˜λ©΄ μ–΄λ–¨μ§€"
2011
2059
  // "νŠΉμ • ν”Œλž«νΌμ˜ API ... ν˜ΈμΆœμ†λ„κ°€ μ΄ˆλ‹Ή 5회 μ΄ν•˜λ‘œλ§Œ ν•΄μ•Όν•œλ‹€λŠ” κ·œμ •μ„ λ¨Όμ € 확인"
@@ -2956,11 +3004,55 @@ function requestsCmd(root, sub, ...rest) {
2956
3004
  if (!sub || sub === 'help' || sub === '--help') {
2957
3005
  log(`# leerness requests (1.9.207) β€” μ‚¬μš©μž μš”μ²­ λˆ„λ½ 확인 절차`);
2958
3006
  log('');
2959
- log(` audit β†’ λˆ„λ½ 후보 + tracked + stale 보고 (--json κ°€λŠ₯)`);
2960
- log(` add "<text>" β†’ μ‚¬μš©μž μš”μ²­ μˆ˜λ™ 기둝`);
2961
- log(` list β†’ 전체 μš”μ²­ 좜λ ₯ (--json κ°€λŠ₯, --status open|completed|dropped)`);
2962
- log(` complete <id> β†’ μš”μ²­ μ™„λ£Œ ν‘œμ‹œ (예: UR-0003)`);
2963
- log(` drop <id> β†’ μš”μ²­ λ“œλ‘­ ν‘œμ‹œ`);
3007
+ log(` audit β†’ λˆ„λ½ 후보 + tracked + stale 보고 (--json κ°€λŠ₯)`);
3008
+ log(` add "<text>" β†’ μ‚¬μš©μž μš”μ²­ μˆ˜λ™ 기둝`);
3009
+ log(` list β†’ 전체 μš”μ²­ 좜λ ₯ (--json κ°€λŠ₯, --status open|completed|dropped)`);
3010
+ log(` complete <id> β†’ μš”μ²­ μ™„λ£Œ ν‘œμ‹œ (예: UR-0003)`);
3011
+ log(` drop <id> β†’ μš”μ²­ λ“œλ‘­ ν‘œμ‹œ`);
3012
+ log(` auto-complete β†’ "Round X.Y.Z β€” κ΅¬ν˜„ μ™„λ£Œ" νŒ¨ν„΄ μžλ™ 감지 (default: dry-run, --apply μ‹œ 적용) [1.9.223]`);
3013
+ return;
3014
+ }
3015
+
3016
+ // 1.9.223: auto-complete β€” 자기-기둝된 "κ΅¬ν˜„ μ™„λ£Œ" νŒ¨ν„΄ μžλ™ 정리 (default dry-run)
3017
+ if (sub === 'auto-complete') {
3018
+ const apply = has('--apply');
3019
+ const detected = _detectDeliveredRequests(root);
3020
+ if (has('--json')) {
3021
+ const result = { ...detected, applied: false, completedIds: [] };
3022
+ if (apply) {
3023
+ for (const c of detected.candidates) {
3024
+ const u = _updateUserRequest(root, c.id, { status: 'completed', autoCompletedAt: new Date().toISOString(), autoCompleteReason: 'delivered-pattern-1.9.223' });
3025
+ if (u) result.completedIds.push(c.id);
3026
+ }
3027
+ result.applied = true;
3028
+ }
3029
+ log(JSON.stringify(result, null, 2));
3030
+ return;
3031
+ }
3032
+ log(cyan(`# leerness requests auto-complete (1.9.223) β€” delivered pattern μžλ™ 감지`));
3033
+ log('');
3034
+ log(` ν˜„μž¬ 버전: ${detected.currentVersion} Β· open ${detected.total} Β· 후보 ${detected.candidates.length}건`);
3035
+ log('');
3036
+ if (detected.candidates.length === 0) {
3037
+ log(grn(` βœ“ delivered νŒ¨ν„΄ 후보 μ—†μŒ β€” open μš”μ²­μ€ λͺ¨λ‘ μƒˆλ‘œμš΄ μž‘μ—…`));
3038
+ return;
3039
+ }
3040
+ log(yel(` πŸ“₯ delivered νŒ¨ν„΄ 후보 ${detected.candidates.length}건:`));
3041
+ detected.candidates.forEach(c => {
3042
+ log(` β€’ [${c.id}] v${c.claimedVersion} (${c.deliveredKeyword}) β€” ${c.text.slice(0, 80)}…`);
3043
+ });
3044
+ log('');
3045
+ if (apply) {
3046
+ let ok = 0;
3047
+ for (const c of detected.candidates) {
3048
+ const u = _updateUserRequest(root, c.id, { status: 'completed', autoCompletedAt: new Date().toISOString(), autoCompleteReason: 'delivered-pattern-1.9.223' });
3049
+ if (u) ok++;
3050
+ }
3051
+ log(grn(` βœ“ μžλ™ μ™„λ£Œ ${ok}/${detected.candidates.length}건`));
3052
+ } else {
3053
+ log(dim(` β†’ μ μš©ν•˜λ €λ©΄: leerness requests auto-complete --apply`));
3054
+ log(dim(` β†’ μ•ˆμ „ 원칙: κΈ°λ³Έ dry-run (λ³€κ²½ X), --apply λͺ…μ‹œ μ‹œμ—λ§Œ 적용`));
3055
+ }
2964
3056
  return;
2965
3057
  }
2966
3058
 
@@ -5723,6 +5815,16 @@ function handoff(root) {
5723
5815
  tracked: reqAudit.tracked ? reqAudit.tracked.length : 0,
5724
5816
  stale: reqAudit.stale ? reqAudit.stale.length : 0
5725
5817
  };
5818
+ // 1.9.223: delivered νŒ¨ν„΄ μžλ™ 감지 톡합 (handoff JSON 5번째 톡합 ν•„λ“œ)
5819
+ try {
5820
+ const delivered = _detectDeliveredRequests(root);
5821
+ result.deliveredRequests = {
5822
+ candidates: delivered.candidates.length,
5823
+ currentVersion: delivered.currentVersion,
5824
+ autoCompleteAvailable: delivered.candidates.length > 0,
5825
+ ids: delivered.candidates.map(c => c.id)
5826
+ };
5827
+ } catch {}
5726
5828
  } catch {}
5727
5829
  try {
5728
5830
  const pwState = _loadPreWakeReport(root);
@@ -5912,9 +6014,14 @@ function handoff(root) {
5912
6014
  }
5913
6015
  } catch {}
5914
6016
  // 13) 1.9.207: μ‚¬μš©μž μš”μ²­ λˆ„λ½ 확인 절차 (μ‚¬μš©μž λͺ…μ‹œ) β€” λˆ„λ½ 후보 β‰₯ 1 μ‹œ ν—€λ“œλΌμΈ λ…ΈμΆœ
6017
+ // 1.9.223: delivered νŒ¨ν„΄ 후보 μ‹œ `πŸ“₯ μžλ™μ™„λ£Œκ°€λŠ₯ N건` μš°μ„  λ…ΈμΆœ (μˆ˜λ™ 정리 λΆ€λ‹΄ κ°μ†Œ)
5915
6018
  try {
5916
6019
  const audit = _auditUserRequests(root);
5917
- if (audit.missing && audit.missing.length > 0) {
6020
+ let detected = { candidates: [] };
6021
+ try { detected = _detectDeliveredRequests(root); } catch {}
6022
+ if (detected.candidates && detected.candidates.length > 0) {
6023
+ parts.push(`πŸ“₯ μžλ™μ™„λ£Œκ°€λŠ₯ ${detected.candidates.length}건 (1.9.223)`);
6024
+ } else if (audit.missing && audit.missing.length > 0) {
5918
6025
  parts.push(`πŸ“₯ λ―Έλ‹΅ μš”μ²­ ${audit.missing.length}건`);
5919
6026
  } else if (audit.open > 0) {
5920
6027
  parts.push(`πŸ“₯ μš”μ²­ ${audit.open} (tracked)`);
@@ -6032,6 +6139,28 @@ function handoff(root) {
6032
6139
  }
6033
6140
  } catch {}
6034
6141
 
6142
+ // 1.9.224: handoff λ³Έλ¬Έ μžλ™ λ…ΈμΆœ β€” delivered νŒ¨ν„΄ 후보 β‰₯ 1건 μ‹œ (1.9.223 ν™•μž₯)
6143
+ // "Round X.Y.Z β€” κ΅¬ν˜„ μ™„λ£Œ" νŒ¨ν„΄μ΄ λˆ„μ λ˜λ©΄ λ³Έλ¬Έ μ„Ήμ…˜μœΌλ‘œ μžλ™ μ•ˆλ‚΄ (ν—€λ“œλΌμΈλ§ŒμœΌλ‘œλŠ” λ†“μΉ˜κΈ° 쉬움)
6144
+ try {
6145
+ const delivered = _detectDeliveredRequests(root);
6146
+ if (delivered.candidates && delivered.candidates.length > 0) {
6147
+ const isTty = process.stdout && process.stdout.isTTY;
6148
+ const cy4 = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
6149
+ const yl4 = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
6150
+ const dm4 = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
6151
+ log('');
6152
+ log(yl4(`## πŸ“₯ μ‚¬μš©μž μš”μ²­ μžλ™ μ™„λ£Œ κ°€λŠ₯ (1.9.224, ${delivered.candidates.length}건)`));
6153
+ delivered.candidates.slice(0, 5).forEach(c => {
6154
+ log(` β€’ [${c.id}] v${c.claimedVersion} (${c.deliveredKeyword}) β€” ${c.text.slice(0, 70)}${c.text.length > 70 ? '…' : ''}`);
6155
+ });
6156
+ if (delivered.candidates.length > 5) {
6157
+ log(dm4(` ... +${delivered.candidates.length - 5}건 더`));
6158
+ }
6159
+ log(dm4(` β†’ κ²€ν† : leerness requests auto-complete`));
6160
+ log(dm4(` β†’ 적용: leerness requests auto-complete --apply (μ•ˆμ „ 정리)`));
6161
+ }
6162
+ } catch {}
6163
+
6035
6164
  // 1.9.8: active rules μžλ™ λ…ΈμΆœ (λ§€ μ„Έμ…˜ μ‹œμž‘ μ‹œ AIμ—κ²Œ λ³΄μž„)
6036
6165
  const activeRules = readRules(root).filter(r => r.status === 'active');
6037
6166
  if (activeRules.length) {
@@ -9783,6 +9912,15 @@ function sessionClose(root, opts = {}) {
9783
9912
  tracked: reqAudit.tracked ? reqAudit.tracked.length : 0,
9784
9913
  stale: reqAudit.stale ? reqAudit.stale.length : 0
9785
9914
  };
9915
+ // 1.9.223: delivered νŒ¨ν„΄ μžλ™ 감지 톡합
9916
+ try {
9917
+ const delivered = _detectDeliveredRequests(root);
9918
+ jsonResult.deliveredRequests = {
9919
+ candidates: delivered.candidates.length,
9920
+ currentVersion: delivered.currentVersion,
9921
+ autoCompleteAvailable: delivered.candidates.length > 0
9922
+ };
9923
+ } catch {}
9786
9924
  } catch {}
9787
9925
  try {
9788
9926
  // 1.9.209: pre-wake-audit μžλ™ μ‹€ν–‰ + μ €μž₯ (sleep μ „ μžλ™ 점검)
@@ -9834,11 +9972,26 @@ function sessionClose(root, opts = {}) {
9834
9972
 
9835
9973
  log('');
9836
9974
  log(`## πŸ”š session close μžλ™ 톡합 보고 (1.9.217)`);
9837
- // 1.9.207
9975
+ // 1.9.207 + 1.9.223 (delivered νŒ¨ν„΄ μžλ™ ꢌμž₯) + 1.9.224 (--auto-apply-delivered μ˜΅μ…˜)
9838
9976
  try {
9839
9977
  const reqAudit = _auditUserRequests(root);
9840
9978
  const missCnt = reqAudit.missing ? reqAudit.missing.length : 0;
9841
- if (missCnt > 0) {
9979
+ let delivered = { candidates: [] };
9980
+ try { delivered = _detectDeliveredRequests(root); } catch {}
9981
+ if (delivered.candidates && delivered.candidates.length > 0) {
9982
+ if (has('--auto-apply-delivered')) {
9983
+ // 1.9.224: μžλ™ 정리 (마감 μ‹œ 호좜 β€” μ•ˆμ „: νŒ¨ν„΄ λ§€μΉ­ + 버전 κ°€λ“œ)
9984
+ let ok = 0;
9985
+ for (const c of delivered.candidates) {
9986
+ const u = _updateUserRequest(root, c.id, { status: 'completed', autoCompletedAt: new Date().toISOString(), autoCompleteReason: 'session-close-auto-apply-1.9.224' });
9987
+ if (u) ok++;
9988
+ }
9989
+ log(grn(` βœ“ delivered νŒ¨ν„΄ ${ok}건 μžλ™ μ™„λ£Œ (--auto-apply-delivered 1.9.224)`));
9990
+ } else {
9991
+ log(yel(` πŸ“₯ delivered νŒ¨ν„΄ ${delivered.candidates.length}건 (1.9.223) β€” μžλ™ μ™„λ£Œ κ°€λŠ₯`));
9992
+ log(dim(` β†’ leerness requests auto-complete --apply (μˆ˜λ™) λ˜λŠ” session close --auto-apply-delivered (1.9.224)`));
9993
+ }
9994
+ } else if (missCnt > 0) {
9842
9995
  log(red(` ⚠ λ―Έλ‹΅ μ‚¬μš©μž μš”μ²­ ${missCnt}건 (task-log/plan/decisions λ§€μΉ­ μ•ˆ 됨)`));
9843
9996
  } else if (reqAudit.open > 0) {
9844
9997
  log(grn(` βœ“ μ‚¬μš©μž μš”μ²­ ${reqAudit.open}건 λͺ¨λ‘ tracked`));
@@ -13697,7 +13850,8 @@ function mcpServeCmd(root) {
13697
13850
  { name: 'leerness_pre_wake_audit', description: '1.9.216 (1.9.209 μ‚¬μš©μž λͺ…μ‹œ) β€” sleep μ „ sub-agent audit. 6 μ˜μ—­ 점검: missing-user-requests / stale-in-progress / drift-handoff-stale / wakeup-missed / next-action-pending / auto-resume-plan. μ™ΈλΆ€ AIκ°€ "κΉ¨μ–΄λ‚˜κΈ° μ „ 점검할 λΆ€λΆ„"을 회수. 응닡: { auditedAt, findings: {critical, warning, info}, summary }. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
13698
13851
  { name: 'leerness_intent_classify', description: '1.9.216 (1.9.213 μ‚¬μš©μž λͺ…μ‹œ) β€” μ‚¬μš©μž μ˜λ„ νŒŒμ•… + scope expansion 게이트. 응닡: { intent: precise|broad|default, signals, domain, explicitMentions, expansionCandidates, mode: dry-run }. 3원칙 μ•ˆμ „: (1) Always-Off Opt-In, (2) Dry-run κΈ°λ³Έ (μ‹€ν–‰ X), (3) λͺ…μ‹œ vs μΆ”λ‘  뢄리 라벨링. 5 도메인 (game/web/api/cli/data). μ™ΈλΆ€ AIκ°€ "이 μš”μ²­μ€ μ •ν™•νžˆ κ·Έκ²ƒλ§Œ / 포괄적 / 기본인가?"λ₯Ό 회수. 인자: { request (required), path? }', inputSchema: { type: 'object', properties: { request: { type: 'string' }, path: { type: 'string' } }, required: ['request'] } },
13699
13852
  { 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' } } } },
13700
- { 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' } } } }
13853
+ { 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' } } } },
13854
+ { 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' } } } }
13701
13855
  ];
13702
13856
 
13703
13857
  function send(obj) {
@@ -13853,6 +14007,11 @@ function mcpServeCmd(root) {
13853
14007
  // 1.9.221 (1.9.220): 비정상 μ’…λ£Œ 감지 + 자율 재개
13854
14008
  cliArgs = ['session-resume', '--path', targetPath, '--json'];
13855
14009
  break;
14010
+ case 'leerness_requests_auto_complete':
14011
+ // 1.9.224 (1.9.223): delivered νŒ¨ν„΄ μžλ™ 감지 + μžλ™ μ™„λ£Œ
14012
+ cliArgs = ['requests', 'auto-complete', '--path', targetPath, '--json'];
14013
+ if (args.apply === true) cliArgs.push('--apply');
14014
+ break;
13856
14015
  default:
13857
14016
  return send({ jsonrpc: '2.0', id, error: { code: -32601, message: `Unknown tool: ${name}` } });
13858
14017
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.222",
3
+ "version": "1.9.224",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",