leerness 1.9.241 β†’ 1.9.243

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,90 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.243 β€” 2026-05-26
4
+
5
+ **🌏 UR-0014 3단계: CJK λ‹€κ΅­μ–΄ λΆ„λ₯˜ (ν•œκ΅­μ–΄/일본어/쀑ꡭ어) + session close --auto-fix-encoding + handoff body --apply μ§„μž…μ .**
6
+
7
+ ### μ‚¬μš©μž λͺ…μ‹œ (UR-0014) β€” 3단계 λ‹€κ΅­μ–΄ 보강 + 마감 μžλ™ν™”
8
+
9
+ 1.9.241 (감지) β†’ 1.9.242 (μžλ™ μˆ˜μ •) β†’ 1.9.243 (λ‹€κ΅­μ–΄ + 마감 μžλ™ 톡합):
10
+
11
+ ### 1. CJK λΆ„λ₯˜ β€” Korean/Japanese/Chinese μžλ™ 식별
12
+ - `_scanShellScriptsEncoding()` 에 `riskType` ν•„λ“œ μΆ”κ°€
13
+ - UTF-8 byte range λΆ„λ₯˜:
14
+ - Korean (Hangul U+AC00-D7AF) β†’ 0xEA-0xED μ‹œμž‘ β†’ CP949 μœ„ν—˜
15
+ - Japanese (Hiragana/Katakana U+3040-30FF) β†’ 0xE3 μ‹œμž‘ β†’ CP932 (Shift-JIS) μœ„ν—˜
16
+ - Chinese (CJK Unified U+4E00-9FFF) β†’ 0xE4-0xE9 μ‹œμž‘ β†’ CP936 (GBK) μœ„ν—˜
17
+ - `atRisk[].cjk`: { korean, japanese, chinese, other } μ„ΈλΆ€ 카운트
18
+ - `result.riskTypeCounts`: λΆ„λ₯˜λ³„ μš”μ•½ 톡계
19
+ - Locale 별 μ •ν™•ν•œ μœ„ν—˜ λ©”μ‹œμ§€ ("Windows ν•œκ΅­μ–΄/일본어/쀑ꡭ어 PowerShell μ—μ„œ CPxxx 둜 μ˜€μΈμ‹ κ°€λŠ₯")
20
+
21
+ ### 2. session close --auto-fix-encoding 마감 μžλ™ 회볡
22
+ - 1.9.224 (--auto-apply-delivered) + 1.9.237 (--auto-cleanup-branches) νŒ¨ν„΄ ν™•μž₯
23
+ - 마감 μ‹œ μ…Έ 슀크립트 인코딩 μœ„ν—˜ μžλ™ BOM μΆ”κ°€
24
+ - κΈ°λ³Έ: μ•ˆλ‚΄λ§Œ (`μžλ™ 회볡 κ°€λŠ₯` hint) Β· `--auto-fix-encoding` λͺ…μ‹œ μ‹œμ—λ§Œ λ³€κ²½
25
+ - agent-mode stop 흐름에 μžλ™ chain (`session close --auto-apply-delivered --auto-cleanup-branches --auto-fix-encoding`)
26
+
27
+ ### 3. handoff body --apply μ§„μž…μ  ν‘œμ‹œ
28
+ - 인코딩 μœ„ν—˜ 발견 μ‹œ 본문에 μžλ™ μ•ˆλ‚΄ 2쀄 μΆ”κ°€:
29
+ - `β†’ μžλ™ 회볡: leerness env encoding --apply (UTF-8 BOM μžλ™ μΆ”κ°€, 1.9.242)`
30
+ - `β†’ 마감 μ‹œ μžλ™: session close --auto-fix-encoding (1.9.243)`
31
+ - CJK λΆ„λ₯˜ μš”μ•½ ν‘œμ‹œ: `λΆ„λ₯˜: korean=N, japanese=M, chinese=K (1.9.243 CJK)`
32
+
33
+ ### 4. λˆ„μ  νšŒκ·€ (1.9.207~242) β€” λͺ¨λ‘ μœ μ§€
34
+ - MCP 69 도ꡬ μœ μ§€
35
+ - JSON 톡합 10 ν•„λ“œ (envInfo) μœ μ§€
36
+ - drift check --auto-fix env encoding 톡합 μœ μ§€
37
+
38
+ ### 5. stress-v188 β€” **24/24 PASS Β· 100%**
39
+ - 1.9.243 (8): VERSION + CJK 3μ’… λΆ„λ₯˜ 정확도 + riskTypeCounts + atRisk[].cjk + session close μžλ™/κΈ°λ³Έ λͺ¨λ“œ + handoff body μ§„μž…μ  + agent-mode stop chain
40
+ - μ„±λŠ₯ (2): cold_start avg 333ms Β· session close --auto-fix 438ms
41
+ - λˆ„μ  νšŒκ·€ (14): 1.9.207~242 + λ³΄μ•ˆ + npm publish
42
+
43
+ ### 6. μžλ™ release (105 λΌμš΄λ“œ main-push streak Β· 66 λΌμš΄λ“œ npm publish streak)
44
+
45
+ 🌏 **λ‹€κ΅­μ–΄ CJK μ™„μ „ μžλ™ν™”** β€” Korean/Japanese/Chinese PowerShell 인코딩 μ˜€μΈμ‹ 사전 차단
46
+
47
+ ---
48
+
49
+ ## 1.9.242 β€” 2026-05-26
50
+
51
+ **🌐 UR-0014 2단계: env encoding --apply (BOM μžλ™ μΆ”κ°€) + JSON 10 ν•„λ“œ envInfo + drift --auto-fix 톡합.**
52
+
53
+ ### μ‚¬μš©μž λͺ…μ‹œ (UR-0014) β€” 2단계 μžλ™ 회볡
54
+
55
+ 1.9.241 μ—μ„œ κ°μ§€λ§Œ μ œκ³΅ν–ˆλ˜ μœ„ν—˜μ„ 1.9.242 κ°€ **μ‹€μ œ μžλ™ μˆ˜μ •**으둜 ν™•μž₯:
56
+
57
+ ### 1. `leerness env encoding --apply` μžλ™ BOM μΆ”κ°€
58
+ - `.ps1` / `.bat` / `.cmd` / `.sh` 의 μœ„ν—˜ νŒŒμΌμ— UTF-8 BOM (`EF BB BF`) μžλ™ prepend
59
+ - `--auto-fix-bom` 별칭 동일 λ™μž‘
60
+ - κΈ°λ³Έ dry-run μœ μ§€ (--apply λͺ…μ‹œ μ‹œμ—λ§Œ λ³€κ²½) β€” μ•ˆμ „ 원칙
61
+ - 적용 κ²°κ³Ό `result.applied` λ°°μ—΄λ‘œ λ…ΈμΆœ (file/action: utf8-bom-added | failed)
62
+
63
+ ### 2. handoff/session close/health --json **10 ν•„λ“œ** envInfo 톡합
64
+ - JSON 톡합 맀트릭슀 9 β†’ **10 ν•„λ“œ** μ§„ν™” (3 λͺ…λ Ή Γ— 10 = 30 톡합 포인트)
65
+ - `envInfo`: os / isKoreanWindows / codepage / nodeVersion / shellScriptsScanned / encodingRiskCount / encodingRiskFiles[0..5]
66
+ - μ™ΈλΆ€ AI κ°€ ν™˜κ²½ μ»¨ν…μŠ€νŠΈ + 인코딩 μœ„ν—˜μ„ 단일 ν˜ΈμΆœμ—μ„œ 회수
67
+
68
+ ### 3. drift check --auto-fix env encoding BOM 톡합
69
+ - 1.9.82 λ³΄μ•ˆ 회볡 + 1.9.225 delivered + 1.9.236 release cleanup μžλ™ 회볡 νŒ¨ν„΄ ν™•μž₯
70
+ - `drift check --auto-fix` μ‹œ μ…Έ 슀크립트 μœ„ν—˜ μžλ™ BOM μΆ”κ°€
71
+ - log: `🌐 --auto-fix ν™œμ„± (1.9.242) β€” μ…Έ 슀크립트 인코딩 μœ„ν—˜ N건 BOM μžλ™ μΆ”κ°€ 쀑...`
72
+
73
+ ### 4. λˆ„μ  νšŒκ·€ (1.9.207~241) β€” λͺ¨λ‘ μœ μ§€
74
+ - MCP 69 도ꡬ μœ μ§€ (1.9.242 λŠ” μ‹ κ·œ CLI μ˜΅μ…˜ + 톡합, 도ꡬ 증가 X)
75
+ - handoff JSON: userRequestsAudit + preWakeAudit + idempotencyAudit + abnormalShutdown + deliveredRequests + roundHistory + milestones + recentChanges + pyFiles + **envInfo** (10)
76
+
77
+ ### 5. stress-v187 β€” **22/22 PASS Β· 100%**
78
+ - 1.9.242 (8): VERSION + env --apply (격리) + --auto-fix-bom 별칭 + handoff/session close/health 10ν•„λ“œ envInfo + drift --auto-fix BOM 톡합 + envInfo μŠ€ν‚€λ§ˆ
79
+ - μ„±λŠ₯ (2): cold_start avg 371ms Β· env --apply 372ms
80
+ - λˆ„μ  νšŒκ·€ (12): 1.9.207~241
81
+
82
+ ### 6. μžλ™ release (104 λΌμš΄λ“œ main-push streak Β· 65 λΌμš΄λ“œ npm publish streak)
83
+
84
+ 🌐 **UR-0014 μ™„μ „ μžλ™ν™”** β€” 감지 (1.9.241) β†’ μžλ™ μˆ˜μ • (1.9.242)
85
+
86
+ ---
87
+
3
88
  ## 1.9.241 β€” 2026-05-24
