leerness 1.9.189 β†’ 1.9.190

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,115 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.190 β€” 2026-05-21
4
+
5
+ **🚨 μ„€μΉ˜ κ°€μ΄λ“œ Ctrl+C λ―Έμž‘λ™ BUG fix (μ‚¬μš©μž λͺ…μ‹œ β€” npx μ§„ν–‰ 차단).**
6
+
7
+ 자율 λͺ¨λ“œ 120 λΌμš΄λ“œ. μ‚¬μš©μž λͺ…μ‹œ BUG:
8
+ > `npx --yes leerness@latest init` 으둜 μ„€μΉ˜ κ°€μ΄λ“œμ—μ„œ Ctrl+C λˆ„λ₯΄λ©΄ μ„€μΉ˜ 진행이 μ·¨μ†Œλ˜μ–΄μ•Ό ν•˜λŠ”λ°, μ§„ν–‰λ˜λŠ” 버그.
9
+
10
+ ### κ·Όλ³Έ 원인 (μ½”λ“œ audit)
11
+
12
+ 1.9.34~1.9.189 의 `_selectOne` / `_selectMany` 의 raw mode μ½”λ“œ:
13
+ ```js
14
+ } else if (key === '\x03' || key === 'q' || key === '\x1b') {
15
+ cleanup();
16
+ stdout.write('\n μ·¨μ†Œλ¨\n');
17
+ resolve(opts.defaultIndex != null ? options[opts.defaultIndex] : null); // ← BUG
18
+ }
19
+ ```
20
+
21
+ Ctrl+C (`\x03`)κ°€ `q`/`\x1b`(ESC)와 같은 λΆ„κΈ°λ‘œ λ“€μ–΄κ°€μ„œ **default κ°’ λ°˜ν™˜** β†’ install 흐름이 default μ˜΅μ…˜μœΌλ‘œ 계속 μ§„ν–‰ β†’ μ‚¬μš©μž μ˜λ„(μ·¨μ†Œ)와 μ •λ°˜λŒ€ λ™μž‘.
22
+
23
+ λ˜ν•œ 1.9.184μ—μ„œ μΆ”κ°€ν•œ `process.on('SIGINT', _sigintHandler)` 의 2단계 confirm 은 readline raw mode κ°€ SIGINT signal 자체λ₯Ό κ°€λ‘œμ±„μ„œ ν˜ΈμΆœλ˜μ§€ μ•ŠμŒ.
24
+
25
+ ### Fix #1 β€” `_selectOne` / `_selectMany`: Ctrl+C λͺ…μ‹œ λΆ„κΈ° + μ¦‰μ‹œ μ’…λ£Œ
26
+
27
+ ```diff
28
+ } else if (key === '\x03') {
29
+ + // 1.9.190 (μ‚¬μš©μž λͺ…μ‹œ BUG fix): raw mode μ—μ„œ Ctrl+C β†’ μ¦‰μ‹œ μ„€μΉ˜ μ’…λ£Œ (npx μ§„ν–‰ 차단).
30
+ + // 이전 (1.9.34~1.9.189): default κ°’ λ°˜ν™˜ β†’ install 흐름 계속 μ§„ν–‰ (μ‚¬μš©μž BUG 보고).
31
+ cleanup();
32
+ stdout.write('\n \x1b[31mβœ— μ„€μΉ˜ 쀑단됨 (Ctrl+C)\x1b[0m\n');
33
+ process.exit(130);
34
+ +} else if (key === '\x1b' || key === 'q' || key === 'Q') {
35
+ + // ESC/q/Q β†’ λ‹¨μˆœ μ·¨μ†Œ (default λ°˜ν™˜, install 흐름 계속)
36
+ cleanup();
37
+ stdout.write('\n μ·¨μ†Œλ¨\n');
38
+ resolve(opts.defaultIndex != null ? options[opts.defaultIndex] : null);
39
+ }
40
+ ```
41
+
42
+ Ctrl+C (\x03) 와 ESC/q λΆ„κΈ°λ₯Ό **μ™„μ „ 뢄리**:
43
+ - **Ctrl+C** β†’ `process.exit(130)` μ¦‰μ‹œ μ’…λ£Œ
44
+ - **ESC/q/Q** β†’ default κ°’ λ°˜ν™˜ (μ·¨μ†Œλ§Œ, 흐름 계속)
45
+
46
+ ### Fix #2 β€” `ask()` readline SIGINT λͺ…μ‹œ 처리
47
+
48
+ ```js
49
+ function ask(question) {
50
+ return new Promise(resolve => {
51
+ const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
52
+ // 1.9.190: readline 의 자체 SIGINT 처리 β€” Ctrl+C μ‹œ μ¦‰μ‹œ μ’…λ£Œ.
53
+ rl.on('SIGINT', () => {
54
+ try { rl.close(); } catch {}
55
+ process.stdout.write('\n \x1b[31mβœ— μ„€μΉ˜ 쀑단됨 (Ctrl+C)\x1b[0m\n');
56
+ process.exit(130);
57
+ });
58
+ rl.question(question, answer => { rl.close(); resolve(String(answer || '').trim()); });
59
+ });
60
+ }
61
+ ```
62
+
63
+ readline 의 `'SIGINT'` 이벀트 β†’ λͺ…μ‹œ 처리 β†’ μ¦‰μ‹œ exit.
64
+
65
+ ### Fix #3 β€” `install()` SIGINT ν•Έλ“€λŸ¬ λ‹¨μˆœν™” (2단계 confirm 제거)
66
+
67
+ ```diff
68
+ - // 1.9.184: 2단계 confirm (첫 λˆ„λ¦„ β†’ μ•ˆλ‚΄, 2초 λ‚΄ 두 번째 β†’ μ’…λ£Œ)
69
+ - let _sigintCount = 0; let _sigintTimer = null;
70
+ - const _sigintHandler = () => {
71
+ - _sigintCount++;
72
+ - if (_sigintCount === 1) {
73
+ - process.stdout.write('Ctrl+C λ₯Ό 2초 이내에 ν•œ 번 더 λˆ„λ₯΄λ©΄ μ’…λ£Œλ©λ‹ˆλ‹€...');
74
+ - _sigintTimer = setTimeout(() => { _sigintCount = 0; }, 2000);
75
+ - return;
76
+ - }
77
+ - process.exit(130);
78
+ - };
79
+ + // 1.9.184+1.9.190: μ„€μΉ˜ 도쀑 Ctrl+C μ‹œ μ¦‰μ‹œ μ’…λ£Œ (μ‚¬μš©μž λͺ…μ‹œ BUG fix β€” npx μ§„ν–‰ 차단).
80
+ + // readline raw mode κ°€ SIGINT κ°€λ‘œμ±„μ„œ 1.9.184 2단계 confirm λ™μž‘ μ•ˆ 함 β†’ μ¦‰μ‹œ μ’…λ£Œλ‘œ λ‹¨μˆœν™”.
81
+ + const _sigintHandler = () => {
82
+ + process.stdout.write('\n \x1b[31mβœ— μ„€μΉ˜ 쀑단됨 (Ctrl+C)\x1b[0m\n');
83
+ + process.exit(130);
84
+ + };
85
+ ```
86
+
87
+ ### μ‹œκ° 효과 (λͺ¨λ“  fix 톡합)
88
+
89
+ ```
90
+ $ npx --yes leerness@latest init
91
+ 🎁 leerness μ„€μΉ˜ μ‹œμž‘...
92
+ μ„€μΉ˜ μ–Έμ–΄λ₯Ό μ„ νƒν•˜μ„Έμš”
93
+ ↑↓ 이동, Enter ν™•μ •, q μ·¨μ†Œ
94
+ ❯ μžλ™ 감지
95
+ ν•œκ΅­μ–΄
96
+ English
97
+ ^C
98
+ βœ— μ„€μΉ˜ 쀑단됨 (Ctrl+C)
99
+ $
100
+ ```
101
+
102
+ (이전엔 Ctrl+C 후에도 μ„€μΉ˜κ°€ 계속 μ§„ν–‰λ˜μ–΄ default κ°’μœΌλ‘œ λͺ¨λ“  파일 생성됨 β†’ μ‚¬μš©μž μ˜λ„μ™€ μ •λ°˜λŒ€)
103
+
104
+ ### Verified
105
+ - stress-v135: **13/13 PASS** (μ‚¬μš©μž λͺ…μ‹œ 5 + νšŒκ·€ 2 + λˆ„μ  6)
106
+ - e2e 217/217 baseline μœ μ§€
107
+ - `--yes` non-interactive install: νšŒκ·€ μ—†μŒ
108
+ - ESC/q λ‹¨μˆœ μ·¨μ†Œ: νšŒκ·€ μ—†μŒ (default λ°˜ν™˜)
109
+ - VERSION = 1.9.190 Β· autonomous-rounds = 120 Β· main μžλ™ push 51 λΌμš΄λ“œ 연속
110
+
111
+ ---
112
+
3
113
  ## 1.9.189 β€” 2026-05-21
