leerness 1.9.240 β†’ 1.9.241

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,50 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.241 β€” 2026-05-24
4
+
5
+ **🌐 leerness env + ν•œκ΅­μ–΄ PowerShell 인코딩 μœ„ν—˜ 사전 감지 (μ‚¬μš©μž λͺ…μ‹œ UR-0014).**
6
+
7
+ ### μ‚¬μš©μž λͺ…μ‹œ (UR-0014)
8
+ > *"leerness μ„€μΉ˜ ν”„λ‘œμ νŠΈμ—μ„œ PowerShell/터미널 ν•œκ΅­μ–΄ 인코딩 였λ₯˜ 사전 감지 β€” OS/μ–Έμ–΄μ„€μ •/ν•˜λ“œμ›¨μ–΄/터미널/SW 버전 데이터 μˆ™μ§€λ‘œ 개발/ν…ŒμŠ€νŠΈ/디버깅 μ‹œ ν™˜κ²½ ν˜Έν™˜μ„± 보μž₯"*
9
+
10
+ λ°°κ²½: μ‚¬μš©μžκ°€ `3d-map-maker` ν”„λ‘œμ νŠΈμ—μ„œ `npm run setup` β†’ `setup.ps1` νŒŒμ‹± 였λ₯˜ (ν•œκ΅­μ–΄ + BOM μ—†λŠ” UTF-8 β†’ PowerShell CP949 μ˜€μΈμ‹)
11
+
12
+ ### 1. `leerness env summary` μƒˆ λͺ…λ Ή (1.9.145 envCheck 와 λ³„κ°œ)
13
+ - ν™˜κ²½ μ’…ν•© 좜λ ₯: OS / Node / Locale / ν•œκ΅­μ–΄ Windows 감지 / Hardware / Terminal (PowerShell 버전) / Tools (git/npm/python)
14
+ - `chcp` 좜λ ₯으둜 μ½”λ“œνŽ˜μ΄μ§€ μžλ™ 감지 (949 = ν•œκ΅­μ–΄)
15
+ - `--json` μ˜΅μ…˜
16
+ - μ‹ κ·œ helper: `_collectRuntimeEnv()` (κΈ°μ‘΄ `_detectEnvironment` 와 λͺ…λͺ… 뢄리)
17
+
18
+ ### 2. `leerness env encoding` μƒˆ λͺ…λ Ή β€” μ…Έ 슀크립트 인코딩 μœ„ν—˜ 감지
19
+ - `.ps1` / `.bat` / `.cmd` / `.sh` μžλ™ μŠ€μΊ”
20
+ - BOM μ—†λŠ” λΉ„-ASCII λ°”μ΄νŠΈ κ²€μΆœ (μž¬κ·€, 4096 byte 이내)
21
+ - μœ„ν—˜ μ‹œ: `Windows ν•œκ΅­μ–΄ PowerShell μ—μ„œ CP949 둜 μ˜€μΈμ‹ κ°€λŠ₯ (BOM μΆ”κ°€ ꢌμž₯)`
22
+ - ν•΄κ²°μ±… μžλ™ μ œμ‹œ (UTF-8 BOM μΆ”κ°€ λ˜λŠ” `$OutputEncoding` μ„€μ •)
23
+
24
+ ### 3. handoff body μžλ™ λ…ΈμΆœ
25
+ - `## ⚠ μ…Έ 슀크립트 인코딩 μœ„ν—˜ N건 (1.9.241, UR-0014)` λ³Έλ¬Έ μ„Ήμ…˜
26
+ - top 3 μœ„ν—˜ 파일 + ν•΄κ²° μ•ˆλ‚΄ (BOM μΆ”κ°€)
27
+ - `leerness env encoding` κ°€μ΄λ“œ μžλ™
28
+
29
+ ### 4. MCP 69번째 도ꡬ β€” `leerness_env_info`
30
+ - μ™ΈλΆ€ AI κ°€ ν™˜κ²½ ν˜Έν™˜μ„± 사전 인지 β†’ 인코딩 였λ₯˜ 예방
31
+ - `encodingCheck: true` μ˜΅μ…˜ μ‹œ μ…Έ 슀크립트 μŠ€μΊ”
32
+ - MCP 68 β†’ **69** (+1)
33
+
34
+ ### 5. λˆ„μ  νšŒκ·€ (1.9.207~240) β€” λͺ¨λ‘ μœ μ§€
35
+ - κΈ°μ‘΄ `_detectEnvironment(root)` (1.9.145) 와 좩돌 없이 곡쑴
36
+
37
+ ### 6. stress-v186 β€” 16/16 PASS
38
+ - 1.9.241 (7): VERSION + env summary + JSON + encoding μœ„ν—˜ 감지 + μ•ˆμ „ μ‹œ μœ„ν—˜ 0 + handoff body + MCP 69
39
+ - μ„±λŠ₯ (1): cold_start avg 598ms
40
+ - λˆ„μ  νšŒκ·€ (8): 1.9.207~240
41
+
42
+ ### 7. μžλ™ release (103 λΌμš΄λ“œ main-push streak Β· 64 λΌμš΄λ“œ npm publish streak)
43
+
44
+ 🌐 **ν•œκ΅­μ–΄ Windows ν™˜κ²½ ν˜Έν™˜μ„± 보강** β€” μ‚¬μš©μž 보고 .ps1 νŒŒμ‹± 였λ₯˜ 같은 μΌ€μ΄μŠ€ 사전 감지
45
+
46
+ ---
47
+
3
48
  ## 1.9.240 β€” 2026-05-24