4
89
 
5
90
  **🌐 leerness env + ν•œκ΅­μ–΄ PowerShell 인코딩 μœ„ν—˜ 사전 감지 (μ‚¬μš©μž λͺ…μ‹œ UR-0014).**
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.241-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v186-16%2F16-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-69-brightgreen)]() [![cli](https://img.shields.io/badge/CLI_commands-56-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-197-blueviolet)]() [![main-push](https://img.shields.io/badge/πŸŽ‰_main--push_streak-103_rounds-success)]() [![npm-streak](https://img.shields.io/badge/πŸŽ‰_npm--publish-64_streak-success)]() [![encoding](https://img.shields.io/badge/🌐_env_encoding-ν•œκ΅­μ–΄_PS_μœ„ν—˜_감지-blueviolet)]() [![perf](https://img.shields.io/badge/cold_start-598ms-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.243-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v188-24%2F24-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-69-brightgreen)]() [![cli](https://img.shields.io/badge/CLI_commands-56-brightgreen)]() [![json-fields](https://img.shields.io/badge/JSON_톡합-10_ν•„λ“œ-blueviolet)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-199-blueviolet)]() [![main-push](https://img.shields.io/badge/πŸŽ‰_main--push_streak-105_rounds-success)]() [![npm-streak](https://img.shields.io/badge/πŸŽ‰_npm--publish-66_streak-success)]() [![cjk](https://img.shields.io/badge/🌏_CJK_λΆ„λ₯˜-Ko_Ja_Zh-blueviolet)]() [![perf](https://img.shields.io/badge/cold_start-333ms-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.241 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.243 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ 🌐 env encoding μœ„ν—˜ 사전 감지 (μ‚¬μš©μž λͺ…μ‹œ UR-0014) β•‘
17
+ β•‘ 🌏 CJK λΆ„λ₯˜ + session close --auto-fix-encoding (UR-0014 3) β•‘
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.241';
10
+ const VERSION = '1.9.243';
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 처리).
@@ -2308,7 +2308,7 @@ function agentModeCmd(root, sub) {
2308
2308
  log('');
2309
2309
  log(` start μ§„μž… (handoff + drift --auto-fix + session-resume --auto-fix)`);
2310
2310
  log(` tick λ§€ λΌμš΄λ“œ (pulse + 핡심 μ‹ ν˜Έ)`);
2311
- log(` stop 마감 (session close --auto-apply-delivered --auto-cleanup-branches)`);
2311
+ log(` stop 마감 (session close --auto-apply-delivered --auto-cleanup-branches --auto-fix-encoding)`);
2312
2312
  log('');
2313
2313
  log(dm(` μ‚¬μš©μž λͺ…μ‹œ UR-0013 (Python + agent-mode νš¨μœ¨ν™”)`));
2314
2314
  return;
@@ -2337,8 +2337,8 @@ function agentModeCmd(root, sub) {
2337
2337
  }
2338
2338
  if (sub === 'stop') {
2339
2339
  log(cy(`# leerness agent-mode stop (1.9.239) β€” 톡합 마감`));
2340
- log(dm(` β†’ session close --auto-apply-delivered --auto-cleanup-branches`));
2341
- const r = spawnChild(['session', 'close', root, '--auto-apply-delivered', '--auto-cleanup-branches']);
2340
+ log(dm(` β†’ session close --auto-apply-delivered --auto-cleanup-branches --auto-fix-encoding`));
2341
+ const r = spawnChild(['session', 'close', root, '--auto-apply-delivered', '--auto-cleanup-branches', '--auto-fix-encoding']);
2342
2342
  process.stdout.write(r.stdout || '');
2343
2343
  return;
2344
2344
  }
@@ -2442,6 +2442,35 @@ function _collectRuntimeEnv() {
2442
2442
  // μœ„ν—˜ μ‹œκ·Έλ„: ν•œκ΅­μ–΄ (λ˜λŠ” λΉ„-ASCII) 문자 + BOM μ—†μŒ + Windows ν™˜κ²½
2443
2443
  function _scanShellScriptsEncoding(root) {
2444
2444
  const result = { scanned: 0, atRisk: [], notes: [] };
2445
+ // 1.9.243: CJK λΆ„λ₯˜ β€” Korean/Japanese/Chinese λΉ„-ASCII νŒ¨ν„΄ 톡계 (UR-0014 3단계)
2446
+ function _classifyCJK(buf, len) {
2447
+ let korean = 0, japanese = 0, chinese = 0, other = 0;
2448
+ for (let i = 0; i < Math.min(buf.length, len); i++) {
2449
+ const b = buf[i];
2450
+ if (b < 0x80) continue;
2451
+ // UTF-8 3-byte μ‹œν€€μŠ€ μ‹œμž‘ λ°”μ΄νŠΈ λΆ„λ₯˜
2452
+ // Korean Hangul Syllables U+AC00-D7AF β†’ 3-byte: EA B0 80 ~ ED 9F BF
2453
+ if (b >= 0xEA && b <= 0xED) korean++;
2454
+ // Japanese Hiragana/Katakana U+3040-30FF β†’ 3-byte: E3 81 80 ~ E3 83 BF
2455
+ else if (b === 0xE3) japanese++;
2456
+ // CJK Unified Ideographs U+4E00-9FFF β†’ 3-byte: E4 B8 80 ~ E9 BF BF
2457
+ else if (b >= 0xE4 && b <= 0xE9) chinese++;
2458
+ else other++;
2459
+ }
2460
+ return { korean, japanese, chinese, other };
2461
+ }
2462
+ function _riskLabel(cjk) {
2463
+ if (cjk.korean >= cjk.japanese && cjk.korean >= cjk.chinese && cjk.korean > 0) {
2464
+ return { type: 'korean', risk: 'Windows ν•œκ΅­μ–΄ PowerShell μ—μ„œ CP949 둜 μ˜€μΈμ‹ κ°€λŠ₯ (BOM μΆ”κ°€ ꢌμž₯)' };
2465
+ }
2466
+ if (cjk.japanese > cjk.korean && cjk.japanese >= cjk.chinese) {
2467
+ return { type: 'japanese', risk: 'Windows 일본어 PowerShell μ—μ„œ CP932 (Shift-JIS) 둜 μ˜€μΈμ‹ κ°€λŠ₯ (BOM μΆ”κ°€ ꢌμž₯)' };
2468
+ }
2469
+ if (cjk.chinese > 0) {
2470
+ return { type: 'chinese', risk: 'Windows 쀑ꡭ어 PowerShell μ—μ„œ CP936 (GBK) 둜 μ˜€μΈμ‹ κ°€λŠ₯ (BOM μΆ”κ°€ ꢌμž₯)' };
2471
+ }
2472
+ return { type: 'non-ascii', risk: 'Windows λΉ„-ASCII μ…Έ 슀크립트 β€” BOM μ—†λŠ” UTF-8 인코딩 μ˜€μΈμ‹ κ°€λŠ₯ (BOM μΆ”κ°€ ꢌμž₯)' };
2473
+ }
2445
2474
  function walk(dir, depth = 0) {
2446
2475
  if (depth > 3) return; // λ„ˆλ¬΄ 깊이 λ“€μ–΄κ°€μ§€ μ•ŠμŒ
2447
2476
  let entries;
@@ -2462,12 +2491,17 @@ function _scanShellScriptsEncoding(root) {
2462
2491
  if (buf[i] >= 0x80) { nonAscii = true; break; }
2463
2492
  }
2464
2493
  if (nonAscii && !hasBOM) {
2494
+ // 1.9.243: CJK λΆ„λ₯˜ μΆ”κ°€
2495
+ const cjk = _classifyCJK(buf, 4096);
2496
+ const label = _riskLabel(cjk);
2465
2497
  result.atRisk.push({
2466
2498
  file: path.relative(root, fp),
2467
2499
  ext: path.extname(fp),
2468
2500
  hasBOM: false,
2469
2501
  nonAscii: true,
2470
- risk: 'Windows ν•œκ΅­μ–΄ PowerShell μ—μ„œ CP949 둜 μ˜€μΈμ‹ κ°€λŠ₯ (BOM μΆ”κ°€ ꢌμž₯)'
2502
+ riskType: label.type,
2503
+ cjk,
2504
+ risk: label.risk
2471
2505
  });
2472
2506
  }
2473
2507
  } catch {}
@@ -2478,6 +2512,9 @@ function _scanShellScriptsEncoding(root) {
2478
2512
  if (result.atRisk.length > 0) {
2479
2513
  result.notes.push('ν•΄κ²°: 파일 μ‹œμž‘μ— UTF-8 BOM μΆ”κ°€ (EF BB BF) λ˜λŠ” .ps1 첫 쀄에 $OutputEncoding/[Console]::OutputEncoding μ„€μ •');
2480
2514
  }
2515
+ // 1.9.243: λΆ„λ₯˜λ³„ 톡계 μš”μ•½
2516
+ const types = result.atRisk.reduce((m, r) => { m[r.riskType] = (m[r.riskType] || 0) + 1; return m; }, {});
2517
+ result.riskTypeCounts = types;
2481
2518
  return result;
2482
2519
  }
2483
2520
 
@@ -2491,8 +2528,25 @@ function envCmd(root, sub) {
2491
2528
  const dm = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
2492
2529
  if (sub === 'encoding-check') {
2493
2530
  const result = _scanShellScriptsEncoding(root);
2531
+ // 1.9.242: --apply μ˜΅μ…˜ β€” μœ„ν—˜ νŒŒμΌμ— UTF-8 BOM μžλ™ μΆ”κ°€ (μ‚¬μš©μž λͺ…μ‹œ UR-0014 2단계)
2532
+ const apply = has('--apply') || has('--auto-fix-bom');
2533
+ if (apply) result.applied = [];
2534
+ if (apply && result.atRisk.length > 0) {
2535
+ for (const r of result.atRisk) {
2536
+ try {
2537
+ const fullPath = path.join(root, r.file);
2538
+ const orig = fs.readFileSync(fullPath);
2539
+ const bom = Buffer.from([0xEF, 0xBB, 0xBF]);
2540
+ const fixed = Buffer.concat([bom, orig]);
2541
+ fs.writeFileSync(fullPath, fixed);
2542
+ result.applied.push({ file: r.file, action: 'utf8-bom-added' });
2543
+ } catch (e) {
2544
+ result.applied.push({ file: r.file, action: 'failed', error: e.message });
2545
+ }
2546
+ }
2547
+ }
2494
2548
  if (has('--json')) { log(JSON.stringify(result, null, 2)); return; }
2495
- log(cy(`# leerness env encoding-check (1.9.241, UR-0014) β€” μ…Έ 슀크립트 인코딩 μœ„ν—˜ 감지`));
2549
+ log(cy(`# leerness env encoding-check (1.9.241/242, UR-0014) β€” μ…Έ 슀크립트 인코딩 μœ„ν—˜ 감지`));
2496
2550
  log('');
2497
2551
  log(` μŠ€μΊ”: ${gr(String(result.scanned))} 파일 (.ps1/.bat/.cmd/.sh)`);
2498
2552
  if (result.atRisk.length === 0) {
@@ -2504,7 +2558,14 @@ function envCmd(root, sub) {
2504
2558
  log(dm(` ${r.risk}`));
2505
2559
  });
2506
2560
  log('');
2507
- result.notes.forEach(n => log(yl(` πŸ’‘ ${n}`)));
2561
+ if (apply) {
2562
+ const okCnt = (result.applied || []).filter(a => a.action === 'utf8-bom-added').length;
2563
+ const failCnt = (result.applied || []).filter(a => a.action === 'failed').length;
2564
+ log(gr(` βœ“ UTF-8 BOM μΆ”κ°€ ${okCnt}건${failCnt > 0 ? ` (μ‹€νŒ¨ ${failCnt}건)` : ''} (1.9.242 --apply)`));
2565
+ } else {
2566
+ result.notes.forEach(n => log(yl(` πŸ’‘ ${n}`)));
2567
+ log(dm(` β†’ μžλ™ μˆ˜μ •: leerness env encoding --apply (BOM μžλ™ μΆ”κ°€)`));
2568
+ }
2508
2569
  }
2509
2570
  return;
2510
2571
  }
@@ -6554,6 +6615,20 @@ function handoff(root) {
6554
6615
  totalClasses: analyses.reduce((s, a) => s + a.classes, 0)
6555
6616
  };
6556
6617
  } catch {}
6618
+ // 1.9.242: envInfo 톡합 (handoff JSON 10번째 톡합 ν•„λ“œ) β€” ν™˜κ²½ + 인코딩 μœ„ν—˜ (UR-0014 2단계)
6619
+ try {
6620
+ const runtimeEnv = _collectRuntimeEnv();
6621
+ const encScan = _scanShellScriptsEncoding(root);
6622
+ result.envInfo = {
6623
+ os: runtimeEnv.os.platform,
6624
+ isKoreanWindows: runtimeEnv.locale.isKoreanWindows || false,
6625
+ codepage: runtimeEnv.locale.codepage || null,
6626
+ nodeVersion: runtimeEnv.node.version,
6627
+ shellScriptsScanned: encScan.scanned,
6628
+ encodingRiskCount: encScan.atRisk.length,
6629
+ encodingRiskFiles: encScan.atRisk.slice(0, 5).map(r => r.file)
6630
+ };
6631
+ } catch {}
6557
6632
  } catch {}
6558
6633
  try {
6559
6634
  const pwState = _loadPreWakeReport(root);
@@ -6910,8 +6985,13 @@ function handoff(root) {
6910
6985
  log(dm5(` ${r.risk}`));
6911
6986
  });
6912
6987
  if (scan.atRisk.length > 3) log(dm5(` ... +${scan.atRisk.length - 3}건 더`));
6988
+ // 1.9.243: CJK λΆ„λ₯˜ μš”μ•½ (UR-0014 3단계)
6989
+ const types = scan.riskTypeCounts || {};
6990
+ const tList = Object.entries(types).filter(([_, c]) => c > 0).map(([t, c]) => `${t}=${c}`);
6991
+ if (tList.length > 0) log(dm5(` λΆ„λ₯˜: ${tList.join(', ')} (1.9.243 CJK)`));
6913
6992
  log(dm5(` β†’ 상세: leerness env encoding`));
6914
- log(dm5(` β†’ ν•΄κ²°: 파일 μ‹œμž‘μ— UTF-8 BOM (EF BB BF) μΆ”κ°€`));
6993
+ log(dm5(` β†’ μžλ™ 회볡: leerness env encoding --apply (UTF-8 BOM μžλ™ μΆ”κ°€, 1.9.242)`));
6994
+ log(dm5(` β†’ 마감 μ‹œ μžλ™: session close --auto-fix-encoding (1.9.243)`));
6915
6995
  }
6916
6996
  } catch {}
