leerness 1.9.254 → 1.9.255

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,38 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.255 — 2026-05-29 — UR-0019 2단계: PATH 등록 실제 테스트/디버그 + require.main 가드
4
+
5
+ **🧪 사용자 명시 (UR-0019 후속 "테스트 및 디버그 다음라운드 참고"): PATH 자동 등록 실제 동작 검증 + 테스트 인프라.**
6
+
7
+ ### 배경
8
+ 1.9.254에서 `path-setup` 구현 후, 실제 `--apply` 등록 로직(Unix shell-rc append, Windows PS-script)의 깊은 테스트/디버그가 필요. 단, 실 PATH 변경은 위험 → 단위 테스트 가능하도록 리팩터링.
9
+
10
+ ### 구현 (테스트 가능성 + 안전 검증)
11
+ 1. **`require.main === module` 가드** (footgun fix):
12
+ - `node harness.js` / `npx leerness` CLI 직접 실행 시에만 `main()` 호출
13
+ - `require('harness.js')` 시 init 부작용 없이 내부 함수 import 가능 → 단위 테스트 활성화
14
+ - 기존 CLI 동작 100% 보존 (e2e 217 검증)
15
+ 2. **순수 함수 추출** (테스트/디버그 가능):
16
+ - `_winPathPsScript(bin)` — Windows User PATH 등록 PowerShell 스크립트 생성 (실행과 분리)
17
+ - `_unixPathBlock(bin)` — Unix shell-rc export 블록 생성 (마커 멱등)
18
+ - `_registerPath` — `process.platform` → `diag.platform` (테스트 시 플랫폼 주입 가능)
19
+ 3. **8종 헬퍼 module.exports** (test 인프라)
20
+
21
+ ### 실제 테스트/디버그 결과 (stress-v200, 실 PATH 미변경)
22
+ - **Unix shell-rc 실제 등록**: temp 파일에 1차 등록(shell-rc) → 2차 멱등(already, export 1줄만) → 마커 존재 → 기존 rc 내용 보존(append-only) ✓
23
+ - **Windows PS-script**: User scope SetEnvironmentVariable / setx 미사용(truncation 회피) / -notcontains 멱등 가드 / bin JSON 이스케이프 ✓
24
+ - **edge**: globalBin null → ok=false / shellRc null → ok=false / _dirInPath trailing-slash 정규화 ✓
25
+
26
+ ### stress-v200 — **27/27 PASS · 100%**
27
+ - 테스트 인프라 (4): require.main 가드 + 8종 export + CLI 동작
28
+ - Unix 실제 등록 (5) + Windows PS (4) + edge (5) + 성능/누적 회귀 (9)
29
+
30
+ ### 자동 release (117 main-push streak · 78 npm publish streak · R211)
31
+
32
+ 🧪 **PATH 등록 실증 + 테스트 인프라** — require.main 가드로 내부 함수 단위 테스트 가능 (이후 라운드 회귀 강화 기반).
33
+
34
+ ---
35
+
3
36
  ## 1.9.254 — 2026-05-29 — UR-0019 leerness CLI PATH 자동 등록
4
37
 
