leerness 1.9.203 → 1.9.205

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,87 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.205 — 2026-05-22
4
+
5
+ **⚡ ScheduleWakeup 사용자 요청 우선 갱신 (사용자 명시) — wakeup miss 실측 후속.**
6
+
7
+ ### 사용자 명시 + 보고
8
+ > *"자동모드로 진행할때, 예정된 알람이전에 사용자 요청이 들어오면 백그라운드의 알람을 종료후 다시 갱신하는 방식은 어떤지"*
9
+ > *"ScheduleWakeup 10:51 KST 가 지났어"* — 사용자가 명시 보고한 wakeup miss 사례
10
+
11
+ ### 1. `.harness/active-wakeups.json` 등록 추적
12
+ - release sync-main 마무리 시 다음 wakeup 자동 등록
13
+ - 구조: `{ expectedFireAt, intervalMin, source, status: pending/fired/missed/superseded, registeredAt }`
14
+ - 최근 20개 유지
15
+ - 함수: `_loadActiveWakeups` / `_writeActiveWakeups` / `_recordWakeup` / `_activeWakeupsPath`
16
+
17
+ ### 2. handoff 진입 시 wakeup 상태 분석 (`_analyzeWakeupStatus`)
18
+ | kind | 조건 | 알림 |
19
+ |---|---|---|
20
+ | `early` | delta < -5min (미래) | `## ⚡ 사용자 조기 진입 감지` |
21
+ | `on-time` | -5 ~ +5min | `✓ ScheduleWakeup 정시 진입` |
22
+ | `late` | +5 ~ +30min | (조용히) |
23
+ | `missed` | > +30min | `## ⏰ ScheduleWakeup miss 확정` + status 자동 갱신 |
24
+
25
+ ### 3. 조기 진입 시 자동 갱신 가이드
26
+ 사용자가 wakeup 예정 시각 이전 진입 → "라운드 마무리 시 새 wakeup 등록 (이전 자동 superseded)" 안내
27
+
28
+ ### 4. miss 자동 status 갱신
29
+ fire 시간 지남 + 30분 이상 → status `pending` → `missed` 자동 업데이트 → 통계 누적
30
+
31
+ ### 5. 누적 회귀 (1.9.199~204) — 모두 유지
32
+
33
+ ### 6. stress-v150 — 16/16 PASS
34
+ - 등록 추적 (3) + 상태 분석 (2) + 임시 워크스페이스 통합 (2) + 성능 (2) + 누적 (7)
35
+ - 성능: --version cold start avg **464 ms** · MCP 54 도구 **460 ms**
36
+
37
+ ### 7. 자동 release
38
+ - main 자동 push **67 라운드 연속** (1.9.140~205)
39
+ - npm publish 자동 (1.9.178~)
40
+
41
+ ---
42
+
43
+ ## 1.9.204 — 2026-05-22
44
+
45
+ **⏰ timezone 보강 + 🔄 auto-loop 활성 라벨 (사용자 명시 2종).**
46
+
47
+ ### 사용자 명시
48
+ > 1. *"시간관련 기능 등은 해당 국가의 시간대를 고려해서 작업이 될 수 있는지"*
49
+ > 2. *"자동으로 깨어나서 작업하는 모드도 활성룰에 표시되어야 할 것 같은데, 활성 룰 에 표시되는 기능 점검"*
50
+
51
+ ### 1. timezone 시스템 (Intl.DateTimeFormat 기반, 의존성 0)
52
+ - `_getLocalTz()` — `process.env.LEERNESS_TZ` → 시스템 timezone → `Asia/Seoul` fallback
53
+ - `_formatLocal(iso, opts)` — ISO UTC → 사용자 local time (예: `2026-05-22 10:13 KST`)
54
+ - 단축 라벨: KST / JST / UTC / 자동 추출
55
+ - **저장은 UTC ISO 유지** (이식성/일관성), **display 만 local time 변환**
56
+
57
+ ### 2. resume CLI local time 표시
58
+ ```
59
+ $ leerness resume
60
+ 📅 plan 저장: 2026-05-22 09:48 KST (29분 전)
61
+ ⏰ 예상 fire: 2026-05-22 10:13 KST (정시)
62
+ ```
63
+ 이전: `2026-05-22T00:48:23.298Z` (한국 사용자 +9 머릿속 변환 필요)
64
+ 이후: `2026-05-22 09:48 KST` (즉시 인지 가능)
65
+
66
+ ### 3. 자동 모드 활성 라벨 (헤드라인 12번째)
67
+ ```
68
+ 📊 헤드라인 (1.9.81/93/113/152/162/192/197/204): ... · 🔄 auto-loop 25min · ...
69
+ ```
70
+ - `_getAutoLoopRule(root)` — `R-XXXX [every-round]` 활성 룰 자동 감지
71
+ - 룰 텍스트에서 `25분` 또는 `1500초` 패턴 추출 → 분 단위 표시
72
+
73
+ ### 4. 누적 회귀 (1.9.198~203) — 모두 유지
74
+
75
+ ### 5. stress-v149 — 16/16 PASS
76
+ - timezone (3) + resume display (1) + headline label (2) + 실 검증 (1) + 성능 (2) + 누적 (7)
77
+ - 성능: --version cold start avg **471 ms** · MCP 54 도구 **438 ms**
78
+
79
+ ### 6. 자동 release 흐름
80
+ - main 자동 push **66 라운드 연속** (1.9.140~204)
81
+ - npm publish 자동 (1.9.178~)
82
+
83
+ ---
84
+
3
85
  ## 1.9.203 — 2026-05-22
