leerness 1.9.248 → 1.9.249
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 +37 -0
- package/README.md +3 -3
- package/bin/harness.js +69 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.249 — 2026-05-28 — UR-0018 터미널 인코딩 자동 회복 (한국어 Windows)
|
|
4
|
+
|
|
5
|
+
**🌐 사용자 명시 (UR-0018): "leerness가 적용된 프로젝트에서 터미널 출력이 깨지지 않게, 하드웨어의 언어 등을 사전에 참고하여 진행".**
|
|
6
|
+
|
|
7
|
+
### 배경
|
|
8
|
+
사용자 보고: `.harness/protected-files.md` 출력에서 "?뚯씪 ??젣/?뺣━" 패턴 (UTF-8 → CP949 오해석) 발생. 한국어 Windows 기본 코드페이지(CP949, 949)에서 leerness가 UTF-8로 출력한 한글이 깨져 보이는 문제.
|
|
9
|
+
|
|
10
|
+
### 구현 (bootstrap 자동 회복 + 가시화)
|
|
11
|
+
1. **`_ensureStdoutEncoding()` IIFE** (harness.js bootstrap, DEP0190 다음):
|
|
12
|
+
- `process.stdout/stderr.setEncoding('utf8')` 즉시 강제
|
|
13
|
+
- Windows + 비-65001 코드페이지 감지 시 → `chcp.com 65001` 자동 호출 (best-effort)
|
|
14
|
+
- 결과를 `process.env._LEERNESS_AUTOCHCP_APPLIED` 에 기록
|
|
15
|
+
- 무한 재호출 방지: `_LEERNESS_CHCP_DONE='1'` 자식 process 가드
|
|
16
|
+
- opt-out: `LEERNESS_NO_AUTOCHCP=1`
|
|
17
|
+
2. **env summary** 강화:
|
|
18
|
+
- "터미널 인코딩 UTF-8 (65001) — 안전" / "터미널 코드페이지 CP949 — 한국어 출력 깨짐 위험" 명시
|
|
19
|
+
3. **handoff body** 한국어 Windows + 비-65001 시 경고 섹션 추가
|
|
20
|
+
4. **JSON envInfo** 신규 2필드:
|
|
21
|
+
- `terminalEncodingOk: codepage === 65001`
|
|
22
|
+
- `autoChcpApplied: process.env._LEERNESS_AUTOCHCP_APPLIED` (적용된 이전 코드페이지)
|
|
23
|
+
5. **BUG fix** (`_collectRuntimeEnv`): chcp 출력 파싱 regex `\d{3,4}` → `\d{3,5}` (65001 5자리 캡처)
|
|
24
|
+
|
|
25
|
+
### 영향 받지 않은 영역
|
|
26
|
+
- 1.9.248 agy / 1.9.247 fallback / 1.9.246 status bar / 1.9.245 api-skill / 1.9.244 HOTFIX 모두 유지
|
|
27
|
+
- handoff JSON 11 필드 매트릭스 유지 (envInfo 확장)
|
|
28
|
+
|
|
29
|
+
### stress-v194 — **23/23 PASS · 100%**
|
|
30
|
+
- 1.9.249 (10): VERSION + IIFE bootstrap + chcp 자동/opt-out/재호출 가드 + env summary + handoff body + JSON envInfo 2필드 + 실제 응답 (terminalEncodingOk=true) + env summary 실제 출력
|
|
31
|
+
- 성능 (2): cold start avg 385ms (autochcp 포함) · 357ms (opt-out)
|
|
32
|
+
- 누적 회귀 (11): 1.9.207~248 + 보안
|
|
33
|
+
|
|
34
|
+
### 자동 release (111 main-push streak · 72 npm publish streak · R205)
|
|
35
|
+
|
|
36
|
+
🌐 **사용자 환경 자동 회복** — Windows 한국어 사용자의 한글 깨짐을 코드페이지 차원에서 사전 차단.
|
|
37
|
+
|
|
38
|
+
---
|
|
39
|
+
|
|
3
40
|
## 1.9.248 — 2026-05-26 — UR-0017 Gemini CLI 제거 + Antigravity CLI (agy) 도입
|
|
4
41
|
|
|
5
42
|
**🔄 사용자 명시 (UR-0017): Gemini CLI 제거 + Antigravity (agy) 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) []() []() []() []() []() []() []() []() []() []() []() []()
|
|
6
6
|
|
|
7
7
|
```
|
|
8
8
|
╔══════════════════════════════════════════════════════════════╗
|
|
@@ -12,9 +12,9 @@
|
|
|
12
12
|
║ ██║ ██╔══╝ ██╔══╝ ██╔══██╗██║╚██╗██║██╔══╝ ╚════██║ ║
|
|
13
13
|
║ ███████╗███████╗███████╗██║ ██║██║ ╚████║███████╗███████║ ║
|
|
14
14
|
║ ╚══════╝╚══════╝╚══════╝╚═╝ ╚═╝╚═╝ ╚═══╝╚══════╝╚══════╝ ║
|
|
15
|
-
║ v1.9.
|
|
15
|
+
║ v1.9.249 AI Agent Reliability Harness + Sandbox ║
|
|
16
16
|
║ verify · remember · orchestrate · audit · sandbox · drift ║
|
|
17
|
-
║
|
|
17
|
+
║ 🌐 터미널 인코딩 자동 회복 (한국어 Windows · UR-0018) ║
|
|
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.249';
|
|
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 처리).
|
|
@@ -24,6 +24,36 @@ if (!/--no-deprecation/.test(process.env.NODE_OPTIONS || '')) {
|
|
|
24
24
|
process.env.NODE_OPTIONS = ((process.env.NODE_OPTIONS || '') + ' --no-deprecation').trim();
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
+
// 1.9.249 (사용자 명시 UR-0018): 터미널 출력 한국어 깨짐 사전 방지
|
|
28
|
+
// 사용자 보고: protected-files.md 같은 한국어 텍스트가 ? / 한자 조각으로 깨짐 (UTF-8 → CP949 오인식)
|
|
29
|
+
// 조치: (1) stdout/stderr UTF-8 명시 (2) Windows + 한국어 OS + 비-65001 코드페이지 시 chcp 65001 자동 시도
|
|
30
|
+
// 안전: 1회만 시도 (LEERNESS_NO_AUTOCHCP=1 로 opt-out), 실패 시 무시
|
|
31
|
+
(function _ensureStdoutEncoding() {
|
|
32
|
+
try {
|
|
33
|
+
if (process.stdout && process.stdout.setEncoding) process.stdout.setEncoding('utf8');
|
|
34
|
+
if (process.stderr && process.stderr.setEncoding) process.stderr.setEncoding('utf8');
|
|
35
|
+
} catch {}
|
|
36
|
+
// Windows + 한국어 사용자 환경 (chcp 949 = CP949) → 한국어 콘솔 자동 65001 (UTF-8)
|
|
37
|
+
if (process.platform !== 'win32') return;
|
|
38
|
+
if (process.env.LEERNESS_NO_AUTOCHCP === '1') return;
|
|
39
|
+
if (process.env._LEERNESS_CHCP_DONE === '1') return; // 자식 spawn 무한 재호출 방지
|
|
40
|
+
try {
|
|
41
|
+
const r = cp.spawnSync('chcp.com', [], { encoding: 'utf8', timeout: 2000, shell: true });
|
|
42
|
+
const out = (r.stdout || '') + (r.stderr || '');
|
|
43
|
+
const m = out.match(/(\d+)\s*$/m);
|
|
44
|
+
const code = m ? parseInt(m[1], 10) : null;
|
|
45
|
+
// 65001 = UTF-8, 949 = CP949 (한국), 932 = CP932 (일본), 936 = CP936 (중국)
|
|
46
|
+
if (code && code !== 65001) {
|
|
47
|
+
// 자동 chcp 65001 시도 (실패해도 무시 — 출력 방향 X 인 비-TTY 등)
|
|
48
|
+
try {
|
|
49
|
+
cp.spawnSync('chcp.com', ['65001'], { encoding: 'utf8', timeout: 2000, shell: true, stdio: 'ignore' });
|
|
50
|
+
process.env._LEERNESS_AUTOCHCP_APPLIED = String(code); // 진단용 — handoff body 에서 노출
|
|
51
|
+
} catch {}
|
|
52
|
+
}
|
|
53
|
+
process.env._LEERNESS_CHCP_DONE = '1';
|
|
54
|
+
} catch {}
|
|
55
|
+
})();
|
|
56
|
+
|
|
27
57
|
const MARK = '<!-- leerness:managed -->';
|
|
28
58
|
const README_START = '<!-- leerness:project-readme:start -->';
|
|
29
59
|
const README_END = '<!-- leerness:project-readme:end -->';
|
|
@@ -2408,9 +2438,9 @@ function _collectRuntimeEnv() {
|
|
|
2408
2438
|
// 한국어 Windows 감지
|
|
2409
2439
|
try {
|
|
2410
2440
|
if (env.os.platform === 'win32') {
|
|
2411
|
-
// chcp 출력으로 코드페이지 확인
|
|
2441
|
+
// chcp 출력으로 코드페이지 확인 (1.9.249 fix: 65001 (5자리) 까지 매칭 — 이전 \d{3,4} 는 "6500" 만 캡처 BUG)
|
|
2412
2442
|
const r = cp.spawnSync('chcp', [], { encoding: 'utf8', shell: true, timeout: 3000 });
|
|
2413
|
-
const m = (r.stdout || '').match(/(\d{3,
|
|
2443
|
+
const m = (r.stdout || '').match(/(\d{3,5})/);
|
|
2414
2444
|
if (m) {
|
|
2415
2445
|
const cp_num = parseInt(m[1], 10);
|
|
2416
2446
|
env.locale.codepage = cp_num;
|
|
@@ -2577,6 +2607,17 @@ function envCmd(root, sub) {
|
|
|
2577
2607
|
log(` 💻 OS: ${gr(env.os.platform)} ${env.os.release} (${env.os.arch})`);
|
|
2578
2608
|
log(` 📦 Node: ${gr(env.node.version)}`);
|
|
2579
2609
|
log(` 🌐 Locale: LANG=${env.locale.LANG || 'unset'} · CP=${env.locale.codepage || 'unknown'}${env.locale.isKoreanWindows ? yl(' (한국어 Windows)') : ''}`);
|
|
2610
|
+
// 1.9.249 (UR-0018): 터미널 출력 인코딩 자동 점검 — 한국어 Windows + 비-65001 → 경고 + 자동 회복 결과
|
|
2611
|
+
if (env.locale.isKoreanWindows && env.locale.codepage !== 65001) {
|
|
2612
|
+
log(yl(` ⚠ 터미널 코드페이지 ${env.locale.codepage} (CP949) — 한국어 출력 깨짐 위험`));
|
|
2613
|
+
if (process.env._LEERNESS_AUTOCHCP_APPLIED) {
|
|
2614
|
+
log(gr(` ✓ chcp 65001 자동 적용됨 (이전: ${process.env._LEERNESS_AUTOCHCP_APPLIED}, 1.9.249)`));
|
|
2615
|
+
} else {
|
|
2616
|
+
log(dm(` → 수동: chcp 65001 (또는 LEERNESS_NO_AUTOCHCP=0)`));
|
|
2617
|
+
}
|
|
2618
|
+
} else if (env.locale.codepage === 65001) {
|
|
2619
|
+
log(gr(` ✓ 터미널 인코딩 UTF-8 (65001) — 한국어 출력 안전`));
|
|
2620
|
+
}
|
|
2580
2621
|
log(` 🖥 Hardware: ${env.hardware.cpus} CPUs · ${env.hardware.memGB} GB RAM`);
|
|
2581
2622
|
log(` 🖱 Terminal: TTY=${env.terminal.isTTY}${env.terminal.powershell ? ` · PowerShell v${env.terminal.powershell}` : ''}`);
|
|
2582
2623
|
log(` 🔧 Tools:`);
|
|
@@ -6977,6 +7018,7 @@ function handoff(root) {
|
|
|
6977
7018
|
};
|
|
6978
7019
|
} catch {}
|
|
6979
7020
|
// 1.9.242: envInfo 통합 (handoff JSON 10번째 통합 필드) — 환경 + 인코딩 위험 (UR-0014 2단계)
|
|
7021
|
+
// 1.9.249: terminalEncodingOk + autoChcpApplied 필드 추가 (UR-0018)
|
|
6980
7022
|
try {
|
|
6981
7023
|
const runtimeEnv = _collectRuntimeEnv();
|
|
6982
7024
|
const encScan = _scanShellScriptsEncoding(root);
|
|
@@ -6987,7 +7029,10 @@ function handoff(root) {
|
|
|
6987
7029
|
nodeVersion: runtimeEnv.node.version,
|
|
6988
7030
|
shellScriptsScanned: encScan.scanned,
|
|
6989
7031
|
encodingRiskCount: encScan.atRisk.length,
|
|
6990
|
-
encodingRiskFiles: encScan.atRisk.slice(0, 5).map(r => r.file)
|
|
7032
|
+
encodingRiskFiles: encScan.atRisk.slice(0, 5).map(r => r.file),
|
|
7033
|
+
// 1.9.249 (UR-0018): 터미널 출력 인코딩 안전 여부 + 자동 회복 결과
|
|
7034
|
+
terminalEncodingOk: runtimeEnv.locale.codepage === 65001 || !runtimeEnv.locale.isKoreanWindows,
|
|
7035
|
+
autoChcpApplied: process.env._LEERNESS_AUTOCHCP_APPLIED || null
|
|
6991
7036
|
};
|
|
6992
7037
|
} catch {}
|
|
6993
7038
|
// 1.9.245: apiSkills 통합 (handoff JSON 11번째 통합 필드) — UR-0015 API 문서 캐시
|
|
@@ -7375,6 +7420,26 @@ function handoff(root) {
|
|
|
7375
7420
|
}
|
|
7376
7421
|
} catch {}
|
|
7377
7422
|
|
|
7423
|
+
// 1.9.249 (UR-0018): 터미널 코드페이지 자동 점검 + 자동 회복 노출
|
|
7424
|
+
// 한국어 Windows + 비-65001 → leerness 출력 깨짐 위험 → 자동 chcp 65001 (bootstrap), 결과 본문 노출
|
|
7425
|
+
try {
|
|
7426
|
+
const runtimeEnv = _collectRuntimeEnv();
|
|
7427
|
+
if (runtimeEnv.locale.isKoreanWindows && runtimeEnv.locale.codepage !== 65001) {
|
|
7428
|
+
const isTty5 = process.stdout && process.stdout.isTTY;
|
|
7429
|
+
const yl6 = s => isTty5 ? `\x1b[33m${s}\x1b[0m` : s;
|
|
7430
|
+
const gr6 = s => isTty5 ? `\x1b[32m${s}\x1b[0m` : s;
|
|
7431
|
+
const dm6 = s => isTty5 ? `\x1b[2m${s}\x1b[0m` : s;
|
|
7432
|
+
log('');
|
|
7433
|
+
log(yl6(`## ⚠ 터미널 인코딩 — 한국어 Windows + CP${runtimeEnv.locale.codepage} (1.9.249, UR-0018)`));
|
|
7434
|
+
log(dm6(` leerness 출력의 한국어가 ?/한자 조각으로 깨질 수 있음 (UTF-8 → CP949 오인식)`));
|
|
7435
|
+
if (process.env._LEERNESS_AUTOCHCP_APPLIED) {
|
|
7436
|
+
log(gr6(` ✓ 자동 회복: chcp 65001 적용됨 (이전: ${process.env._LEERNESS_AUTOCHCP_APPLIED})`));
|
|
7437
|
+
} else {
|
|
7438
|
+
log(dm6(` → 수동: chcp 65001 + 새 터미널 또는 LEERNESS_NO_AUTOCHCP 미설정 시 다음 호출 자동 회복`));
|
|
7439
|
+
}
|
|
7440
|
+
}
|
|
7441
|
+
} catch {}
|
|
7442
|
+
|
|
7378
7443
|
// 1.9.245: API skill cache 자동 참조 (사용자 명시 UR-0015)
|
|
7379
7444
|
// 현재 task 키워드 기반으로 .harness/api-skills/ 매칭 → 사용자가 정리해둔 API 문서 자동 노출
|
|
7380
7445
|
try {
|