leerness 1.9.221 β†’ 1.9.223

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,82 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.223 β€” 2026-05-22
4
+
5
+ **πŸ“₯ μ‚¬μš©μž μš”μ²­ auto-complete (delivered νŒ¨ν„΄ μžλ™ 감지) + handoff/session close 톡합.**
6
+
7
+ ### 1. `leerness requests auto-complete` β€” delivered νŒ¨ν„΄ μžλ™ 정리
8
+ - 1.9.207 μ‹œμŠ€ν…œ 운영 쀑 λˆ„μ λœ "Round X.Y.Z β€” κ΅¬ν˜„ μ™„λ£Œ" νŒ¨ν„΄μ˜ 자기-기둝 μš”μ²­μ„ μžλ™ 감지
9
+ - μ •κ·œμ‹: `(Round\s+)?\d+\.\d+\.\d+` + `(κ΅¬ν˜„ μ™„λ£Œ|implemented|delivered|shipped|배포 μ™„λ£Œ)`
10
+ - ν˜„μž¬ package.json 버전 μ΄ν•˜λ§Œ 후보 (미래 버전 λ¬΄μ‹œ) β€” μ•ˆμ „ κ°€λ“œ
11
+ - κΈ°λ³Έ dry-run (λͺ©λ‘λ§Œ), `--apply` λͺ…μ‹œ μ‹œμ—λ§Œ μ‹€μ œ μƒνƒœ λ³€κ²½
12
+ - μƒνƒœ λ³€κ²½ μ‹œ `autoCompletedAt` + `autoCompleteReason: 'delivered-pattern-1.9.223'` 좔적 기둝
13
+
14
+ ### 2. handoff ν—€λ“œλΌμΈ μžλ™ λ…ΈμΆœ
15
+ - delivered 후보 β‰₯ 1건 μ‹œ: `πŸ“₯ μžλ™μ™„λ£Œκ°€λŠ₯ N건 (1.9.223)` β€” λ―Έλ‹΅ μš”μ²­ μ‹ ν˜Έ λŒ€μ‹  μš°μ„  λ…ΈμΆœ
16
+ - 운영 쀑 λˆ„μ λœ κ°€μ§œ λ―Έλ‹΅ μ‹ ν˜Έ (μ‹€μ œλŠ” λͺ¨λ‘ 처리됨) μžλ™ 정리 ꢌμž₯
17
+
18
+ ### 3. handoff/session close JSON 5번째 톡합 ν•„λ“œ β€” `deliveredRequests`
19
+ - `{ candidates, currentVersion, autoCompleteAvailable, ids[] }`
20
+ - 1.9.218 4 ν•„λ“œ (userRequests/preWake/idempotency/abnormalShutdown) β†’ **5 ν•„λ“œ μ™„μ„±**
21
+ - μ™ΈλΆ€ λͺ¨λ‹ˆν„°λ§/CI μžλ™ 감지
22
+
23
+ ### 4. session close μžλ™ 톡합 λ³Έλ¬Έ λ…ΈμΆœ
24
+ - 1.9.207/209/212/220 톡합 νŒ¨ν„΄μ— 1.9.223 μΆ”κ°€
25
+ - delivered 후보 μ‹œ: `πŸ“₯ delivered νŒ¨ν„΄ N건 (1.9.223) β€” μžλ™ μ™„λ£Œ κ°€λŠ₯`
26
+ - `β†’ leerness requests auto-complete --apply` κ°€μ΄λ“œ μžλ™
27
+
28
+ ### 5. μ‹€ 운영 검증 β€” λˆ„μ  8건 정리 μ™„λ£Œ
29
+ - UR-0004~UR-0011 (1.9.208~213 λΌμš΄λ“œ 자기-기둝) β€” λͺ¨λ‘ μˆ˜λ™ 정리 μ™„λ£Œ
30
+ - 1.9.223 μ‹œμŠ€ν…œμ€ ν–₯ν›„ 재발 μ‹œ 1쀄 λͺ…λ ΉμœΌλ‘œ μ¦‰μ‹œ 정리
31
+
32
+ ### 6. λˆ„μ  νšŒκ·€ (1.9.207~222) β€” λͺ¨λ‘ μœ μ§€
33
+
34
+ ### 7. stress-v168 β€” 15/15 PASS
35
+ - 1.9.223 (6): VERSION + μžλ™ 감지 + --apply + dry-run + handoff JSON + session close JSON
36
+ - μ„±λŠ₯ (2): cold_start avg 401ms / auto-complete 432ms
37
+ - λˆ„μ  νšŒκ·€ (7): 1.9.207~222
38
+
39
+ ### 8. μžλ™ release (85 λΌμš΄λ“œ main-push streak Β· 46 λΌμš΄λ“œ npm publish streak)
40
+
41
+ πŸ“₯ **μ‚¬μš©μž μš”μ²­ 라이프사이클 μ™„μ„±**: 1.9.207 (μˆ˜λ™ audit) β†’ 1.9.217 (마감 μžλ™) β†’ **1.9.223 (μžλ™ μ™„λ£Œ)** β€” 3 λΌμš΄λ“œ λˆ„μ 
42
+
43
+ ---
44
+
45
+ ## 1.9.222 β€” 2026-05-22
46
+
47
+ **πŸ›‘ session-resume --auto-fix μ•ˆμ „ 회볡 + handoff λ³Έλ¬Έ 비정상 μ’…λ£Œ μžλ™ λ…ΈμΆœ.**
48
+
49
+ ### 1. `leerness session-resume --auto-fix` β€” μ•ˆμ „ν•œ μžλ™ 회볡
50
+ - 1.9.220 detect / 1.9.221 MCP / 1.9.222 **auto-fix**
51
+ - `wakeup-missed` μ‹ ν˜Έ 감지 μ‹œ β†’ 30λΆ„+ μ§€λ‚œ pending wakeup μžλ™ `superseded`
52
+ - `state.supersededReason: 'auto-fix-1.9.222-abnormal-shutdown'`
53
+ - μ‚¬μš©μž/AIκ°€ μˆ˜λ™ 정리할 ν•„μš” 없이 λ‹€μŒ wakeup이 정상 λ™μž‘
54
+ - `--auto-fix` μ˜΅μ…˜μ΄ μ—†μœΌλ©΄ κΈ°μ‘΄ λ™μž‘ κ·ΈλŒ€λ‘œ (κ°μ§€λ§Œ, λ³€κ²½ X)
55
+
56
+ ### 2. handoff λ³Έλ¬Έ μžλ™ λ…ΈμΆœ β€” 비정상 μ’…λ£Œ μ„Ήμ…˜
57
+ - 1.9.220 ν—€λ“œλΌμΈ (`πŸ”Œ λΉ„μ •μƒμ’…λ£Œ <severity>`) + 1.9.222 **λ³Έλ¬Έ**
58
+ - `severity: high` β†’ `## 🚨 비정상 μ’…λ£Œ 감지 (1.9.220, severity: high)` λ³Έλ¬Έ μ„Ήμ…˜ μžλ™
59
+ - `severity: medium` β†’ `## ⚠ 비정상 μ’…λ£Œ 감지` λ³Έλ¬Έ μ„Ήμ…˜ μžλ™
60
+ - λ³Έλ¬Έ 좜λ ₯ μœ„μΉ˜: pre-wake-audit μ„Ήμ…˜ λ‹€μŒ, ν™œμ„± λ£° 이전
61
+ - top 3 μ‹ ν˜Έ + 재개 κ°€μ΄λ“œ + auto-fix κ°€μ΄λ“œ ν•œ λ²ˆμ— λ…ΈμΆœ
62
+
63
+ ### 3. CLAUDE.md / AGENTS.md 1.9.214~221 운영 κ°•ν™” 8 λΌμš΄λ“œ 톡합
64
+ - drift 차단을 μœ„ν•œ `## 운영 κ°•ν™” 8 λΌμš΄λ“œ (1.9.214~221)` μ„Ήμ…˜ λˆ„μ 
65
+ - 9 λΌμš΄λ“œ (drift 차단 / handoff ν—€λ“œλΌμΈ / MCP 5μ’… / session close / handoff JSON 4 톡합 / 80 λΌμš΄λ“œ / 비정상 μ’…λ£Œ / MCP 60 / auto-fix) λͺ¨λ‘ 단일 μ‹œμ•Ό λ…ΈμΆœ
66
+
67
+ ### 4. λˆ„μ  νšŒκ·€ (1.9.207~221) β€” λͺ¨λ‘ μœ μ§€
68
+
69
+ ### 5. stress-v167 β€” 14/14 PASS
70
+ - 1.9.222 (6): VERSION + auto-fix (격리) + auto-fix λ―Έμ§€μ • μ‹œ λ™μž‘ + handoff λ³Έλ¬Έ λ…ΈμΆœ + handoff 정상 μ‹œ λ―Έλ…ΈμΆœ + CLAUDE 8 λΌμš΄λ“œ μ„Ήμ…˜
71
+ - μ„±λŠ₯ (2): cold_start avg 408ms / MCP 60 도ꡬ 413ms
72
+ - λˆ„μ  νšŒκ·€ (6): 1.9.207~221
73
+
74
+ ### 6. μžλ™ release (84 λΌμš΄λ“œ main-push streak Β· 45 λΌμš΄λ“œ npm publish streak)
75
+
76
+ πŸ›‘ **비정상 μ’…λ£Œ μ‹œμŠ€ν…œ μ™„μ„±**: 1.9.220 detect β†’ 1.9.221 MCP β†’ 1.9.222 **auto-fix** + **λ³Έλ¬Έ μžλ™ λ…ΈμΆœ** (3 λΌμš΄λ“œ λˆ„μ )
77
+
78
+ ---
79
+
3
80
  ## 1.9.221 β€” 2026-05-22