6917
6997
 
@@ -10777,6 +10857,20 @@ function sessionClose(root, opts = {}) {
10777
10857
  totalClasses: analyses.reduce((s, a) => s + a.classes, 0)
10778
10858
  };
10779
10859
  } catch {}
10860
+ // 1.9.242: envInfo 톡합 (session close JSON 10번째 톡합 ν•„λ“œ) β€” UR-0014 2단계
10861
+ try {
10862
+ const runtimeEnv = _collectRuntimeEnv();
10863
+ const encScan = _scanShellScriptsEncoding(root);
10864
+ jsonResult.envInfo = {
10865
+ os: runtimeEnv.os.platform,
10866
+ isKoreanWindows: runtimeEnv.locale.isKoreanWindows || false,
10867
+ codepage: runtimeEnv.locale.codepage || null,
10868
+ nodeVersion: runtimeEnv.node.version,
10869
+ shellScriptsScanned: encScan.scanned,
10870
+ encodingRiskCount: encScan.atRisk.length,
10871
+ encodingRiskFiles: encScan.atRisk.slice(0, 5).map(r => r.file)
10872
+ };
10873
+ } catch {}
10780
10874
  } catch {}
10781
10875
  try {
10782
10876
  // 1.9.209: pre-wake-audit μžλ™ μ‹€ν–‰ + μ €μž₯ (sleep μ „ μžλ™ 점검)
@@ -10915,6 +11009,31 @@ function sessionClose(root, opts = {}) {
10915
11009
  }
10916
11010
  }
