leerness 1.9.203 → 1.9.204
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 +42 -0
- package/README.md +3 -3
- package/bin/harness.js +67 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,47 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.204 — 2026-05-22
|
|
4
|
+
|
|
5
|
+
**⏰ timezone 보강 + 🔄 auto-loop 활성 라벨 (사용자 명시 2종).**
|
|
6
|
+
|
|
7
|
+
### 사용자 명시
|
|
8
|
+
> 1. *"시간관련 기능 등은 해당 국가의 시간대를 고려해서 작업이 될 수 있는지"*
|
|
9
|
+
> 2. *"자동으로 깨어나서 작업하는 모드도 활성룰에 표시되어야 할 것 같은데, 활성 룰 에 표시되는 기능 점검"*
|
|
10
|
+
|
|
11
|
+
### 1. timezone 시스템 (Intl.DateTimeFormat 기반, 의존성 0)
|
|
12
|
+
- `_getLocalTz()` — `process.env.LEERNESS_TZ` → 시스템 timezone → `Asia/Seoul` fallback
|
|
13
|
+
- `_formatLocal(iso, opts)` — ISO UTC → 사용자 local time (예: `2026-05-22 10:13 KST`)
|
|
14
|
+
- 단축 라벨: KST / JST / UTC / 자동 추출
|
|
15
|
+
- **저장은 UTC ISO 유지** (이식성/일관성), **display 만 local time 변환**
|
|
16
|
+
|
|
17
|
+
### 2. resume CLI local time 표시
|
|
18
|
+
```
|
|
19
|
+
$ leerness resume
|
|
20
|
+
📅 plan 저장: 2026-05-22 09:48 KST (29분 전)
|
|
21
|
+
⏰ 예상 fire: 2026-05-22 10:13 KST (정시)
|
|
22
|
+
```
|
|
23
|
+
이전: `2026-05-22T00:48:23.298Z` (한국 사용자 +9 머릿속 변환 필요)
|
|
24
|
+
이후: `2026-05-22 09:48 KST` (즉시 인지 가능)
|
|
25
|
+
|
|
26
|
+
### 3. 자동 모드 활성 라벨 (헤드라인 12번째)
|
|
27
|
+
```
|
|
28
|
+
📊 헤드라인 (1.9.81/93/113/152/162/192/197/204): ... · 🔄 auto-loop 25min · ...
|
|
29
|
+
```
|
|
30
|
+
- `_getAutoLoopRule(root)` — `R-XXXX [every-round]` 활성 룰 자동 감지
|
|
31
|
+
- 룰 텍스트에서 `25분` 또는 `1500초` 패턴 추출 → 분 단위 표시
|
|
32
|
+
|
|
33
|
+
### 4. 누적 회귀 (1.9.198~203) — 모두 유지
|
|
34
|
+
|
|
35
|
+
### 5. stress-v149 — 16/16 PASS
|
|
36
|
+
- timezone (3) + resume display (1) + headline label (2) + 실 검증 (1) + 성능 (2) + 누적 (7)
|
|
37
|
+
- 성능: --version cold start avg **471 ms** · MCP 54 도구 **438 ms**
|
|
38
|
+
|
|
39
|
+
### 6. 자동 release 흐름
|
|
40
|
+
- main 자동 push **66 라운드 연속** (1.9.140~204)
|
|
41
|
+
- npm publish 자동 (1.9.178~)
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
3
45
|
## 1.9.203 — 2026-05-22
|
|
4
46
|
|
|
5
47
|
**📋 자동 라운드 plan 자동 정리 + `leerness resume` 신규 CLI (사용자 명시).**
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **AI 코딩 에이전트의 거짓 완료·중복·망각·충돌을 막아주는 검수·기억·협업 CLI 하네스.**
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/leerness) [](https://www.npmjs.com/package/leerness) []() []() []() []() []() []() [-brightgreen)]() []() []() []() []() []()
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
╔══════════════════════════════════════════════════════════════╗
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
|
|
13
13
|
║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
|
|
14
14
|
║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
|
|
15
|
-
║ v1.9.
|
|
15
|
+
║ v1.9.204 AI Agent Reliability Harness + Sandbox ║
|
|
16
16
|
║ verify · remember · orchestrate · audit · sandbox · drift ║
|
|
17
|
-
║
|
|
17
|
+
║ ⏰ timezone 보강 (KST) · 🔄 auto-loop 25min 헤드라인 라벨 ║
|
|
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.
|
|
10
|
+
const VERSION = '1.9.204';
|
|
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() {
|
|
@@ -1813,8 +1859,8 @@ function resumeCmd(root) {
|
|
|
1813
1859
|
const dim = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
1814
1860
|
log(cyan(`# 🔄 leerness resume (1.9.203 자동 라운드 plan 적용)`));
|
|
1815
1861
|
log('');
|
|
1816
|
-
log(` 📅 plan 저장: ${plan.savedAt} (${plan.ageMin}분 전)`);
|
|
1817
|
-
log(` ⏰ 예상 fire: ${plan.expectedFireAt}` + (plan.elapsedFromExpected > 0 ? yel(` (${plan.elapsedFromExpected}분 지연)`) : ' (정시)'));
|
|
1862
|
+
log(` 📅 plan 저장: ${_formatLocal(plan.savedAt)} (${plan.ageMin}분 전)`);
|
|
1863
|
+
log(` ⏰ 예상 fire: ${_formatLocal(plan.expectedFireAt)}` + (plan.elapsedFromExpected > 0 ? yel(` (${plan.elapsedFromExpected}분 지연)`) : ' (정시)'));
|
|
1818
1864
|
log(` 🎯 focus: ${plan.focus}`);
|
|
1819
1865
|
log('');
|
|
1820
1866
|
log(grn(`## 다음 라운드: ${plan.nextRoundVersion}`));
|
|
@@ -3850,6 +3896,23 @@ function handoff(root) {
|
|
|
3850
3896
|
parts.push(`🌐 official ${m.total}/${m.cacheTotal} (${ageStr}${status})`);
|
|
3851
3897
|
}
|
|
3852
3898
|
} catch {}
|
|
3899
|
+
// 12) 1.9.204: 자동 모드 활성 표시 (사용자 명시) — R-0001 every-round 룰 활성 시 헤드라인 노출
|
|
3900
|
+
try {
|
|
3901
|
+
const autoRule = _getAutoLoopRule(root);
|
|
3902
|
+
if (autoRule) {
|
|
3903
|
+
// 룰 텍스트에서 "25분" 우선, 없으면 "1500초" 패턴 추출 (분 우선순위)
|
|
3904
|
+
const txt = autoRule.rule || '';
|
|
3905
|
+
const minMatch = txt.match(/(\d+)\s*분/);
|
|
3906
|
+
const secMatch = txt.match(/(\d+)\s*초/);
|
|
3907
|
+
let label = '🔄 auto-loop';
|
|
3908
|
+
if (minMatch) {
|
|
3909
|
+
label = `🔄 auto-loop ${minMatch[1]}min`;
|
|
3910
|
+
} else if (secMatch) {
|
|
3911
|
+
label = `🔄 auto-loop ${Math.round(Number(secMatch[1]) / 60)}min`;
|
|
3912
|
+
}
|
|
3913
|
+
parts.push(label);
|
|
3914
|
+
}
|
|
3915
|
+
} catch {}
|
|
3853
3916
|
// 11) 1.9.197: A축 (범용 AI 하네스) 9.5→10 보강 — provider probe 60분 캐시 자동 노출
|
|
3854
3917
|
// 사용자 명시: "범용 AI 하네스 ... 최고의 도구" — 매 handoff 시 11 backend ready 상태 즉시 노출
|
|
3855
3918
|
try {
|
|
@@ -3862,7 +3925,7 @@ function handoff(root) {
|
|
|
3862
3925
|
if (parts.length) {
|
|
3863
3926
|
const isTty = process.stdout && process.stdout.isTTY;
|
|
3864
3927
|
const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
|
|
3865
|
-
log(cy(`📊 헤드라인 (1.9.81/93/113/152/162/192/197): ${parts.join(' · ')}`));
|
|
3928
|
+
log(cy(`📊 헤드라인 (1.9.81/93/113/152/162/192/197/204): ${parts.join(' · ')}`));
|
|
3866
3929
|
}
|
|
3867
3930
|
} catch {}
|
|
3868
3931
|
}
|