4
81
 
5
82
  **πŸŽ‰ MCP 60 도ꡬ λ§ˆμΌμŠ€ν†€ + abnormalShutdown JSON 톡합 (handoff/session close).**
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.221-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v166-16%2F16-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-60_πŸŽ‰-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-151-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-83_rounds-success)]() [![goal-matrix](https://img.shields.io/badge/λͺ©μ _맀트릭슀-100%2F100_(5μΆ•)-brightgreen)]() [![mcp-60](https://img.shields.io/badge/πŸŽ‰_MCP-60_도ꡬ_λ§ˆμΌμŠ€ν†€-blueviolet)]() [![perf](https://img.shields.io/badge/cold_start-374ms-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.223-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v168-15%2F15-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-60_πŸŽ‰-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-153-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-85_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-401ms-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.221 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.223 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ πŸŽ‰ MCP 60 도ꡬ λ§ˆμΌμŠ€ν†€ Β· handoff JSON 4 톡합 ν•„λ“œ μ™„μ„± β•‘
17
+ β•‘ πŸ“₯ requests auto-complete Β· handoff JSON 5 톡합 ν•„λ“œ β•‘
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.221';
10
+ const VERSION = '1.9.223';
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
 
@@ -3055,6 +3147,7 @@ function requestsCmd(root, sub, ...rest) {
3055
3147
  }
3056
3148
 
3057
3149
  // 1.9.220: leerness session-resume β€” 비정상 μ’…λ£Œ 감지 + 자율 재개 κ°€μ΄λ“œ (μ‚¬μš©μž λͺ…μ‹œ)
3150
+ // 1.9.222: --auto-fix μ˜΅μ…˜ μΆ”κ°€ β€” μ•ˆμ „ν•œ μžλ™ 회볡 (wakeup supersede)
3058
3151
  function sessionResumeCmd(root) {
3059
3152
  root = absRoot(root);
3060
3153
  const isTty = process.stdout && process.stdout.isTTY;
@@ -3065,6 +3158,45 @@ function sessionResumeCmd(root) {
3065
3158
  const dim = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
3066
3159
 
3067
3160
  const result = _detectAbnormalShutdown(root);
3161
+
3162
+ // 1.9.222: --auto-fix β€” μ•ˆμ „ν•œ μ‹ ν˜Έλ³„ μžλ™ 회볡
3163
+ const autoFix = has('--auto-fix');
3164
+ if (autoFix && result.abnormalShutdown) {
3165
+ result.autoFixed = [];
3166
+ // 1) wakeup-missed β†’ 였래된 pending wakeup supersede (μ•ˆμ „: λ‹¨μˆœ μƒνƒœ λ³€κ²½)
3167
+ if (result.signals.some(s => s.kind === 'wakeup-missed')) {
3168
+ try {
3169
+ const state = _loadActiveWakeups(root);
3170
+ let fixed = 0;
3171
+ state.wakeups = state.wakeups.map(w => {
3172
+ if (w.status === 'pending') {
3173
+ const expectedMs = new Date(w.expectedFireAt).getTime();
3174
+ const deltaMin = Math.floor((Date.now() - expectedMs) / 60000);
3175
+ if (deltaMin > 30) {
3176
+ w.status = 'superseded';
3177
+ w.supersededReason = 'auto-fix-1.9.222-abnormal-shutdown';
3178
+ w.supersededAt = new Date().toISOString();
3179
+ fixed++;
3180
+ }
3181
+ }
3182
+ return w;
3183
+ });
3184
+ if (fixed > 0) {
3185
+ _writeActiveWakeups(root, state.wakeups);
3186
+ result.autoFixed.push({ kind: 'wakeup-missed', action: 'superseded', count: fixed });
3187
+ }
3188
+ } catch {}
3189
+ }
3190
+ // 2) auto-resume-plan-unused β†’ 60min+ λ―Έμ‚¬μš©μ€ μ•ˆλ‚΄λ§Œ (μžλ™ μ‚­μ œ X, μ‚¬μš©μžκ°€ κ²€ν† ν•΄μ•Ό 함)
3191
+ if (result.signals.some(s => s.kind === 'auto-resume-plan-unused')) {
3192
+ result.autoFixed.push({ kind: 'auto-resume-plan-unused', action: 'guidance-only', detail: 'leerness resume 으둜 적용 κ²€ν† ' });
3193
+ }
3194
+ // 3) in-progress-stale β†’ drop 결정은 μ‚¬μš©μž μ˜μ—­ (μžλ™ X)
3195
+ if (result.signals.some(s => s.kind === 'in-progress-stale')) {
3196
+ result.autoFixed.push({ kind: 'in-progress-stale', action: 'guidance-only', detail: 'leerness task list --status in-progress ν›„ μ‚¬μš©μž κ²°μ • ν•„μš”' });
3197
+ }
3198
+ }
3199
+
3068
3200
  if (has('--json')) { log(JSON.stringify(result, null, 2)); return; }
3069
3201
  log(cyan(`# leerness session-resume (1.9.220) β€” 비정상 μ’…λ£Œ 감지 + 자율 재개`));
3070
3202
  log(` detected at: ${result.detectedAt}`);
@@ -3093,6 +3225,18 @@ function sessionResumeCmd(root) {
3093
3225
  for (const g of result.resumeGuide) {
3094
3226
  log(` ${g}`);
3095
3227
  }
3228
+ // 1.9.222: --auto-fix κ²°κ³Ό λ…ΈμΆœ
3229
+ if (result.autoFixed && result.autoFixed.length > 0) {
3230
+ log('');
3231
+ log(grn(`## πŸ”§ auto-fix κ²°κ³Ό (${result.autoFixed.length})`));
3232
+ for (const af of result.autoFixed) {
3233
+ const icon = af.action === 'superseded' ? 'βœ“' : 'β„Ή';
3234
+ log(` ${icon} [${af.kind}] ${af.action}${af.count ? ` (count: ${af.count})` : ''}`);
3235
+ if (af.detail) log(dim(` ${af.detail}`));
3236
+ }
3237
+ } else if (autoFix) {
3238
+ log(dim(` (auto-fix 적용 λŒ€μƒ μ—†μŒ)`));
3239
+ }
3096
3240
  }
3097
3241
 
3098
3242
  // 1.9.208: leerness constraints <list|check|add> β€” ν”Œλž«νΌ/API μ œμ•½ 사전 체크 CLI (μ‚¬μš©μž λͺ…μ‹œ)
@@ -5671,6 +5815,16 @@ function handoff(root) {
5671
5815
  tracked: reqAudit.tracked ? reqAudit.tracked.length : 0,
5672
5816
  stale: reqAudit.stale ? reqAudit.stale.length : 0
5673
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 {}
5674
5828
  } catch {}
5675
5829
  try {
5676
5830
  const pwState = _loadPreWakeReport(root);
@@ -5860,9 +6014,14 @@ function handoff(root) {
5860
6014
  }
5861
6015
  } catch {}
5862
6016
  // 13) 1.9.207: μ‚¬μš©μž μš”μ²­ λˆ„λ½ 확인 절차 (μ‚¬μš©μž λͺ…μ‹œ) β€” λˆ„λ½ 후보 β‰₯ 1 μ‹œ ν—€λ“œλΌμΈ λ…ΈμΆœ
6017
+ // 1.9.223: delivered νŒ¨ν„΄ 후보 μ‹œ `πŸ“₯ μžλ™μ™„λ£Œκ°€λŠ₯ N건` μš°μ„  λ…ΈμΆœ (μˆ˜λ™ 정리 λΆ€λ‹΄ κ°μ†Œ)
5863
6018
  try {
5864
6019
  const audit = _auditUserRequests(root);
5865
- 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) {
5866
6025
  parts.push(`πŸ“₯ λ―Έλ‹΅ μš”μ²­ ${audit.missing.length}건`);
5867
6026
  } else if (audit.open > 0) {
5868
6027
  parts.push(`πŸ“₯ μš”μ²­ ${audit.open} (tracked)`);
@@ -5956,6 +6115,30 @@ function handoff(root) {
5956
6115
  }
5957
6116
  }
5958
6117
  } catch {}
6118
+
6119
+ // 1.9.222: 비정상 μ’…λ£Œ 감지 λ³Έλ¬Έ μžλ™ λ…ΈμΆœ (handoff 직후 β€” κ°€μž₯ λ¨Όμ € 봐야 함)
6120
+ // high/medium severity μ‹œ μ¦‰μ‹œ λ…ΈμΆœ, low/none 은 λ¬΄μ‹œ
6121
+ try {
6122
+ const ad = _detectAbnormalShutdown(root);
6123
+ if (ad.abnormalShutdown && (ad.severity === 'high' || ad.severity === 'medium')) {
6124
+ const isTty = process.stdout && process.stdout.isTTY;
6125
+ const cy3 = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
6126
+ const rd3 = s => isTty ? `\x1b[31m${s}\x1b[0m` : s;
6127
+ const yl3 = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
6128
+ const dm3 = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
6129
+ log('');
6130
+ const sevIcon = ad.severity === 'high' ? '🚨' : '⚠';
6131
+ const sevColor = ad.severity === 'high' ? rd3 : yl3;
6132
+ log(sevColor(`## ${sevIcon} 비정상 μ’…λ£Œ 감지 (1.9.220, severity: ${ad.severity})`));
6133
+ ad.signals.slice(0, 3).forEach(s => {
6134
+ const ic = s.severity === 'high' ? '🚨' : (s.severity === 'medium' ? '⚠' : 'β„Ή');
6135
+ log(` ${ic} [${s.kind}] ${s.detail}`);
6136
+ });
6137
+ log(dm3(` β†’ 재개 κ°€μ΄λ“œ: leerness session-resume`));
6138
+ log(dm3(` β†’ μžλ™ 회볡: leerness session-resume --auto-fix (1.9.222)`));
6139
+ }
6140
+ } catch {}
6141
+
5959
6142
  // 1.9.8: active rules μžλ™ λ…ΈμΆœ (λ§€ μ„Έμ…˜ μ‹œμž‘ μ‹œ AIμ—κ²Œ λ³΄μž„)
5960
6143
  const activeRules = readRules(root).filter(r => r.status === 'active');
5961
6144
  if (activeRules.length) {
@@ -9707,6 +9890,15 @@ function sessionClose(root, opts = {}) {
9707
9890
  tracked: reqAudit.tracked ? reqAudit.tracked.length : 0,
9708
9891
  stale: reqAudit.stale ? reqAudit.stale.length : 0
9709
9892
  };
9893
+ // 1.9.223: delivered νŒ¨ν„΄ μžλ™ 감지 톡합
9894
+ try {
9895
+ const delivered = _detectDeliveredRequests(root);
9896
+ jsonResult.deliveredRequests = {
9897
+ candidates: delivered.candidates.length,
9898
+ currentVersion: delivered.currentVersion,
9899
+ autoCompleteAvailable: delivered.candidates.length > 0
9900
+ };
9901
+ } catch {}
9710
9902
  } catch {}
9711
9903
  try {
9712
9904
  // 1.9.209: pre-wake-audit μžλ™ μ‹€ν–‰ + μ €μž₯ (sleep μ „ μžλ™ 점검)
@@ -9758,11 +9950,16 @@ function sessionClose(root, opts = {}) {
9758
9950
 
9759
9951
  log('');
9760
9952
  log(`## πŸ”š session close μžλ™ 톡합 보고 (1.9.217)`);
9761
- // 1.9.207
9953
+ // 1.9.207 + 1.9.223 (delivered νŒ¨ν„΄ μžλ™ ꢌμž₯)
9762
9954
  try {
9763
9955
  const reqAudit = _auditUserRequests(root);
9764
9956
  const missCnt = reqAudit.missing ? reqAudit.missing.length : 0;
9765
- if (missCnt > 0) {
9957
+ let delivered = { candidates: [] };
9958
+ try { delivered = _detectDeliveredRequests(root); } catch {}
9959
+ if (delivered.candidates && delivered.candidates.length > 0) {
9960
+ log(yel(` πŸ“₯ delivered νŒ¨ν„΄ ${delivered.candidates.length}건 (1.9.223) β€” μžλ™ μ™„λ£Œ κ°€λŠ₯`));
9961
+ log(dim(` β†’ leerness requests auto-complete --apply (μ•ˆμ „ 정리)`));
9962
+ } else if (missCnt > 0) {
9766
9963
  log(red(` ⚠ λ―Έλ‹΅ μ‚¬μš©μž μš”μ²­ ${missCnt}건 (task-log/plan/decisions λ§€μΉ­ μ•ˆ 됨)`));
9767
9964
  } else if (reqAudit.open > 0) {
9768
9965
  log(grn(` βœ“ μ‚¬μš©μž μš”μ²­ ${reqAudit.open}건 λͺ¨λ‘ tracked`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.221",
3
+ "version": "1.9.223",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",