5
38
  **🔗 사용자 명시 (UR-0019): "leerness 설치 시 leerness CLI가 PATH를 자동으로 등록될 수 있게 구현". (테스트/디버그는 다음 라운드 참고)**
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.254-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v199-25%2F25-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-210-blueviolet)]() [![main-push](https://img.shields.io/badge/🎉_main--push_streak-116_rounds-success)]() [![npm-streak](https://img.shields.io/badge/🎉_npm--publish-77_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.255-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v200-27%2F27-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-211-blueviolet)]() [![main-push](https://img.shields.io/badge/🎉_main--push_streak-117_rounds-success)]() [![npm-streak](https://img.shields.io/badge/🎉_npm--publish-78_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.254 AI Agent Reliability Harness + Sandbox ║
15
+ ║ v1.9.255 AI Agent Reliability Harness + Sandbox ║
16
16
  ║ verify · remember · orchestrate · audit · sandbox · drift ║
17
- 🔗 CLI PATH 자동 등록 (path-setup · UR-0019 · R210)
17
+ 🧪 PATH 등록 실증 + require.main 가드 (UR-0019 · R211)
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.254';
10
+ const VERSION = '1.9.255';
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 처리).
@@ -2775,24 +2775,39 @@ function _pathDiagnose() {
2775
2775
  return { globalBin, inPath, resolvable, platform: process.platform, shellRc };
2776
2776
  }
2777
2777
 
2778
+ // 1.9.255 (UR-0019 2단계): Windows User PATH 등록용 PowerShell 스크립트 생성 (순수 함수 — 테스트/디버그 가능)
2779
+ // setx 의 1024자 truncation 회피 위해 [Environment]::SetEnvironmentVariable User scope 사용.
2780
+ // 멱등: 현재 User PATH 에 이미 있으면 EXISTS, 없으면 append 후 ADDED. 시스템 PATH 불변 (관리자 권한 불요).
2781
+ function _winPathPsScript(bin) {
2782
+ return [
2783
+ "$b = " + JSON.stringify(bin) + ";",
2784
+ "$u = [Environment]::GetEnvironmentVariable('PATH','User'); if (-not $u) { $u = '' }",
2785
+ "$parts = $u.Split(';') | Where-Object { $_ -ne '' }",
2786
+ "if ($parts -notcontains $b) {",
2787
+ " $nu = if ($u -eq '') { $b } else { $u.TrimEnd(';') + ';' + $b }",
2788
+ " [Environment]::SetEnvironmentVariable('PATH', $nu, 'User')",
2789
+ " Write-Output 'ADDED'",
2790
+ "} else { Write-Output 'EXISTS' }"
2791
+ ].join(' ');
2792
+ }
2793
+
2794
+ // 1.9.255 (UR-0019 2단계): Unix shell rc 등록 블록 마커 (순수 — 테스트/디버그 가능, 멱등 키)
2795
+ const _UNIX_PATH_MARKER = '# >>> leerness PATH (managed) >>>';
2796
+ const _UNIX_PATH_END = '# <<< leerness PATH <<<';
2797
+ function _unixPathBlock(bin) {
2798
+ return `\n${_UNIX_PATH_MARKER}\nexport PATH="$PATH:${bin}"\n${_UNIX_PATH_END}\n`;
2799
+ }
2800
+
2778
2801
  // PATH 에 globalBin 등록 (플랫폼별 안전 방법). 반환: { ok, method, detail }
2802
+ // 1.9.255: process.platform → diag.platform 으로 변경 (테스트 가능 — diag.platform 은 실사용 시 process.platform 과 동일).
2779
2803
  function _registerPath(diag) {
2780
2804
  const bin = diag.globalBin;
2781
2805
  if (!bin) return { ok: false, method: 'none', detail: 'npm global bin 경로를 찾을 수 없음' };
2782
- if (process.platform === 'win32') {
2783
- // Windows: PowerShell [Environment]::SetEnvironmentVariable User scope — setx 의 1024자 truncation 회피
2784
- // 현재 User PATH 읽어 중복 없으면 append (멱등). 시스템 PATH 는 건드리지 않음 (관리자 권한 불요).
2806
+ const platform = diag.platform || process.platform;
2807
+ if (platform === 'win32') {
2808
+ // Windows: PowerShell [Environment]::SetEnvironmentVariable User scope (멱등, 관리자 권한 불요)
2785
2809
  try {
2786
- const ps = [
2787
- "$b = " + JSON.stringify(bin) + ";",
2788
- "$u = [Environment]::GetEnvironmentVariable('PATH','User'); if (-not $u) { $u = '' }",
2789
- "$parts = $u.Split(';') | Where-Object { $_ -ne '' }",
2790
- "if ($parts -notcontains $b) {",
2791
- " $nu = if ($u -eq '') { $b } else { $u.TrimEnd(';') + ';' + $b }",
2792
- " [Environment]::SetEnvironmentVariable('PATH', $nu, 'User')",
2793
- " Write-Output 'ADDED'",
2794
- "} else { Write-Output 'EXISTS' }"
2795
- ].join(' ');
2810
+ const ps = _winPathPsScript(bin);
2796
2811
  const r = cp.spawnSync('powershell', ['-NoProfile', '-Command', ps], { encoding: 'utf8', timeout: 10000 });
2797
2812
  const out = (r.stdout || '').trim();
2798
2813
  if (r.status === 0 && /ADDED/.test(out)) return { ok: true, method: 'powershell-user-path', detail: `User PATH 에 추가됨 (새 터미널부터 적용): ${bin}` };
@@ -2803,13 +2818,11 @@ function _registerPath(diag) {
2803
2818
  // Unix: shell rc 파일에 export 블록 append (멱등 — 마커로 중복 차단)
2804
2819
  try {
2805
2820
  const rc = diag.shellRc;
2806
- const MARKER = '# >>> leerness PATH (1.9.254) >>>';
2807
- const END = '# <<< leerness PATH <<<';
2821
+ if (!rc) return { ok: false, method: 'shell-rc', detail: 'shell rc 경로 미확인' };
2808
2822
  let cur = '';
2809
2823
  try { cur = fs.readFileSync(rc, 'utf8'); } catch {}
2810
- if (cur.includes(MARKER)) return { ok: true, method: 'already', detail: `${rc} 에 이미 등록됨` };
2811
- const block = `\n${MARKER}\nexport PATH="$PATH:${bin}"\n${END}\n`;
2812
- fs.appendFileSync(rc, block, 'utf8');
2824
+ if (cur.includes(_UNIX_PATH_MARKER)) return { ok: true, method: 'already', detail: `${rc} 에 이미 등록됨` };
2825
+ fs.appendFileSync(rc, _unixPathBlock(bin), 'utf8');
2813
2826
  return { ok: true, method: 'shell-rc', detail: `${rc} 에 추가됨 (source 또는 새 터미널부터 적용): ${bin}` };
2814
2827
  } catch (e) { return { ok: false, method: 'shell-rc', detail: `예외: ${e.message}` }; }
2815
2828
  }
@@ -20024,6 +20037,18 @@ async function main() {
20024
20037
  }
20025
20038
 
20026
20039
  // 1.9.4 B: main 종료 후 exitCode를 명시적으로 process.exit으로 강제 (셸/wrapper 차 무시).
20027
- main()
20028
- .then(() => { if (process.exitCode && process.exitCode !== 0) process.exit(process.exitCode); })
20029
- .catch(err => { fail(err && err.message ? err.message : String(err)); process.exit(1); });
20040
+ // 1.9.255 (UR-0019 2단계): require.main 가드 — `require('harness.js')` 시 main() 미실행 (init 부작용 차단 + 내부 함수 단위 테스트 가능).
20041
+ // CLI 직접 실행 (node harness.js / npx leerness) 시에만 main() 호출. 기존 동작 100% 보존.
20042
+ if (require.main === module) {
20043
+ main()
20044
+ .then(() => { if (process.exitCode && process.exitCode !== 0) process.exit(process.exitCode); })
20045
+ .catch(err => { fail(err && err.message ? err.message : String(err)); process.exit(1); });
20046
+ }
20047
+
20048
+ // 1.9.255: 테스트/디버그용 내부 함수 export (require.main 가드 덕분에 require 시 CLI 미실행).
20049
+ // PATH 등록 (UR-0019) 검증 + 향후 단위 테스트 확장에 사용.
20050
+ module.exports = {
20051
+ VERSION,
20052
+ _npmGlobalBin, _dirInPath, _leernessResolvable, _pathDiagnose, _registerPath,
20053
+ _winPathPsScript, _unixPathBlock, pathSetupCmd
20054
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.254",
3
+ "version": "1.9.255",
4
4
  "description": "Leerness: 비파괴 마이그레이션, 자동 버전 감지·업데이트, 계획/진행/핸드오프 자동화, 게으름·시크릿·인코딩 자동 가드, Claude Code 슬래시 통합을 갖춘 한국어 우선 AI 개발 하네스.",
5
5
  "keywords": [
6
6
  "leerness",