leerness 1.9.180 β†’ 1.9.182

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,171 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.9.182 β€” 2026-05-21
4
+
5
+ **🌐 곡식 쑰직 μŠ€ν‚¬ catalog μžλ™ 탐색 β€” vercel-labs/agent-skills + anthropics/skills 직접 톡합 (μ‚¬μš©μž λͺ…μ‹œ).**
6
+
7
+ 자율 λͺ¨λ“œ 112 λΌμš΄λ“œ. μ‚¬μš©μž λͺ…μ‹œ: *"곡식 쑰직의 μŠ€ν‚¬ λͺ¨μŒμ„ νƒμƒ‰ν•΄μ„œ λ‹€μš΄λ‘œλ“œ λ°›μ•„μ„œ μŠ€ν‚¬μ„ μ‚¬μš©ν•˜λŠ”μ§€ 확인 (vercel-labs/agent-skills, anthropics/skills) + μ§€κΈˆ μ„€κ³„ν•˜λŠ” λ°©ν–₯이 μ˜¬λ°”λ₯Έμ§€ νŒλ‹¨λ„ λ„ˆκ°€ ν•΄μ€˜"*.
8
+
9
+ ### 핡심 μΆ”κ°€
10
+
11
+ #### 1. `SKILL_CATALOG_PRESETS` β€” λ‚΄μž₯ 곡식 catalog
12
+ ```js
13
+ const SKILL_CATALOG_PRESETS = {
14
+ 'vercel': { owner: 'vercel-labs', repo: 'agent-skills', branch: 'main', path: 'skills' },
15
+ 'anthropic': { owner: 'anthropics', repo: 'skills', branch: 'main', path: 'skills' }
16
+ };
17
+ ```
18
+
19
+ #### 2. `_fetchGitHubSkills(owner, repo, branch, dirPath)` β€” GitHub Contents API 직접 호좜
20
+ - rate limit: 60 req/hr (LEERNESS_GITHUB_TOKEN μ‹œ 5000 req/hr)
21
+ - 응닡을 ν‘œμ€€ entry ν˜•μ‹μœΌλ‘œ λ³€ν™˜ (name, url, description, source, homepage)
22
+ - raw.githubusercontent.com URL μžλ™ ꡬ성
23
+
24
+ #### 3. `leerness skill discover` ν™•μž₯
25
+ ```bash
26
+ $ leerness skill discover --preset anthropic
27
+ # leerness skill discover (1.9.182 β€” GitHub presets)
28
+ targets: anthropics/skills#main:skills
29
+ fetching anthropics/skills...
30
+ βœ“ anthropics/skills: 17개 skill 발견
31
+ 전체 17건 (전체 ν‘œμ‹œ β€” λ§€μΉ­ μ—†μŒ)
32
+
33
+ | name | source | url |
34
+ |---|---|---|
35
+ | algorithmic-art | github:anthropics/skills | https://raw.githubusercontent.com/anthropics/skills/main/skills/algorithmic-art/SKILL.md |
36
+ | mcp-builder | github:anthropics/skills | https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md |
37
+ | ...
38
+ ```
39
+
40
+ μ‹ κ·œ μ˜΅μ…˜:
41
+ - `--preset <name>` β€” λ‚΄μž₯ catalog (vercel, anthropic)
42
+ - `--all-presets` β€” λͺ¨λ“  preset λ™μ‹œ 탐색
43
+ - `--github owner/repo[#branch][:path]` β€” 직접 GitHub repo μ§€μ •
44
+
45
+ #### 4. `leerness skill auto-install` β€” μ‹ κ·œ λͺ…λ Ή (μ‚¬μš©μž λͺ…μ‹œ 핡심)
46
+ ```bash
47
+ $ leerness skill auto-install --query "mcp"
48
+ # leerness skill auto-install (1.9.182)
49
+ presets: vercel, anthropic
50
+ query: mcp
51
+ mode: 🟑 dry-run (LEERNESS_SKILL_AUTO_INSTALL=1 λ˜λŠ” --yes ν•„μš”)
52
+ fetching anthropics/skills...
53
+ βœ“ anthropics/skills: 17개 skill
54
+ 맀칭 1/17건 (query: mcp)
55
+ | mcp-builder | github:anthropics/skills | https://raw.githubusercontent.com/anthropics/skills/main/skills/mcp-builder/SKILL.md |
56
+
57
+ πŸ’‘ μžλ™ install ν™œμ„±ν™”: .env 에 LEERNESS_SKILL_AUTO_INSTALL=1 λ˜λŠ” leerness skill auto-install --yes
58
+ ```
59
+
60
+ - handoff μ»¨ν…μŠ€νŠΈμ—μ„œ μžλ™ query μΆ”μΆœ (μ§„ν–‰ task ν‚€μ›Œλ“œ)
61
+ - LEERNESS_SKILL_AUTO_INSTALL=1 μ‹œ μ‹€μ œ λ‹€μš΄λ‘œλ“œ (λ³΄μ•ˆ opt-in)
62
+ - λ―Έμ„€μ • μ‹œ dry-run (μΆ”μ²œλ§Œ)
63
+
64
+ #### 5. .env.example 보강
65
+ ```
66
+ LEERNESS_SKILL_AUTO_INSTALL=0
67
+ LEERNESS_SKILL_AUTO_PRESETS=vercel,anthropic
68
+ ```
69
+
70
+ ### μ‹€ 검증 (live GitHub API)
71
+ - **anthropics/skills 17개 skill μžλ™ 발견**:
72
+ algorithmic-art, brand-guidelines, canvas-design, claude-api, doc-coauthoring, **docx**, frontend-design, internal-comms, **mcp-builder**, **pdf**, **pptx**, skill-creator, slack-gif-creator, theme-factory, web-artifacts-builder, **webapp-testing**, **xlsx**
73
+ - **mcp-builder μ‹€ λ‹€μš΄λ‘œλ“œ + μ„€μΉ˜ 성곡**:
74
+ `leerness-stress/.harness/skills/mcp-builder/SKILL.md` λ‹€μš΄λ‘œλ“œ 검증
75
+ description: "Guide for creating high-quality MCP (Model Context Protocol) servers..."
76
+
77
+ ### λ°©ν–₯μ„± 평가 λ³΄κ³ μ„œ (μ‚¬μš©μž λͺ…μ‹œ)
78
+ μ‚¬μš©μž μš”μ²­: *"μ§€κΈˆ μ„€κ³„ν•˜λŠ” λ°©ν–₯이 μ˜¬λ°”λ₯Έ λ°©ν–₯인지 νŒλ‹¨λ„ λ„ˆκ°€ ν•΄μ€˜"*. λ³΄κ³ μ„œ: `_reports/direction-1.9.182.md` (λΉ„κ³΅κ°œ).
79
+
80
+ **κ²°λ‘ **:
81
+ | 평가 ν•­λͺ© | 점수 |
82
+ |---|---|
83
+ | μžμœ¨μ„± (auto loop Β· review Β· install) | 8.5/10 |
84
+ | μ„±μž₯μ„± (learn Β· suggest Β· skill catalog) | 8/10 |
85
+ | μ•ˆμ „μ„± (sandbox Β· permissions Β· env opt-in) | 9/10 |
86
+ | λ²”μš©μ„± (multi-provider Β· MCP Β· bridges) | 8/10 |
87
+ | 검증 κ°€λŠ₯μ„± (verify-claim Β· stress Β· e2e) | 9.5/10 |
88
+
89
+ **μ’…ν•©**: 86/100. **μ˜¬λ°”λ₯Έ λ°©ν–₯**. 약점은 *μŠ€ν‚¬ 적용 ν›„ 회고 λΆ€μž¬ + μ™ΈλΆ€ μŠ€ν‚¬ μ‹ λ’°μ„± 검증* β€” 1.9.183~190 λ§ˆμΌμŠ€ν†€.
90
+
91
+ ### Verified
92
+ - stress-v127: **16/16** (preset 4 + .env λΌμš°νŒ… 2 + μ‹€ GitHub fetch 3 + VERSION+λˆ„μ  νšŒκ·€ 7)
93
+ - e2e 217/217 baseline μœ μ§€
94
+ - VERSION = 1.9.182 Β· autonomous-rounds = 112 Β· main μžλ™ push 43 λΌμš΄λ“œ 연속
95
+
96
+ ---
97
+
98
+ ## 1.9.181 β€” 2026-05-21
99
+
100
+ **πŸšͺ REPL μ§„μž… 흐름 정리 β€” μ‚¬μš©μž λͺ…μ‹œ 4μ’… + 직접 ꡬ동 μ‹€ 호좜 검증.**
101
+
102
+ 자율 λͺ¨λ“œ 111 λΌμš΄λ“œ. μ‚¬μš©μž 직접 ꡬ동 ν…ŒμŠ€νŠΈ κ²°κ³Ό 보고 (μŠ€ν¬λ¦°μƒ· 첨뢀):
103
+ 1. *"1.9.149 Hermes/OpenClaw μŠ€νƒ€μΌ λ“±μ˜ λ¬Έκ΅¬λŠ” 제거"* β†’ μ„€μΉ˜ μ™„λ£Œ λ©”μ‹œμ§€ λ‹¨μˆœν™”
104
+ 2. *"이 λ‹¨κ³„μ—μ„œ Ollama μ œμ™Έν•œ λͺ¨λΈμ„ μ„ νƒν–ˆλŠ”λ° REPL μ§„μž… μ‹œ Ollama μš°μ„  호좜"* β†’ installβ†’REPL provider μžλ™ 선택
105
+ 3. *"ν”„λ‘œλ°”μ΄λ” μ „ν™˜ 선택 단계 없이 λ°”λ‘œ μ±„νŒ… λͺ¨λ“œλ‘œ μ§„μž…"* β†’ μ§„μž… prompt 제거
106
+ 4. *"REPL을 직접 κ΅¬λ™ν•΄μ„œ λͺ…λ Ή μž…λ ₯해보고 개발/μ›Ή/PC/μΆ”λ‘ /질문-λ‹΅λ³€ λ™μž‘ ν…ŒμŠ€νŠΈ"* β†’ agents multi --execute μ‹€ 호좜 검증
107
+
108
+ ### Fix #1 β€” 문ꡬ λ‹¨μˆœν™”
109
+ ```diff
110
+ - log('πŸš€ μ„€μΉ˜ μ™„λ£Œ β€” REPL agent λͺ¨λ“œλ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€ (1.9.149 Hermes/OpenClaw μŠ€νƒ€μΌ)...');
111
+ + log('πŸš€ μ„€μΉ˜ μ™„λ£Œ β€” REPL agent λͺ¨λ“œλ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€...');
112
+ ```
113
+
114
+ ### Fix #2 β€” installβ†’REPL provider ν•˜λ“œμ½”λ”© 제거
115
+ ```diff
116
+ - await _agentRepl(root, { provider: 'ollama', role: 'actor' }); // 1.9.151 β€” 무쑰건 ollama
117
+ + await _agentRepl(root, { role: 'actor' }); // provider λ―Έμ§€μ • β†’ auto-select λ™μž‘ (1.9.181 fix)
118
+ ```
119
+
120
+ ### Fix #3 β€” λΉ„-Ollama μš°μ„  μžλ™ 선택 (Ollama μš°μ„  호좜 X)
121
+ ```js
122
+ const ready = EXTERNAL_AGENTS.map(a => ({ def: a, status: _checkAgent(a) }))
123
+ .filter(x => x.status.status === 'ready');
124
+ const nonOllama = ready.filter(x => x.def.id !== 'ollama');
125
+ if (nonOllama.length >= 1) {
126
+ // λΉ„-Ollama ν™œμ„± β†’ 첫 번째 μžλ™ (μ‚¬μš©μž λͺ…μ‹œ: Ollama μš°μ„  호좜 X)
127
+ initialProvider = nonOllama[0].def.id;
128
+ _autoPickNote = nonOllama.length === 1
129
+ ? `${initialProvider} μžλ™ 선택 (ν™œμ„± CLI 1개)`
130
+ : `${initialProvider} μžλ™ 선택 (ν™œμ„± CLI ${nonOllama.length}개 Β· Tab으둜 μ „ν™˜)`;
131
+ }
132
+ ```
133
+
134
+ ### Fix #4 β€” provider μ „ν™˜ prompt 단계 제거 (μžλ™ μ „ν™˜)
135
+ 이전:
136
+ ```
137
+ ⚠ Ollama 미가동 λ˜λŠ” λͺ¨λΈ μ—†μŒ
138
+ πŸ’‘ ν™œμ„± μ™ΈλΆ€ CLI 4개 발견 β€” provider μ „ν™˜ κ°€λŠ₯:
139
+ 1) claude (v2.1.145)
140
+ 2) codex (vcodex-cli 0.132.0)
141
+ ...
142
+ provider μ „ν™˜ (번호 / Enter=ollama 계속): _
143
+ ```
144
+ μ§€κΈˆ:
145
+ ```
146
+ β–Έ Provider: claude μžλ™ 선택 (ν™œμ„± CLI 4개 Β· Tab으둜 μ „ν™˜)
147
+ [μ±„νŒ… λͺ¨λ“œ μ¦‰μ‹œ μ§„μž…]
148
+ ```
149
+
150
+ ### 직접 ꡬ동 μ‹€ 호좜 검증 (μ‚¬μš©μž λͺ…μ‹œ 4번째 μš”μ²­)
151
+ ```bash
152
+ $ leerness agents multi "1+1=? 숫자만 λ‹΅ν•΄μ£Όμ„Έμš”." --only claude,gemini --execute --timeout 30
153
+ βœ“ claude Β· 4810ms Β· 1 토큰
154
+ βœ— gemini Β· 1266ms Β· exit=null
155
+ best: claude Β· score=0.600
156
+ --- 처음 600자 ---
157
+ 2
158
+ ```
159
+ **claude μΆ”λ‘  응닡 정상**. geminiλŠ” 별도 ν™˜κ²½ 이슈 (--yolo κΆŒν•œ λ˜λŠ” quota β€” 1.9.182μ—μ„œ μΆ”κ°€ 디버그 후보).
160
+
161
+ ### Verified
162
+ - stress-v126: **18/18** (μ‚¬μš©μž λͺ…μ‹œ 4 + μžλ™ 선택 λ™μž‘ 4 + 직접 ꡬ동 2 + VERSION+λˆ„μ  νšŒκ·€ 8)
163
+ - e2e 217/217 baseline μœ μ§€
164
+ - claude μ‹€ 호좜 4810ms Β· 응닡 "2" β€” REPL agent의 핡심 λŠ₯λ ₯ (μ‹€ λͺ¨λΈ 호좜 + μΆ”λ‘  + 응닡 μˆ˜μ‹ ) λ™μž‘ 확인
165
+ - VERSION = 1.9.181 Β· autonomous-rounds = 111 Β· main μžλ™ push 42 λΌμš΄λ“œ 연속
166
+
167
+ ---
168
+
3
169
  ## 1.9.180 β€” 2026-05-21