4
114
 
5
115
  **⌨️ "/" slash λͺ…λ Ή μžλ™ list + Tab cycle ν•œ 쀄 κ°±μ‹  (μ‚¬μš©μž λͺ…μ‹œ 3μ’…).**
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.189-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v134-15%2F15-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-119-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-50_rounds-success)]() [![slash-list](https://img.shields.io/badge/"/"_slash-μžλ™_λͺ…λ Ή_list-success)]() [![cycle-clean](https://img.shields.io/badge/Tab_cycle-ν•œ_쀄_κ°±μ‹ -success)]() [![hangul](https://img.shields.io/badge/ν•œκΈ€_prompt-stdin_정확전달-success)]() [![install](https://img.shields.io/badge/npm_i_leerness-μ¦‰μ‹œ_μ‚¬μš©_κ°€λŠ₯-success)]() [![sandbox](https://img.shields.io/badge/runCommandSafe-cwd_jail%2Benv_scrub-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.190-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v135-13%2F13-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-120-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-51_rounds-success)]() [![ctrl-c](https://img.shields.io/badge/μ„€μΉ˜_Ctrl%2BC-μ¦‰μ‹œ_μ’…λ£Œ_BUG_fix-success)]() [![slash-list](https://img.shields.io/badge/"/"_slash-μžλ™_λͺ…λ Ή_list-success)]() [![cycle-clean](https://img.shields.io/badge/Tab_cycle-ν•œ_쀄_κ°±μ‹ -success)]() [![install](https://img.shields.io/badge/npm_i_leerness-μ¦‰μ‹œ_μ‚¬μš©_κ°€λŠ₯-success)]() [![sandbox](https://img.shields.io/badge/runCommandSafe-cwd_jail%2Benv_scrub-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.189 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.190 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ ⌨ "/" slash μžλ™ list Β· Tab cycle ν•œ 쀄 κ°±μ‹  Β· 50 λΌμš΄λ“œ β•‘
17
+ β•‘ 🚨 μ„€μΉ˜ Ctrl+C μ¦‰μ‹œ μ’…λ£Œ BUG fix Β· 51 λΌμš΄λ“œ main push β•‘
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.189';
10
+ const VERSION = '1.9.190';
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 처리).
@@ -163,6 +163,13 @@ function fm(role, readWhen, updateWhen, body) {
163
163
  function ask(question) {
164
164
  return new Promise(resolve => {
165
165
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
166
+ // 1.9.190 (μ‚¬μš©μž λͺ…μ‹œ BUG fix): readline 의 자체 SIGINT 처리 β€” Ctrl+C μ‹œ μ¦‰μ‹œ μ’…λ£Œ.
167
+ // process.on('SIGINT') ν•Έλ“€λŸ¬λŠ” readline raw mode 에 κ°€λ €μ Έ 호좜 μ•ˆ 됨 β†’ μ—¬κΈ°μ„œ λͺ…μ‹œ 처리.
168
+ rl.on('SIGINT', () => {
169
+ try { rl.close(); } catch {}
170
+ process.stdout.write('\n \x1b[31mβœ— μ„€μΉ˜ 쀑단됨 (Ctrl+C)\x1b[0m\n');
171
+ process.exit(130);
172
+ });
166
173
  rl.question(question, answer => { rl.close(); resolve(String(answer || '').trim()); });
167
174
  });
168
175
  }
@@ -783,20 +790,12 @@ async function resolveInstallOptions(root, opts = {}) {
783
790
 
784
791
  async function install(root, opts = {}) {
785
792
  root = absRoot(root); mkdirp(root);
786
- // 1.9.184 (μ‚¬μš©μž λͺ…μ‹œ): μ„€μΉ˜ 도쀑 Ctrl+C μ‹œ μ’…λ£Œ 확인 prompt.
787
- // 첫 Ctrl+C β†’ μ•ˆλ‚΄ (2초 이내 ν•œ 번 더 β†’ μ’…λ£Œ, κ·Έ μ™Έ β†’ 계속).
788
- // readline raw mode μ‹œ readline 이 'SIGINT' 이벀트둜 ν‘μˆ˜ν•  수 μžˆμ–΄ process-level + finally ν•΄μ œ.
789
- let _sigintCount = 0; let _sigintTimer = null;
793
+ // 1.9.184+1.9.190: μ„€μΉ˜ 도쀑 Ctrl+C μ‹œ μ¦‰μ‹œ μ’…λ£Œ (μ‚¬μš©μž λͺ…μ‹œ BUG fix β€” npx μ„€μΉ˜ μ§„ν–‰ 차단).
794
+ // 1.9.184 의 2단계 confirm 은 readline raw mode κ°€ SIGINT κ°€λ‘œμ±„μ„œ 호좜 μ•ˆ 됨 β†’ 1.9.190 μ¦‰μ‹œ μ’…λ£Œλ‘œ λ‹¨μˆœν™”.
795
+ // _selectOne/_selectMany λŠ” stdin '\x03' 직접 처리, ask() λŠ” rl.on('SIGINT') 처리, 이 ν•Έλ“€λŸ¬λŠ” fallback.
796
+ let _sigintTimer = null; // back-compat: 1.9.184 placeholder
790
797
  const _sigintHandler = () => {
791
- _sigintCount++;
792
- if (_sigintCount === 1) {
793
- try { process.stdout.write('\n\n ⚠ μ„€μΉ˜ μ€‘λ‹¨ν•˜μ‹œκ² μŠ΅λ‹ˆκΉŒ? Ctrl+C λ₯Ό 2초 이내에 ν•œ 번 더 λˆ„λ₯΄λ©΄ μ’…λ£Œλ©λ‹ˆλ‹€. (κ·Έ μ™Έ β†’ 계속 μ§„ν–‰)\n'); } catch {}
794
- clearTimeout(_sigintTimer);
795
- _sigintTimer = setTimeout(() => { _sigintCount = 0; }, 2000);
796
- return;
797
- }
798
- clearTimeout(_sigintTimer);
799
- try { process.stdout.write('\n βœ— μ„€μΉ˜ 쀑단됨 (μ‚¬μš©μž μš”μ²­)\n'); } catch {}
798
+ try { process.stdout.write('\n \x1b[31mβœ— μ„€μΉ˜ 쀑단됨 (Ctrl+C)\x1b[0m\n'); } catch {}
800
799
  process.exit(130);
801
800
  };
802
801
  if (!opts.migration && !opts.nonInteractive && process.stdin.isTTY) {
@@ -5539,7 +5538,13 @@ async function _selectOne(question, options, opts = {}) {
5539
5538
  cleanup();
5540
5539
  stdout.write('\n');
5541
5540
  resolve(options[idx]);
5542
- } else if (key === '' || key === 'q' || key === '') {
5541
+ } else if (key === '') {
5542
+ // 1.9.190 (μ‚¬μš©μž λͺ…μ‹œ BUG fix): raw mode μ—μ„œ Ctrl+C β†’ μ¦‰μ‹œ μ„€μΉ˜ μ’…λ£Œ (npx μ§„ν–‰ 차단).
5543
+ // 이전 (1.9.34~1.9.189): default κ°’ λ°˜ν™˜ β†’ install 흐름 계속 μ§„ν–‰ (μ‚¬μš©μž BUG 보고).
5544
+ cleanup();
5545
+ stdout.write('\n \x1b[31mβœ— μ„€μΉ˜ 쀑단됨 (Ctrl+C)\x1b[0m\n');
5546
+ process.exit(130);
5547
+ } else if (key === '' || key === 'q' || key === 'Q') {
5543
5548
  cleanup();
5544
5549
  stdout.write('\n' + C.dim(' μ·¨μ†Œλ¨') + '\n');
5545
5550
  resolve(opts.defaultIndex != null ? options[opts.defaultIndex] : null);
@@ -5607,7 +5612,13 @@ async function _selectMany(question, options, opts = {}) {
5607
5612
  cleanup();
5608
5613
  stdout.write('\n');
5609
5614
  resolve([...selected].sort((a, b) => a - b).map(i => options[i]));
5610
- } else if (key === '' || key === 'q' || key === '') {
5615
+ } else if (key === '') {
5616
+ // 1.9.190 (μ‚¬μš©μž λͺ…μ‹œ BUG fix): raw mode μ—μ„œ Ctrl+C β†’ μ¦‰μ‹œ μ„€μΉ˜ μ’…λ£Œ (npx μ§„ν–‰ 차단).
5617
+ // 이전 (1.9.34~1.9.189): default κ°’ λ°˜ν™˜ β†’ install 흐름 계속 μ§„ν–‰ (μ‚¬μš©μž BUG 보고).
5618
+ cleanup();
5619
+ stdout.write('\n \x1b[31mβœ— μ„€μΉ˜ 쀑단됨 (Ctrl+C)\x1b[0m\n');
5620
+ process.exit(130);
5621
+ } else if (key === '' || key === 'q' || key === 'Q') {
5611
5622
  cleanup();
5612
5623
  stdout.write('\n' + C.dim(' μ·¨μ†Œλ¨ (κΈ°λ³Έκ°’ μ‚¬μš©)') + '\n');
5613
5624
  resolve((opts.defaults || []).map(d => typeof d === 'number' ? options[d] : d).filter(Boolean));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.189",
3
+ "version": "1.9.190",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",