10917
11011
  } catch {}
11012
+ // 1.9.243: session close --auto-fix-encoding β€” μ…Έ 슀크립트 인코딩 μœ„ν—˜ μžλ™ 회볡 (UR-0014 3단계)
11013
+ // 1.9.224 (--auto-apply-delivered) / 1.9.237 (--auto-cleanup-branches) νŒ¨ν„΄ ν™•μž₯
11014
+ // 마감 μ‹œ ν•œκ΅­μ–΄/일본어/쀑ꡭ어 PowerShell 인코딩 μœ„ν—˜ μžλ™ BOM μΆ”κ°€
11015
+ try {
11016
+ const encScan = _scanShellScriptsEncoding(root);
11017
+ if (encScan.atRisk && encScan.atRisk.length > 0) {
11018
+ if (has('--auto-fix-encoding')) {
11019
+ let ok = 0;
11020
+ for (const r of encScan.atRisk) {
11021
+ try {
11022
+ const fullPath = path.join(root, r.file);
11023
+ const orig = fs.readFileSync(fullPath);
11024
+ const bom = Buffer.from([0xEF, 0xBB, 0xBF]);
11025
+ const fixed = Buffer.concat([bom, orig]);
11026
+ fs.writeFileSync(fullPath, fixed);
11027
+ ok++;
11028
+ } catch {}
11029
+ }
11030
+ log(grn(` βœ“ 인코딩 μœ„ν—˜ ${ok}/${encScan.atRisk.length}건 UTF-8 BOM μžλ™ μΆ”κ°€ (--auto-fix-encoding 1.9.243)`));
11031
+ } else {
11032
+ log(yel(` ⚠ μ…Έ 슀크립트 인코딩 μœ„ν—˜ ${encScan.atRisk.length}건 (1.9.241) β€” μžλ™ 회볡 κ°€λŠ₯`));
11033
+ log(dim(` β†’ leerness env encoding --apply (μˆ˜λ™) λ˜λŠ” session close --auto-fix-encoding (1.9.243 μžλ™)`));
11034
+ }
11035
+ }
11036
+ } catch {}
10918
11037
  // 1.9.232: 마감 μ‹œ pulse ν•œ 쀄 μžλ™ λ…ΈμΆœ β€” λ‹€μŒ λΌμš΄λ“œ μ§„μž… μ‹œ μ¦‰μ‹œ μƒνƒœ 인지