4
49
 
5
50
  **🐍 UR-0013 2단계: handoff/session close/health --json pyFiles (9 ν•„λ“œ) + ν—€λ“œλΌμΈ μžλ™.**
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.240-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v185-16%2F16-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-68-brightgreen)]() [![cli](https://img.shields.io/badge/CLI_commands-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-196-blueviolet)]() [![main-push](https://img.shields.io/badge/πŸŽ‰_main--push_streak-102_rounds-success)]() [![npm-streak](https://img.shields.io/badge/πŸŽ‰_npm--publish-63_streak-success)]() [![json-9fields](https://img.shields.io/badge/πŸ“œ_JSON_톡합-9%2F9_ν•„λ“œ-blueviolet)]() [![langs](https://img.shields.io/badge/🐍_md_%2B_py-μžλ™_detect-blueviolet)]() [![perf](https://img.shields.io/badge/cold_start-386ms-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.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)]()
6
6
 
7
7
  ```
8
8
  ╔══════════════════════════════════════════════════════════════╗
@@ -12,9 +12,9 @@
12
12
  β•‘ β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•”β•β•β• β–ˆβ–ˆβ•”β•β•β• β–ˆβ–ˆβ•”β•β•β–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β•šβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•”β•β•β• β•šβ•β•β•β•β–ˆβ–ˆβ•‘ β•‘
13
13
  β•‘ β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ•‘ β–ˆβ–ˆβ•‘β–ˆβ–ˆβ•‘ β•šβ–ˆβ–ˆβ–ˆβ–ˆβ•‘β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•—β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ•‘ β•‘
14
14
  β•‘ β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β• β•šβ•β•β•šβ•β• β•šβ•β•β•β•β•šβ•β•β•β•β•β•β•β•šβ•β•β•β•β•β•β• β•‘
15
- β•‘ v1.9.240 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.241 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ 🐍 JSON 9 ν•„λ“œ (3 λͺ…λ Ή Γ— 9 = 27 톡합 β€” UR-0013 2단계) β•‘
17
+ β•‘ 🌐 env encoding μœ„ν—˜ 사전 감지 (μ‚¬μš©μž λͺ…μ‹œ UR-0014) β•‘
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.240';
10
+ const VERSION = '1.9.241';
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 처리).
@@ -2376,6 +2376,157 @@ function _computeRecentChanges(root, limit = 5) {
2376
2376
  return items;
2377
2377
  }
2378
2378
 
2379
+ // 1.9.241: ν™˜κ²½ 정보 μ’…ν•© μˆ˜μ§‘ + μ…Έ 슀크립트 인코딩 μœ„ν—˜ 감지 (μ‚¬μš©μž λͺ…μ‹œ UR-0014)
2380
+ // μ‚¬μš©μž 보고: Windows + ν•œκ΅­μ–΄ PowerShell μ—μ„œ .ps1 UTF-8 (no BOM) 파일이 CP949 둜 잘λͺ» μΈμ‹λ˜μ–΄ νŒŒμ‹± 였λ₯˜
2381
+ // ν•΄κ²°: leerness κ°€ ν™˜κ²½ 정보 + μ…Έ 슀크립트 인코딩을 사전 감지 β†’ handoff body κ²½κ³ 
2382
+ // 주의: κΈ°μ‘΄ _detectEnvironment (1.9.145) 와 λͺ…λͺ… 좩돌 β†’ _collectRuntimeEnv 둜 뢄리
2383
+ function _collectRuntimeEnv() {
2384
+ const env = {
2385
+ os: { platform: os.platform(), release: os.release(), arch: os.arch() },
2386
+ node: { version: process.version, exe: process.execPath },
2387
+ locale: {
2388
+ LANG: process.env.LANG || null,
2389
+ LC_ALL: process.env.LC_ALL || null,
2390
+ LC_CTYPE: process.env.LC_CTYPE || null,
2391
+ // Windows ν•œκ΅­μ–΄ ν™˜κ²½ detect
2392
+ OEMCP: process.env.OEMCP || null, // CP949 = 949
2393
+ isKoreanWindows: false
2394
+ },
2395
+ hardware: {
2396
+ cpus: os.cpus().length,
2397
+ memGB: Math.round(os.totalmem() / 1024 / 1024 / 1024),
2398
+ hostname: os.hostname()
2399
+ },
2400
+ terminal: {
2401
+ isTTY: !!(process.stdout && process.stdout.isTTY),
2402
+ term: process.env.TERM || null,
2403
+ shell: process.env.SHELL || process.env.ComSpec || null,
2404
+ powershell: null
2405
+ },
2406
+ tools: { git: null, npm: null, python: null, python3: null }
2407
+ };
2408
+ // ν•œκ΅­μ–΄ Windows 감지
2409
+ try {
2410
+ if (env.os.platform === 'win32') {
2411
+ // chcp 좜λ ₯으둜 μ½”λ“œνŽ˜μ΄μ§€ 확인
2412
+ const r = cp.spawnSync('chcp', [], { encoding: 'utf8', shell: true, timeout: 3000 });
2413
+ const m = (r.stdout || '').match(/(\d{3,4})/);
2414
+ if (m) {
2415
+ const cp_num = parseInt(m[1], 10);
2416
+ env.locale.codepage = cp_num;
2417
+ if (cp_num === 949 || cp_num === 51949) env.locale.isKoreanWindows = true;
2418
+ }
2419
+ }
2420
+ } catch {}
2421
+ // PowerShell 감지
2422
+ try {
2423
+ if (env.os.platform === 'win32') {
2424
+ const r = cp.spawnSync('powershell', ['-NoProfile', '-Command', '$PSVersionTable.PSVersion.Major'], { encoding: 'utf8', timeout: 5000 });
2425
+ if (r.status === 0) env.terminal.powershell = (r.stdout || '').trim().split('\n')[0];
2426
+ }
2427
+ } catch {}
2428
+ // 도ꡬ 버전 감지
2429
+ for (const [tool, cmd] of [['git', 'git --version'], ['npm', 'npm --version'], ['python', 'python --version'], ['python3', 'python3 --version']]) {
2430
+ try {
2431
+ const r = cp.spawnSync(cmd.split(' ')[0], cmd.split(' ').slice(1), { encoding: 'utf8', shell: true, timeout: 3000 });
2432
+ if (r.status === 0) {
2433
+ const v = (r.stdout || r.stderr || '').trim().split('\n')[0];
2434
+ env.tools[tool] = v;
2435
+ }
2436
+ } catch {}
2437
+ }
2438
+ return env;
2439
+ }
2440
+
2441
+ // μ…Έ 슀크립트 (.ps1/.bat/.cmd/.sh) 인코딩 μœ„ν—˜ 감지
2442
+ // μœ„ν—˜ μ‹œκ·Έλ„: ν•œκ΅­μ–΄ (λ˜λŠ” λΉ„-ASCII) 문자 + BOM μ—†μŒ + Windows ν™˜κ²½
2443
+ function _scanShellScriptsEncoding(root) {
2444
+ const result = { scanned: 0, atRisk: [], notes: [] };
2445
+ function walk(dir, depth = 0) {
2446
+ if (depth > 3) return; // λ„ˆλ¬΄ 깊이 λ“€μ–΄κ°€μ§€ μ•ŠμŒ
2447
+ let entries;
2448
+ try { entries = fs.readdirSync(dir, { withFileTypes: true }); } catch { return; }
2449
+ for (const e of entries) {
2450
+ const fp = path.join(dir, e.name);
2451
+ if (e.isDirectory()) {
2452
+ if (/^(node_modules|\.git|__pycache__|venv|\.venv|dist|build)$/.test(e.name)) continue;
2453
+ walk(fp, depth + 1);
2454
+ } else if (e.isFile() && /\.(ps1|bat|cmd|sh)$/i.test(e.name)) {
2455
+ result.scanned++;
2456
+ try {
2457
+ const buf = fs.readFileSync(fp);
2458
+ const hasBOM = buf.length >= 3 && buf[0] === 0xEF && buf[1] === 0xBB && buf[2] === 0xBF;
2459
+ // λΉ„-ASCII (β‰₯ 0x80) λ°”μ΄νŠΈ 쑴재 μ—¬λΆ€ 검사
2460
+ let nonAscii = false;
2461
+ for (let i = 0; i < Math.min(buf.length, 4096); i++) {
2462
+ if (buf[i] >= 0x80) { nonAscii = true; break; }
2463
+ }
2464
+ if (nonAscii && !hasBOM) {
2465
+ result.atRisk.push({
2466
+ file: path.relative(root, fp),
2467
+ ext: path.extname(fp),
2468
+ hasBOM: false,
2469
+ nonAscii: true,
2470
+ risk: 'Windows ν•œκ΅­μ–΄ PowerShell μ—μ„œ CP949 둜 μ˜€μΈμ‹ κ°€λŠ₯ (BOM μΆ”κ°€ ꢌμž₯)'
2471
+ });
2472
+ }
2473
+ } catch {}
2474
+ }
2475
+ }
2476
+ }
2477
+ walk(root);
2478
+ if (result.atRisk.length > 0) {
2479
+ result.notes.push('ν•΄κ²°: 파일 μ‹œμž‘μ— UTF-8 BOM μΆ”κ°€ (EF BB BF) λ˜λŠ” .ps1 첫 쀄에 $OutputEncoding/[Console]::OutputEncoding μ„€μ •');
2480
+ }
2481
+ return result;
2482
+ }
2483
+
2484
+ function envCmd(root, sub) {
2485
+ root = absRoot(root);
2486
+ const isTty = process.stdout && process.stdout.isTTY;
2487
+ const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
2488
+ const gr = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
2489
+ const yl = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
2490
+ const rd = s => isTty ? `\x1b[31m${s}\x1b[0m` : s;
2491
+ const dm = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
2492
+ if (sub === 'encoding-check') {
2493
+ const result = _scanShellScriptsEncoding(root);
2494
+ if (has('--json')) { log(JSON.stringify(result, null, 2)); return; }
2495
+ log(cy(`# leerness env encoding-check (1.9.241, UR-0014) β€” μ…Έ 슀크립트 인코딩 μœ„ν—˜ 감지`));
2496
+ log('');
2497
+ log(` μŠ€μΊ”: ${gr(String(result.scanned))} 파일 (.ps1/.bat/.cmd/.sh)`);
2498
+ if (result.atRisk.length === 0) {
2499
+ log(gr(` βœ“ 인코딩 μœ„ν—˜ μ—†μŒ`));
2500
+ } else {
2501
+ log(rd(` ⚠ μœ„ν—˜ ${result.atRisk.length}건 (BOM μ—†λŠ” λΉ„-ASCII):`));
2502
+ result.atRisk.slice(0, 10).forEach(r => {
2503
+ log(` β€’ ${r.file}`);
2504
+ log(dm(` ${r.risk}`));
2505
+ });
2506
+ log('');
2507
+ result.notes.forEach(n => log(yl(` πŸ’‘ ${n}`)));
2508
+ }
2509
+ return;
2510
+ }
2511
+ // default: env summary
2512
+ const env = _collectRuntimeEnv();
2513
+ if (has('--json')) { log(JSON.stringify(env, null, 2)); return; }
2514
+ log(cy(`# leerness env (1.9.241, UR-0014) β€” ν™˜κ²½ μ’…ν•©`));
2515
+ log('');
2516
+ log(` πŸ’» OS: ${gr(env.os.platform)} ${env.os.release} (${env.os.arch})`);
2517
+ log(` πŸ“¦ Node: ${gr(env.node.version)}`);
2518
+ log(` 🌐 Locale: LANG=${env.locale.LANG || 'unset'} Β· CP=${env.locale.codepage || 'unknown'}${env.locale.isKoreanWindows ? yl(' (ν•œκ΅­μ–΄ Windows)') : ''}`);
2519
+ log(` πŸ–₯ Hardware: ${env.hardware.cpus} CPUs Β· ${env.hardware.memGB} GB RAM`);
2520
+ log(` πŸ–± Terminal: TTY=${env.terminal.isTTY}${env.terminal.powershell ? ` Β· PowerShell v${env.terminal.powershell}` : ''}`);
2521
+ log(` πŸ”§ Tools:`);
2522
+ for (const [k, v] of Object.entries(env.tools)) {
2523
+ log(` ${v ? gr('βœ“') : dm('βœ—')} ${k}: ${v || dm('not found')}`);
2524
+ }
2525
+ log('');
2526
+ log(dm(` β†’ 인코딩 검사: leerness env encoding-check (μ…Έ 슀크립트 .ps1/.bat μœ„ν—˜ 감지)`));
2527
+ log(dm(` β†’ JSON 좜λ ₯: leerness env --json`));
2528
+ }
2529
+
2379
2530
  // 1.9.231: leerness pulse β€” ν•œ 쀄 μ’…ν•© μš”μ•½ (10 핡심 μ§€ν‘œ)
2380
2531
  // handoff ν—€λ“œλΌμΈμ˜ μΆ•μ•½ 버전 β€” μ‚¬μš©μžκ°€ λΉ λ₯΄κ²Œ μƒνƒœ νŒŒμ•…
2381
2532
  // 응닡 (--json): { version, roundCount, mcpTools, memorySurface, security, health, driftScore, nextMilestone, etaDays, abnormal }
@@ -6743,6 +6894,27 @@ function handoff(root) {
6743
6894
  }
6744
6895
  } catch {}
6745
6896
 
6897
+ // 1.9.241: handoff body μ…Έ 슀크립트 인코딩 μœ„ν—˜ 감지 (μ‚¬μš©μž λͺ…μ‹œ UR-0014)
6898
+ // ν•œκ΅­μ–΄ Windows + .ps1/.bat λΉ„-ASCII + BOM μ—†μŒ β†’ CP949 μ˜€μΈμ‹ μœ„ν—˜
6899
+ try {
6900
+ const scan = _scanShellScriptsEncoding(root);
6901
+ if (scan.atRisk && scan.atRisk.length > 0) {
6902
+ const isTty = process.stdout && process.stdout.isTTY;
6903
+ const rd5 = s => isTty ? `\x1b[31m${s}\x1b[0m` : s;
6904
+ const yl5 = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
6905
+ const dm5 = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
6906
+ log('');
6907
+ log(rd5(`## ⚠ μ…Έ 슀크립트 인코딩 μœ„ν—˜ ${scan.atRisk.length}건 (1.9.241, UR-0014)`));
6908
+ scan.atRisk.slice(0, 3).forEach(r => {
6909
+ log(yl5(` β€’ ${r.file}`));
6910
+ log(dm5(` ${r.risk}`));
6911
+ });
6912
+ if (scan.atRisk.length > 3) log(dm5(` ... +${scan.atRisk.length - 3}건 더`));
6913
+ log(dm5(` β†’ 상세: leerness env encoding`));
6914
+ log(dm5(` β†’ ν•΄κ²°: 파일 μ‹œμž‘μ— UTF-8 BOM (EF BB BF) μΆ”κ°€`));
6915
+ }
6916
+ } catch {}
6917
+
6746
6918
  // 1.9.237: handoff λ³Έλ¬Έ release/* λˆ„μ  κ²½κ³  β€” 50+ branches merged μ‹œ μžλ™ μ•ˆλ‚΄
6747
6919
  // 1.9.220 λΉ„μ •μƒμ’…λ£Œ release-branch-pending μ‹ ν˜Έμ˜ λ³Έλ¬Έ 보강 (handoff ν—€λ“œλΌμΈμ€ β€˜πŸ”Œ λΉ„μ •μƒμ’…λ£Œβ€™ 만 ν‘œμ‹œ)
6748
6920
  try {
@@ -14743,7 +14915,8 @@ function mcpServeCmd(root) {
14743
14915
  { name: 'leerness_commands', description: '1.9.233 β€” μΉ΄ν…Œκ³ λ¦¬ν™”λœ 전체 CLI λͺ…λ Ή λͺ©λ‘ (9 μΉ΄ν…Œκ³ λ¦¬). 응닡: { version, totalCommands, categories: { status, task, memory, audit, workflow, release, skill, bridge, config } }. μ™ΈλΆ€ AIκ°€ "leerness κ°€ μ œκ³΅ν•˜λŠ” λͺ…령이 뭐가 μžˆλ‚˜"λ₯Ό 직접 회수. 맀뉴얼/도움말 동적 생성. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
14744
14916
  { name: 'leerness_release_cleanup', description: '1.9.236 (1.9.235 μžλ™ 회수) β€” local release/* branches 정리. main 에 merge된 κ²ƒλ§Œ 후보 (unmerged 보호, ν˜„μž¬ branch 보호). 응닡: { apply, keep, total, merged, unmerged, deleteCount, toDelete[], recent[], unmergedSample[] }. μ™ΈλΆ€ AIκ°€ "운영 λˆ„μ  release branches 정리 κ°€λŠ₯?"을 회수. κΈ°λ³Έ dry-run, apply: true μ‹œμ—λ§Œ μ‹€ μ‚­μ œ. 인자: { path?, apply? (default false), keep? (default 5) }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, apply: { type: 'boolean' }, keep: { type: 'number' } } } },
14745
14917
  { name: 'leerness_py_check', description: '1.9.239 (μ‚¬μš©μž λͺ…μ‹œ UR-0013) β€” Python 파일 뢄석. μ˜μ‘΄μ„± 0 regex fallback. .md μ™Έ .py 도 leerness 인지. 응닡: { totalFiles, totalLOC, totalImports, totalFuncs, totalClasses, totalTodos, biggest: [{ file, loc, funcs, classes }] }. μ™ΈλΆ€ AIκ°€ "이 ν”„λ‘œμ νŠΈ Python ν‘œλ©΄μ΄ μ–Όλ§ˆλ‚˜ λ˜λ‚˜"λ₯Ό 회수. 인자: { path? }', inputSchema: { type: 'object', properties: { path: { type: 'string' } } } },
14746
- { name: 'leerness_agent_mode', description: '1.9.239 (μ‚¬μš©μž λͺ…μ‹œ UR-0013) β€” 자율 λͺ¨λ“œ μ „μš© 톡합 λͺ…λ Ή. start: handoff + drift --auto-fix + session-resume --auto-fix (μ§„μž…). tick: pulse ν•œ 쀄 (λ§€ λΌμš΄λ“œ). stop: session close --auto-apply-delivered --auto-cleanup-branches (마감). μ™ΈλΆ€ AI κ°€ 자율 λΌμš΄λ“œ μ§„μž…/λ§€ λΌμš΄λ“œ/λ§ˆκ°μ„ 단일 호좜둜 μˆ˜ν–‰. 인자: { path?, sub (required: "start"|"tick"|"stop"|"help") }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string', enum: ['start', 'tick', 'stop', 'help'] } }, required: ['sub'] } }
14918
+ { name: 'leerness_agent_mode', description: '1.9.239 (μ‚¬μš©μž λͺ…μ‹œ UR-0013) β€” 자율 λͺ¨λ“œ μ „μš© 톡합 λͺ…λ Ή. start: handoff + drift --auto-fix + session-resume --auto-fix (μ§„μž…). tick: pulse ν•œ 쀄 (λ§€ λΌμš΄λ“œ). stop: session close --auto-apply-delivered --auto-cleanup-branches (마감). μ™ΈλΆ€ AI κ°€ 자율 λΌμš΄λ“œ μ§„μž…/λ§€ λΌμš΄λ“œ/λ§ˆκ°μ„ 단일 호좜둜 μˆ˜ν–‰. 인자: { path?, sub (required: "start"|"tick"|"stop"|"help") }', inputSchema: { type: 'object', properties: { path: { type: 'string' }, sub: { type: 'string', enum: ['start', 'tick', 'stop', 'help'] } }, required: ['sub'] } },
14919
+ { name: 'leerness_env_info', description: '1.9.241 (μ‚¬μš©μž λͺ…μ‹œ UR-0014) β€” ν™˜κ²½ μ’…ν•© 정보 회수. OS / μ–Έμ–΄ (LANG, μ½”λ“œνŽ˜μ΄μ§€) / ν•œκ΅­μ–΄ Windows / ν•˜λ“œμ›¨μ–΄ / 터미널 (TTY, PowerShell 버전) / 도ꡬ 버전 (git, npm, python). μ™ΈλΆ€ AI κ°€ ν™˜κ²½ ν˜Έν™˜μ„±μ„ 미리 인지 β†’ 인코딩 였λ₯˜ 예방. 응닡: { os, node, locale, hardware, terminal, tools }. 인자: { path?, encodingCheck? }. encodingCheck: true μ‹œ μ…Έ 슀크립트 (.ps1/.bat/.cmd/.sh) BOM μ—†λŠ” λΉ„-ASCII μœ„ν—˜ 감지', inputSchema: { type: 'object', properties: { path: { type: 'string' }, encodingCheck: { type: 'boolean' } } } }
14747
14920
  ];
14748
14921
 
14749
14922
  function send(obj) {
@@ -14928,6 +15101,10 @@ function mcpServeCmd(root) {
14928
15101
  // 1.9.239 (UR-0013): 자율 λͺ¨λ“œ 톡합 (start/tick/stop)
14929
15102
  cliArgs = ['agent-mode', String(args.sub || 'help'), '--path', targetPath];
14930
15103
  break;
15104
+ case 'leerness_env_info':
15105
+ // 1.9.241 (UR-0014): ν™˜κ²½ μ’…ν•© + 인코딩 μœ„ν—˜ 감지
15106
+ cliArgs = ['env', args.encodingCheck === true ? 'encoding' : 'summary', '--path', targetPath, '--json'];
15107
+ break;
14931
15108
  case 'leerness_release_cleanup':
14932
15109
  // 1.9.236 (1.9.235): local release/* branches 정리
14933
15110
  cliArgs = ['release', 'cleanup', '--json'];
@@ -18616,6 +18793,10 @@ async function main() {
18616
18793
  if (cmd === 'py-check') return pyCheckCmd(arg('--path', process.cwd()));
18617
18794
  // 1.9.239: leerness agent-mode <start|tick|stop> β€” 자율 λͺ¨λ“œ μ „μš© 톡합 λͺ…λ Ή (μ‚¬μš©μž λͺ…μ‹œ UR-0013)
18618
18795
  if (cmd === 'agent-mode') return agentModeCmd(arg('--path', process.cwd()), args[1]);
18796
+ // 1.9.241: leerness env summary|encoding β€” ν™˜κ²½ μ’…ν•© + μ…Έ 슀크립트 인코딩 μœ„ν—˜ (μ‚¬μš©μž λͺ…μ‹œ UR-0014)
18797
+ // κΈ°μ‘΄ env check/sync/detect (1.9.71) 와 μΆ©λŒν•˜μ§€ μ•ŠμŒ β€” summary/encoding μ‹ κ·œ μ„œλΈŒ
18798
+ if (cmd === 'env' && (args[1] === 'summary' || args[1] === 'encoding' || args[1] === 'encoding-check'))
18799
+ return envCmd(arg('--path', process.cwd()), args[1] === 'summary' ? null : 'encoding-check');
18619
18800
  // 1.9.208: leerness constraints <list|check|add> β€” ν”Œλž«νΌ/API μ œμ•½ 사전 체크 (μ‚¬μš©μž λͺ…μ‹œ)
18620
18801
  if (cmd === 'constraints') return constraintsCmd(arg('--path', process.cwd()), args[1], ...args.slice(2));
18621
18802
  // 1.9.209: leerness pre-wake-audit β€” sleep μ „ sub-agent audit (μ‚¬μš©μž λͺ…μ‹œ)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.240",
3
+ "version": "1.9.241",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",