leerness 1.9.256 → 1.9.257

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,30 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.257 — 2026-05-30 — CJK 분류 함수 추출+단위테스트 + release 브랜치 정리 (🎉 80 npm streak)
4
+
5
+ **🧪 1.9.255/256 테스트 인프라 연장 + 🧹 누적 release 브랜치 정리 (handoff 장기 경고 해소).**
6
+
7
+ ### 구현
8
+ 1. **CJK 분류 순수 함수 모듈 스코프 추출** (테스트 가능성):
9
+ - `_classifyCJK(buf, len)` / `_riskLabel(cjk)` — `_scanShellScriptsEncoding` 내부 중첩 → 모듈 스코프로 추출 + export
10
+ - UTF-8 lead byte 기반 Korean(EA-ED)/Japanese(E3)/Chinese(E4-E9) 분류 → CP949/CP932/CP936 위험 판정 (UR-0014 계열)
11
+ - 실 동작 단위 테스트: 한국어/일본어/중국어/ASCII 버퍼 분류 + 동률 시 korean 우선 + len 범위 제한 + risk label 4종
12
+ - env encoding 명령 동작 보존 (추출 부작용 0 — e2e + A3 검증)
13
+ 2. **release 브랜치 누적 정리** (🧹 maintenance):
14
+ - 로컬 release/* 브랜치 **213 → 20** (`release cleanup --apply --keep 12`)
15
+ - handoff 가 수십 라운드 경고하던 "50+ branches 누적" 해소. 태그 + 원격이 히스토리 보존 → 로컬 정리 안전.
16
+
17
+ ### stress-v202 — **24/24 PASS · 100%**
18
+ - 1.9.257 (13): export + _classifyCJK (5) + _riskLabel (5) + env encoding 보존
19
+ - 성능 (1): cold start avg 385ms
20
+ - 누적 회귀 (10): 1.9.207~256 — 실 함수 호출 검증 (_isSecretKey/PATH/posix 등)
21
+
22
+ ### 자동 release (119 main-push streak · 🎉 80 npm publish streak · R213)
23
+
24
+ 🎉 **80 npm publish streak** — 테스트 인프라 3 라운드 누적(1.9.255 require.main + 1.9.256 보안/버전 + 1.9.257 CJK) + 누적 브랜치 정리.
25
+
26
+ ---
27
+
3
28
  ## 1.9.256 — 2026-05-29 — 단위 테스트 인프라 확대 (보안/정확성-핵심 순수 함수)
4
29
 
5
30
  **🧪 1.9.255 require.main 가드 기반 확장: 소스 regex 가 아닌 실제 함수 동작을 검증하는 단위 테스트로 회귀 강화.**
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.256-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v201-24%2F24-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-70-brightgreen)]() [![cli](https://img.shields.io/badge/CLI_commands-57-brightgreen)]() [![json-fields](https://img.shields.io/badge/JSON_통합-11_필드-blueviolet)]() [![rounds](https://img.shields.io/badge/autonomous_R-212-blueviolet)]() [![main-push](https://img.shields.io/badge/🎉_main--push_streak-118_rounds-success)]() [![npm-streak](https://img.shields.io/badge/🎉_npm--publish-79_streak-success)]() [![encoding](https://img.shields.io/badge/🌐_인코딩보호-Windows%2BPOSIX-blueviolet)]() [![perf](https://img.shields.io/badge/cold_start-368ms-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.257-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v202-24%2F24-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-70-brightgreen)]() [![cli](https://img.shields.io/badge/CLI_commands-57-brightgreen)]() [![json-fields](https://img.shields.io/badge/JSON_통합-11_필드-blueviolet)]() [![rounds](https://img.shields.io/badge/autonomous_R-213-blueviolet)]() [![main-push](https://img.shields.io/badge/🎉_main--push_streak-119_rounds-success)]() [![npm-streak](https://img.shields.io/badge/🎉_npm--publish-80_streak-success)]() [![encoding](https://img.shields.io/badge/🌐_인코딩보호-Windows%2BPOSIX-blueviolet)]() [![perf](https://img.shields.io/badge/cold_start-368ms-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.256 AI Agent Reliability Harness + Sandbox ║
15
+ ║ v1.9.257 AI Agent Reliability Harness + Sandbox ║
16
16
  ║ verify · remember · orchestrate · audit · sandbox · drift ║
17
- 🧪 단위 테스트 인프라 확대 (보안/버전 동작 · R212)
17
+ 🎉 80 npm streak · CJK 분류 단위테스트 + 브랜치 정리 (R213)
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.256';
10
+ const VERSION = '1.9.257';
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 처리).
@@ -2559,39 +2559,41 @@ function _terminalEncodingNotice(opts = {}) {
2559
2559
  return { ok, lines };
2560
2560
  }
2561
2561
 
2562
+ // 1.9.243: CJK 분류 — Korean/Japanese/Chinese 비-ASCII 패턴 통계 (UR-0014 3단계)
2563
+ // 1.9.257: _scanShellScriptsEncoding 내부 중첩 → 모듈 스코프로 추출 (순수 함수 — 단위 테스트 가능).
2564
+ // UTF-8 3-byte 시퀀스 시작 바이트로 CJK 언어 분류 (인코딩 오인식 위험 판단용).
2565
+ function _classifyCJK(buf, len) {
2566
+ let korean = 0, japanese = 0, chinese = 0, other = 0;
2567
+ for (let i = 0; i < Math.min(buf.length, len); i++) {
2568
+ const b = buf[i];
2569
+ if (b < 0x80) continue;
2570
+ // Korean Hangul Syllables U+AC00-D7AF → 3-byte: EA B0 80 ~ ED 9F BF
2571
+ if (b >= 0xEA && b <= 0xED) korean++;
2572
+ // Japanese Hiragana/Katakana U+3040-30FF → 3-byte: E3 81 80 ~ E3 83 BF
2573
+ else if (b === 0xE3) japanese++;
2574
+ // CJK Unified Ideographs U+4E00-9FFF → 3-byte: E4 B8 80 ~ E9 BF BF
2575
+ else if (b >= 0xE4 && b <= 0xE9) chinese++;
2576
+ else other++;
2577
+ }
2578
+ return { korean, japanese, chinese, other };
2579
+ }
2580
+ function _riskLabel(cjk) {
2581
+ if (cjk.korean >= cjk.japanese && cjk.korean >= cjk.chinese && cjk.korean > 0) {
2582
+ return { type: 'korean', risk: 'Windows 한국어 PowerShell 에서 CP949 로 오인식 가능 (BOM 추가 권장)' };
2583
+ }
2584
+ if (cjk.japanese > cjk.korean && cjk.japanese >= cjk.chinese) {
2585
+ return { type: 'japanese', risk: 'Windows 일본어 PowerShell 에서 CP932 (Shift-JIS) 로 오인식 가능 (BOM 추가 권장)' };
2586
+ }
2587
+ if (cjk.chinese > 0) {
2588
+ return { type: 'chinese', risk: 'Windows 중국어 PowerShell 에서 CP936 (GBK) 로 오인식 가능 (BOM 추가 권장)' };
2589
+ }
2590
+ return { type: 'non-ascii', risk: 'Windows 비-ASCII 셸 스크립트 — BOM 없는 UTF-8 인코딩 오인식 가능 (BOM 추가 권장)' };
2591
+ }
2592
+
2562
2593
  // 셸 스크립트 (.ps1/.bat/.cmd/.sh) 인코딩 위험 감지
2563
2594
  // 위험 시그널: 한국어 (또는 비-ASCII) 문자 + BOM 없음 + Windows 환경
2564
2595
  function _scanShellScriptsEncoding(root) {
2565
2596
  const result = { scanned: 0, atRisk: [], notes: [] };
2566
- // 1.9.243: CJK 분류 — Korean/Japanese/Chinese 비-ASCII 패턴 통계 (UR-0014 3단계)
2567
- function _classifyCJK(buf, len) {
2568
- let korean = 0, japanese = 0, chinese = 0, other = 0;
2569
- for (let i = 0; i < Math.min(buf.length, len); i++) {
2570
- const b = buf[i];
2571
- if (b < 0x80) continue;
2572
- // UTF-8 3-byte 시퀀스 시작 바이트 분류
2573
- // Korean Hangul Syllables U+AC00-D7AF → 3-byte: EA B0 80 ~ ED 9F BF
2574
- if (b >= 0xEA && b <= 0xED) korean++;
2575
- // Japanese Hiragana/Katakana U+3040-30FF → 3-byte: E3 81 80 ~ E3 83 BF
2576
- else if (b === 0xE3) japanese++;
2577
- // CJK Unified Ideographs U+4E00-9FFF → 3-byte: E4 B8 80 ~ E9 BF BF
2578
- else if (b >= 0xE4 && b <= 0xE9) chinese++;
2579
- else other++;
2580
- }
2581
- return { korean, japanese, chinese, other };
2582
- }
2583
- function _riskLabel(cjk) {
2584
- if (cjk.korean >= cjk.japanese && cjk.korean >= cjk.chinese && cjk.korean > 0) {
2585
- return { type: 'korean', risk: 'Windows 한국어 PowerShell 에서 CP949 로 오인식 가능 (BOM 추가 권장)' };
2586
- }
2587
- if (cjk.japanese > cjk.korean && cjk.japanese >= cjk.chinese) {
2588
- return { type: 'japanese', risk: 'Windows 일본어 PowerShell 에서 CP932 (Shift-JIS) 로 오인식 가능 (BOM 추가 권장)' };
2589
- }
2590
- if (cjk.chinese > 0) {
2591
- return { type: 'chinese', risk: 'Windows 중국어 PowerShell 에서 CP936 (GBK) 로 오인식 가능 (BOM 추가 권장)' };
2592
- }
2593
- return { type: 'non-ascii', risk: 'Windows 비-ASCII 셸 스크립트 — BOM 없는 UTF-8 인코딩 오인식 가능 (BOM 추가 권장)' };
2594
- }
2595
2597
  function walk(dir, depth = 0) {
2596
2598
  if (depth > 3) return; // 너무 깊이 들어가지 않음
2597
2599
  let entries;
@@ -20053,5 +20055,7 @@ module.exports = {
20053
20055
  VERSION,
20054
20056
  _npmGlobalBin, _dirInPath, _leernessResolvable, _pathDiagnose, _registerPath,
20055
20057
  _winPathPsScript, _unixPathBlock, pathSetupCmd,
20056
- _isSecretKey, compareVer, parseHarnessVersion
20058
+ _isSecretKey, compareVer, parseHarnessVersion,
20059
+ // 1.9.257: CJK 인코딩 분류 (UR-0014 계열) 순수 함수 — 단위 테스트
20060
+ _classifyCJK, _riskLabel
20057
20061
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.256",
3
+ "version": "1.9.257",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",