leerness 1.9.178 โ†’ 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,140 @@
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
+
71
+ ## 1.9.179 โ€” 2026-05-21
72
+
73
+ **๐ŸŽจ REPL ํ™˜์˜ ํ™”๋ฉด ์žฌ๋””์ž์ธ โ€” Hermes/Claude/Codex/Gemini CLI ์Šคํƒ€์ผ (์‚ฌ์šฉ์ž ๋ช…์‹œ).**
74
+
75
+ ์ž์œจ ๋ชจ๋“œ 109 ๋ผ์šด๋“œ. ์‚ฌ์šฉ์ž ๋ช…์‹œ: ์ฒจ๋ถ€ ์ด๋ฏธ์ง€ (Hermes Agent v0.7.0 / Claude Code v2.1.126 / OpenAI Codex v0.132.0 / Gemini CLI v0.42.0) ์ฒ˜๋Ÿผ ๊ตฌ์กฐํ™”๋œ ํ™˜์˜ ํ™”๋ฉด.
76
+
77
+ ### ๋””์ž์ธ ๊ตฌ์„ฑ โ€” 5 ์„น์…˜
78
+ ```
79
+ โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ
80
+ โ”‚ Leerness Agent v1.9.179 (2026-05-21) ยท ๊ฒ€์ˆ˜ยท๊ธฐ์–ตยท์ƒŒ๋“œ๋ฐ•์Šค ํ†ตํ•ฉ AI โ”‚
81
+ โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ
82
+
83
+ โ–ธ Welcome back ยท leerness (.)
84
+ โ–ธ Session: sess-2026-05-21T13-41-23
85
+
86
+ โ”Œโ”€ Tips for getting started โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
87
+ โ”‚ Tab / Shift+Tab โ€” provider / model ์ „ํ™˜ (1.9.170) โ”‚
88
+ โ”‚ :review "<req>" โ€” ๋ฌด์กฐ๊ฑด ๊ตฌํ˜„ ์ „ ์‚ฌ์ „ ๊ฒ€ํ†  (1.9.176) โ”‚
89
+ โ”‚ :permissions <m> โ€” ์ฆ‰์‹œ ๊ถŒํ•œ ๋ณ€๊ฒฝ basic|extended|full (1.9.174) โ”‚
90
+ โ”‚ :stream on|off โ€” ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ํ† ๊ธ€ (default ON, 1.9.170/172) โ”‚
91
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
92
+
93
+ โ”Œโ”€ What's new (1.9.170~178) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
94
+ โ”‚ โ€ข REPL Tab cycle + ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ (spinner / tool_use / diff ์ƒ‰๊น”) โ”‚
95
+ โ”‚ โ€ข Bridge slash :web/:pc/:lsp REPL ์ฆ‰์‹œ ํ˜ธ์ถœ + LSP ๋‹ค๊ตญ์–ด 5์ข… โ”‚
96
+ โ”‚ โ€ข review-request ์‚ฌ์ „ ๊ฒ€ํ†  + task add ์ž๋™ trigger โ”‚
97
+ โ”‚ โ€ข release sync-main ์ž๋™ npm publish (.env NPM_TOKEN) โ”‚
98
+ โ”‚ โ€ข 6 ๋Šฅ๋ ฅ ๋งคํŠธ๋ฆญ์Šค 72% production-ready ยท MCP 54 ๋„๊ตฌ โ”‚
99
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
100
+
101
+ Available Slash (5 ๊ทธ๋ฃน)
102
+ โ€ข meta: :help :model :role :provider :status :stream :clear :quit
103
+ โ€ข internal: :verify :audit :handoff :health
104
+ โ€ข memory: :lessons :brainstorm :tasks :plan
105
+ โ€ข bridge: :web :pc :lsp (๊ฐ sub: check/symbols/click/screenshot/...)
106
+ โ€ข review: :review "<request>" ยท :permissions [basic|extended|full]
107
+
108
+ โŒจ Tab=provider cycle ยท Shift+Tab=model ยท Ctrl+C=quit
109
+
110
+ โšก provider=ollama ยท model=llama3 ยท role=actor ยท perms=basic ยท โ–ถ stream=on
111
+
112
+ agent[ollama/actor/โ–ถ]> _
113
+ ```
114
+
115
+ ### ๋””์ž์ธ ๊ฒฐ์ •
116
+ - **๋‘ฅ๊ทผ ๋ชจ์„œ๋ฆฌ** (`โ•ญ โ•ฐ`) ํ—ค๋” ๋ฐ•์Šค โ†’ ์นœ๊ทผํ•œ ์ธ์ƒ
117
+ - **์‚ฌ๊ฐ ๋ชจ์„œ๋ฆฌ** (`โ”Œ โ””`) Tips/What's new ๋ฐ•์Šค โ†’ ์ •๋ณด ๋ฐ•์Šค ๊ตฌ๋ถ„
118
+ - **์ƒ‰๊น” ํ† ํฐ**: cy(ํ—ค๋”), yel(Tips), green(What's new), mag(model), bold(์ค‘์š” ๋ผ๋ฒจ)
119
+ - **5 ๊ทธ๋ฃน Slash ์นดํƒˆ๋กœ๊ทธ**: Hermes-style "Available Tools / Skills" ์˜๊ฐ
120
+ - **์ƒํƒœ๋ฐ” โšก**: provider/model/role/perms/stream 5์š”์†Œ + ์ƒ‰๊น” ๊ตฌ๋ถ„
121
+ - **ํ‚ค๋ณด๋“œ ๋‹จ์ถ•ํ‚ค โŒจ**: Tab/Shift+Tab/Ctrl+C ๋ช…์‹œ
122
+
123
+ ### ์ฒจ๋ถ€ ์ด๋ฏธ์ง€ ์ฐธ๊ณ  spirit (์ •ํ™•ํžˆ ๋ณต์ œ X)
124
+ | ์ด๋ฏธ์ง€ | ์ ์šฉ๋œ ์š”์†Œ |
125
+ |---|---|
126
+ | Hermes Agent v0.7.0 | "Available ..." ์นดํƒˆ๋กœ๊ทธ 5 ๊ทธ๋ฃน |
127
+ | Claude Code v2.1.126 | "Welcome back" + "Tips/What's new" ๋ฐ•์Šค |
128
+ | OpenAI Codex v0.132.0 | ๋ชจ๋ธ/๋””๋ ‰ํ† ๋ฆฌ ๋ฐ•์Šค + Tips |
129
+ | Gemini CLI v0.42.0 | ์ƒํƒœ๋ฐ” (sandbox/model/quota โ†’ provider/model/perms/stream) |
130
+
131
+ ### Verified
132
+ - e2e 217/217 baseline ์œ ์ง€
133
+ - stress-v124: **19/19** (ํ—ค๋” ๋ฐ•์Šค 4 + Slash 5๊ทธ๋ฃน 3 + ์ƒํƒœ๋ฐ” 3 + 1.9.179 ์ฃผ์„ 2 + ๋ˆ„์  ํšŒ๊ท€ 7)
134
+ - VERSION = 1.9.179 ยท autonomous-rounds = 109 ยท main ์ž๋™ push 40 ๋ผ์šด๋“œ ์—ฐ์†
135
+
136
+ ---
137
+
3
138
  ## 1.9.178 โ€” 2026-05-21
4
139
 
5
140
  **๐Ÿ“ฆ ์‚ฌ์šฉ์ž ๋ช…์‹œ: `release sync-main` ์ž๋™ npm publish โ€” .env NPM_TOKEN ์‚ฌ์šฉ.**
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.178-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v123-22%2F22-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-108-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-39_rounds-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)]() [![repl-slash](https://img.shields.io/badge/REPL_slash-:web%2F:pc%2F:lsp%2F:review-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.178 AI Agent Reliability Harness + Sandbox โ•‘
15
+ โ•‘ v1.9.180 AI Agent Reliability Harness + Sandbox โ•‘
16
16
  โ•‘ verify ยท remember ยท orchestrate ยท audit ยท sandbox ยท drift โ•‘
17
- โ•‘ ๐Ÿ“ฆ release sync-main ์ž๋™ npm publish (NPM_TOKEN .env ํ†ตํ•ฉ) โ•‘
17
+ โ•‘ ๐Ÿ”ง REPL Tab cycle fix ยท ์ฑ„ํŒ… ์˜์—ญ separator (๊ณ ์ • ํ—ค๋”+์ฑ„ํŒ…) โ•‘
18
18
  โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•
19
19
  ```
20
20
 
package/bin/harness.js CHANGED
@@ -4,9 +4,10 @@
4
4
  const fs = require('fs');
5
5
  const path = require('path');
6
6
  const cp = require('child_process');
7
+ const os = require('os'); // 1.9.178: _publishToNpm ์—์„œ os.tmpdir() ์‚ฌ์šฉ (์ „์—ญ import)
7
8
  const readline = require('readline');
8
9
 
9
- const VERSION = '1.9.178';
10
+ const VERSION = '1.9.180';
10
11
  const MARK = '<!-- leerness:managed -->';
11
12
  const README_START = '<!-- leerness:project-readme:start -->';
12
13
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -7822,8 +7823,18 @@ function releaseSyncMainCmd(root) {
7822
7823
  // - ์ด๋ฏธ publish๋œ ๋ฒ„์ „ ์ž๋™ detect โ†’ skip (์ค‘๋ณต publish ์ฐจ๋‹จ)
7823
7824
  function _publishToNpm(root, opts = {}) {
7824
7825
  root = absRoot(root || process.cwd());
7825
- // .env ์ž๋™ ๋กœ๋“œ (์‚ฌ์šฉ์ž ๋ช…์‹œ ํŒจํ„ด โ€” 1.9.153)
7826
- try { _loadEnvFile(root); } catch {}
7826
+ // .env ์ž๋™ ๋กœ๋“œ โ€” root + ์ƒ์œ„ 3๋‹จ๊ณ„๊นŒ์ง€ ํƒ์ƒ‰ (์›Œํฌ์ŠคํŽ˜์ด์Šค root ์˜ .env ๋„ ์ธ์‹).
7827
+ // ์˜ˆ: leerness-pkg/ ์—์„œ sync-main ํ˜ธ์ถœ ์‹œ ../leerness/.env ๋„ ์ž๋™ ํƒ์ƒ‰.
7828
+ {
7829
+ let dir = root;
7830
+ for (let i = 0; i < 4; i++) {
7831
+ try { _loadEnvFile(dir); } catch {}
7832
+ if (process.env.LEERNESS_NPM_TOKEN || process.env.NPM_TOKEN) break;
7833
+ const parent = path.resolve(dir, '..');
7834
+ if (parent === dir) break; // ๋ฃจํŠธ ๋„๋‹ฌ
7835
+ dir = parent;
7836
+ }
7837
+ }
7827
7838
  const token = process.env.LEERNESS_NPM_TOKEN || process.env.NPM_TOKEN;
7828
7839
  if (!token) {
7829
7840
  log(` โš  npm ์ž๋™ ๋ฐฐํฌ ์Šคํ‚ต: NPM_TOKEN ๋ฏธ์„ค์ • (.env ์— NPM_TOKEN=npm_xxxxx ์ถ”๊ฐ€)`);
@@ -7865,8 +7876,11 @@ function _publishToNpm(root, opts = {}) {
7865
7876
  const tmpNpmrc = path.join(tmpDir, '.npmrc');
7866
7877
  fs.writeFileSync(tmpNpmrc, `//registry.npmjs.org/:_authToken=${token}\n`, { mode: 0o600 });
7867
7878
  // 3) npm publish (--userconfig ๋กœ ์ž„์‹œ .npmrc ์‚ฌ์šฉ, --access public)
7868
- const args = opts.dryRun ? ['publish', '--userconfig', tmpNpmrc, '--access', 'public', '--dry-run'] :
7869
- ['publish', '--userconfig', tmpNpmrc, '--access', 'public'];
7879
+ // 1.9.178+: 2FA OTP ์ง€์› โ€” LEERNESS_NPM_OTP ํ™˜๊ฒฝ๋ณ€์ˆ˜ ๋˜๋Š” --npm-otp ์ธ์ž
7880
+ const otp = process.env.LEERNESS_NPM_OTP || arg('--npm-otp', null) || opts.otp;
7881
+ const baseArgs = ['publish', '--userconfig', tmpNpmrc, '--access', 'public'];
7882
+ if (otp) baseArgs.push(`--otp=${otp}`);
7883
+ const args = opts.dryRun ? [...baseArgs, '--dry-run'] : baseArgs;
7870
7884
  log(` ${opts.dryRun ? '(dry-run) ' : ''}npm publish ์‹œ๋„ ์ค‘...`);
7871
7885
  const pubR = cp.spawnSync('npm', args, {
7872
7886
  cwd: root, encoding: 'utf8', shell: true, timeout: 60000,
@@ -7879,6 +7893,12 @@ function _publishToNpm(root, opts = {}) {
7879
7893
  const errOut = (pubR.stderr || pubR.stdout || '').slice(-400);
7880
7894
  if (/EPUBLISHCONFLICT|already exists|cannot publish over/i.test(errOut)) {
7881
7895
  log(` โœ“ ์ด๋ฏธ publish๋จ (race condition) โ€” skip`);
7896
+ } else if (/EOTP|one-time password|--otp=/i.test(errOut)) {
7897
+ // 1.9.178+: 2FA ํ™œ์„ฑํ™”๋œ ๊ณ„์ • โ€” Automation ํ† ํฐ ๋˜๋Š” OTP ํ•„์š”
7898
+ warn(`npm publish ์‹คํŒจ: 2FA OTP ํ•„์š” (NPM ๊ณ„์ •์— 2FA ํ™œ์„ฑํ™”๋จ).`);
7899
+ log(` ํ•ด๊ฒฐ 1: LEERNESS_NPM_OTP=<6์ž๋ฆฌ์ฝ”๋“œ> ๋˜๋Š” --npm-otp <code> ์˜ต์…˜ ์‚ฌ์šฉ`);
7900
+ log(` ํ•ด๊ฒฐ 2: Automation ํ† ํฐ ๋ฐœ๊ธ‰ โ€” npm token create --read-only=false (2FA bypass)`);
7901
+ log(` ๋ฐœ๊ธ‰ ํ›„ .env ์˜ NPM_TOKEN ์„ ์ƒˆ ํ† ํฐ์œผ๋กœ ๊ต์ฒด`);
7882
7902
  } else if (/EAUTH|forbidden|401|403/i.test(errOut)) {
7883
7903
  warn(`npm publish ์‹คํŒจ: ํ† ํฐ ๊ถŒํ•œ ๋ถ€์กฑ ๋˜๋Š” ๋งŒ๋ฃŒ โ€” .env NPM_TOKEN ์žฌ๋ฐœ๊ธ‰ ํ•„์š”`);
7884
7904
  } else if (/ENEEDAUTH/i.test(errOut)) {
@@ -11033,7 +11053,13 @@ async function _agentRepl(root, opts) {
11033
11053
  // 1.9.153: .env ์ž๋™ ๋กœ๋“œ (REPL ์ง„์ž… ์ง์ „) โ€” install ์งํ›„ LEERNESS_ENABLE_* ์ฆ‰์‹œ ๋ฐ˜์˜
11034
11054
  try { _loadEnvFile(root); } catch {}
11035
11055
  const readline = require('readline');
11036
- 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
+ });
11037
11063
  const isTty = process.stdout.isTTY;
11038
11064
  const C = isTty ? {
11039
11065
  cy: s => `\x1b[36m${s}\x1b[0m`, dim: s => `\x1b[2m${s}\x1b[0m`,
@@ -11079,12 +11105,17 @@ async function _agentRepl(root, opts) {
11079
11105
  writeUtf8(sessionPath(), lines + '\n');
11080
11106
  } catch {}
11081
11107
  };
11082
- // ํ™˜์˜ ๋ฉ”์‹œ์ง€ + ๋ชจ๋ธ ์„ ํƒ
11108
+ // 1.9.179: ํ™˜์˜ ํ™”๋ฉด ์žฌ๋””์ž์ธ (์‚ฌ์šฉ์ž ๋ช…์‹œ โ€” Hermes/Claude/Codex/Gemini ์Šคํƒ€์ผ).
11109
+ // ๊ธฐ์กด ๋‹จ์ˆœ ๋ฐ•์Šค โ†’ ํ—ค๋” ๋ฐ•์Šค + Tips / What's new ๋ฐ•์Šค + Slash ์นดํƒˆ๋กœ๊ทธ 5 ๊ทธ๋ฃน.
11110
+ const today = new Date().toISOString().slice(0, 10);
11111
+ const wsName = path.basename(absRoot(root));
11112
+ log('');
11113
+ log(C.bold(C.cy(' โ•ญโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฎ')));
11114
+ log(C.bold(C.cy(` โ”‚ Leerness Agent v${VERSION} (${today}) ยท ๊ฒ€์ˆ˜ยท๊ธฐ์–ตยท์ƒŒ๋“œ๋ฐ•์Šค ํ†ตํ•ฉ AI โ”‚`)));
11115
+ log(C.bold(C.cy(' โ•ฐโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ•ฏ')));
11083
11116
  log('');
11084
- log(C.bold(C.cy(' โ•”โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•—')));
11085
- log(C.bold(C.cy(' โ•‘ leerness agent โ€” REPL mode โ•‘')));
11086
- log(C.bold(C.cy(' โ•‘ ๊ฒ€์ˆ˜ยท๊ธฐ์–ตยท์ƒŒ๋“œ๋ฐ•์Šค ํ†ตํ•ฉ ์ž์œจ AI ์—์ด์ „ํŠธ โ•‘')));
11087
- log(C.bold(C.cy(' โ•šโ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•โ•')));
11117
+ log(C.dim(` โ–ธ Welcome back ยท ${wsName} (${rel(process.cwd(), absRoot(root))})`));
11118
+ log(C.dim(` โ–ธ Session: ${state.sessionId}`));
11088
11119
  log('');
11089
11120
  // Ollama ๋ชจ๋ธ ์ž๋™ ๊ฐ์ง€ โ€” model์ด ๋ช…์‹œ๋˜์ง€ ์•Š์•˜์œผ๋ฉด ์‚ฌ์šฉ์ž์—๊ฒŒ ์„ ํƒ์ง€ ์ œ๊ณต
11090
11121
  if (state.provider === 'ollama' && !state.model) {
@@ -11129,15 +11160,44 @@ async function _agentRepl(root, opts) {
11129
11160
  }
11130
11161
  }
11131
11162
  }
11163
+ // 1.9.179: Tips + What's new ๋ฐ•์Šค (์‚ฌ์šฉ์ž ๋ช…์‹œ โ€” ์ฒจ๋ถ€ ์ด๋ฏธ์ง€ spirit ๋ฐ˜์˜)
11164
+ log('');
11165
+ log(C.bold(C.yel(' โ”Œโ”€ Tips for getting started โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”')));
11166
+ log(C.yel(' โ”‚ ') + C.dim('Tab / Shift+Tab') + ' ' + C.dim('โ€” provider / model ์ „ํ™˜ (1.9.170)') + C.yel(' '.repeat(Math.max(1, 17)) + 'โ”‚'));
11167
+ log(C.yel(' โ”‚ ') + C.dim(':review "<req>"') + ' ' + C.dim('โ€” ๋ฌด์กฐ๊ฑด ๊ตฌํ˜„ ์ „ ์‚ฌ์ „ ๊ฒ€ํ†  (1.9.176)') + C.yel(' '.repeat(Math.max(1, 13)) + 'โ”‚'));
11168
+ log(C.yel(' โ”‚ ') + C.dim(':permissions <m>') + ' ' + C.dim('โ€” ์ฆ‰์‹œ ๊ถŒํ•œ ๋ณ€๊ฒฝ basic|extended|full (1.9.174)') + C.yel(' โ”‚'));
11169
+ log(C.yel(' โ”‚ ') + C.dim(':stream on|off') + ' ' + C.dim('โ€” ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ ํ† ๊ธ€ (default ON, 1.9.170/172)') + C.yel(' โ”‚'));
11170
+ log(C.bold(C.yel(' โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜')));
11171
+ log('');
11172
+ log(C.bold(C.green(' โ”Œโ”€ What\'s new (1.9.170~178) โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”')));
11173
+ log(C.green(' โ”‚ ') + C.dim('โ€ข REPL Tab cycle + ์‹ค์‹œ๊ฐ„ ์ŠคํŠธ๋ฆฌ๋ฐ (spinner / tool_use / diff ์ƒ‰๊น”)') + C.green(' โ”‚'));
11174
+ log(C.green(' โ”‚ ') + C.dim('โ€ข Bridge slash :web/:pc/:lsp REPL ์ฆ‰์‹œ ํ˜ธ์ถœ + LSP ๋‹ค๊ตญ์–ด 5์ข…') + C.green(' โ”‚'));
11175
+ log(C.green(' โ”‚ ') + C.dim('โ€ข review-request ์‚ฌ์ „ ๊ฒ€ํ†  + task add ์ž๋™ trigger') + C.green(' โ”‚'));
11176
+ log(C.green(' โ”‚ ') + C.dim('โ€ข release sync-main ์ž๋™ npm publish (.env NPM_TOKEN)') + C.green(' โ”‚'));
11177
+ log(C.green(' โ”‚ ') + C.dim('โ€ข 6 ๋Šฅ๋ ฅ ๋งคํŠธ๋ฆญ์Šค 72% production-ready ยท MCP 54 ๋„๊ตฌ') + C.green(' โ”‚'));
11178
+ log(C.bold(C.green(' โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜')));
11179
+ log('');
11180
+ log(C.bold(' Available Slash (5 ๊ทธ๋ฃน)'));
11181
+ log(C.dim(' โ€ข meta: ') + ':help :model :role :provider :status :stream :clear :quit');
11182
+ log(C.dim(' โ€ข internal: ') + ':verify :audit :handoff :health');
11183
+ log(C.dim(' โ€ข memory: ') + ':lessons :brainstorm :tasks :plan');
11184
+ log(C.dim(' โ€ข bridge: ') + ':web :pc :lsp ' + C.dim('(๊ฐ sub: check/symbols/click/screenshot/...)'));
11185
+ log(C.dim(' โ€ข review: ') + ':review "<request>" ยท :permissions [basic|extended|full]');
11186
+ log('');
11187
+ log(C.dim(' โŒจ Tab=provider cycle ยท Shift+Tab=model ยท Ctrl+C=quit'));
11188
+ log('');
11189
+ // 1.9.179: ์ƒํƒœ๋ฐ” ํ•œ ์ค„ โ€” ์ƒ‰๊น”/๊ตฌ๋ถ„์ž ๊ฐ•ํ™”
11190
+ const permMode = _readPermissions(root).mode || 'basic';
11191
+ log(' ' + C.bold('โšก ') + C.cy(`provider=${state.provider}`) + ' ยท '
11192
+ + C.mag(`model=${state.model || '(๊ธฐ๋ณธ)'}`) + ' ยท '
11193
+ + C.green(`role=${state.role}`) + ' ยท '
11194
+ + C.yel(`perms=${permMode}`) + ' ยท '
11195
+ + (state.streamMode ? C.green('โ–ถ stream=on') : C.dim('โ–ก stream=off')));
11196
+ // 1.9.180: ์ฑ„ํŒ… ์˜์—ญ ์ง„์ž… separator (์‚ฌ์šฉ์ž ๋ช…์‹œ โ€” "๊ณ ์ • ํ—ค๋” + ์ฑ„ํŒ… ํ˜•์‹")
11197
+ // ํ™˜์˜ ํ™”๋ฉด ๋์— ๋ช…ํ™•ํ•œ ๊ตฌ๋ถ„์„  + ์•ˆ๋‚ด โ†’ ์ฑ„ํŒ… ์‹œ์ž‘ ๋ช…ํ™•.
11132
11198
  log('');
11133
- log(C.dim(' ๋ฉ”ํƒ€ ๋ช…๋ น: :help | :model <m> | :role <r> | :provider <p> | :status | :clear | :save | :history | :quit'));
11134
- log(C.dim(' Slash ๋ช…๋ น (1.9.150): :verify | :audit | :handoff | :health'));
11135
- log(C.dim(' Memory Slash (1.9.161): :lessons | :brainstorm <topic> | :tasks | :plan'));
11136
- log(C.dim(' ๐Ÿ†• 1.9.170 โ€” Tab=provider cycle, Shift+Tab=model cycle, :stream on|off (์‹ค์‹œ๊ฐ„ ์ถœ๋ ฅ)'));
11137
- log(C.dim(' ๐Ÿ†• 1.9.174 โ€” :permissions [basic|extended|full] ๋กœ ์ฆ‰์‹œ ๊ถŒํ•œ ๋ณ€๊ฒฝ (default: basic ์•ˆ์ „)'));
11138
- log(C.dim(' ๐Ÿ†• 1.9.175 โ€” :web / :pc / :lsp ์œผ๋กœ Bridge 3์ข… REPL ์•ˆ์—์„œ ์ฆ‰์‹œ ํ˜ธ์ถœ (์ฝ”๋“œ ๋ถ„์„/์›น/PC)'));
11139
- log(C.dim(' ๐Ÿ†• 1.9.176 โ€” :review "<์š”์ฒญ>" ์œผ๋กœ ์‚ฌ์šฉ์ž ์š”์ฒญ ์‚ฌ์ „ ๊ฒ€ํ†  (์ถฉ๋Œ/์žฌ์‚ฌ์šฉ/ํšจ์œจ/๊ถŒ์žฅ ๋‹จ๊ณ„)'));
11140
- log(C.dim(` ํ˜„์žฌ โ€” provider=${state.provider} model=${state.model || '(๊ธฐ๋ณธ)'} role=${state.role} permissions=${_readPermissions(root).mode}`));
11199
+ log(C.dim(' โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ ์ฑ„ํŒ… ์‹œ์ž‘ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€'));
11200
+ log(C.dim(' ๋ฉ”์‹œ์ง€ ์ž…๋ ฅ ํ›„ Enter ยท :help ์œผ๋กœ ๋ช…๋ น ๋ชฉ๋ก ยท Ctrl+C ๋กœ ์ข…๋ฃŒ'));
11141
11201
  // 1.9.155: REPL ์ง„์ž… ์‹œ handoff ์ปจํ…์ŠคํŠธ ์ž๋™ ๋…ธ์ถœ (UX ๊ฐœ์„  โ€” ์‚ฌ์šฉ์ž๊ฐ€ ๋งค๋ฒˆ :handoff ์•ˆ ํ•ด๋„ ์ปจํ…์ŠคํŠธ ์ธ์ง€)
11142
11202
  try {
11143
11203
  const hf = cp.spawnSync(process.execPath, [__filename, 'handoff', root, '--compact', '--no-drift-check', '--no-headline'], {
@@ -11175,20 +11235,34 @@ async function _agentRepl(root, opts) {
11175
11235
  } catch {}
11176
11236
  return _PROVIDER_CYCLE_ORDER.slice();
11177
11237
  };
11238
+ // 1.9.180: cycleProvider/cycleModel โ€” ์‹œ๊ฐ ํ”ผ๋“œ๋ฐฑ ๊ฐ•ํ™” (์‚ฌ์šฉ์ž ๋ช…์‹œ "์›ํ™œํ•˜์ง€ ์•Š๋‹ค").
11239
+ // ์ด์ „: ํ•œ ์ค„ โ‡„ ๋ฉ”์‹œ์ง€๋งŒ. 1.9.180: provider ์œ„์น˜ ํ‘œ์‹œ [1/5] + bold highlight + ํ™œ์„ฑ ์—ฌ๋ถ€ ํ‘œ์‹œ.
11178
11240
  const cycleProvider = (reverse) => {
11179
11241
  const list = getProviders();
11180
11242
  let idx = list.indexOf(state.provider);
11181
11243
  if (idx < 0) idx = 0;
11182
11244
  idx = reverse ? (idx - 1 + list.length) % list.length : (idx + 1) % list.length;
11183
11245
  state.provider = list[idx];
11184
- state.model = null; // ์ƒˆ provider ๊ธฐ๋ณธ ๋ชจ๋ธ
11246
+ state.model = null;
11185
11247
  const cat = _PROVIDER_MODEL_CATALOG[state.provider];
11186
- const hint = cat?.length ? ` (${cat.length}๊ฐœ ๋ชจ๋ธ catalog โ€” Shift+Tab์œผ๋กœ cycle)` : '';
11187
- // ํ˜„์žฌ ์ž…๋ ฅ ๋ผ์ธ ๋ณด์กด: 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 {}
11188
11259
  process.stdout.write('\r\x1b[K');
11189
- 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
+ }
11190
11264
  rl.setPrompt(prompt());
11191
- rl.prompt(true); // preserve cursor
11265
+ rl.prompt(true);
11192
11266
  };
11193
11267
  const cycleModel = (reverse) => {
11194
11268
  const cat = _PROVIDER_MODEL_CATALOG[state.provider] || [];
@@ -11203,17 +11277,25 @@ async function _agentRepl(root, opts) {
11203
11277
  idx = reverse ? (idx - 1 + cat.length) % cat.length : (idx + 1) % cat.length;
11204
11278
  state.model = cat[idx].id;
11205
11279
  process.stdout.write('\r\x1b[K');
11206
- 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
+ }
11207
11284
  rl.setPrompt(prompt());
11208
11285
  rl.prompt(true);
11209
11286
  };
11210
11287
  process.stdin.on('keypress', (str, key) => {
11211
11288
  if (!key) return;
11212
11289
  if (key.name === 'tab') {
11213
- 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
+ }
11214
11298
  }
11215
- // Shift+\ ๋˜๋Š” ๋‹ค๋ฅธ ๋ชจ๋ธ cycle alias โ€” ์ผ๋ถ€ ํ„ฐ๋ฏธ๋„์—์„œ Shift+Tab ์ฒ˜๋ฆฌ ์–ด๋ ค์›€ ๋Œ€๋น„
11216
- // (Shift+Tab์€ key.name='tab' + key.shift=true ๋กœ ์œ„์—์„œ ์ฒ˜๋ฆฌ๋จ)
11217
11299
  });
11218
11300
  } catch (e) {
11219
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.178",
3
+ "version": "1.9.180",
4
4
  "description": "Leerness: ๋น„ํŒŒ๊ดด ๋งˆ์ด๊ทธ๋ ˆ์ด์…˜, ์ž๋™ ๋ฒ„์ „ ๊ฐ์ง€ยท์—…๋ฐ์ดํŠธ, ๊ณ„ํš/์ง„ํ–‰/ํ•ธ๋“œ์˜คํ”„ ์ž๋™ํ™”, ๊ฒŒ์œผ๋ฆ„ยท์‹œํฌ๋ฆฟยท์ธ์ฝ”๋”ฉ ์ž๋™ ๊ฐ€๋“œ, Claude Code ์Šฌ๋ž˜์‹œ ํ†ตํ•ฉ์„ ๊ฐ–์ถ˜ ํ•œ๊ตญ์–ด ์šฐ์„  AI ๊ฐœ๋ฐœ ํ•˜๋„ค์Šค.",
5
5
  "keywords": [
6
6
  "leerness",