leerness 1.9.202 β†’ 1.9.203

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,72 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.203 β€” 2026-05-22
4
+
5
+ **πŸ“‹ μžλ™ λΌμš΄λ“œ plan μžλ™ 정리 + `leerness resume` μ‹ κ·œ CLI (μ‚¬μš©μž λͺ…μ‹œ).**
6
+
7
+ ### μ‚¬μš©μž λͺ…μ‹œ
8
+ > *"μžλ™λͺ¨λ“œλŠ” λ°±κ·ΈλΌμš΄λ“œμ— λ‹€μŒ μž‘μ—…μ΄ μ‹œμž‘κ°€λŠ₯ν•œ μ˜ˆμƒ μ‹œκ°„μ„ μ„€μ •ν•΄μ„œ λ°±κ·ΈλΌμš΄λ“œμ—μ„œ μ•ŒλžŒ 트리거 같은걸 κ΅¬ν˜„ν•˜κ³  μΌμ–΄λ‚¬μ„λ•Œ ν•΄μ•Όν•˜λŠ” 일을 μ •λ¦¬ν•΄μ„œ 또 μ§„ν–‰ν•˜κ³  반볡으둜 ν•˜λŠ”κ±΄ μ–΄λ–¨κΉŒ"*
9
+
10
+ ### 1. `.harness/auto-resume-plan.json` μžλ™ μ €μž₯
11
+ - λΌμš΄λ“œ 마무리 (release sync-main μ‹œ npm publish μ™„λ£Œ ν›„) **μžλ™ μ €μž₯**
12
+ - plan ꡬ쑰:
13
+ - `savedAt`, `nextRoundVersion`, `expectedFireAt`, `intervalMin`
14
+ - `focus` (자유 ν…μŠ€νŠΈ)
15
+ - `contextSnapshot` (currentVersion, activeTaskId, activeTaskRequest, memorySurface, r0001Rule)
16
+ - `nextActions` (1.9.201 next-action-queue snapshot)
17
+ - ν•¨μˆ˜: `_loadAutoResumePlan` / `_writeAutoResumePlan` / `_buildAutoResumePlan` / `_autoResumePlanPath`
18
+
19
+ ### 2. `leerness resume` μ‹ κ·œ CLI
20
+ 일어났을 λ•Œ μ¦‰μ‹œ 무엇뢀터 ν• μ§€ ν•œλˆˆμ—:
21
+ ```
22
+ $ leerness resume
23
+ # πŸ”„ leerness resume (1.9.203 μžλ™ λΌμš΄λ“œ plan 적용)
24
+ πŸ“… plan μ €μž₯: 2026-05-22T00:10:10.797Z (15λΆ„ μ „)
25
+ ⏰ μ˜ˆμƒ fire: 2026-05-22T00:35:10.797Z (μ •μ‹œ)
26
+ 🎯 focus: λ‹€μŒ λΌμš΄λ“œ: handoff β†’ next-action take β†’ μ‚¬μš©μž λͺ…μ‹œ λ˜λŠ” 5μΆ• 맀트릭슀 보강
27
+
28
+ ## λ‹€μŒ λΌμš΄λ“œ: next after 1.9.203
29
+ ν˜„μž¬ 버전: 1.9.203
30
+ ν™œμ„± task: T-9999 β€” ...
31
+ memory: T2/D15/R1/L3
32
+ λ£°: 25λΆ„ 간격 (μ‚¬μš©μž λͺ…μ‹œ, R-0001)
33
+
34
+ ## 사전 μ •λ¦¬λœ next-actions (3건)
35
+ πŸ›‘ lessons.md "X" κ΄€λ ¨ ...
36
+ `leerness lessons --auto --path .`
37
+ ...
38
+ β†’ μ¦‰μ‹œ task μΆ”κ°€: leerness next-action take
39
+
40
+ βœ“ resume μ€€λΉ„ μ™„λ£Œ β€” ꢌμž₯: leerness handoff . λ˜λŠ” leerness next-action take
41
+ ```
42
+ - `--json` μ˜΅μ…˜ (μžλ™ν™”/MCP)
43
+
44
+ ### 3. handoff μžλ™ plan μ•Œλ¦Ό
45
+ handoff μ§„μž… μ‹œ plan 쑴재 μ‹œ μžλ™ λ…ΈμΆœ:
46
+ ```
47
+ ## πŸ“‹ auto-resume-plan λ‘œλ“œ (1.9.203) β€” 15λΆ„ μ „ μ €μž₯ (μ •μ‹œ)
48
+ 🎯 focus: λ‹€μŒ λΌμš΄λ“œ: ...
49
+ πŸ“¦ λ‹€μŒ 버전: next after 1.9.203
50
+ πŸ“₯ 사전 μ •λ¦¬λœ actions: 3건 β†’ leerness next-action take
51
+ β†’ 상세: leerness resume
52
+ ```
53
+ 끄기: `LEERNESS_NO_RESUME_PLAN=1`
54
+
55
+ ### 4. 자율 λͺ¨λ“œ cycle μ™„μ„±
56
+ - λΌμš΄λ“œ 마무리 β†’ plan μžλ™ μ €μž₯ β†’ ScheduleWakeup 25λΆ„ β†’ wakeup β†’ handoff plan μžλ™ λ‘œλ“œ β†’ resume / next-action take β†’ μ¦‰μ‹œ μ§„μž…
57
+
58
+ ### 5. λˆ„μ  νšŒκ·€ (1.9.197~202) β€” λͺ¨λ‘ μœ μ§€
59
+
60
+ ### 6. stress-v148 β€” 17/17 PASS
61
+ - auto-resume-plan 5 + resume CLI 3 + handoff 톡합 + μ„±λŠ₯ 2 + λˆ„μ  νšŒκ·€ 7
62
+ - μ„±λŠ₯: --version cold start avg **464 ms** Β· MCP 54 도ꡬ **449 ms**
63
+
64
+ ### 7. μžλ™ release 흐름
65
+ - main μžλ™ push **65 λΌμš΄λ“œ 연속** (1.9.140~203)
66
+ - npm publish μžλ™ (1.9.178~)
67
+
68
+ ---
69
+
3
70
  ## 1.9.202 β€” 2026-05-22
