leerness 1.9.179 β†’ 1.9.180

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,73 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.180 β€” 2026-05-21
4
+
5
+ **πŸ”§ REPL Tab cycle 핡심 fix + μ±„νŒ… μ˜μ—­ separator β€” μ‚¬μš©μž λͺ…μ‹œ (직접 ꡬ동 ν…ŒμŠ€νŠΈ κ²°κ³Ό).**
6
+
7
+ 자율 λͺ¨λ“œ 110 λΌμš΄λ“œ. μ‚¬μš©μž λͺ…μ‹œ: *"REPL agent λͺ¨λ“œλ₯Ό λ„€κ°€ 직접 κ΅¬λ™ν•΄μ„œ ν…ŒμŠ€νŠΈν•΄μ€˜ / REPL agent λͺ¨λ“œλŠ” κ³ μ •λœ 헀더와 μ±„νŒ…ν˜•μ‹μ΄μ–΄μ•Όν•΄ / 그리고 λͺ¨λΈμ΄λ‚˜ ν”„λ‘œλ°”μ΄λ” μ „ν™˜μ΄ μ›ν™œν•˜μ§€μ•Šμ€κ±°κ°™μ•„"*.
8
+
9
+ ### 핡심 fix β€” Tab cycle μ‹€ λ™μž‘ 보μž₯
10
+
11
+ #### 1. readline `completer` no-op
12
+ ```js
13
+ // 1.9.180: completer no-op β€” readline의 자체 Tab completion이 keypress λ¦¬μŠ€λ„ˆλ₯Ό κ°€λ‘œμ±„λŠ” 문제 차단
14
+ const rl = readline.createInterface({
15
+ input: process.stdin,
16
+ output: process.stdout,
17
+ completer: (line) => [[], line]
18
+ });
19
+ ```
20
+ 이전: μ‚¬μš©μžκ°€ Tab을 λˆ„λ₯΄λ©΄ readline κΈ°λ³Έ completerκ°€ 빈 κ²°κ³Όλ₯Ό ν‘œμ‹œν•˜λ©° promptλ₯Ό 재좜λ ₯ν•΄ keypress λ¦¬μŠ€λ„ˆκ°€ λ™μž‘ν•˜μ§€ μ•Šμ„ λ•Œκ°€ μžˆμ—ˆμŒ.
21
+ μ§€κΈˆ: `completer: () => [[], line]` λͺ…μ‹œλ‘œ readline의 Tab κ°€λ‘œμ±„κΈ° 차단 β†’ keypress λ¦¬μŠ€λ„ˆκ°€ 항상 λ°œλ™.
22
+
23
+ #### 2. Shift+Tab β†’ `cycleModel(false)` λ§€ν•‘ μˆ˜μ • (CRITICAL)
24
+ ```js
25
+ process.stdin.on('keypress', (str, key) => {
26
+ if (!key) return;
27
+ if (key.name === 'tab') {
28
+ // 1.9.180 fix: Shift+Tab β†’ cycleModel (이전 cycleProvider 잘λͺ»)
29
+ if (key.shift === true) {
30
+ cycleModel(false); // Shift+Tab β†’ ν˜„μž¬ provider의 λͺ¨λΈ cycle
31
+ } else {
32
+ cycleProvider(false); // Tab β†’ λ‹€μŒ provider
33
+ }
34
+ }
35
+ });
36
+ ```
37
+ 이전 (1.9.170): `cycleProvider(key.shift)` β€” Shift+Tab은 provider reverse μ˜€κ³  model cycle ν‚€κ°€ μ—†μ—ˆμŒ.
38
+ μ§€κΈˆ: μ‚¬μš©μž μ˜λ„λŒ€λ‘œ `Tab=provider`, `Shift+Tab=model`.
39
+
40
+ ### μ‹œκ° ν”Όλ“œλ°± κ°•ν™” (μ‚¬μš©μž λͺ…μ‹œ: "μ›ν™œν•˜μ§€ μ•ŠμŒ")
41
+ ```
42
+ ⇄ provider [3/5]: claude βœ“ ready
43
+ β”” 7개 λͺ¨λΈ catalog Β· Shift+Tab으둜 model cycle
44
+
45
+ ⇄ model [2/7]: claude-opus-4
46
+ β”” μ΅œμ‹  thinking λͺ¨λΈ
47
+ ```
48
+ - bold green provider Β· bold magenta model
49
+ - `[idx/total]` μœ„μΉ˜/총수 ν‘œμ‹œ
50
+ - ready/⚠ status ν™œμ„± μ—¬λΆ€ ν‘œμ‹œ
51
+ - catalog λͺ¨λΈ 수 λ…ΈμΆœ
52
+
53
+ ### μ±„νŒ… μ˜μ—­ separator (μ‚¬μš©μž λͺ…μ‹œ: "κ³ μ •λœ 헀더와 μ±„νŒ…ν˜•μ‹")
54
+ ```
55
+ [... ν™˜μ˜ ν™”λ©΄ (헀더 + Tips + What's new + Slash + ν‚€λ³΄λ“œ + μƒνƒœλ°”) ...]
56
+
57
+ ───────────────────────────── μ±„νŒ… μ‹œμž‘ ─────────────────────────────
58
+ λ©”μ‹œμ§€ μž…λ ₯ ν›„ Enter Β· :help 으둜 λͺ…λ Ή λͺ©λ‘ Β· Ctrl+C 둜 μ’…λ£Œ
59
+
60
+ agent[ollama/actor/β–Ά]> _
61
+ ```
62
+ ν™˜μ˜ ν™”λ©΄ (κ³ μ • 헀더) κ³Ό μž…λ ₯ μ˜μ—­ (μ±„νŒ…) 의 μ‹œκ°μ  ꡬ뢄을 λͺ…ν™•ν•˜κ²Œ.
63
+
64
+ ### Verified
65
+ - e2e 217/217 baseline μœ μ§€
66
+ - stress-v125: **17/17** (Tab cycle fix 3 + μ‹œκ° ν”Όλ“œλ°± 4 + μ±„νŒ… μ˜μ—­ 3 + λˆ„μ  νšŒκ·€ 7)
67
+ - VERSION = 1.9.180 Β· autonomous-rounds = 110 Β· main μžλ™ push 41 λΌμš΄λ“œ 연속
68
+
69
+ ---
70
+
3
71
  ## 1.9.179 β€” 2026-05-21
