leerness 1.9.253 → 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 +72 -0
- package/README.md +3 -3
- package/bin/harness.js +199 -4
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,77 @@
|
|
|
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
|
+
|
|
36
|
+
## 1.9.254 — 2026-05-29 — UR-0019 leerness CLI PATH 자동 등록
|
|
37
|
+
|
|
38
|
+
**🔗 사용자 명시 (UR-0019): "leerness 설치 시 leerness CLI가 PATH를 자동으로 등록될 수 있게 구현". (테스트/디버그는 다음 라운드 참고)**
|
|
39
|
+
|
|
40
|
+
### 배경
|
|
41
|
+
`npm i -g leerness` 설치 후 npm global bin 디렉토리가 PATH에 없으면 `leerness` 명령이 동작하지 않음 (특히 Windows nvm/수동 Node 설치 환경). 설치 직후 자동 감지 + 안전 등록.
|
|
42
|
+
|
|
43
|
+
### 구현 (`leerness path-setup [--apply] [--json]` + install 자동 안내)
|
|
44
|
+
1. **진단 헬퍼 5종**:
|
|
45
|
+
- `_npmGlobalBin()` — `npm prefix -g` 기반 global bin 감지 (Windows=prefix, Unix=prefix/bin, fallback=node dir)
|
|
46
|
+
- `_dirInPath(dir)` — 플랫폼별 정규화(대소문자/구분자/trailing slash) 후 PATH 포함 확인
|
|
47
|
+
- `_leernessResolvable()` — `where`/`which` 로 실제 실행 가능 여부
|
|
48
|
+
- `_pathDiagnose()` — globalBin/inPath/resolvable/shellRc 종합
|
|
49
|
+
- `_registerPath(diag)` — 플랫폼별 안전 등록
|
|
50
|
+
2. **플랫폼별 안전 등록**:
|
|
51
|
+
- **Windows**: PowerShell `[Environment]::SetEnvironmentVariable('PATH', ..., 'User')` — User scope (관리자 권한 불요), `setx`의 1024자 truncation 회피, 멱등(중복 시 EXISTS)
|
|
52
|
+
- **Unix**: shell rc(`.zshrc`/`.bashrc`/`.profile`) 에 `export PATH` 블록 append — 마커(`# >>> leerness PATH`)로 멱등
|
|
53
|
+
3. **안전 원칙** (글로벌 룰: 안정성 > 성능, 엄격 처리):
|
|
54
|
+
- **dry-run 기본** — `--apply` 명시해야 실제 등록
|
|
55
|
+
- **멱등** — 이미 등록/PATH 포함 시 "등록 불필요"
|
|
56
|
+
- **append-only** — 기존 PATH 덮어쓰기 절대 없음
|
|
57
|
+
4. **install 완료 자동 안내**: PATH 미등록 감지 시 `🔗 leerness CLI PATH 미등록` + `path-setup --apply` 안내. opt-out `LEERNESS_NO_PATH_CHECK=1`
|
|
58
|
+
5. **env summary 힌트** + `path` 별칭
|
|
59
|
+
|
|
60
|
+
### 다음 라운드 참고 (사용자 명시)
|
|
61
|
+
- 실제 `--apply` 등록 동작 깊은 테스트/디버그 (Windows User PATH 반영 확인, Unix rc source 후 동작)
|
|
62
|
+
- 신규 셸 세션에서 PATH 반영 검증
|
|
63
|
+
|
|
64
|
+
### stress-v199 — **25/25 PASS · 100%**
|
|
65
|
+
- 1.9.254 (12): VERSION + 헬퍼 5종 + dispatch + Windows PS/Unix rc 등록 + dry-run + install 안내 + 실제 실행(진단/json/별칭/정규화) + env 힌트
|
|
66
|
+
- 성능 (1): cold start avg 434ms
|
|
67
|
+
- 누적 회귀 (12): 1.9.207~253
|
|
68
|
+
|
|
69
|
+
### 자동 release (116 main-push streak · 77 npm publish streak · R210)
|
|
70
|
+
|
|
71
|
+
🔗 **설치 후 즉시 사용 가능** — npm global bin PATH 누락 자동 감지 + 안전 등록 (opt-in).
|
|
72
|
+
|
|
73
|
+
---
|
|
74
|
+
|
|
3
75
|
## 1.9.253 — 2026-05-29 — CLAUDE/AGENTS 문서 누적 갱신 (1.9.238~252 drift 차단)
|
|
4
76
|
|
|
5
77
|
**📚 문서 drift 차단: 메타 지침서가 1.9.237에 멈춰 있어 15 라운드(1.9.238~252) 미반영 → 누적 갱신.**
|
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.255 AI Agent Reliability Harness + Sandbox ║
|
|
16
16
|
║ verify · remember · orchestrate · audit · sandbox · drift ║
|
|
17
|
-
║
|
|
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.
|
|
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 처리).
|
|
@@ -1069,6 +1069,19 @@ async function install(root, opts = {}) {
|
|
|
1069
1069
|
}
|
|
1070
1070
|
} catch {}
|
|
1071
1071
|
}
|
|
1072
|
+
// 1.9.254 (UR-0019): init 완료 시 leerness CLI 가 PATH 에서 안 찾아지면 자동 등록 안내 (사용자 명시)
|
|
1073
|
+
// opt-out: LEERNESS_NO_PATH_CHECK=1. 자동 등록은 강제하지 않고 안내만 (--apply 명시 필요).
|
|
1074
|
+
if (!opts.migration && process.env.LEERNESS_NO_PATH_CHECK !== '1') {
|
|
1075
|
+
try {
|
|
1076
|
+
const diag = _pathDiagnose();
|
|
1077
|
+
if (!diag.resolvable && !diag.inPath && diag.globalBin) {
|
|
1078
|
+
log('');
|
|
1079
|
+
log(`\x1b[33m🔗 leerness CLI PATH 미등록 (1.9.254, UR-0019)\x1b[0m`);
|
|
1080
|
+
log(`\x1b[2m npm global bin (${diag.globalBin}) 이 PATH 에 없어 'leerness' 명령이 안 먹힐 수 있음\x1b[0m`);
|
|
1081
|
+
log(`\x1b[36m → 자동 등록: leerness path-setup --apply\x1b[0m`);
|
|
1082
|
+
}
|
|
1083
|
+
} catch {}
|
|
1084
|
+
}
|
|
1072
1085
|
// 1.9.1 P7: 디폴트 M-0001이 plan에 있고 progress에 row가 없으면 자동 추가
|
|
1073
1086
|
try {
|
|
1074
1087
|
const planText = exists(planPath(root)) ? read(planPath(root)) : '';
|
|
@@ -2699,9 +2712,176 @@ function envCmd(root, sub) {
|
|
|
2699
2712
|
}
|
|
2700
2713
|
log('');
|
|
2701
2714
|
log(dm(` → 인코딩 검사: leerness env encoding-check (셸 스크립트 .ps1/.bat 위험 감지)`));
|
|
2715
|
+
log(dm(` → PATH 등록: leerness path-setup (leerness CLI 가 PATH 에서 안 찾아질 때)`));
|
|
2702
2716
|
log(dm(` → JSON 출력: leerness env --json`));
|
|
2703
2717
|
}
|
|
2704
2718
|
|
|
2719
|
+
// 1.9.254: leerness CLI PATH 자동 등록 (사용자 명시 UR-0019)
|
|
2720
|
+
// 배경: `npm i -g leerness` 했는데 npm global bin 이 PATH 에 없으면 `leerness` 명령이 안 먹힘.
|
|
2721
|
+
// 해결: global bin 경로 감지 + PATH 포함 여부 확인 → 미등록 시 플랫폼별 안전 등록 (opt-in --apply).
|
|
2722
|
+
// 안전 원칙 (글로벌 룰: 안정성 > 성능, 엄격 처리): dry-run 기본 / 멱등 / append-only / 덮어쓰기 금지.
|
|
2723
|
+
|
|
2724
|
+
// npm global bin 디렉토리 감지 (npm prefix -g + bin, fallback: process.execPath 기반)
|
|
2725
|
+
function _npmGlobalBin() {
|
|
2726
|
+
// 1) npm prefix -g (가장 정확) — Windows 는 prefix 자체가 bin, Unix 는 prefix/bin
|
|
2727
|
+
try {
|
|
2728
|
+
const r = cp.spawnSync('npm', ['prefix', '-g'], { encoding: 'utf8', shell: true, timeout: 5000 });
|
|
2729
|
+
if (r.status === 0) {
|
|
2730
|
+
const prefix = (r.stdout || '').trim().split(/\r?\n/).pop();
|
|
2731
|
+
if (prefix) return process.platform === 'win32' ? prefix : path.join(prefix, 'bin');
|
|
2732
|
+
}
|
|
2733
|
+
} catch {}
|
|
2734
|
+
// 2) fallback: 현재 실행 중인 node 의 디렉토리 (global bin 이 보통 같은 위치)
|
|
2735
|
+
try { return path.dirname(process.execPath); } catch {}
|
|
2736
|
+
return null;
|
|
2737
|
+
}
|
|
2738
|
+
|
|
2739
|
+
// 주어진 디렉토리가 현재 PATH 에 포함되어 있는지 (대소문자/구분자 정규화)
|
|
2740
|
+
function _dirInPath(dir) {
|
|
2741
|
+
if (!dir) return false;
|
|
2742
|
+
const sep = process.platform === 'win32' ? ';' : ':';
|
|
2743
|
+
const norm = s => {
|
|
2744
|
+
let p = (s || '').trim().replace(/["']/g, '');
|
|
2745
|
+
try { p = path.normalize(p); } catch {}
|
|
2746
|
+
p = p.replace(/[\\/]+$/, '');
|
|
2747
|
+
return process.platform === 'win32' ? p.toLowerCase() : p;
|
|
2748
|
+
};
|
|
2749
|
+
const want = norm(dir);
|
|
2750
|
+
return (process.env.PATH || '').split(sep).map(norm).filter(Boolean).includes(want);
|
|
2751
|
+
}
|
|
2752
|
+
|
|
2753
|
+
// leerness 가 PATH 에서 실행 가능한지 (which/where 사용)
|
|
2754
|
+
function _leernessResolvable() {
|
|
2755
|
+
try {
|
|
2756
|
+
const probe = process.platform === 'win32' ? 'where' : 'which';
|
|
2757
|
+
const r = cp.spawnSync(probe, ['leerness'], { encoding: 'utf8', shell: true, timeout: 5000 });
|
|
2758
|
+
return r.status === 0 && /leerness/i.test((r.stdout || ''));
|
|
2759
|
+
} catch { return false; }
|
|
2760
|
+
}
|
|
2761
|
+
|
|
2762
|
+
// PATH 등록 상태 진단 (반환: { globalBin, inPath, resolvable, platform, shellRc })
|
|
2763
|
+
function _pathDiagnose() {
|
|
2764
|
+
const globalBin = _npmGlobalBin();
|
|
2765
|
+
const inPath = _dirInPath(globalBin);
|
|
2766
|
+
const resolvable = _leernessResolvable();
|
|
2767
|
+
let shellRc = null;
|
|
2768
|
+
if (process.platform !== 'win32') {
|
|
2769
|
+
const home = os.homedir();
|
|
2770
|
+
const shell = process.env.SHELL || '';
|
|
2771
|
+
if (/zsh/.test(shell)) shellRc = path.join(home, '.zshrc');
|
|
2772
|
+
else if (/bash/.test(shell)) shellRc = path.join(home, '.bashrc');
|
|
2773
|
+
else shellRc = path.join(home, '.profile');
|
|
2774
|
+
}
|
|
2775
|
+
return { globalBin, inPath, resolvable, platform: process.platform, shellRc };
|
|
2776
|
+
}
|
|
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
|
+
|
|
2801
|
+
// PATH 에 globalBin 등록 (플랫폼별 안전 방법). 반환: { ok, method, detail }
|
|
2802
|
+
// 1.9.255: process.platform → diag.platform 으로 변경 (테스트 가능 — diag.platform 은 실사용 시 process.platform 과 동일).
|
|
2803
|
+
function _registerPath(diag) {
|
|
2804
|
+
const bin = diag.globalBin;
|
|
2805
|
+
if (!bin) return { ok: false, method: 'none', detail: 'npm global bin 경로를 찾을 수 없음' };
|
|
2806
|
+
const platform = diag.platform || process.platform;
|
|
2807
|
+
if (platform === 'win32') {
|
|
2808
|
+
// Windows: PowerShell [Environment]::SetEnvironmentVariable User scope (멱등, 관리자 권한 불요)
|
|
2809
|
+
try {
|
|
2810
|
+
const ps = _winPathPsScript(bin);
|
|
2811
|
+
const r = cp.spawnSync('powershell', ['-NoProfile', '-Command', ps], { encoding: 'utf8', timeout: 10000 });
|
|
2812
|
+
const out = (r.stdout || '').trim();
|
|
2813
|
+
if (r.status === 0 && /ADDED/.test(out)) return { ok: true, method: 'powershell-user-path', detail: `User PATH 에 추가됨 (새 터미널부터 적용): ${bin}` };
|
|
2814
|
+
if (r.status === 0 && /EXISTS/.test(out)) return { ok: true, method: 'already', detail: `User PATH 에 이미 존재: ${bin}` };
|
|
2815
|
+
return { ok: false, method: 'powershell-user-path', detail: `등록 실패 (exit ${r.status}): ${(r.stderr || out || '').trim().slice(0, 200)}` };
|
|
2816
|
+
} catch (e) { return { ok: false, method: 'powershell-user-path', detail: `예외: ${e.message}` }; }
|
|
2817
|
+
}
|
|
2818
|
+
// Unix: shell rc 파일에 export 블록 append (멱등 — 마커로 중복 차단)
|
|
2819
|
+
try {
|
|
2820
|
+
const rc = diag.shellRc;
|
|
2821
|
+
if (!rc) return { ok: false, method: 'shell-rc', detail: 'shell rc 경로 미확인' };
|
|
2822
|
+
let cur = '';
|
|
2823
|
+
try { cur = fs.readFileSync(rc, 'utf8'); } catch {}
|
|
2824
|
+
if (cur.includes(_UNIX_PATH_MARKER)) return { ok: true, method: 'already', detail: `${rc} 에 이미 등록됨` };
|
|
2825
|
+
fs.appendFileSync(rc, _unixPathBlock(bin), 'utf8');
|
|
2826
|
+
return { ok: true, method: 'shell-rc', detail: `${rc} 에 추가됨 (source 또는 새 터미널부터 적용): ${bin}` };
|
|
2827
|
+
} catch (e) { return { ok: false, method: 'shell-rc', detail: `예외: ${e.message}` }; }
|
|
2828
|
+
}
|
|
2829
|
+
|
|
2830
|
+
function pathSetupCmd(root, opts = {}) {
|
|
2831
|
+
const isTty = process.stdout && process.stdout.isTTY;
|
|
2832
|
+
const cy = s => isTty ? `\x1b[36m${s}\x1b[0m` : s;
|
|
2833
|
+
const gr = s => isTty ? `\x1b[32m${s}\x1b[0m` : s;
|
|
2834
|
+
const yl = s => isTty ? `\x1b[33m${s}\x1b[0m` : s;
|
|
2835
|
+
const dm = s => isTty ? `\x1b[2m${s}\x1b[0m` : s;
|
|
2836
|
+
const diag = _pathDiagnose();
|
|
2837
|
+
if (opts.json) {
|
|
2838
|
+
let applied = null;
|
|
2839
|
+
if (opts.apply && !diag.resolvable && !diag.inPath) applied = _registerPath(diag);
|
|
2840
|
+
log(JSON.stringify({ ...diag, applied }, null, 2));
|
|
2841
|
+
return;
|
|
2842
|
+
}
|
|
2843
|
+
log(cy(`# leerness path-setup (1.9.254, UR-0019) — CLI PATH 자동 등록`));
|
|
2844
|
+
log('');
|
|
2845
|
+
log(` npm global bin: ${diag.globalBin ? gr(diag.globalBin) : yl('감지 실패')}`);
|
|
2846
|
+
log(` PATH 포함 여부: ${diag.inPath ? gr('✓ 포함됨') : yl('✗ 미포함')}`);
|
|
2847
|
+
log(` leerness 실행 가능: ${diag.resolvable ? gr('✓ 가능 (which/where 성공)') : yl('✗ 불가')}`);
|
|
2848
|
+
log('');
|
|
2849
|
+
if (diag.resolvable || diag.inPath) {
|
|
2850
|
+
log(gr(` ✓ leerness CLI 가 이미 PATH 에서 정상 동작합니다 — 등록 불필요`));
|
|
2851
|
+
return;
|
|
2852
|
+
}
|
|
2853
|
+
if (!diag.globalBin) {
|
|
2854
|
+
log(yl(` ⚠ npm global bin 경로를 찾을 수 없어 자동 등록 불가`));
|
|
2855
|
+
log(dm(` → 수동: npm prefix -g 로 경로 확인 후 PATH 에 추가`));
|
|
2856
|
+
return;
|
|
2857
|
+
}
|
|
2858
|
+
if (!opts.apply) {
|
|
2859
|
+
// dry-run (기본)
|
|
2860
|
+
log(yl(` ⚠ leerness 가 PATH 에 없습니다 — 'leerness' 명령이 동작하지 않을 수 있음`));
|
|
2861
|
+
log(` ${dm('적용 시 추가될 경로:')} ${diag.globalBin}`);
|
|
2862
|
+
if (diag.platform === 'win32') {
|
|
2863
|
+
log(dm(` 방법: PowerShell [Environment]::SetEnvironmentVariable('PATH', ..., 'User') (관리자 권한 불요)`));
|
|
2864
|
+
} else {
|
|
2865
|
+
log(dm(` 방법: ${diag.shellRc} 에 export PATH 블록 append (멱등)`));
|
|
2866
|
+
}
|
|
2867
|
+
log('');
|
|
2868
|
+
log(cy(` → 자동 등록: leerness path-setup --apply`));
|
|
2869
|
+
return;
|
|
2870
|
+
}
|
|
2871
|
+
// --apply
|
|
2872
|
+
const res = _registerPath(diag);
|
|
2873
|
+
if (res.ok) {
|
|
2874
|
+
log(gr(` ✓ PATH 등록 완료 (${res.method})`));
|
|
2875
|
+
log(` ${res.detail}`);
|
|
2876
|
+
log('');
|
|
2877
|
+
if (diag.platform === 'win32') log(dm(` → 새 터미널을 열면 'leerness' 명령을 바로 쓸 수 있습니다.`));
|
|
2878
|
+
else log(dm(` → source ${diag.shellRc} 또는 새 터미널에서 'leerness' 명령 사용 가능.`));
|
|
2879
|
+
} else {
|
|
2880
|
+
log(yl(` ⚠ 자동 등록 실패 (${res.method}): ${res.detail}`));
|
|
2881
|
+
log(dm(` → 수동 등록: PATH 에 ${diag.globalBin} 추가`));
|
|
2882
|
+
}
|
|
2883
|
+
}
|
|
2884
|
+
|
|
2705
2885
|
// 1.9.245: API skill cache — 공식 문서 + 관련 링크 자동 정리 (사용자 명시 UR-0015)
|
|
2706
2886
|
// .harness/api-skills/<id>.md 에 frontmatter + 본문 저장. handoff 자동 매칭.
|
|
2707
2887
|
// Node built-in https (의존성 0), depth=1 same-domain crawl, max 10 links.
|
|
@@ -19713,6 +19893,9 @@ async function main() {
|
|
|
19713
19893
|
// 기존 env check/sync/detect (1.9.71) 와 충돌하지 않음 — summary/encoding 신규 서브
|
|
19714
19894
|
if (cmd === 'env' && (args[1] === 'summary' || args[1] === 'encoding' || args[1] === 'encoding-check'))
|
|
19715
19895
|
return envCmd(arg('--path', process.cwd()), args[1] === 'summary' ? null : 'encoding-check');
|
|
19896
|
+
// 1.9.254: leerness path-setup [--apply] — CLI PATH 자동 등록 (사용자 명시 UR-0019)
|
|
19897
|
+
// npm global bin 경로 감지 + leerness 가 PATH 에서 찾아지는지 확인 → 미등록 시 플랫폼별 등록
|
|
19898
|
+
if (cmd === 'path-setup' || cmd === 'path') return pathSetupCmd(arg('--path', process.cwd()), { apply: has('--apply'), json: has('--json') });
|
|
19716
19899
|
// 1.9.245: API skill cache — 공식 문서·관련링크 자동 정리 (사용자 명시 UR-0015)
|
|
19717
19900
|
if (cmd === 'api-skill') return apiSkillCmd(arg('--path', process.cwd()), args[1] || 'help');
|
|
19718
19901
|
// 1.9.208: leerness constraints <list|check|add> — 플랫폼/API 제약 사전 체크 (사용자 명시)
|
|
@@ -19854,6 +20037,18 @@ async function main() {
|
|
|
19854
20037
|
}
|
|
19855
20038
|
|
|
19856
20039
|
// 1.9.4 B: main 종료 후 exitCode를 명시적으로 process.exit으로 강제 (셸/wrapper 차 무시).
|
|
19857
|
-
main()
|
|
19858
|
-
|
|
19859
|
-
|
|
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
|
+
};
|