4
71
 
5
72
  **🌐 CμΆ• (곡식 ν‘œμ€€ μŠ€ν‚¬ μžλ™ ν™œμš©) 9.0/10 β†’ 9.5/10 보강 β€” matched skill μ„€μΉ˜ μƒνƒœ ν‘œμ‹œ + leerness skill install-top.**
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.202-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v147-15%2F15-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-132-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-64_rounds-success)]() [![goal-matrix](https://img.shields.io/badge/λͺ©μ _맀트릭슀-98%2F100_(5μΆ•)-brightgreen)]() [![skill-install-top](https://img.shields.io/badge/skill-install--top_μ¦‰μ‹œ-success)]() [![perf](https://img.shields.io/badge/cold_start-457ms-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.203-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v148-17%2F17-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-133-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-65_rounds-success)]() [![goal-matrix](https://img.shields.io/badge/λͺ©μ _맀트릭슀-98%2F100_(5μΆ•)-brightgreen)]() [![auto-resume](https://img.shields.io/badge/auto--resume--plan-λΌμš΄λ“œ_마무리_μžλ™-success)]() [![perf](https://img.shields.io/badge/cold_start-464ms-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.202 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.203 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ 🌐 skill install-top + μ„€μΉ˜ μƒνƒœ Β· 맀트릭슀 98/100 Β· AΒ·DΒ·E 10/10 β•‘
17
+ β•‘ πŸ“‹ auto-resume-plan + leerness resume Β· 자율 cycle μ™„μ„± β•‘
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.202';
10
+ const VERSION = '1.9.203';
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 처리).
@@ -1733,6 +1733,115 @@ async function skillAutoCacheCmd(root, sub) {
1733
1733
  if (c.expired) log(` β†’ κ°±μ‹ : leerness skill auto-cache refresh`);
1734
1734
  }
1735
1735
 
1736
+ // 1.9.203: μžλ™ λΌμš΄λ“œ plan 정리 β€” μ‚¬μš©μž λͺ…μ‹œ
1737
+ // "λ°±κ·ΈλΌμš΄λ“œμ— λ‹€μŒ μž‘μ—…μ΄ μ‹œμž‘κ°€λŠ₯ν•œ μ˜ˆμƒ μ‹œκ°„ + μΌμ–΄λ‚¬μ„λ•Œ ν•΄μ•Όν•˜λŠ” 일을 정리"
1738
+ // λΌμš΄λ“œ 마무리 μ‹œ .harness/auto-resume-plan.json μžλ™ μ €μž₯ β†’ λ‹€μŒ wakeup μ‹œ μ¦‰μ‹œ μ‹€ν–‰ κ°€λŠ₯
1739
+ // plan ꡬ쑰: { savedAt, nextRoundVersion, expectedFireAt, focus, contextSnapshot, nextActions }
1740
+ function _autoResumePlanPath(root) { return path.join(root, '.harness', 'auto-resume-plan.json'); }
1741
+ function _loadAutoResumePlan(root) {
1742
+ try {
1743
+ const fp = _autoResumePlanPath(root);
1744
+ if (!exists(fp)) return null;
1745
+ const j = JSON.parse(read(fp));
1746
+ if (!j.savedAt) return null;
1747
+ const ageMs = Date.now() - new Date(j.savedAt).getTime();
1748
+ const expiredMs = j.expectedFireAt ? Date.now() - new Date(j.expectedFireAt).getTime() : 0;
1749
+ return { ...j, ageMs, ageMin: Math.floor(ageMs / 60000), elapsedFromExpected: Math.floor(expiredMs / 60000) };
1750
+ } catch { return null; }
1751
+ }
1752
+ function _writeAutoResumePlan(root, plan) {
1753
+ try {
1754
+ mkdirp(path.join(root, '.harness'));
1755
+ const payload = { savedAt: new Date().toISOString(), ...plan };
1756
+ writeUtf8(_autoResumePlanPath(root), JSON.stringify(payload, null, 2));
1757
+ return true;
1758
+ } catch { return false; }
1759
+ }
1760
+ // ν˜„μž¬ μ›Œν¬μŠ€νŽ˜μ΄μŠ€ μƒνƒœμ—μ„œ λ‹€μŒ λΌμš΄λ“œ plan μžλ™ 생성 (ν˜„μž¬ task / matrix / handoff gap / next-action queue κ²°ν•©)
1761
+ function _buildAutoResumePlan(root, opts) {
1762
+ opts = opts || {};
1763
+ const intervalMin = opts.intervalMin || 25; // R-0001 영ꡬ 룰
1764
+ const now = Date.now();
1765
+ const expectedFireAt = new Date(now + intervalMin * 60 * 1000).toISOString();
1766
+ // ν˜„μž¬ in-progress task
1767
+ const rows = readProgressRows(root);
1768
+ const active = rows.find(r => r.status === 'in-progress' || r.status === '[μ§„ν–‰]');
1769
+ // ν˜„μž¬ VERSION
1770
+ const currentVersion = VERSION;
1771
+ // next-action queue snapshot
1772
+ const queueState = _loadNextActionQueue(root);
1773
+ const queueTop = queueState.queue.length > 0 ? queueState.queue[queueState.queue.length - 1] : null;
1774
+ // memory surface counts
1775
+ const memorySurface = {
1776
+ tasksInProgress: rows.filter(r => r.status === 'in-progress').length,
1777
+ decisions: exists(decisionsPath(root)) ? (read(decisionsPath(root)).match(/^### \d{4}-\d{2}-\d{2}/gm) || []).length : 0,
1778
+ rulesActive: readRules(root).filter(r => r.status === 'active').length,
1779
+ lessons: exists(lessonsPath(root)) ? (read(lessonsPath(root)).match(/^### \d{4}-\d{2}-\d{2}/gm) || []).length : 0
1780
+ };
1781
+ return {
1782
+ nextRoundVersion: opts.nextRoundVersion || `next after ${currentVersion}`,
1783
+ expectedFireAt,
1784
+ intervalMin,
1785
+ focus: opts.focus || (active ? `${active.id} β€” ${(active.request || '').slice(0, 80)}` : 'μ‚¬μš©μž λͺ…μ‹œ λŒ€κΈ° λ˜λŠ” 5μΆ• 맀트릭슀 보강'),
1786
+ contextSnapshot: {
1787
+ currentVersion,
1788
+ activeTaskId: active ? active.id : null,
1789
+ activeTaskRequest: active ? (active.request || '').slice(0, 200) : null,
1790
+ memorySurface,
1791
+ r0001Rule: '25λΆ„ 간격 (μ‚¬μš©μž λͺ…μ‹œ, R-0001)'
1792
+ },
1793
+ nextActions: (queueState.queue || []).slice(-3).map(a => ({ icon: a.icon, title: a.title, command: a.command || null })),
1794
+ note: opts.note || 'λ‹€μŒ wakeup μ‹œ leerness resume λ˜λŠ” leerness handoff μ‹€ν–‰ β†’ plan μžλ™ 적용'
1795
+ };
1796
+ }
1797
+
1798
+ // 1.9.203: leerness resume μ‹ κ·œ CLI β€” auto-resume-plan 읽고 μ¦‰μ‹œ μ§„ν–‰ν•  일 μ•ˆλ‚΄
1799
+ function resumeCmd(root) {
1800
+ root = absRoot(root);
1801
+ const plan = _loadAutoResumePlan(root);
1802
+ if (!plan) {
1803
+ log(`# leerness resume (1.9.203)`);
1804
+ log(` (auto-resume-plan μ—†μŒ β€” 첫 λΌμš΄λ“œ)`);
1805
+ log(` β†’ leerness handoff . 둜 μ‹œμž‘`);
1806
+ return;
1807
+ }
1808
+ if (has('--json')) { log(JSON.stringify(plan, null, 2)); return; }
1809
+ const isTty = process.stdout && process.stdout.isTTY;
1810
+ const cyan = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
1811
+ const grn = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
1812
+ const yel = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
1813
+ const dim = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
1814
+ log(cyan(`# πŸ”„ leerness resume (1.9.203 μžλ™ λΌμš΄λ“œ plan 적용)`));
1815
+ log('');
1816
+ log(` πŸ“… plan μ €μž₯: ${plan.savedAt} (${plan.ageMin}λΆ„ μ „)`);
1817
+ log(` ⏰ μ˜ˆμƒ fire: ${plan.expectedFireAt}` + (plan.elapsedFromExpected > 0 ? yel(` (${plan.elapsedFromExpected}λΆ„ μ§€μ—°)`) : ' (μ •μ‹œ)'));
1818
+ log(` 🎯 focus: ${plan.focus}`);
1819
+ log('');
1820
+ log(grn(`## λ‹€μŒ λΌμš΄λ“œ: ${plan.nextRoundVersion}`));
1821
+ if (plan.contextSnapshot) {
1822
+ const c = plan.contextSnapshot;
1823
+ log(` ν˜„μž¬ 버전: ${c.currentVersion}`);
1824
+ if (c.activeTaskId) log(` ν™œμ„± task: ${c.activeTaskId} β€” ${(c.activeTaskRequest || '').slice(0, 70)}`);
1825
+ const m = c.memorySurface || {};
1826
+ log(` memory: T${m.tasksInProgress || 0}/D${m.decisions || 0}/R${m.rulesActive || 0}/L${m.lessons || 0}`);
1827
+ log(` λ£°: ${c.r0001Rule || '-'}`);
1828
+ }
1829
+ if (plan.nextActions && plan.nextActions.length > 0) {
1830
+ log('');
1831
+ log(grn(`## 사전 μ •λ¦¬λœ next-actions (${plan.nextActions.length}건)`));
1832
+ for (const a of plan.nextActions) {
1833
+ log(` ${a.icon || 'β€’'} ${a.title}`);
1834
+ if (a.command) log(dim(` \`${a.command}\``));
1835
+ }
1836
+ log('');
1837
+ log(dim(` β†’ μ¦‰μ‹œ task μΆ”κ°€: leerness next-action take`));
1838
+ }
1839
+ log('');
1840
+ log(dim(plan.note || ''));
1841
+ log('');
1842
+ log(grn(`βœ“ resume μ€€λΉ„ μ™„λ£Œ β€” ꢌμž₯: leerness handoff . λ˜λŠ” leerness next-action take`));
1843
+ }
1844
+
1736
1845
  // 1.9.199: wakeup miss μžλ™ 회볡 κ°•ν™” β€” last-handoff timestamp 기둝 + 25min κΈ°λŒ€ interval μ •λ°€ μΈ‘μ •
1737
1846
  // μ‚¬μš©μž λͺ…μ‹œ (1.9.198 후속): "22:01에 λΌμš΄λ“œμ— μžλ™μ§„μž…λ˜μ§€μ•Šμ•˜μ–΄"
1738
1847
  // 1.9.196 task-log mtime 기반 detector λŠ” task-log κ°€ 자주 μ•ˆ μ—…λ°μ΄νŠΈλ˜λ©΄ false positive.
@@ -3535,6 +3644,9 @@ function handoff(root) {
3535
3644
  let _priorHandoffGap = { hasLast: false };
3536
3645
  try { _priorHandoffGap = _getLastHandoffGap(root); } catch {}
3537
3646
  try { _recordLastHandoff(root); } catch {}
3647
+ // 1.9.203: auto-resume-plan μžλ™ λ‘œλ“œ (μ‚¬μš©μž λͺ…μ‹œ β€” μžλ™ λͺ¨λ“œ μ•ŒλžŒ 트리거)
3648
+ let _autoResumePlan = null;
3649
+ try { _autoResumePlan = _loadAutoResumePlan(root); } catch {}
3538
3650
  // 1.9.96: --json μ˜΅μ…˜ (ꡬ쑰화 좜λ ₯, MCP 톡합 / μ™ΈλΆ€ AI μΉœν™”)
3539
3651
  if (has('--json')) {
3540
3652
  const result = {
@@ -4049,6 +4161,26 @@ function handoff(root) {
4049
4161
  }
4050
4162
  } catch {}
4051
4163
  }
4164
+ // 1.9.203: auto-resume-plan μ•Œλ¦Ό β€” λΌμš΄λ“œ 마무리 μ‹œ μ €μž₯된 plan μžλ™ λ…ΈμΆœ
4165
+ // μ‚¬μš©μž λͺ…μ‹œ: "λ°±κ·ΈλΌμš΄λ“œμ— λ‹€μŒ μž‘μ—…μ΄ μ‹œμž‘κ°€λŠ₯ν•œ μ˜ˆμƒ μ‹œκ°„ + μΌμ–΄λ‚¬μ„λ•Œ ν•΄μ•Όν•˜λŠ” 일을 정리"
4166
+ if (_autoResumePlan && !has('--quiet') && process.env.LEERNESS_NO_RESUME_PLAN !== '1') {
4167
+ try {
4168
+ const isTty = process.stdout && process.stdout.isTTY;
4169
+ const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
4170
+ const dim = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
4171
+ const yel = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
4172
+ const elapsed = _autoResumePlan.elapsedFromExpected || 0;
4173
+ const status = elapsed > 5 ? yel(`(${elapsed}λΆ„ μ§€μ—°)`) : (elapsed > -5 ? '(μ •μ‹œ)' : dim(`(${Math.abs(elapsed)}λΆ„ 일찍)`));
4174
+ log(cy(`## πŸ“‹ auto-resume-plan λ‘œλ“œ (1.9.203) β€” ${_autoResumePlan.ageMin}λΆ„ μ „ μ €μž₯ ${status}`));
4175
+ log(dim(` 🎯 focus: ${_autoResumePlan.focus || '(none)'}`));
4176
+ log(dim(` πŸ“¦ λ‹€μŒ 버전: ${_autoResumePlan.nextRoundVersion || '(none)'}`));
4177
+ if (_autoResumePlan.nextActions && _autoResumePlan.nextActions.length > 0) {
4178
+ log(dim(` πŸ“₯ 사전 μ •λ¦¬λœ actions: ${_autoResumePlan.nextActions.length}건 β†’ leerness next-action take`));
4179
+ }
4180
+ log(dim(` β†’ 상세: leerness resume`));
4181
+ log('');
4182
+ } catch {}
4183
+ }
4052
4184
  // 1.9.196/199: ScheduleWakeup miss detector β€” 자율 λͺ¨λ“œ μ‚¬μš©μž λͺ…μ‹œ "λͺ»μΌμ–΄λ‚˜λŠ” 경우 μ’…μ’… 있음"
4053
4185
  // 1.9.196: task-log mtime 기반 (60min+) β€” false positive κ°€λŠ₯
4054
4186
  // 1.9.199: last-handoff.json μ •λ°€ μΈ‘μ • β€” R-0001 λ£° (25min) λŒ€λΉ„ gap μΈ‘μ •
@@ -9130,6 +9262,16 @@ function _publishToNpm(root, opts = {}) {
9130
9262
  if (pubR.status === 0) {
9131
9263
  ok(`npm publish μ™„λ£Œ: ${pkgName}@${pkgVersion}`);
9132
9264
  try { _recordRun(root, { kind: 'npm_publish', package: pkgName, version: pkgVersion, dryRun: !!opts.dryRun, ok: true }); } catch {}
9265
+ // 1.9.203: λΌμš΄λ“œ 마무리 μ‹œ λ‹€μŒ λΌμš΄λ“œ plan μžλ™ μ €μž₯
9266
+ try {
9267
+ const plan = _buildAutoResumePlan(root, {
9268
+ nextRoundVersion: `next after ${pkgVersion}`,
9269
+ focus: 'λ‹€μŒ λΌμš΄λ“œ: handoff β†’ next-action take β†’ μ‚¬μš©μž λͺ…μ‹œ λ˜λŠ” 5μΆ• 맀트릭슀 보강',
9270
+ intervalMin: 25,
9271
+ note: `λ‹€μŒ wakeup μ‹œ: 1) leerness resume . 으둜 plan 확인 2) leerness handoff . μ‹€ν–‰ 3) leerness next-action take 둜 μ¦‰μ‹œ task μ§„μž…`
9272
+ });
9273
+ _writeAutoResumePlan(root, plan);
9274
+ } catch {}
9133
9275
  } else {
9134
9276
  const errOut = (pubR.stderr || pubR.stdout || '').slice(-400);
9135
9277
  if (/EPUBLISHCONFLICT|already exists|cannot publish over/i.test(errOut)) {
@@ -14965,6 +15107,8 @@ async function main() {
14965
15107
  if (cmd === 'roadmap') return roadmapCmd(args[1] || process.cwd());
14966
15108
  // 1.9.201: next-action queue CLI (EμΆ• 9.5β†’10)
14967
15109
  if (cmd === 'next-action') return nextActionCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
15110
+ // 1.9.203: leerness resume β€” auto-resume-plan 읽고 λ‹€μŒ λΌμš΄λ“œ μ¦‰μ‹œ μ•ˆλ‚΄ (μ‚¬μš©μž λͺ…μ‹œ)
15111
+ if (cmd === 'resume') return resumeCmd(arg('--path', process.cwd()));
14968
15112
  if (cmd === 'rule' && args[1] === 'add') return ruleAdd(arg('--path', process.cwd()), args.slice(2).filter(x => !x.startsWith('-')).join(' '));
14969
15113
  if (cmd === 'rule' && args[1] === 'list') return ruleList(arg('--path', process.cwd()));
14970
15114
  if (cmd === 'rule' && args[1] === 'remove') return ruleRemove(arg('--path', process.cwd()), args[2]);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.202",
3
+ "version": "1.9.203",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",