10919
11038
  try {
10920
11039
  const rh = _computeRoundHistory(root);
@@ -13868,6 +13987,31 @@ function driftCheckCmd(root, opts = {}) {
13868
13987
  log(`⚠ auto-fix λ³΄μ•ˆ 회볡 였λ₯˜: ${e.message}`);
13869
13988
  }
13870
13989
  }
13990
+ // 1.9.242: drift check --auto-fix 에 env encoding BOM μžλ™ μΆ”κ°€ 톡합 (μ‚¬μš©μž λͺ…μ‹œ UR-0014 2단계)
13991
+ // 1.9.82 νŒ¨ν„΄ ν™•μž₯ β€” drift 회볡 μ‹œ μ…Έ 슀크립트 인코딩 μœ„ν—˜λ„ μžλ™ ν•΄κ²°
13992
+ if (autoFix) {
13993
+ try {
13994
+ const encScan = _scanShellScriptsEncoding(root);
13995
+ if (encScan.atRisk && encScan.atRisk.length > 0) {
13996
+ log('');
13997
+ log(`🌐 --auto-fix ν™œμ„± (1.9.242) β€” μ…Έ 슀크립트 인코딩 μœ„ν—˜ ${encScan.atRisk.length}건 BOM μžλ™ μΆ”κ°€ 쀑...`);
13998
+ let ok = 0;
13999
+ for (const r of encScan.atRisk) {
14000
+ try {
14001
+ const fullPath = path.join(root, r.file);
14002
+ const orig = fs.readFileSync(fullPath);
14003
+ const bom = Buffer.from([0xEF, 0xBB, 0xBF]);
14004
+ const fixed = Buffer.concat([bom, orig]);
14005
+ fs.writeFileSync(fullPath, fixed);
14006
+ ok++;
14007
+ } catch {}
14008
+ }
14009
+ log(`βœ“ UTF-8 BOM μΆ”κ°€ ${ok}/${encScan.atRisk.length}건 (1.9.242 UR-0014)`);
14010
+ }
14011
+ } catch (e) {
14012
+ log(`⚠ env encoding auto-fix 였λ₯˜ (1.9.242): ${e.message}`);
14013
+ }
14014
+ }
13871
14015
  // 1.9.225: drift check --auto-fix 에 delivered νŒ¨ν„΄ μžλ™ 적용 톡합 (1.9.223/224 μ‹œμŠ€ν…œ 회수)