4
170
 
5
171
  **πŸ”§ REPL Tab cycle 핡심 fix + μ±„νŒ… μ˜μ—­ separator β€” μ‚¬μš©μž λͺ…μ‹œ (직접 ꡬ동 ν…ŒμŠ€νŠΈ κ²°κ³Ό).**
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.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)]()
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.182-green)]() [![tests](https://img.shields.io/badge/e2e-217%2F217-success)]() [![stress](https://img.shields.io/badge/stress--v127-16%2F16-success)]() [![mcp](https://img.shields.io/badge/MCP--tools-54-brightgreen)]() [![rounds](https://img.shields.io/badge/autonomous--rounds-112-blueviolet)]() [![main-push](https://img.shields.io/badge/release--main--push-43_rounds-success)]() [![skill-presets](https://img.shields.io/badge/skill_catalog-vercel%2Banthropic_톡합-success)]() [![skill-auto](https://img.shields.io/badge/skill_auto--install-handoff_톡합_(opt--in)-success)]() [![direction](https://img.shields.io/badge/λ°©ν–₯μ„±_평가-86%2F100_(μ˜¬λ°”λ₯Έ_λ°©ν–₯)-brightgreen)]() [![npm-auto](https://img.shields.io/badge/npm_auto--publish-NPM__TOKEN_톡합-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.180 AI Agent Reliability Harness + Sandbox β•‘
15
+ β•‘ v1.9.182 AI Agent Reliability Harness + Sandbox β•‘
16
16
  β•‘ verify Β· remember Β· orchestrate Β· audit Β· sandbox Β· drift β•‘
17
- β•‘ πŸ”§ REPL Tab cycle fix Β· μ±„νŒ… μ˜μ—­ separator (κ³ μ • 헀더+μ±„νŒ…) β•‘
17
+ β•‘ 🌐 곡식 catalog μžλ™ 탐색 (vercel/anthropic) Β· μžκ°€ μ„±μž₯ν˜• β•‘
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.180';
10
+ const VERSION = '1.9.182';
11
11
  const MARK = '<!-- leerness:managed -->';
12
12
  const README_START = '<!-- leerness:project-readme:start -->';
13
13
  const README_END = '<!-- leerness:project-readme:end -->';
@@ -872,7 +872,11 @@ async function install(root, opts = {}) {
872
872
  '# μ˜ˆμ‹œ URL: https://agentskills.io/llms.txt',
873
873
  'LEERNESS_SKILL_DISCOVER_URL=',
874
874
  '# (선택) μ‚¬μš©μž μš”μ²­ 뢄석 μ‹œ μžλ™ λ§€μΉ­ μŠ€ν‚¬ μΆ”μ²œ. 1=ν™œμ„±, 0/λ―Έμ„€μ •=λΉ„ν™œμ„±.',
875
- 'LEERNESS_SKILL_AUTO_DISCOVER=0'
875
+ 'LEERNESS_SKILL_AUTO_DISCOVER=0',
876
+ '# 1.9.182 β€” handoff μ‹œ 곡식 catalog (vercel-labs, anthropics) μžλ™ 탐색 + λ§€μΉ­ μ‹œ μžλ™ install. 1=ν™œμ„± (opt-in λ³΄μ•ˆ).',
877
+ 'LEERNESS_SKILL_AUTO_INSTALL=0',
878
+ '# 1.9.182 β€” handoff μžλ™ 탐색 λŒ€μƒ preset (콀마 ꡬ뢄). 예: vercel,anthropic',
879
+ 'LEERNESS_SKILL_AUTO_PRESETS=vercel,anthropic'
876
880
  ];
877
881
  mergeLinesFile(path.join(root, '.env.example'), envLines);
878
882
  // 1.9.153: .env 직접 생성/λ§ˆμ΄κ·Έλ ˆμ΄μ…˜ (μ‚¬μš©μž λͺ…μ‹œ μš”μ²­). λ³΄μ•ˆ = 빈 κ°’λ§Œ β€” μ‚¬μš©μžκ°€ 직접 토큰 채움.
@@ -934,12 +938,13 @@ async function install(root, opts = {}) {
934
938
  // resolveInstallOptions (1.9.146) κ°€ 이미 λͺ¨λ“  prompt λͺ¨μ€ μœ„μΉ˜μ— ν†΅ν•©λœ 4μ§€μ„ λ‹€ prompt 있음.
935
939
  // 별도 setupAgents λͺ…령은 μ‚¬μš©μžκ°€ λͺ…μ‹œμ μœΌλ‘œ `leerness setup-agents` 호좜 μ‹œμ—λ§Œ.
936
940
  // 1.9.151: μ„€μΉ˜ μ™„λ£Œ 직후 β€” startRepl 선택 μ‹œ REPL agent λͺ¨λ“œ μ¦‰μ‹œ μ§„μž… (μ‚¬μš©μž λͺ…μ‹œ μš”μ²­)
941
+ // 1.9.181: 문ꡬ λ‹¨μˆœν™” + provider ν•˜λ“œμ½”λ”© 제거 (μ‚¬μš©μž λͺ…μ‹œ β€” install μ„ νƒν•œ CLIλ₯Ό REPL이 μžλ™ 선택)
937
942
  if (resolved.startRepl && !opts.migration && process.stdin.isTTY && process.env.LEERNESS_NO_PROMPT !== '1') {
938
943
  log('');
939
- log('πŸš€ μ„€μΉ˜ μ™„λ£Œ β€” REPL agent λͺ¨λ“œλ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€ (1.9.149 Hermes/OpenClaw μŠ€νƒ€μΌ)...');
944
+ log('πŸš€ μ„€μΉ˜ μ™„λ£Œ β€” REPL agent λͺ¨λ“œλ₯Ό μ‹œμž‘ν•©λ‹ˆλ‹€...');
940
945
  log('');
941
946
  try {
942
- await _agentRepl(root, { provider: 'ollama', role: 'actor' });
947
+ await _agentRepl(root, { role: 'actor' }); // provider λ―Έμ§€μ • β†’ _agentRepl 의 auto-select λ™μž‘ (1.9.181 fix)
943
948
  } catch (e) { warn('REPL μ§„μž… μ‹€νŒ¨: ' + e.message); }
944
949
  }
945
950
  }
@@ -1321,15 +1326,135 @@ function _parseSkillCatalog(body, sourceUrl) {
1321
1326
  return entries;
1322
1327
  }
1323
1328
 
1324
- // skill discover β€” agentskills.io λ˜λŠ” μ‚¬μš©μž μ§€μ • URL의 μΉ΄νƒˆλ‘œκ·Έ μΈλ±μŠ€μ—μ„œ λ§€μΉ­ μΆ”μ²œ
1329
+ // 1.9.182: 곡식 쑰직 μŠ€ν‚¬ catalog presets β€” μ‚¬μš©μž λͺ…μ‹œ (vercel-labs, anthropics 같은 1st-party μžλ™ 탐색).
1330
+ // 각 entry: GitHub repo의 skills/ 디렉토리에 SKILL.md 듀이 μžˆλŠ” ν‘œμ€€ ꡬ쑰.
1331
+ // sync: leerness skill discover --preset <name> Β· --all-presets Β· --github <owner/repo>[#branch]
1332
+ const SKILL_CATALOG_PRESETS = {
1333
+ 'vercel': { owner: 'vercel-labs', repo: 'agent-skills', branch: 'main', path: 'skills',
1334
+ homepage: 'https://github.com/vercel-labs/agent-skills' },
1335
+ 'anthropic': { owner: 'anthropics', repo: 'skills', branch: 'main', path: 'skills',
1336
+ homepage: 'https://github.com/anthropics/skills' }
1337
+ };
1338
+
1339
+ // 1.9.182: GitHub repo 의 skills/ 디렉토리 μžλ™ 탐색.
1340
+ // GitHub Contents API (no auth 60req/hr, .env GITHUB_TOKEN μ‹œ 5000req/hr) μ‚¬μš©.
1341
+ // 응닡을 ν‘œμ€€ entry ν˜•μ‹μœΌλ‘œ λ³€ν™˜: { name, url, description, format: 'github', source }
1342
+ async function _fetchGitHubSkills(owner, repo, branch, dirPath) {
1343
+ branch = branch || 'main'; dirPath = dirPath || 'skills';
1344
+ const apiUrl = `https://api.github.com/repos/${owner}/${repo}/contents/${dirPath}?ref=${branch}`;
1345
+ const token = process.env.LEERNESS_GITHUB_TOKEN || process.env.GITHUB_TOKEN || '';
1346
+ const headers = token ? { Authorization: `Bearer ${token}` } : {};
1347
+ // _httpFetch κ°€ headersλ₯Ό μ§€μ›ν•˜μ§€ μ•ŠμœΌλ©΄ raw https μ‚¬μš©
1348
+ let body, status;
1349
+ try {
1350
+ const https = require('https');
1351
+ const u = new URL(apiUrl);
1352
+ body = await new Promise((resolve, reject) => {
1353
+ const req = https.get({
1354
+ hostname: u.hostname, path: u.pathname + u.search,
1355
+ headers: { 'User-Agent': `leerness/${VERSION}`, ...headers }
1356
+ }, res => {
1357
+ status = res.statusCode;
1358
+ const chunks = [];
1359
+ res.on('data', c => chunks.push(c));
1360
+ res.on('end', () => resolve(Buffer.concat(chunks).toString('utf8')));
1361
+ });
1362
+ req.on('error', reject);
1363
+ req.setTimeout(10000, () => { req.destroy(new Error('timeout')); });
1364
+ });
1365
+ } catch (e) { return { ok: false, error: e.message, skills: [] }; }
1366
+ if (status !== 200) return { ok: false, error: `HTTP ${status}`, skills: [] };
1367
+ let items; try { items = JSON.parse(body); } catch { return { ok: false, error: 'invalid JSON', skills: [] }; }
1368
+ if (!Array.isArray(items)) return { ok: false, error: 'not array', skills: [] };
1369
+ const skills = items
1370
+ .filter(i => i && i.type === 'dir')
1371
+ .map(i => ({
1372
+ name: i.name,
1373
+ url: `https://raw.githubusercontent.com/${owner}/${repo}/${branch}/${i.path}/SKILL.md`,
1374
+ description: '',
1375
+ format: 'github',
1376
+ source: `github:${owner}/${repo}`,
1377
+ homepage: i.html_url
1378
+ }));
1379
+ return { ok: true, skills };
1380
+ }
1381
+
1382
+ // skill discover β€” agentskills.io / GitHub repo / 곡식 preset μΉ΄νƒˆλ‘œκ·Έ μΈλ±μŠ€μ—μ„œ λ§€μΉ­ μΆ”μ²œ
1325
1383
  async function skillDiscoverCmd(root) {
1326
- const url = arg('--source', null) || process.env.LEERNESS_SKILL_DISCOVER_URL || null;
1384
+ // 1.9.182: --preset / --all-presets / --github μΆ”κ°€ (μ‚¬μš©μž λͺ…μ‹œ β€” vercel-labs/agent-skills, anthropics/skills λ“±)
1385
+ const preset = arg('--preset', null);
1386
+ const allPresets = has('--all-presets');
1387
+ const github = arg('--github', null); // 예: vercel-labs/agent-skills λ˜λŠ” owner/repo#branch:path
1327
1388
  const query = arg('--query', null);
1389
+
1390
+ // preset / --all-presets / --github 처리 β€” 단일 source URLκ³Ό λ³„κ°œ 흐름
1391
+ if (preset || allPresets || github) {
1392
+ const targets = [];
1393
+ if (allPresets) for (const k of Object.keys(SKILL_CATALOG_PRESETS)) targets.push({ key: k, ...SKILL_CATALOG_PRESETS[k] });
1394
+ else if (preset) {
1395
+ const p = SKILL_CATALOG_PRESETS[preset];
1396
+ if (!p) { fail(`μ•Œ 수 μ—†λŠ” preset: ${preset} (μ‚¬μš© κ°€λŠ₯: ${Object.keys(SKILL_CATALOG_PRESETS).join(', ')})`); return process.exit(1); }
1397
+ targets.push({ key: preset, ...p });
1398
+ }
1399
+ if (github) {
1400
+ // owner/repo λ˜λŠ” owner/repo#branch λ˜λŠ” owner/repo#branch:path
1401
+ const m = github.match(/^([^/]+)\/([^#:]+)(?:#([^:]+))?(?::(.+))?$/);
1402
+ if (!m) { fail(`--github ν˜•μ‹ 였λ₯˜ (예: vercel-labs/agent-skills λ˜λŠ” owner/repo#main:skills)`); return process.exit(1); }
1403
+ targets.push({ key: 'custom', owner: m[1], repo: m[2], branch: m[3] || 'main', path: m[4] || 'skills', homepage: `https://github.com/${m[1]}/${m[2]}` });
1404
+ }
1405
+ log(`# leerness skill discover (1.9.182 β€” GitHub presets)`);
1406
+ if (query) log(`query: ${query}`);
1407
+ log(`targets: ${targets.map(t => `${t.owner}/${t.repo}#${t.branch}:${t.path}`).join(', ')}`);
1408
+ log('');
1409
+ const allEntries = [];
1410
+ for (const t of targets) {
1411
+ log(` fetching ${t.owner}/${t.repo}...`);
1412
+ const r = await _fetchGitHubSkills(t.owner, t.repo, t.branch, t.path);
1413
+ if (!r.ok) {
1414
+ log(` ⚠ ${t.owner}/${t.repo}: ${r.error}`);
1415
+ continue;
1416
+ }
1417
+ log(` βœ“ ${t.owner}/${t.repo}: ${r.skills.length}개 skill 발견`);
1418
+ for (const s of r.skills) allEntries.push(s);
1419
+ }
1420
+ log('');
1421
+ if (has('--json')) { log(JSON.stringify({ presets: targets.map(t => t.key), query, entries: allEntries }, null, 2)); return; }
1422
+ if (!allEntries.length) {
1423
+ log(' (μŠ€ν‚¬ 0건 β€” preset/path/branch 확인 λ˜λŠ” rate limit κ°€λŠ₯)');
1424
+ return;
1425
+ }
1426
+ let matched = allEntries;
1427
+ if (query) {
1428
+ const q = query.toLowerCase();
1429
+ matched = allEntries.filter(e => e.name.toLowerCase().includes(q) || (e.description || '').toLowerCase().includes(q));
1430
+ log(`맀칭 ${matched.length}/${allEntries.length}건 (query: ${query})`);
1431
+ } else {
1432
+ log(`전체 ${allEntries.length}건 (전체 ν‘œμ‹œ β€” λ§€μΉ­ μ—†μŒ)`);
1433
+ }
1434
+ log('');
1435
+ log('| name | source | url |');
1436
+ log('|---|---|---|');
1437
+ for (const e of matched.slice(0, 40)) {
1438
+ log(`| ${e.name} | ${e.source} | ${e.url} |`);
1439
+ }
1440
+ log('');
1441
+ log(`πŸ’‘ μ„€μΉ˜: leerness skill install <url> Β· μžλ™ install (env opt-in): LEERNESS_SKILL_AUTO_INSTALL=1`);
1442
+ return;
1443
+ }
1444
+
1445
+ // κΈ°μ‘΄ 단일 URL 흐름 (1.9.42)
1446
+ const url = arg('--source', null) || process.env.LEERNESS_SKILL_DISCOVER_URL || null;
1328
1447
  if (!url) {
1329
1448
  fail([
1330
1449
  'LEERNESS_SKILL_DISCOVER_URL ν™˜κ²½λ³€μˆ˜ λ˜λŠ” --source URL ν•„μš”.',
1450
+ '',
1451
+ 'λ˜λŠ” (1.9.182 μ‹ κ·œ) β€” 곡식 쑰직 catalog presets:',
1452
+ ' leerness skill discover --preset vercel # vercel-labs/agent-skills',
1453
+ ' leerness skill discover --preset anthropic # anthropics/skills',
1454
+ ' leerness skill discover --all-presets # λͺ¨λ“  preset λ™μ‹œ 탐색',
1455
+ ' leerness skill discover --github owner/repo # 직접 GitHub repo μ§€μ •',
1456
+ '',
1331
1457
  '예: leerness skill discover --source https://agentskills.io/llms.txt',
1332
- 'λ˜λŠ” .env에 LEERNESS_SKILL_DISCOVER_URL=...',
1333
1458
  '',
1334
1459
  '(μ •μ±…: leernessλŠ” μ‚¬μš©μž λ™μ˜ 없이 μ™ΈλΆ€ URL을 fetchν•˜μ§€ μ•ŠμŒ β€” 1.9.42 opt-in)'
1335
1460
  ].join('\n'));
@@ -1371,6 +1496,78 @@ async function skillDiscoverCmd(root) {
1371
1496
  log(`πŸ’‘ μ„€μΉ˜: leerness skill install <url>`);
1372
1497
  }
1373
1498
 
1499
+ // 1.9.182: skill auto-install β€” 곡식 organization catalog μžλ™ 탐색 β†’ skill match β†’ μžλ™ install.
1500
+ // μ‚¬μš©μž λͺ…μ‹œ: "μŠ€ν‚¬μ„ μ‚¬μš©ν•˜κ±°λ‚˜ μ μš©ν•΄λ‘λ©΄ 쒋을 뢀뢄듀을 μ•Œμ•„μ„œ μ›Ήμ—μ„œ 곡식 쑰직의 μŠ€ν‚¬ λͺ¨μŒμ„ νƒμƒ‰ν•΄μ„œ λ‹€μš΄λ‘œλ“œ λ°›μ•„μ„œ μ‚¬μš©"
1501
+ // μ •μ±…: LEERNESS_SKILL_AUTO_INSTALL=1 일 λ•Œλ§Œ μ‹€μ œ install (λ³΄μ•ˆ opt-in). λ―Έμ„€μ • μ‹œ dry-run (μΆ”μ²œλ§Œ).
1502
+ async function skillAutoInstallCmd(root) {
1503
+ root = absRoot(root);
1504
+ const presets = (process.env.LEERNESS_SKILL_AUTO_PRESETS || arg('--presets', 'vercel,anthropic') || '').split(',').map(s => s.trim()).filter(Boolean);
1505
+ const autoInstall = process.env.LEERNESS_SKILL_AUTO_INSTALL === '1' || has('--yes');
1506
+ const query = arg('--query', null) || _autoInstallQueryFromHandoff(root);
1507
+ const maxInstall = Number(arg('--max', '3'));
1508
+ log(`# leerness skill auto-install (1.9.182)`);
1509
+ log(`presets: ${presets.join(', ') || '(none)'}`);
1510
+ log(`query: ${query || '(auto β€” handoff μ»¨ν…μŠ€νŠΈ)'}`);
1511
+ log(`mode: ${autoInstall ? '🟒 μžλ™ install' : '🟑 dry-run (LEERNESS_SKILL_AUTO_INSTALL=1 λ˜λŠ” --yes ν•„μš”)'}`);
1512
+ log('');
1513
+ const allEntries = [];
1514
+ for (const key of presets) {
1515
+ const p = SKILL_CATALOG_PRESETS[key];
1516
+ if (!p) { log(` ⚠ μ•Œ 수 μ—†λŠ” preset: ${key}`); continue; }
1517
+ log(` fetching ${p.owner}/${p.repo}...`);
1518
+ const r = await _fetchGitHubSkills(p.owner, p.repo, p.branch, p.path);
1519
+ if (!r.ok) { log(` ⚠ ${p.owner}/${p.repo}: ${r.error}`); continue; }
1520
+ log(` βœ“ ${p.owner}/${p.repo}: ${r.skills.length}개 skill`);
1521
+ for (const s of r.skills) allEntries.push(s);
1522
+ }
1523
+ log('');
1524
+ // query λ§€μΉ­ (substring) β€” query μ—†μœΌλ©΄ λͺ¨λ“  ν•­λͺ©
1525
+ let matched = allEntries;
1526
+ if (query) {
1527
+ const q = String(query).toLowerCase();
1528
+ matched = allEntries.filter(e => e.name.toLowerCase().includes(q) || (e.description || '').toLowerCase().includes(q));
1529
+ log(`맀칭 ${matched.length}/${allEntries.length}건 (query: ${query})`);
1530
+ } else {
1531
+ log(`전체 ${allEntries.length}건 (λͺ¨λ“  ν•­λͺ©)`);
1532
+ }
1533
+ if (!matched.length) { log(' (λ§€μΉ­ 0건 β€” query λ³€κ²½ λ˜λŠ” --presets μΆ”κ°€)'); return; }
1534
+ log('');
1535
+ log('| name | source | url |');
1536
+ log('|---|---|---|');
1537
+ for (const e of matched.slice(0, maxInstall)) {
1538
+ log(`| ${e.name} | ${e.source} | ${e.url} |`);
1539
+ }
1540
+ log('');
1541
+ if (!autoInstall) {
1542
+ log(`πŸ’‘ μžλ™ install ν™œμ„±ν™”: .env 에 LEERNESS_SKILL_AUTO_INSTALL=1 λ˜λŠ” \`leerness skill auto-install --yes\``);
1543
+ log(` ν˜„μž¬ ${Math.min(maxInstall, matched.length)}건이 install λŒ€μƒ (μ‹€μ œ λ‹€μš΄λ‘œλ“œ X β€” dry-run)`);
1544
+ return;
1545
+ }
1546
+ log(`πŸ“₯ μžλ™ install μ‹œμž‘ β€” μ΅œλŒ€ ${maxInstall}건`);
1547
+ let installed = 0, failed = 0;
1548
+ for (const e of matched.slice(0, maxInstall)) {
1549
+ try {
1550
+ await skillInstallCmd(root, e.url);
1551
+ installed++;
1552
+ } catch (err) {
1553
+ log(` ⚠ ${e.name} install μ‹€νŒ¨: ${err.message}`);
1554
+ failed++;
1555
+ }
1556
+ }
1557
+ log('');
1558
+ log(`βœ“ auto-install μ™„λ£Œ: ${installed}건 μ„€μΉ˜ / ${failed}건 μ‹€νŒ¨ / ${matched.length - installed - failed}건 skip`);
1559
+ }
1560
+
1561
+ // 1.9.182: handoff μ»¨ν…μŠ€νŠΈ (ν˜„μž¬ in-progress task) μ—μ„œ μžλ™ query μΆ”μΆœ
1562
+ function _autoInstallQueryFromHandoff(root) {
1563
+ try {
1564
+ const rows = readProgressRows(root);
1565
+ const active = rows.find(r => r.status === '[μ§„ν–‰]' || r.status === '[in-progress]' || r.status === 'in_progress');
1566
+ if (active) return (active.request || '').split(/\s+/).slice(0, 3).join(' ');
1567
+ } catch {}
1568
+ return null;
1569
+ }
1570
+
1374
1571
  // skill export <id> β€” κΈ°μ‘΄ 자체 skill을 agentskills.io ν‘œμ€€ SKILL.md둜 export
1375
1572
  function skillExportCmd(root, id) {
1376
1573
  if (!id) { fail('μ‚¬μš©λ²•: leerness skill export <id>'); return process.exit(1); }
@@ -11066,23 +11263,28 @@ async function _agentRepl(root, opts) {
11066
11263
  bold: s => `\x1b[1m${s}\x1b[0m`, green: s => `\x1b[32m${s}\x1b[0m`,
11067
11264
  yel: s => `\x1b[33m${s}\x1b[0m`, mag: s => `\x1b[35m${s}\x1b[0m`
11068
11265
  } : { cy:s=>s, dim:s=>s, bold:s=>s, green:s=>s, yel:s=>s, mag:s=>s };
11069
- // 1.9.153: provider μžλ™ 선택 β€” opts.provider λͺ…μ‹œ μ•ˆ 됨 + ν™œμ„± CLI κ°€ 있으면 μ‚¬μš©μžμ—κ²Œ 선택지 ν‘œμ‹œ
11266
+ // 1.9.181 fix: provider μžλ™ 선택 β€” prompt 단계 μ œκ±°ν•˜κ³  μ¦‰μ‹œ μ±„νŒ… λͺ¨λ“œ μ§„μž… (μ‚¬μš©μž λͺ…μ‹œ).
11267
+ // μ •μ±…: λΉ„-Ollama ν™œμ„± CLI μš°μ„  β†’ Ollama β†’ fallback. 볡수 ν™œμ„± μ‹œ prompt 없이 첫 λΉ„-Ollama μžλ™ 선택.
11268
+ // μ‚¬μš©μž μ˜λ„: "ν”„λ‘œλ°”μ΄λ” μ „ν™˜ 선택 단계없이 λ°”λ‘œ μ±„νŒ… λͺ¨λ“œλ‘œ μ§„μž…ν•΄λ„ 될거같아" + "Ollamaλ₯Ό μš°μ„  호좜 X".
11269
+ // Tab 으둜 μ–Έμ œλ“  cycle κ°€λŠ₯ν•˜λ―€λ‘œ μžλ™ 선택해도 μ‚¬μš©μžκ°€ μ¦‰μ‹œ μ „ν™˜ κ°€λŠ₯.
11070
11270
  let initialProvider = opts.provider;
11271
+ let _autoPickNote = '';
11071
11272
  if (!initialProvider) {
11072
11273
  const ready = EXTERNAL_AGENTS.map(a => ({ def: a, status: _checkAgent(a) }))
11073
11274
  .filter(x => x.status.status === 'ready');
11074
- if (ready.length === 1) {
11075
- initialProvider = ready[0].def.id; // 단일 ν™œμ„± β†’ μžλ™ 선택
11076
- } else if (ready.length > 1 && isTty) {
11077
- // 볡수 ν™œμ„± β†’ μ‚¬μš©μžμ—κ²Œ 선택지 (Ollama μš°μ„ μ΄ μ•„λ‹Œ, ν™œμ„±λœ CLI 쀑 선택)
11078
- console.log('');
11079
- console.log(` μ‚¬μš© κ°€λŠ₯ν•œ CLI μ—μ΄μ „νŠΈ ${ready.length}개:`);
11080
- ready.forEach((x, i) => console.log(` ${i + 1}) ${x.def.id}${x.status.version ? ' (v' + x.status.version + ')' : ''}`));
11081
- const choice = await new Promise(res => rl.question(`\n provider 선택 (Enter=1): `, res));
11082
- const idx = parseInt(choice, 10) - 1;
11083
- initialProvider = (idx >= 0 && idx < ready.length) ? ready[idx].def.id : ready[0].def.id;
11275
+ const nonOllama = ready.filter(x => x.def.id !== 'ollama');
11276
+ if (nonOllama.length >= 1) {
11277
+ // λΉ„-Ollama ν™œμ„± β†’ 첫 번째 μžλ™ (μ‚¬μš©μž λͺ…μ‹œ: Ollama μš°μ„  호좜 X)
11278
+ initialProvider = nonOllama[0].def.id;
11279
+ _autoPickNote = nonOllama.length === 1
11280
+ ? `${initialProvider} μžλ™ 선택 (ν™œμ„± CLI 1개)`
11281
+ : `${initialProvider} μžλ™ 선택 (ν™œμ„± CLI ${nonOllama.length}개 Β· Tab으둜 μ „ν™˜)`;
11282
+ } else if (ready.length === 1) {
11283
+ initialProvider = ready[0].def.id; // ollama 단독
11284
+ _autoPickNote = `${initialProvider} μžλ™ 선택`;
11084
11285
  } else {
11085
- initialProvider = 'ollama'; // ν™œμ„± 0개 β†’ fallback (μ‚¬μš© μ‹œ friendly κ²½κ³ )
11286
+ initialProvider = 'ollama'; // ν™œμ„± 0개 β†’ fallback
11287
+ _autoPickNote = 'fallback ollama (ν™œμ„± CLI μ—†μŒ β€” .env μ—μ„œ LEERNESS_ENABLE_* ν™œμ„±ν™” ꢌμž₯)';
11086
11288
  }
11087
11289
  }
11088
11290
  // μ„Έμ…˜ state
@@ -11116,47 +11318,27 @@ async function _agentRepl(root, opts) {
11116
11318
  log('');
11117
11319
  log(C.dim(` β–Έ Welcome back Β· ${wsName} (${rel(process.cwd(), absRoot(root))})`));
11118
11320
  log(C.dim(` β–Έ Session: ${state.sessionId}`));
11321
+ if (_autoPickNote) log(C.dim(` β–Έ Provider: ${_autoPickNote}`));
11119
11322
  log('');
11120
- // Ollama λͺ¨λΈ μžλ™ 감지 β€” model이 λͺ…μ‹œλ˜μ§€ μ•Šμ•˜μœΌλ©΄ μ‚¬μš©μžμ—κ²Œ 선택지 제곡
11323
+ // 1.9.181 fix: provider μ§„μž… μ‹œμ  prompt 단계 제거 β€” μžλ™ μ „ν™˜λ§Œ μˆ˜ν–‰ (μ‚¬μš©μž λͺ…μ‹œ β€” λ°”λ‘œ μ±„νŒ… λͺ¨λ“œ μ§„μž…).
11121
11324
  if (state.provider === 'ollama' && !state.model) {
11122
- log(C.dim(' Ollama λͺ¨λΈ λͺ©λ‘ 쑰회 쀑...'));
11325
+ // Ollama μ‚¬μš© κ°€λŠ₯ β†’ 첫 λͺ¨λΈ μžλ™ 선택 (μ‚¬μš©μž prompt μ—†μŒ)
11123
11326
  const r = await _ollamaListModels();
11124
11327
  if (r.ok && r.models.length) {
11125
- log(C.green(` μ‚¬μš© κ°€λŠ₯ λͺ¨λΈ ${r.models.length}개:`));
11126
- r.models.slice(0, 8).forEach((m, i) => log(` ${i + 1}) ${m}`));
11127
- const choice = await new Promise(res => rl.question(C.cy('\n λͺ¨λΈ 번호 선택 (Enter=1): '), res));
11128
- const idx = parseInt(choice, 10) - 1;
11129
- state.model = (idx >= 0 && idx < r.models.length) ? r.models[idx] : r.models[0];
11130
- log(C.green(` βœ“ λͺ¨λΈ 선택: ${state.model}`));
11328
+ state.model = process.env.LEERNESS_OLLAMA_MODEL || r.models[0];
11329
+ log(C.dim(` β–Έ Model: ${state.model} (Ollama ${r.models.length}개 catalog Β· Shift+Tab으둜 λ³€κ²½)`));
11131
11330
  } else {
11132
- log(C.yel(` ⚠ Ollama 미가동 λ˜λŠ” λͺ¨λΈ μ—†μŒ`));
11133
- // 1.9.164: Ollama μ‹€νŒ¨ μ‹œ λ‹€λ₯Έ ν™œμ„± CLI μ¦‰μ‹œ μ œμ•ˆ (UX κ°œμ„  β€” μ‚¬μš©μž λͺ…μ‹œ μš”μ²­)
11134
- try {
11135
- const readyCli = EXTERNAL_AGENTS.filter(a => a.id !== 'ollama')
11136
- .map(a => ({ def: a, status: _checkAgent(a) }))
11137
- .filter(x => x.status.status === 'ready');
11138
- if (readyCli.length) {
11139
- log('');
11140
- log(C.cy(` πŸ’‘ ν™œμ„± μ™ΈλΆ€ CLI ${readyCli.length}개 발견 β€” provider μ „ν™˜ κ°€λŠ₯:`));
11141
- readyCli.forEach((x, i) => log(` ${i + 1}) ${x.def.id} (v${x.status.version || '?'})`));
11142
- const choice = await new Promise(res => rl.question(C.cy('\n provider μ „ν™˜ (번호 / Enter=ollama 계속): '), res));
11143
- const idx = parseInt(choice, 10) - 1;
11144
- if (idx >= 0 && idx < readyCli.length) {
11145
- state.provider = readyCli[idx].def.id;
11146
- state.model = null; // μƒˆ provider κΈ°λ³Έ λͺ¨λΈ μ‚¬μš©
11147
- log(C.green(` βœ“ provider μ „ν™˜: ${state.provider} (λ©”μ‹œμ§€ μž…λ ₯ μ¦‰μ‹œ μ‚¬μš©)`));
11148
- } else {
11149
- state.model = process.env.LEERNESS_OLLAMA_MODEL || 'llama3';
11150
- log(C.dim(` ollama fallback: ${state.model} β€” μΆ”ν›„ :provider <이름> 으둜 μ „ν™˜ κ°€λŠ₯`));
11151
- }
11152
- } else {
11153
- log(C.dim(` ollama serve + ollama pull <model> / λ˜λŠ” .env μ—μ„œ LEERNESS_ENABLE_CLAUDE=1 λ“± ν™œμ„±ν™”`));
11154
- state.model = process.env.LEERNESS_OLLAMA_MODEL || 'llama3';
11155
- log(C.dim(` fallback: ${state.model} (μ‹€ 호좜 μ‹€νŒ¨ μ‹œ :provider 메뉴 λ˜λŠ” :quit)`));
11156
- }
11157
- } catch {
11331
+ // Ollama 미가동 β†’ λΉ„-Ollama ν™œμ„± CLI κ°€ 있으면 μžλ™ μ „ν™˜, μ—†μœΌλ©΄ fallback ollama (warn)
11332
+ const readyCli = EXTERNAL_AGENTS.filter(a => a.id !== 'ollama')
11333
+ .map(a => ({ def: a, status: _checkAgent(a) }))
11334
+ .filter(x => x.status.status === 'ready');
11335
+ if (readyCli.length) {
11336
+ state.provider = readyCli[0].def.id;
11337
+ state.model = null;
11338
+ log(C.green(` β–Έ Ollama 미가동 β†’ ${state.provider} μžλ™ μ „ν™˜ (Tab으둜 λ‹€λ₯Έ provider μ „ν™˜ κ°€λŠ₯)`));
11339
+ } else {
11158
11340
  state.model = process.env.LEERNESS_OLLAMA_MODEL || 'llama3';
11159
- log(C.dim(` fallback: ${state.model}`));
11341
+ log(C.yel(` ⚠ Ollama 미가동 + ν™œμ„± CLI μ—†μŒ β€” fallback ${state.model} (μ‹€ 호좜 μ‹€νŒ¨ μ‹œ :quit λ˜λŠ” :provider)`));
11160
11342
  }
11161
11343
  }
11162
11344
  }
@@ -11658,7 +11840,7 @@ async function _agentRepl(root, opts) {
11658
11840
  async function agentCmd(root, taskArg) {
11659
11841
  root = absRoot(root || process.cwd());
11660
11842
  const task = (taskArg || arg('--task', '') || '').trim();
11661
- // 1.9.149: REPL μ§„μž… β€” 인자 μ—†κ±°λ‚˜ --interactive λͺ…μ‹œ (Hermes/OpenClaw μŠ€νƒ€μΌ)
11843
+ // 1.9.149+1.9.181: REPL μ§„μž… β€” 인자 μ—†κ±°λ‚˜ --interactive λͺ…μ‹œ (provider μžλ™ 선택)
11662
11844
  if (!task || has('--interactive') || has('--repl')) {
11663
11845
  if (process.stdin.isTTY && !has('--no-repl') && process.env.LEERNESS_NO_PROMPT !== '1') {
11664
11846
  const t0 = Date.now();
@@ -13554,6 +13736,8 @@ async function main() {
13554
13736
  if (cmd === 'benchmark') return benchmarkCmd(absRoot(args[1] || arg('--path', process.cwd())));
13555
13737
  if (cmd === 'skill' && args[1] === 'publish') return skillPublishCmd(absRoot(arg('--path', process.cwd())));
13556
13738
  if (cmd === 'skill' && args[1] === 'suggest') return skillSuggestCmd(absRoot(arg('--path', process.cwd())));
13739
+ // 1.9.182: leerness skill auto-install β€” 곡식 catalog μžλ™ 탐색 + skill match λ§€μΉ­ μ‹œ μžλ™ install (μ‚¬μš©μž λͺ…μ‹œ)
13740
+ if (cmd === 'skill' && args[1] === 'auto-install') return await skillAutoInstallCmd(absRoot(arg('--path', process.cwd())));
13557
13741
  if (cmd === 'mcp' && args[1] === 'serve') return mcpServeCmd(absRoot(arg('--path', process.cwd())));
13558
13742
  if (cmd === 'gate') return gate(args[1] || process.cwd());
13559
13743
  if (cmd === 'verify-code') return verifyCodeCmd(args[1] || process.cwd());
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "leerness",
3
- "version": "1.9.180",
3
+ "version": "1.9.182",
4
4
  "description": "Leerness: λΉ„νŒŒκ΄΄ λ§ˆμ΄κ·Έλ ˆμ΄μ…˜, μžλ™ 버전 κ°μ§€Β·μ—…λ°μ΄νŠΈ, κ³„νš/μ§„ν–‰/ν•Έλ“œμ˜€ν”„ μžλ™ν™”, κ²ŒμœΌλ¦„Β·μ‹œν¬λ¦ΏΒ·μΈμ½”λ”© μžλ™ κ°€λ“œ, Claude Code μŠ¬λž˜μ‹œ 톡합을 κ°–μΆ˜ ν•œκ΅­μ–΄ μš°μ„  AI 개발 ν•˜λ„€μŠ€.",
5
5
  "keywords": [
6
6
  "leerness",