4
72
 
5
73
  **🎨 REPL ν™˜μ˜ ν™”λ©΄ μž¬λ””μžμΈ β€” Hermes/Claude/Codex/Gemini CLI μŠ€νƒ€μΌ (μ‚¬μš©μž λͺ…μ‹œ).**
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.179-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v124-19%2F19-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-109-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-40_rounds-success)]() [![repl-design](https://img.shields.io/badge/REPL_ν™˜μ˜-Hermes%2FClaude%2FCodex_μŠ€νƒ€μΌ-success)]() [![npm-auto](https://img.shields.io/badge/npm_auto--publish-NPM__TOKEN_톡합-success)]() [![auto-review](https://img.shields.io/badge/task_add-μžλ™_review_trigger-success)]() [![capability](https://img.shields.io/badge/6_capability-72%25_production--ready-brightgreen)]() [![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.180-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v125-17%2F17-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-110-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-41_rounds-success)]() [![repl-tab](https://img.shields.io/badge/REPL_Tab_cycle-completer_no--op%2BShift_fix-success)]() [![repl-chat](https://img.shields.io/badge/REPL_고정헀더-μ±„νŒ…μ˜μ—­_separator-success)]() [![npm-auto](https://img.shields.io/badge/npm_auto--publish-NPM__TOKEN_톡합-success)]() [![capability](https://img.shields.io/badge/6_capability-72%25_production--ready-brightgreen)]() [![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.179 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.180 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ 🎨 REPL ν™˜μ˜ ν™”λ©΄ μž¬λ””μžμΈ (Hermes/Claude/Codex μŠ€νƒ€μΌ) β•‘
17
+ β•‘ πŸ”§ REPL Tab cycle fix Β· μ±„νŒ… μ˜μ—­ separator (κ³ μ • 헀더+μ±„νŒ…) β•‘
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.179';
10
+ const VERSION = '1.9.180';
11
11
  const MARK = '<!-- leerness:managed -->';
12
12
  const README_START = '<!-- leerness:project-readme:start -->';
13
13
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -11053,7 +11053,13 @@ async function _agentRepl(root, opts) {
11053
11053
  // 1.9.153: .env μžλ™ λ‘œλ“œ (REPL μ§„μž… 직전) β€” install 직후 LEERNESS_ENABLE_* μ¦‰μ‹œ 반영
11054
11054
  try { _loadEnvFile(root); } catch {}
11055
11055
  const readline = require('readline');
11056
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11056
+ // 1.9.180: completer no-op β€” readline 의 Tab completion κ°€λ‘œμ±„κΈ°λ₯Ό 차단해 keypress λ¦¬μŠ€λ„ˆκ°€ Tab cycle을 처리.
11057
+ // (μ‚¬μš©μž λͺ…μ‹œ: "λͺ¨λΈ/ν”„λ‘œλ°”μ΄λ” μ „ν™˜μ΄ μ›ν™œν•˜μ§€ μ•Šλ‹€" β€” readline Tab completion이 cycle ν‚€λ₯Ό κ°€λ‘œμ±„λŠ” 경우 fix)
11058
+ const rl = readline.createInterface({
11059
+ input: process.stdin,
11060
+ output: process.stdout,
11061
+ completer: (line) => [[], line] // 빈 completion + 원본 line λ°˜ν™˜ β†’ Tab은 keypress λ¦¬μŠ€λ„ˆλ‘œλ§Œ 처리
11062
+ });
11057
11063
  const isTty = process.stdout.isTTY;
11058
11064
  const C = isTty ? {
11059
11065
  cy: s => `\x1b[36m${s}\x1b[0m`, dim: s => `\x1b[2m${s}\x1b[0m`,
@@ -11187,6 +11193,11 @@ async function _agentRepl(root, opts) {
11187
11193
  + C.green(`role=${state.role}`) + ' Β· '
11188
11194
  + C.yel(`perms=${permMode}`) + ' Β· '
11189
11195
  + (state.streamMode ? C.green('β–Ά stream=on') : C.dim('β–‘ stream=off')));
11196
+ // 1.9.180: μ±„νŒ… μ˜μ—­ μ§„μž… separator (μ‚¬μš©μž λͺ…μ‹œ β€” "κ³ μ • 헀더 + μ±„νŒ… ν˜•μ‹")
11197
+ // ν™˜μ˜ ν™”λ©΄ 끝에 λͺ…ν™•ν•œ ꡬ뢄선 + μ•ˆλ‚΄ β†’ μ±„νŒ… μ‹œμž‘ λͺ…ν™•.
11198
+ log('');
11199
+ log(C.dim(' ───────────────────────────── μ±„νŒ… μ‹œμž‘ ─────────────────────────────'));
11200
+ log(C.dim(' λ©”μ‹œμ§€ μž…λ ₯ ν›„ Enter Β· :help 으둜 λͺ…λ Ή λͺ©λ‘ Β· Ctrl+C 둜 μ’…λ£Œ'));
11190
11201
  // 1.9.155: REPL μ§„μž… μ‹œ handoff μ»¨ν…μŠ€νŠΈ μžλ™ λ…ΈμΆœ (UX κ°œμ„  β€” μ‚¬μš©μžκ°€ 맀번 :handoff μ•ˆ 해도 μ»¨ν…μŠ€νŠΈ 인지)
11191
11202
  try {
11192
11203
  const hf = cp.spawnSync(process.execPath, [__filename, 'handoff', root, '--compact', '--no-drift-check', '--no-headline'], {
@@ -11224,20 +11235,34 @@ async function _agentRepl(root, opts) {
11224
11235
  } catch {}
11225
11236
  return _PROVIDER_CYCLE_ORDER.slice();
11226
11237
  };
11238
+ // 1.9.180: cycleProvider/cycleModel β€” μ‹œκ° ν”Όλ“œλ°± κ°•ν™” (μ‚¬μš©μž λͺ…μ‹œ "μ›ν™œν•˜μ§€ μ•Šλ‹€").
11239
+ // 이전: ν•œ 쀄 ⇄ λ©”μ‹œμ§€λ§Œ. 1.9.180: provider μœ„μΉ˜ ν‘œμ‹œ [1/5] + bold highlight + ν™œμ„± μ—¬λΆ€ ν‘œμ‹œ.
11227
11240
  const cycleProvider = (reverse) => {
11228
11241
  const list = getProviders();
11229
11242
  let idx = list.indexOf(state.provider);
11230
11243
  if (idx < 0) idx = 0;
11231
11244
  idx = reverse ? (idx - 1 + list.length) % list.length : (idx + 1) % list.length;
11232
11245
  state.provider = list[idx];
11233
- state.model = null; // μƒˆ provider κΈ°λ³Έ λͺ¨λΈ
11246
+ state.model = null;
11234
11247
  const cat = _PROVIDER_MODEL_CATALOG[state.provider];
11235
- const hint = cat?.length ? ` (${cat.length}개 λͺ¨λΈ catalog β€” Shift+Tab으둜 cycle)` : '';
11236
- // ν˜„μž¬ μž…λ ₯ 라인 보쑴: cursorλ₯Ό 라인 μ‹œμž‘μœΌλ‘œ 이동 β†’ 클리어 β†’ status + prompt 재좜λ ₯
11248
+ // ν™œμ„± μ—¬λΆ€ 사전 확인 (1.9.180 UX β€” μ‚¬μš©μžκ°€ μ¦‰μ‹œ 인지)
11249
+ let activeMark = '';
11250
+ try {
11251
+ if (state.provider !== 'ollama') {
11252
+ const agent = EXTERNAL_AGENTS.find(a => a.id === state.provider);
11253
+ if (agent) {
11254
+ const st = _checkAgent(agent);
11255
+ activeMark = st.status === 'ready' ? C.green(' βœ“ ready') : C.yel(` ⚠ ${st.status}`);
11256
+ }
11257
+ }
11258
+ } catch {}
11237
11259
  process.stdout.write('\r\x1b[K');
11238
- process.stdout.write(C.green(` ⇄ provider: ${state.provider}${hint}\n`));
11260
+ process.stdout.write(C.bold(C.green(` ⇄ provider [${idx + 1}/${list.length}]: ${state.provider}`)) + activeMark + '\n');
11261
+ if (cat?.length) {
11262
+ process.stdout.write(C.dim(` β”” ${cat.length}개 λͺ¨λΈ catalog Β· Shift+Tab으둜 model cycle\n`));
11263
+ }
11239
11264
  rl.setPrompt(prompt());
11240
- rl.prompt(true); // preserve cursor
11265
+ rl.prompt(true);
11241
11266
  };
11242
11267
  const cycleModel = (reverse) => {
11243
11268
  const cat = _PROVIDER_MODEL_CATALOG[state.provider] || [];
@@ -11252,17 +11277,25 @@ async function _agentRepl(root, opts) {
11252
11277
  idx = reverse ? (idx - 1 + cat.length) % cat.length : (idx + 1) % cat.length;
11253
11278
  state.model = cat[idx].id;
11254
11279
  process.stdout.write('\r\x1b[K');
11255
- process.stdout.write(C.green(` ⇄ model: ${state.model} ${C.dim('β€” ' + (cat[idx].note || ''))}\n`));
11280
+ process.stdout.write(C.bold(C.mag(` ⇄ model [${idx + 1}/${cat.length}]: ${state.model}`)) + '\n');
11281
+ if (cat[idx].note) {
11282
+ process.stdout.write(C.dim(` β”” ${cat[idx].note}\n`));
11283
+ }
11256
11284
  rl.setPrompt(prompt());
11257
11285
  rl.prompt(true);
11258
11286
  };
11259
11287
  process.stdin.on('keypress', (str, key) => {
11260
11288
  if (!key) return;
11261
11289
  if (key.name === 'tab') {
11262
- cycleProvider(key.shift === true);
11290
+ // 1.9.180 fix: Shift+Tab β†’ cycleModel (μ‚¬μš©μž λͺ…μ‹œ μ˜λ„).
11291
+ // 이전 (1.9.170): cycleProvider(key.shift) β€” Shift+Tab μ‹œ provider reverse cycle만 λ˜μ–΄ model μ „ν™˜μ΄ λΆˆκ°€λŠ₯ν–ˆμŒ.
11292
+ // μ‚¬μš©μž λͺ…μ‹œ: "Tab=provider, Shift+Tab=model 선택과 μ „ν™˜μ΄ κ°„νŽΈν•˜κ²Œ".
11293
+ if (key.shift === true) {
11294
+ cycleModel(false); // Shift+Tab β†’ ν˜„μž¬ provider의 λ‹€μŒ model
11295
+ } else {
11296
+ cycleProvider(false); // Tab β†’ λ‹€μŒ provider
11297
+ }
11263
11298
  }
11264
- // Shift+\ λ˜λŠ” λ‹€λ₯Έ λͺ¨λΈ cycle alias β€” 일뢀 ν„°λ―Έλ„μ—μ„œ Shift+Tab 처리 어렀움 λŒ€λΉ„
11265
- // (Shift+Tab은 key.name='tab' + key.shift=true 둜 μœ„μ—μ„œ 처리됨)
11266
11299
  });
11267
11300
  } catch (e) {
11268
11301
  log(C.dim(` (Tab cycle λΉ„ν™œμ„±: ${e.message})`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.179",
3
+ "version": "1.9.180",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",