13872
14016
  // μ‚¬μš©μž μš”μ²­μ— "κ΅¬ν˜„ μ™„λ£Œ" νŒ¨ν„΄μ΄ λˆ„μ λ˜λ©΄ κ°€μ§œ λ―Έλ‹΅ μ‹ ν˜Έκ°€ drift score λ₯Ό κ°€μ€‘μ‹œν‚¬ 수 있음 β†’ μžλ™ 정리.
13873
14017
  // 1.9.82 audit --fix νŒ¨ν„΄κ³Ό 동일: --auto-fix μ‹œ μ¦‰μ‹œ 적용, 적용 ν›„ μž¬κ²€μ‚¬.
@@ -17348,6 +17492,20 @@ function healthCmd(root) {
17348
17492
  totalClasses: analyses.reduce((s, a) => s + a.classes, 0)
17349
17493
  };
17350
17494
  } catch { out.pyFiles = { error: 'pyFiles 점검 μ‹€νŒ¨' }; }
17495
+ // 1.9.242: health --json envInfo 톡합 (3 λͺ…λ Ή 10 ν•„λ“œ β€” UR-0014 2단계)
17496
+ try {
17497
+ const runtimeEnv = _collectRuntimeEnv();
17498
+ const encScan = _scanShellScriptsEncoding(root);
17499
+ out.envInfo = {
17500
+ os: runtimeEnv.os.platform,
17501
+ isKoreanWindows: runtimeEnv.locale.isKoreanWindows || false,
17502
+ codepage: runtimeEnv.locale.codepage || null,
17503
+ nodeVersion: runtimeEnv.node.version,
17504
+ shellScriptsScanned: encScan.scanned,
17505
+ encodingRiskCount: encScan.atRisk.length,
17506
+ encodingRiskFiles: encScan.atRisk.slice(0, 5).map(r => r.file)
17507
+ };
17508
+ } catch { out.envInfo = { error: 'envInfo 점검 μ‹€νŒ¨' }; }
17351
17509
  // 1.9.163: 5λŠ₯λ ₯ 맀트릭슀 μžλ™ 평가 (1.9.155 sub-agent 점검 β†’ μ½”λ“œ 기반 μžλ™ν™”)
17352
17510
  // 각 λŠ₯λ ₯을 μ½”λ“œ grep 으둜 κ²€μΆœ β†’ 0~100 점수. μ‚¬μš©μžκ°€ λ§€ health 호좜 μ‹œ leerness 자기 평가 확인.
17353
17511
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.241",
3
+ "version": "1.9.243",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",