4
86
 
5
87
  **📋 자동 라운드 plan 자동 정리 + `leerness resume` 신규 CLI (사용자 명시).**
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.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)]()
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.205-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v150-16%2F16-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-135-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-67_rounds-success)]() [![goal-matrix](https://img.shields.io/badge/목적_매트릭스-98%2F100_(5축)-brightgreen)]() [![wakeup-track](https://img.shields.io/badge/wakeup-early%2Fmissed_감지-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.203 AI Agent Reliability Harness + Sandbox ║
15
+ ║ v1.9.205 AI Agent Reliability Harness + Sandbox ║
16
16
  ║ verify · remember · orchestrate · audit · sandbox · drift ║
17
- 📋 auto-resume-plan + leerness resume · 자율 cycle 완성
17
+ wakeup early/missed 감지 + 자동 갱신 가이드 (사용자 명시)
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.203';
10
+ const VERSION = '1.9.205';
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 처리).
@@ -130,6 +130,52 @@ function append(p, s) { mkdirp(path.dirname(p)); fs.appendFileSync(p, s, 'utf8')
130
130
  function rel(root, p) { return path.relative(root, p).replace(/\\/g, '/') || '.'; }
131
131
  function today() { return new Date().toISOString().slice(0, 10); }
132
132
  function now() { return new Date().toISOString(); }
133
+ // 1.9.204: timezone 보강 (사용자 명시)
134
+ // ISO UTC timestamp 저장은 유지 (이식성/일관성) → display 시 사용자 local time 변환
135
+ // 환경변수: LEERNESS_TZ (default = 시스템 timezone, fallback: 'Asia/Seoul')
136
+ function _getLocalTz() {
137
+ if (process.env.LEERNESS_TZ) return process.env.LEERNESS_TZ;
138
+ try {
139
+ const sys = Intl.DateTimeFormat().resolvedOptions().timeZone;
140
+ if (sys && sys !== 'UTC') return sys;
141
+ } catch {}
142
+ return 'Asia/Seoul';
143
+ }
144
+ // ISO timestamp → local time 표시 (예: "2026-05-22 10:13 KST")
145
+ function _formatLocal(iso, opts) {
146
+ if (!iso) return '?';
147
+ opts = opts || {};
148
+ const tz = opts.tz || _getLocalTz();
149
+ try {
150
+ const d = typeof iso === 'string' ? new Date(iso) : iso;
151
+ if (isNaN(d.getTime())) return String(iso);
152
+ const fmt = new Intl.DateTimeFormat('en-CA', {
153
+ timeZone: tz,
154
+ year: 'numeric', month: '2-digit', day: '2-digit',
155
+ hour: '2-digit', minute: '2-digit',
156
+ hour12: false
157
+ });
158
+ const parts = fmt.formatToParts(d);
159
+ const get = (t) => (parts.find(p => p.type === t) || {}).value || '';
160
+ const date = `${get('year')}-${get('month')}-${get('day')}`;
161
+ const time = `${get('hour')}:${get('minute')}`;
162
+ // tz 약어 (KST/JST/UTC 등) — 간단 추출
163
+ const tzShort = tz === 'Asia/Seoul' ? 'KST' : tz === 'Asia/Tokyo' ? 'JST' : tz === 'UTC' ? 'UTC' : tz.split('/').pop().slice(0, 3);
164
+ return opts.dateOnly ? date : `${date} ${time} ${tzShort}`;
165
+ } catch { return String(iso); }
166
+ }
167
+ // 자동 모드 활성 여부 (R-XXXX every-round 룰 존재 시 true)
168
+ function _isAutoLoopActive(root) {
169
+ try {
170
+ const rules = readRules(root);
171
+ return rules.some(r => r.status === 'active' && /every-round|every-session/i.test(r.trigger || ''));
172
+ } catch { return false; }
173
+ }
174
+ function _getAutoLoopRule(root) {
175
+ try {
176
+ return readRules(root).find(r => r.status === 'active' && /every-round/i.test(r.trigger || '')) || null;
177
+ } catch { return null; }
178
+ }
133
179
  function arg(name, def = null) { const i = process.argv.indexOf(name); return i >= 0 ? (process.argv[i + 1] || true) : def; }
134
180
  function has(name) { return process.argv.includes(name); }
135
181
  function nonFlagArgs() {
@@ -1733,6 +1779,60 @@ async function skillAutoCacheCmd(root, sub) {
1733
1779
  if (c.expired) log(` → 갱신: leerness skill auto-cache refresh`);
1734
1780
  }
1735
1781
 
1782
+ // 1.9.205: ScheduleWakeup 등록 추적 (사용자 명시)
1783
+ // "예정된 알람이전에 사용자 요청이 들어오면 백그라운드의 알람을 종료후 다시 갱신"
1784
+ // 라운드 마무리 시 등록된 wakeup id/expectedFireAt 을 .harness/active-wakeups.json 에 기록 →
1785
+ // handoff/resume 진입 시 expected fire 가 과거이면 "wakeup miss 또는 사용자 조기 진입" 감지 →
1786
+ // 메인 에이전트가 새 wakeup 호출하라는 명확한 안내
1787
+ function _activeWakeupsPath(root) { return path.join(root, '.harness', 'active-wakeups.json'); }
1788
+ function _loadActiveWakeups(root) {
1789
+ try {
1790
+ const fp = _activeWakeupsPath(root);
1791
+ if (!exists(fp)) return { wakeups: [] };
1792
+ const j = JSON.parse(read(fp));
1793
+ return { wakeups: Array.isArray(j.wakeups) ? j.wakeups : [] };
1794
+ } catch { return { wakeups: [] }; }
1795
+ }
1796
+ function _writeActiveWakeups(root, wakeups) {
1797
+ try {
1798
+ mkdirp(path.join(root, '.harness'));
1799
+ writeUtf8(_activeWakeupsPath(root), JSON.stringify({ wakeups, updatedAt: new Date().toISOString() }, null, 2));
1800
+ return true;
1801
+ } catch { return false; }
1802
+ }
1803
+ function _recordWakeup(root, entry) {
1804
+ const state = _loadActiveWakeups(root);
1805
+ // entry: { expectedFireAt, intervalMin, source, status: 'pending'|'fired'|'missed'|'superseded' }
1806
+ const e = { ...entry, registeredAt: new Date().toISOString() };
1807
+ // 같은 expectedFireAt 중복 시 replace
1808
+ state.wakeups = state.wakeups.filter(w => w.expectedFireAt !== e.expectedFireAt);
1809
+ state.wakeups.push(e);
1810
+ // 최근 20개만 유지
1811
+ if (state.wakeups.length > 20) state.wakeups = state.wakeups.slice(-20);
1812
+ _writeActiveWakeups(root, state.wakeups);
1813
+ return e;
1814
+ }
1815
+ // wakeup miss / 사용자 조기 진입 감지 — 활성 wakeup 중 가장 가까운 미래/과거 fire 분석
1816
+ function _analyzeWakeupStatus(root) {
1817
+ const state = _loadActiveWakeups(root);
1818
+ const pending = state.wakeups.filter(w => w.status === 'pending');
1819
+ if (pending.length === 0) return { hasWakeup: false };
1820
+ // 가장 최근 등록된 pending wakeup
1821
+ const latest = pending.sort((a, b) => new Date(b.registeredAt).getTime() - new Date(a.registeredAt).getTime())[0];
1822
+ const now = Date.now();
1823
+ const expectedMs = new Date(latest.expectedFireAt).getTime();
1824
+ const deltaMin = Math.floor((now - expectedMs) / 60000);
1825
+ // deltaMin < 0 → 아직 fire 시간 전 (사용자 조기 진입)
1826
+ // deltaMin >= 0 → fire 시간 지남 (정시 또는 miss)
1827
+ // |deltaMin| < 5 → 정시 부근
1828
+ let kind;
1829
+ if (deltaMin < -5) kind = 'early'; // 사용자 조기 진입
1830
+ else if (deltaMin > 30) kind = 'missed'; // wakeup miss
1831
+ else if (deltaMin > 5) kind = 'late'; // 약간 지연
1832
+ else kind = 'on-time';
1833
+ return { hasWakeup: true, latest, deltaMin, kind, pending: pending.length };
1834
+ }
1835
+
1736
1836
  // 1.9.203: 자동 라운드 plan 정리 — 사용자 명시
1737
1837
  // "백그라운드에 다음 작업이 시작가능한 예상 시간 + 일어났을때 해야하는 일을 정리"
1738
1838
  // 라운드 마무리 시 .harness/auto-resume-plan.json 자동 저장 → 다음 wakeup 시 즉시 실행 가능
@@ -1813,8 +1913,8 @@ function resumeCmd(root) {
1813
1913
  const dim = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
1814
1914
  log(cyan(`# 🔄 leerness resume (1.9.203 자동 라운드 plan 적용)`));
1815
1915
  log('');
1816
- log(` 📅 plan 저장: ${plan.savedAt} (${plan.ageMin}분 전)`);
1817
- log(` ⏰ 예상 fire: ${plan.expectedFireAt}` + (plan.elapsedFromExpected > 0 ? yel(` (${plan.elapsedFromExpected}분 지연)`) : ' (정시)'));
1916
+ log(` 📅 plan 저장: ${_formatLocal(plan.savedAt)} (${plan.ageMin}분 전)`);
1917
+ log(` ⏰ 예상 fire: ${_formatLocal(plan.expectedFireAt)}` + (plan.elapsedFromExpected > 0 ? yel(` (${plan.elapsedFromExpected}분 지연)`) : ' (정시)'));
1818
1918
  log(` 🎯 focus: ${plan.focus}`);
1819
1919
  log('');
1820
1920
  log(grn(`## 다음 라운드: ${plan.nextRoundVersion}`));
@@ -3850,6 +3950,23 @@ function handoff(root) {
3850
3950
  parts.push(`🌐 official ${m.total}/${m.cacheTotal} (${ageStr}${status})`);
3851
3951
  }
3852
3952
  } catch {}
3953
+ // 12) 1.9.204: 자동 모드 활성 표시 (사용자 명시) — R-0001 every-round 룰 활성 시 헤드라인 노출
3954
+ try {
3955
+ const autoRule = _getAutoLoopRule(root);
3956
+ if (autoRule) {
3957
+ // 룰 텍스트에서 "25분" 우선, 없으면 "1500초" 패턴 추출 (분 우선순위)
3958
+ const txt = autoRule.rule || '';
3959
+ const minMatch = txt.match(/(\d+)\s*분/);
3960
+ const secMatch = txt.match(/(\d+)\s*초/);
3961
+ let label = '🔄 auto-loop';
3962
+ if (minMatch) {
3963
+ label = `🔄 auto-loop ${minMatch[1]}min`;
3964
+ } else if (secMatch) {
3965
+ label = `🔄 auto-loop ${Math.round(Number(secMatch[1]) / 60)}min`;
3966
+ }
3967
+ parts.push(label);
3968
+ }
3969
+ } catch {}
3853
3970
  // 11) 1.9.197: A축 (범용 AI 하네스) 9.5→10 보강 — provider probe 60분 캐시 자동 노출
3854
3971
  // 사용자 명시: "범용 AI 하네스 ... 최고의 도구" — 매 handoff 시 11 backend ready 상태 즉시 노출
3855
3972
  try {
@@ -3862,7 +3979,7 @@ function handoff(root) {
3862
3979
  if (parts.length) {
3863
3980
  const isTty = process.stdout && process.stdout.isTTY;
3864
3981
  const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
3865
- log(cy(`📊 헤드라인 (1.9.81/93/113/152/162/192/197): ${parts.join(' · ')}`));
3982
+ log(cy(`📊 헤드라인 (1.9.81/93/113/152/162/192/197/204): ${parts.join(' · ')}`));
3866
3983
  }
3867
3984
  } catch {}
3868
3985
  }
@@ -4161,6 +4278,40 @@ function handoff(root) {
4161
4278
  }
4162
4279
  } catch {}
4163
4280
  }
4281
+ // 1.9.205: ScheduleWakeup 상태 분석 + 사용자 조기 진입 / miss 감지 (사용자 명시)
4282
+ if (process.env.LEERNESS_NO_WAKEUP_STATUS !== '1' && !has('--quiet')) {
4283
+ try {
4284
+ const wakeup = _analyzeWakeupStatus(root);
4285
+ if (wakeup.hasWakeup) {
4286
+ const isTty = process.stdout && process.stdout.isTTY;
4287
+ const yel = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
4288
+ const grn = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
4289
+ const dim = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
4290
+ const expectedLocal = _formatLocal(wakeup.latest.expectedFireAt);
4291
+ if (wakeup.kind === 'early') {
4292
+ // 사용자 조기 진입 — 기존 wakeup 갱신 권장
4293
+ log(yel(`## ⚡ 사용자 조기 진입 감지 (1.9.205) — 등록된 wakeup ${Math.abs(wakeup.deltaMin)}분 후 fire 예정`));
4294
+ log(dim(` 예정 fire: ${expectedLocal} (${wakeup.latest.source})`));
4295
+ log(dim(` → 권장: 라운드 마무리 시 새 wakeup 등록 (이전 자동 superseded)`));
4296
+ log('');
4297
+ } else if (wakeup.kind === 'missed') {
4298
+ // wakeup miss — fire 시간 지남
4299
+ log(yel(`## ⏰ ScheduleWakeup miss 확정 (1.9.205) — 예정 fire ${wakeup.deltaMin}분 전`));
4300
+ log(dim(` 예정 fire: ${expectedLocal} (${wakeup.latest.source})`));
4301
+ log(dim(` → 회복: 라운드 마무리 시 새 wakeup 자동 등록`));
4302
+ // 자동으로 status 를 'missed' 로 업데이트
4303
+ try {
4304
+ const s = _loadActiveWakeups(root);
4305
+ const idx = s.wakeups.findIndex(w => w.expectedFireAt === wakeup.latest.expectedFireAt);
4306
+ if (idx >= 0) { s.wakeups[idx].status = 'missed'; _writeActiveWakeups(root, s.wakeups); }
4307
+ } catch {}
4308
+ log('');
4309
+ } else if (wakeup.kind === 'on-time') {
4310
+ log(grn(` ✓ ScheduleWakeup 정시 진입 (1.9.205, delta ${wakeup.deltaMin}분)`));
4311
+ }
4312
+ }
4313
+ } catch {}
4314
+ }
4164
4315
  // 1.9.203: auto-resume-plan 알림 — 라운드 마무리 시 저장된 plan 자동 노출
4165
4316
  // 사용자 명시: "백그라운드에 다음 작업이 시작가능한 예상 시간 + 일어났을때 해야하는 일을 정리"
4166
4317
  if (_autoResumePlan && !has('--quiet') && process.env.LEERNESS_NO_RESUME_PLAN !== '1') {
@@ -9271,6 +9422,14 @@ function _publishToNpm(root, opts = {}) {
9271
9422
  note: `다음 wakeup 시: 1) leerness resume . 으로 plan 확인 2) leerness handoff . 실행 3) leerness next-action take 로 즉시 task 진입`
9272
9423
  });
9273
9424
  _writeAutoResumePlan(root, plan);
9425
+ // 1.9.205: 다음 wakeup 등록 추적 (사용자 명시 갱신용)
9426
+ _recordWakeup(root, {
9427
+ expectedFireAt: plan.expectedFireAt,
9428
+ intervalMin: plan.intervalMin || 25,
9429
+ source: 'release-sync-main',
9430
+ status: 'pending',
9431
+ fromVersion: pkgVersion
9432
+ });
9274
9433
  } catch {}
9275
9434
  } else {
9276
9435
  const errOut = (pubR.stderr || pubR.stdout || '').slice(-400);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.203",
3
+ "version": "1.9.205",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",