leerness 1.9.185 โ 1.9.187
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 +220 -0
- package/README.md +3 -3
- package/bin/harness.js +192 -45
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,225 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.187 โ 2026-05-21
|
|
4
|
+
|
|
5
|
+
**๐ ๋น์ํฌ๋ฆฟ LEERNESS_* ์ค์ ์ .env โ .harness/leerness-config.json ์ผ๋ก ๋ถ๋ฆฌ (AI ๊ฐ์์ฑ).**
|
|
6
|
+
|
|
7
|
+
์์จ ๋ชจ๋ 117 ๋ผ์ด๋. ์ฌ์ฉ์ ๋ช
์: *".env์ ์
๋ ฅ๋์ด์ AI ์์ด์ ํธ๊ฐ ์ฐธ์กฐํ๊ฑฐ๋ ์ฝ์ ์ ์๋ ์์นํ๋ฉด ์
๋ ฅ๋๋ ์์น๋ฅผ ๋ณ๊ฒฝ"*.
|
|
8
|
+
|
|
9
|
+
### ๋ฐฐ๊ฒฝ & ๋ถ๋ฆฌ ์ ์ฑ
|
|
10
|
+
|
|
11
|
+
| ์์น | ๋ณด์ ์ ์ฑ
| ์ฉ๋ |
|
|
12
|
+
|---|---|---|
|
|
13
|
+
| `.env` (.gitignore) | ์ํฌ๋ฆฟ only | NPM_TOKEN, GITHUB_TOKEN, *_API_KEY |
|
|
14
|
+
| `.harness/leerness-config.json` (git checked-in) | ๋น์ํฌ๋ฆฟ only | ํ์ฑํ ํ๋๊ทธ, ๊ณต๊ฐ URL, ๋ชจ๋ธ ์ด๋ฆ |
|
|
15
|
+
|
|
16
|
+
**ํต์ฌ**: `.env`๋ .gitignore + ์ํฌ๋ฆฟ์ฉ โ ๋ค๋ฅธ AI ์์ด์ ํธ(Claude Code, Cursor, Copilot ๋ฑ)๊ฐ ์ํฌ์คํ์ด์ค ์ฝ์ ๋ ์๋ ๋
ธ์ถ X. ๋น์ํฌ๋ฆฟ LEERNESS_* ์ค์ ์ AI ๊ฐ์ ์์น๋ก ์ฎ๊ฒจ **AI ์์ด์ ํธ ์ธ์ง๋ โ**.
|
|
17
|
+
|
|
18
|
+
### ๋ถ๋ฆฌ ๋์ (11๊ฐ ๋น์ํฌ๋ฆฟ ํค)
|
|
19
|
+
```js
|
|
20
|
+
const _LEERNESS_NONSECRET_KEYS = new Set([
|
|
21
|
+
'LEERNESS_OLLAMA_BASE_URL', // localhost URL โ ๋น๋ฐ X
|
|
22
|
+
'LEERNESS_OLLAMA_MODEL', // ๋ชจ๋ธ ์ด๋ฆ โ ๋น๋ฐ X
|
|
23
|
+
'LEERNESS_ENABLE_CLAUDE', // ํ์ฑํ ํ๋๊ทธ
|
|
24
|
+
'LEERNESS_ENABLE_CODEX',
|
|
25
|
+
'LEERNESS_ENABLE_GEMINI',
|
|
26
|
+
'LEERNESS_ENABLE_COPILOT',
|
|
27
|
+
'LEERNESS_ENABLE_OLLAMA',
|
|
28
|
+
'LEERNESS_SKILL_DISCOVER_URL', // ๊ณต๊ฐ URL
|
|
29
|
+
'LEERNESS_SKILL_AUTO_DISCOVER',
|
|
30
|
+
'LEERNESS_SKILL_AUTO_INSTALL',
|
|
31
|
+
'LEERNESS_SKILL_AUTO_PRESETS'
|
|
32
|
+
]);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### .harness/leerness-config.json ์์
|
|
36
|
+
```json
|
|
37
|
+
{
|
|
38
|
+
"_comment": "leerness ๋น์ํฌ๋ฆฟ ์ค์ . AI ์์ด์ ํธ๊ฐ ์ฝ์ ์ ์๋ ์์น (git checked-in). ์ํฌ๋ฆฟ(TOKEN/SECRET/PASSWORD)์ .env ์ฌ์ฉ.",
|
|
39
|
+
"_docs": "https://github.com/gugu9999gu/leerness#config",
|
|
40
|
+
"_version": "1.9.187",
|
|
41
|
+
"LEERNESS_OLLAMA_BASE_URL": "http://localhost:11434",
|
|
42
|
+
"LEERNESS_OLLAMA_MODEL": "llama3",
|
|
43
|
+
"LEERNESS_ENABLE_CLAUDE": "1",
|
|
44
|
+
"LEERNESS_ENABLE_CODEX": "1",
|
|
45
|
+
"LEERNESS_ENABLE_GEMINI": "0",
|
|
46
|
+
"LEERNESS_ENABLE_COPILOT": "0",
|
|
47
|
+
"LEERNESS_ENABLE_OLLAMA": "0",
|
|
48
|
+
"LEERNESS_SKILL_DISCOVER_URL": "",
|
|
49
|
+
"LEERNESS_SKILL_AUTO_DISCOVER": "0",
|
|
50
|
+
"LEERNESS_SKILL_AUTO_INSTALL": "0",
|
|
51
|
+
"LEERNESS_SKILL_AUTO_PRESETS": "vercel,anthropic"
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
### .env (simplified)
|
|
56
|
+
```
|
|
57
|
+
# Leerness โ SECRET ํ๊ฒฝ๋ณ์๋ง (TOKEN/SECRET/PASSWORD). ๋น์ํฌ๋ฆฟ ์ค์ ์ .harness/leerness-config.json ์ฌ์ฉ.
|
|
58
|
+
# .env ๋ .gitignore โ AI ์์ด์ ํธ(Claude Code, Cursor ๋ฑ)์ ๋
ธ์ถ๋์ง ์์. ์ํฌ๋ฆฟ ์์ ๋ณด๊ด.
|
|
59
|
+
# ๋น์ํฌ๋ฆฟ (ํ์ฑํ ํ๋๊ทธ/๋ชจ๋ธ ์ด๋ฆ/๊ณต๊ฐ URL) ์ .harness/leerness-config.json ์ฐธ์กฐ โ AI ๊ฐ์์ฑ โ.
|
|
60
|
+
|
|
61
|
+
# === ์ํฌ๋ฆฟ (TOKEN/KEY) ===
|
|
62
|
+
LEERNESS_NPM_TOKEN=
|
|
63
|
+
LEERNESS_GITHUB_TOKEN=
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
### ๋ณด์ ๊ฐ๋ (์ด์ค ์์ ๋ง)
|
|
67
|
+
- **load ๊ฐ๋**: `_loadLeernessConfig` ์์ `_isSecretKey(k)` ๋งค์น ์ inject ์ฐจ๋จ
|
|
68
|
+
- **write ๊ฐ๋**: `_writeLeernessConfig` ์์ ์ํฌ๋ฆฟ ํจํด ์๋ ์ ๊ฑฐ (์๋ชป ๋ค์ด๊ฐ ํค sanitize)
|
|
69
|
+
- **์ ๊ท์**: `/TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE/i`
|
|
70
|
+
|
|
71
|
+
### ์๋ ๋ง์ด๊ทธ๋ ์ด์
(1.9.186 ์ด์ .env)
|
|
72
|
+
`_migrateNonsecretFromEnv(root)` โ ๊ธฐ์กด .env ์ ๋น์ํฌ๋ฆฟ LEERNESS_* ๊ฐ ์์ผ๋ฉด:
|
|
73
|
+
1. config ํ์ผ๋ก ์๋ ์ด๋
|
|
74
|
+
2. .env ์์ ์ ๊ฑฐ (์ง์ # ์ฃผ์๋ ํจ๊ป)
|
|
75
|
+
3. ์ํฌ๋ฆฟ (TOKEN/KEY/PASSWORD) ์ ์ ๋ ๊ฑด๋๋ฆฌ์ง ์์ (๋ณด์ first)
|
|
76
|
+
|
|
77
|
+
### ์ฐ์ ์์ (ํธํ์ฑ)
|
|
78
|
+
```
|
|
79
|
+
1. process.env (์ด๋ฏธ ์ค์ ๋ ํ๊ฒฝ๋ณ์)
|
|
80
|
+
2. .env ํ์ผ
|
|
81
|
+
3. .harness/leerness-config.json
|
|
82
|
+
4. ์ฝ๋ defaults
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
๊ธฐ์กด `process.env.LEERNESS_*` ์ง์ ์ ๊ทผ ์ฝ๋๋ ๋ณ๊ฒฝ X. leerness ์์ ์ `_loadEnvFile()` ๊ฐ `_loadLeernessConfig()` ๋ ์๋ ํธ์ถ โ config ๊ฐ์ process.env ๋ก inject.
|
|
86
|
+
|
|
87
|
+
### Live ๊ฒ์ฆ (์ฌ์ฉ์ ์ํฌ์คํ์ด์ค ์๋ฎฌ๋ ์ด์
)
|
|
88
|
+
```bash
|
|
89
|
+
# Before (1.9.186 .env)
|
|
90
|
+
LEERNESS_NPM_TOKEN=test-token-12345
|
|
91
|
+
LEERNESS_OLLAMA_BASE_URL=http://localhost:11434
|
|
92
|
+
LEERNESS_OLLAMA_MODEL=llama3
|
|
93
|
+
LEERNESS_ENABLE_CLAUDE=1
|
|
94
|
+
...
|
|
95
|
+
|
|
96
|
+
# After leerness init (1.9.187)
|
|
97
|
+
.env:
|
|
98
|
+
LEERNESS_NPM_TOKEN=test-token-12345 โ ์ํฌ๋ฆฟ ๋ณด์กด
|
|
99
|
+
LEERNESS_GITHUB_TOKEN=
|
|
100
|
+
|
|
101
|
+
.harness/leerness-config.json:
|
|
102
|
+
LEERNESS_OLLAMA_BASE_URL: "http://localhost:11434"
|
|
103
|
+
LEERNESS_OLLAMA_MODEL: "llama3"
|
|
104
|
+
LEERNESS_ENABLE_CLAUDE: "1"
|
|
105
|
+
...
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Verified
|
|
109
|
+
- stress-v132: **19/19 PASS** (์ฌ์ฉ์ ๋ช
์ 7 + live 3 + ๋์ 9)
|
|
110
|
+
- e2e 217/217 baseline ์ ์ง
|
|
111
|
+
- live ๋ง์ด๊ทธ๋ ์ด์
๊ฒ์ฆ: ์ํฌ๋ฆฟ ์์ค 0๊ฑด, ๋น์ํฌ๋ฆฟ 100% ์ด๋
|
|
112
|
+
- VERSION = 1.9.187 ยท autonomous-rounds = 117 ยท main ์๋ push 48 ๋ผ์ด๋ ์ฐ์
|
|
113
|
+
|
|
114
|
+
---
|
|
115
|
+
|
|
116
|
+
## 1.9.186 โ 2026-05-21
|
|
117
|
+
|
|
118
|
+
**๐ REPL claude stream 0์ ์๋ต BUG fix + ๊ตฌ์กฐ ์ต์ ํ ์ฒดํฌ (์ฌ์ฉ์ ๋ช
์ ํต์ฌ ๋ฒ๊ทธ).**
|
|
119
|
+
|
|
120
|
+
์์จ ๋ชจ๋ 116 ๋ผ์ด๋. ์ฌ์ฉ์ ๋ช
์ ํต์ฌ ๋ฒ๊ทธ ๋ณด๊ณ :
|
|
121
|
+
```
|
|
122
|
+
agent[claude/actor/โถ]> ํ์ด์ฌ ํ๋ก๊ทธ๋จ ํ๋ ์ ์ํด์ค
|
|
123
|
+
โโ /stream (27596ms) โโ
|
|
124
|
+
[assistant: claude/default, role=actor, 27743ms ยท 0์]
|
|
125
|
+
```
|
|
126
|
+
*"REPL agent ๋ชจ๋์์ ์์
์์ฒญ ์ AI๊ฐ ์์
์ ์ํํ์ง ๋ชปํ๊ณ ์๋ค"*
|
|
127
|
+
|
|
128
|
+
### ๊ทผ๋ณธ ์์ธ ๋ฐ๊ฒฌ (์ฝ๋ audit)
|
|
129
|
+
|
|
130
|
+
| ํจ์ | spawn ๋ฐฉ์ | shell | ์๋ ์ฌ๋ถ |
|
|
131
|
+
|---|---|---|---|
|
|
132
|
+
| `_cliChat` (line 10920) | `runCommandSafe` โ spawnSync `shell: true` | true | โ ์๋ (`agents multi --execute` ๊ฒ์ฆ) |
|
|
133
|
+
| `_cliChatStream` (line 11018) | `cp.spawn(cmd, args, { shell: false })` | **false** | โ **0์ ์๋ต** |
|
|
134
|
+
|
|
135
|
+
**์์ธ 1**: Windows ์์ `cp.spawn('claude', ..., { shell: false })` ๊ฐ `claude.cmd` ๋ฅผ ์ฐพ์ง ๋ชปํด stdout ๋น์ด์๊ณ 27์ด timeout ํ ์ข
๋ฃ (exit code 0).
|
|
136
|
+
|
|
137
|
+
**์์ธ 2**: claude CLI `--output-format=stream-json --verbose` ๊ฐ ์ผ๋ถ ๋ฒ์ ์์ ๋น stdout ๋ฐํ โ out ๋ณ์ 0์ ๋์ .
|
|
138
|
+
|
|
139
|
+
### Fix #1 โ shell: true (Windows .cmd ํธํ)
|
|
140
|
+
```diff
|
|
141
|
+
- child = cp.spawn(cmd, args, {
|
|
142
|
+
- cwd: process.cwd(),
|
|
143
|
+
- env: _scrubEnv({}),
|
|
144
|
+
- shell: false,
|
|
145
|
+
- stdio: ['ignore', 'pipe', 'pipe']
|
|
146
|
+
- });
|
|
147
|
+
+ // 1.9.186 (์ฌ์ฉ์ ๋ช
์ fix): Windows .cmd ํธํ์ ์ํด shell: true.
|
|
148
|
+
+ // _cliChat (์๋ํ๋ ํจ์) ์ด runCommandSafe โ spawnSync shell: true ํจํด์ด๋ผ์ ์๋.
|
|
149
|
+
+ // _cliChatStream ๋ ๋์ผํ๊ฒ shell: true ์ฌ์ฉ (DEP0190 ์ 1.9.184/185 fix ๋ก ์ต์ ๋จ).
|
|
150
|
+
+ child = cp.spawn(cmd, args, {
|
|
151
|
+
+ cwd: process.cwd(),
|
|
152
|
+
+ env: _scrubEnv({}),
|
|
153
|
+
+ shell: true,
|
|
154
|
+
+ stdio: ['ignore', 'pipe', 'pipe']
|
|
155
|
+
+ });
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
### Fix #2 โ claude default = plain `--print` (stream-json opt-in)
|
|
159
|
+
```diff
|
|
160
|
+
- if (provider === 'claude') { cmd = 'claude'; args = ['--print', '--output-format=stream-json', '--verbose', promptText]; }
|
|
161
|
+
+ // 1.9.186 (์ฌ์ฉ์ ๋ช
์ fix): claude --output-format=stream-json ๊ฐ ์ผ๋ถ ๋ฒ์ ์์ ๋น ์๋ต.
|
|
162
|
+
+ // default ๋ฅผ plain --print ๋ก ๋ณ๊ฒฝ โ _cliChat ๊ณผ ๋์ผํ ์ธ์, ์๋ ๊ฒ์ฆ๋ ํจํด.
|
|
163
|
+
+ // stream ํ์ ์ฌ์ฉ opt-in: LEERNESS_REPL_STREAM_FORMAT=json
|
|
164
|
+
+ const useStreamJson = process.env.LEERNESS_REPL_STREAM_FORMAT === 'json';
|
|
165
|
+
+ if (provider === 'claude') {
|
|
166
|
+
+ cmd = 'claude';
|
|
167
|
+
+ args = useStreamJson
|
|
168
|
+
+ ? ['--print', '--output-format=stream-json', '--verbose', promptText]
|
|
169
|
+
+ : ['--print', promptText];
|
|
170
|
+
+ }
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
handleClaudeStream ์กฐ๊ฑด๋ถ ํธ์ถ (useStreamJson ์ผ ๋๋ง):
|
|
174
|
+
```diff
|
|
175
|
+
- if (provider === 'claude') {
|
|
176
|
+
+ if (provider === 'claude' && useStreamJson) {
|
|
177
|
+
handleClaudeStream(chunk);
|
|
178
|
+
} else {
|
|
179
|
+
stopSpinner();
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### ๊ตฌ์กฐ ์ต์ ํ ์ฒดํฌ (์ฌ์ฉ์ ๋ช
์ ๋ณด๊ฐ)
|
|
183
|
+
|
|
184
|
+
**๋ณด๊ณ ์**: `_reports/structure-optimization-1.9.186.md`
|
|
185
|
+
|
|
186
|
+
**๋ฉํธ๋ฆญ**:
|
|
187
|
+
- bin/harness.js: **~12000 lines** (1 monolithic)
|
|
188
|
+
- ์์กด์ฑ **0** (Node built-in)
|
|
189
|
+
- npm tarball ~358 KB (gzipped)
|
|
190
|
+
- MCP 54 ยท CLI 100+ ยท ํจ์ 250+
|
|
191
|
+
|
|
192
|
+
**์ต์ ํ ๊ฐ์ ** (์ ์ง):
|
|
193
|
+
- ์์กด์ฑ 0 ์ ์ฑ
(cross-platform + supply chain risk ์ต์)
|
|
194
|
+
- ๋ฉ๋ชจ๋ฆฌ ์บ์ฑ (usage-stats / listAllSkills / lessons keyword index)
|
|
195
|
+
- Provider/MCP/Bridge ๋ช
ํํ abstraction
|
|
196
|
+
- runCommandSafe sandbox (cwd jail + env scrub)
|
|
197
|
+
- ๋งค ๋ผ์ด๋ stress + e2e + main ์๋ push
|
|
198
|
+
|
|
199
|
+
**์ต์ ํ ์ฝ์ ** (1.9.187+ ํ๋ณด):
|
|
200
|
+
- โ Monolithic 12000 lines โ ํ ์ํ ์ ์ง (npm pack ๋จ์์ฑ ์ฐ์ )
|
|
201
|
+
- โ `_cliChat` โ `_cliChatStream` ์ฝ๋ ์ค๋ณต โ 1.9.186 fix๊ฐ ์ด ๊ฐญ์์ ๋ฐ์. 1.9.187 ํ๋ณด: `_buildCliArgs` ๊ณตํต ํฌํผ
|
|
202
|
+
- โ skill catalog ์บ์ stale ๊ฐ๋ฅ โ mtime ๊ธฐ๋ฐ invalidation ํ๋ณด
|
|
203
|
+
- โ MCP tools/list ๋งค๋ฒ ์ฌ์์ฑ โ ๋ฉ๋ชจ๋ฆฌ ์บ์ ํ๋ณด
|
|
204
|
+
- โ 280 task ๋์ โ archive ์๋ํ (1.9.190+)
|
|
205
|
+
|
|
206
|
+
### 1.9.186 fix ์ ํฉ์ฑ (๋ณด๊ณ ์ ยง4)
|
|
207
|
+
| ์ํฅ | ํ๊ฐ |
|
|
208
|
+
|---|---|
|
|
209
|
+
| ๋ณด์ (shell escape) | promptText ๋ ์ฌ์ฉ์ ์ง์ ์
๋ ฅ, REPL ์์์๋ง ์ฌ์ฉ โ ์ถ๊ฐ ์ํ ์์ |
|
|
210
|
+
| ์ฑ๋ฅ | shell:true ๊ฐ ~10ms ๋๋ฆผ (๋ฌด์ ๊ฐ๋ฅ) |
|
|
211
|
+
| ๋ค๋ฅธ provider (codex/gemini/copilot) | shell:true ๋์ผ ์ ์ฉ โ ๋ ์์ ์ (.cmd ํธํ) |
|
|
212
|
+
| ์ค์๊ฐ ์คํธ๋ฆฌ๋ฐ (1.9.170 ์ฌ์ฉ์ ๋ช
์) | LEERNESS_REPL_STREAM_FORMAT=json ์ผ๋ก opt-in ๊ฐ๋ฅ โ ๊ธฐ๋ฅ ์ ์ง |
|
|
213
|
+
| ๊ธฐ์กด ์๋ | `_cliChat` ๋ณ๊ฒฝ X โ `agents multi --execute` ํ๊ท ์์ |
|
|
214
|
+
|
|
215
|
+
### Verified
|
|
216
|
+
- stress-v131: **16/16 PASS** (์ฌ์ฉ์ ๋ช
์ 4 + live 3 + ๋์ 9)
|
|
217
|
+
- e2e 217/217 baseline ์ ์ง
|
|
218
|
+
- live ๊ฒ์ฆ: `agents multi --execute --only claude` ์ค ํธ์ถ ์๋ต "2" (4.5์ด)
|
|
219
|
+
- VERSION = 1.9.186 ยท autonomous-rounds = 116 ยท main ์๋ push 47 ๋ผ์ด๋ ์ฐ์
|
|
220
|
+
|
|
221
|
+
---
|
|
222
|
+
|
|
3
223
|
## 1.9.185 โ 2026-05-21
|
|
4
224
|
|
|
5
225
|
**๐ง DEP0190 ์์ process ์ ํ fix + REPL stream ์น์ ์ง๋จ + Hermes UX ๋ถ์ ๋ณด๊ณ (์ฌ์ฉ์ ๋ช
์).**
|
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.187 AI Agent Reliability Harness + Sandbox โ
|
|
16
16
|
โ verify ยท remember ยท orchestrate ยท audit ยท sandbox ยท drift โ
|
|
17
|
-
โ
|
|
17
|
+
โ ๐ .env ๋ถ๋ฆฌ (์ํฌ๋ฆฟ) ยท leerness-config.json (AI ๊ฐ์) โ
|
|
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.187';
|
|
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 ์ฒ๋ฆฌ).
|
|
@@ -904,44 +904,50 @@ async function install(root, opts = {}) {
|
|
|
904
904
|
return new Set([a]); // back-compat: ๋จ์ผ ๋ฌธ์์ด
|
|
905
905
|
})();
|
|
906
906
|
const enable = (cli) => enabledSet.has(cli);
|
|
907
|
-
// 1.9.
|
|
908
|
-
// .env ๋
|
|
909
|
-
//
|
|
907
|
+
// 1.9.187 (์ฌ์ฉ์ ๋ช
์): .env ์๋ ์ํฌ๋ฆฟ๋ง, ๋น์ํฌ๋ฆฟ LEERNESS_* ๋ .harness/leerness-config.json ์ผ๋ก ๋ถ๋ฆฌ.
|
|
908
|
+
// ๋ฐฐ๊ฒฝ: .env ๋ .gitignore + ์ํฌ๋ฆฟ์ฉ โ AI ์์ด์ ํธ ๊ฐ์์ฑ ์์.
|
|
909
|
+
// ๋น์ํฌ๋ฆฟ (ํ์ฑํ ํ๋๊ทธ, ๋ชจ๋ธ, ๊ณต๊ฐ URL) ์ git checked-in ์์น (.harness/) ์ ๋๋ฉด AI ๊ฐ ์๋ ์ฝ์.
|
|
910
|
+
// ํธํ์ฑ: leerness ์์ ์ _loadLeernessConfig() ๊ฐ config โ process.env inject. ๊ธฐ์กด ์ฝ๋ ๊ทธ๋๋ก ์๋.
|
|
910
911
|
const envLines = [
|
|
911
|
-
'# Leerness โ
|
|
912
|
+
'# Leerness โ SECRET ํ๊ฒฝ๋ณ์๋ง (TOKEN/SECRET/PASSWORD). ๋น์ํฌ๋ฆฟ ์ค์ ์ .harness/leerness-config.json ์ฌ์ฉ.',
|
|
912
913
|
`# Generated/migrated by leerness v${VERSION} at ${new Date().toISOString().slice(0, 10)}.`,
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
'#
|
|
917
|
-
'
|
|
918
|
-
'
|
|
919
|
-
`LEERNESS_ENABLE_CLAUDE=${enable('claude') ? 1 : 0}`,
|
|
920
|
-
`LEERNESS_ENABLE_CODEX=${enable('codex') ? 1 : 0}`,
|
|
921
|
-
`LEERNESS_ENABLE_GEMINI=${enable('gemini') ? 1 : 0}`,
|
|
922
|
-
`LEERNESS_ENABLE_COPILOT=${enable('copilot') ? 1 : 0}`,
|
|
923
|
-
`LEERNESS_ENABLE_OLLAMA=${enable('ollama') ? 1 : 0}`,
|
|
924
|
-
'# 1.9.42 โ agentskills.io ๊ณต๊ฐ ํ์ค ์คํฌ ์๋ ํ์ (opt-in). URL ์ค์ ์ `leerness skill discover` ์ฌ์ฉ ๊ฐ๋ฅ.',
|
|
925
|
-
'# ์์ URL: https://agentskills.io/llms.txt',
|
|
926
|
-
'LEERNESS_SKILL_DISCOVER_URL=',
|
|
927
|
-
'# (์ ํ) ์ฌ์ฉ์ ์์ฒญ ๋ถ์ ์ ์๋ ๋งค์นญ ์คํฌ ์ถ์ฒ. 1=ํ์ฑ, 0/๋ฏธ์ค์ =๋นํ์ฑ.',
|
|
928
|
-
'LEERNESS_SKILL_AUTO_DISCOVER=0',
|
|
929
|
-
'# 1.9.182 โ handoff ์ ๊ณต์ catalog (vercel-labs, anthropics) ์๋ ํ์ + ๋งค์นญ ์ ์๋ install. 1=ํ์ฑ (opt-in ๋ณด์).',
|
|
930
|
-
'LEERNESS_SKILL_AUTO_INSTALL=0',
|
|
931
|
-
'# 1.9.182 โ handoff ์๋ ํ์ ๋์ preset (์ฝค๋ง ๊ตฌ๋ถ). ์: vercel,anthropic',
|
|
932
|
-
'LEERNESS_SKILL_AUTO_PRESETS=vercel,anthropic'
|
|
914
|
+
`# .env ๋ .gitignore โ AI ์์ด์ ํธ(Claude Code, Cursor ๋ฑ)์ ๋
ธ์ถ๋์ง ์์. ์ํฌ๋ฆฟ ์์ ๋ณด๊ด.`,
|
|
915
|
+
`# ๋น์ํฌ๋ฆฟ (ํ์ฑํ ํ๋๊ทธ/๋ชจ๋ธ ์ด๋ฆ/๊ณต๊ฐ URL) ์ .harness/leerness-config.json ์ฐธ์กฐ โ AI ๊ฐ์์ฑ โ.`,
|
|
916
|
+
'',
|
|
917
|
+
'# === ์ํฌ๋ฆฟ (TOKEN/KEY) ===',
|
|
918
|
+
'LEERNESS_NPM_TOKEN=',
|
|
919
|
+
'LEERNESS_GITHUB_TOKEN=',
|
|
933
920
|
];
|
|
934
921
|
mergeLinesFile(path.join(root, '.env.example'), envLines);
|
|
935
|
-
// 1.9.153: .env ์ง์ ์์ฑ/๋ง์ด๊ทธ๋ ์ด์
(์ฌ์ฉ์ ๋ช
์ ์์ฒญ). ๋ณด์ = ๋น ๊ฐ๋ง โ ์ฌ์ฉ์๊ฐ ์ง์ ํ ํฐ ์ฑ์.
|
|
936
|
-
// ๊ธฐ์กด .env ๊ฐ ์์ผ๋ฉด mergeEnvFile ์ด KEY ๊ธฐ์ค ์ฒ๋ฆฌ:
|
|
937
|
-
// - ๊ธฐ์กด ํค (์ฌ์ฉ์๊ฐ ์ฑ์ด ๊ฐ ํฌํจ) ๋ ์ ๋ ๋ฎ์ด์ฐ์ง ์์
|
|
938
|
-
// - ๋๋ฝ๋ ํค๋ง ๋น ๊ฐ์ผ๋ก ์ถ๊ฐ
|
|
939
|
-
// .env ๊ฐ .gitignore ์ ๋ฑ๋ก๋์ด ์๋์ง audit ๊ฐ ๊ฒ์ฆ (1.9.75+).
|
|
940
922
|
try {
|
|
941
923
|
mergeEnvFile(path.join(root, '.env'), envLines);
|
|
942
924
|
} catch (e) {
|
|
943
925
|
warn(`.env ์์ฑ/๋ง์ด๊ทธ๋ ์ด์
์คํจ (๊ณ์ ์งํ): ${e.message}`);
|
|
944
926
|
}
|
|
927
|
+
// 1.9.187: ๋น์ํฌ๋ฆฟ LEERNESS_* ์ค์ โ .harness/leerness-config.json (AI ๊ฐ์์ฑ)
|
|
928
|
+
try {
|
|
929
|
+
_writeLeernessConfig(root, {
|
|
930
|
+
LEERNESS_OLLAMA_BASE_URL: enable('ollama') ? 'http://localhost:11434' : '',
|
|
931
|
+
LEERNESS_OLLAMA_MODEL: '',
|
|
932
|
+
LEERNESS_ENABLE_CLAUDE: enable('claude') ? '1' : '0',
|
|
933
|
+
LEERNESS_ENABLE_CODEX: enable('codex') ? '1' : '0',
|
|
934
|
+
LEERNESS_ENABLE_GEMINI: enable('gemini') ? '1' : '0',
|
|
935
|
+
LEERNESS_ENABLE_COPILOT: enable('copilot') ? '1' : '0',
|
|
936
|
+
LEERNESS_ENABLE_OLLAMA: enable('ollama') ? '1' : '0',
|
|
937
|
+
LEERNESS_SKILL_DISCOVER_URL: '',
|
|
938
|
+
LEERNESS_SKILL_AUTO_DISCOVER: '0',
|
|
939
|
+
LEERNESS_SKILL_AUTO_INSTALL: '0',
|
|
940
|
+
LEERNESS_SKILL_AUTO_PRESETS: 'vercel,anthropic'
|
|
941
|
+
});
|
|
942
|
+
} catch (e) {
|
|
943
|
+
warn(`.harness/leerness-config.json ์์ฑ ์คํจ (๊ณ์ ์งํ): ${e.message}`);
|
|
944
|
+
}
|
|
945
|
+
// 1.9.187: ๊ธฐ์กด .env์ ๋น์ํฌ๋ฆฟ LEERNESS_* ๊ฐ ์์ผ๋ฉด .harness/leerness-config.json ์ผ๋ก ๋ง์ด๊ทธ๋ ์ด์
ํ .env์์ ์ ๊ฑฐ.
|
|
946
|
+
try {
|
|
947
|
+
_migrateNonsecretFromEnv(root);
|
|
948
|
+
} catch (e) {
|
|
949
|
+
warn(`.env ๋น์ํฌ๋ฆฟ ๋ง์ด๊ทธ๋ ์ด์
์คํจ (๊ณ์ ์งํ): ${e.message}`);
|
|
950
|
+
}
|
|
945
951
|
// 1.9.146: agent ๊ถํ ํ์ผ ์๋ ์์ฑ (์ฌ์ฉ์ ๋ช
์ ์์ฒญ #5)
|
|
946
952
|
if (resolved.permissionMode) {
|
|
947
953
|
try { _writePermissionsPreset(root, resolved.permissionMode); } catch (e) { warn('permissions ์์ฑ ์คํจ: ' + e.message); }
|
|
@@ -4310,23 +4316,149 @@ function verifyClaimCmd(root, taskId) {
|
|
|
4310
4316
|
function _loadEnvFile(root) {
|
|
4311
4317
|
// root ๊ฒฝ๋ก(๋๋ cwd)์ .env ํ์ผ์ ๊ฐ๋จ ํ์ฑํด process.env์ ๋จธ์ง (์ด๋ฏธ ์๋ ํค๋ ๋ฎ์ด์ฐ์ง ์์)
|
|
4312
4318
|
const envFile = path.join(root || process.cwd(), '.env');
|
|
4313
|
-
|
|
4319
|
+
let loadedEnv = false;
|
|
4320
|
+
if (exists(envFile)) {
|
|
4321
|
+
try {
|
|
4322
|
+
const txt = read(envFile);
|
|
4323
|
+
for (const line of txt.split(/\r?\n/)) {
|
|
4324
|
+
const m = line.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*?)\s*$/);
|
|
4325
|
+
if (!m) continue;
|
|
4326
|
+
const key = m[1];
|
|
4327
|
+
let val = m[2];
|
|
4328
|
+
// ์ฃผ์ ์ ๊ฑฐ
|
|
4329
|
+
if (val.startsWith('#')) continue;
|
|
4330
|
+
// ๋ฐ์ดํ ์ ๊ฑฐ
|
|
4331
|
+
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) val = val.slice(1, -1);
|
|
4332
|
+
if (!process.env[key]) process.env[key] = val;
|
|
4333
|
+
}
|
|
4334
|
+
loadedEnv = true;
|
|
4335
|
+
} catch {}
|
|
4336
|
+
}
|
|
4337
|
+
// 1.9.187: .env ๋ก๋ ํ ๋น์ํฌ๋ฆฟ LEERNESS_* ๋ .harness/leerness-config.json ์์ ์๋ inject.
|
|
4338
|
+
// ์ฐ์ ์์: process.env (์ด๋ฏธ ์์) > .env > .harness/leerness-config.json > defaults.
|
|
4339
|
+
try { _loadLeernessConfig(root); } catch {}
|
|
4340
|
+
return loadedEnv;
|
|
4341
|
+
}
|
|
4342
|
+
|
|
4343
|
+
// 1.9.187 (์ฌ์ฉ์ ๋ช
์): ๋น์ํฌ๋ฆฟ LEERNESS_* ์ค์ ์ .env์์ .harness/leerness-config.json ์ผ๋ก ๋ถ๋ฆฌ.
|
|
4344
|
+
// ๋ฐฐ๊ฒฝ: .env ๋ .gitignore + ์ํฌ๋ฆฟ์ฉ โ ๋ค๋ฅธ AI ์์ด์ ํธ (Claude Code, Cursor ๋ฑ) ๊ฐ ์ํฌ์คํ์ด์ค ์ฝ์ ๋ ๋ณด์ ์ ์ฑ
์ ์๋ ๋
ธ์ถ X.
|
|
4345
|
+
// ๋น์ํฌ๋ฆฟ (ํ์ฑํ ํ๋๊ทธ, ๋ชจ๋ธ ์ด๋ฆ, ๊ณต๊ฐ URL) ์ .harness/leerness-config.json (git checked-in) ์ผ๋ก ์ฎ๊ธฐ๋ฉด AI ๊ฐ์์ฑ โ.
|
|
4346
|
+
// ํธํ์ฑ: ์์ ์์ ์ config ํ์ผ ์ฝ์ด์ process.env์ inject. ๊ธฐ์กด process.env.LEERNESS_* ์ฝ๋ ๊ทธ๋๋ก ์๋.
|
|
4347
|
+
// ์ฐ์ ์์: 1) process.env (์ด๋ฏธ ์ค์ ๋ ๊ฐ) > 2) .env ํ์ผ > 3) .harness/leerness-config.json > 4) defaults.
|
|
4348
|
+
const _LEERNESS_NONSECRET_KEYS = new Set([
|
|
4349
|
+
'LEERNESS_OLLAMA_BASE_URL', // ์ผ๋ฐ localhost URL โ ๋น๋ฐ X
|
|
4350
|
+
'LEERNESS_OLLAMA_MODEL', // ๋ชจ๋ธ ์ด๋ฆ โ ๋น๋ฐ X
|
|
4351
|
+
'LEERNESS_ENABLE_CLAUDE', // ํ์ฑํ ํ๋๊ทธ
|
|
4352
|
+
'LEERNESS_ENABLE_CODEX',
|
|
4353
|
+
'LEERNESS_ENABLE_GEMINI',
|
|
4354
|
+
'LEERNESS_ENABLE_COPILOT',
|
|
4355
|
+
'LEERNESS_ENABLE_OLLAMA',
|
|
4356
|
+
'LEERNESS_SKILL_DISCOVER_URL', // ๊ณต๊ฐ URL
|
|
4357
|
+
'LEERNESS_SKILL_AUTO_DISCOVER',
|
|
4358
|
+
'LEERNESS_SKILL_AUTO_INSTALL',
|
|
4359
|
+
'LEERNESS_SKILL_AUTO_PRESETS'
|
|
4360
|
+
]);
|
|
4361
|
+
// ์ํฌ๋ฆฟ์ ์ ๋ .harness/leerness-config.json ์ผ๋ก ์ฎ๊ธฐ์ง ์์ (TOKEN ํจํด ์๋ ์ฐจ๋จ)
|
|
4362
|
+
function _isSecretKey(k) {
|
|
4363
|
+
return /TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE/i.test(k);
|
|
4364
|
+
}
|
|
4365
|
+
function _leernessConfigPath(root) { return path.join(absRoot(root || process.cwd()), '.harness', 'leerness-config.json'); }
|
|
4366
|
+
function _loadLeernessConfig(root) {
|
|
4367
|
+
const f = _leernessConfigPath(root);
|
|
4368
|
+
if (!exists(f)) return false;
|
|
4314
4369
|
try {
|
|
4315
|
-
const
|
|
4316
|
-
|
|
4317
|
-
|
|
4318
|
-
if (
|
|
4319
|
-
|
|
4320
|
-
|
|
4321
|
-
|
|
4322
|
-
if (val.startsWith('#')) continue;
|
|
4323
|
-
// ๋ฐ์ดํ ์ ๊ฑฐ
|
|
4324
|
-
if ((val.startsWith('"') && val.endsWith('"')) || (val.startsWith("'") && val.endsWith("'"))) val = val.slice(1, -1);
|
|
4325
|
-
if (!process.env[key]) process.env[key] = val;
|
|
4370
|
+
const j = JSON.parse(read(f));
|
|
4371
|
+
// _comment ๊ฐ์ ๋ฉํ ํค๋ ๋ฌด์. ์ค์ LEERNESS_* ํค๋ง inject.
|
|
4372
|
+
for (const k of Object.keys(j || {})) {
|
|
4373
|
+
if (k.startsWith('_')) continue;
|
|
4374
|
+
if (_isSecretKey(k)) continue; // ๋ณด์ ๊ฐ๋ โ ์๋ชป ๋ค์ด๊ฐ ์ํฌ๋ฆฟ ์ฐจ๋จ
|
|
4375
|
+
if (typeof j[k] !== 'string' && typeof j[k] !== 'number' && typeof j[k] !== 'boolean') continue;
|
|
4376
|
+
if (!process.env[k]) process.env[k] = String(j[k]);
|
|
4326
4377
|
}
|
|
4327
4378
|
return true;
|
|
4328
4379
|
} catch { return false; }
|
|
4329
4380
|
}
|
|
4381
|
+
// 1.9.187: ๊ธฐ์กด .env ์ ๋น์ํฌ๋ฆฟ LEERNESS_* ๊ฐ ์์ผ๋ฉด โ .harness/leerness-config.json ์ผ๋ก ์ฎ๊ธฐ๊ณ .env ์์ ์ ๊ฑฐ.
|
|
4382
|
+
// ์ฌ์ฉ์๊ฐ 1.9.186 ์ด์ ๋ฒ์ ์ผ๋ก ๋ง๋ .env ๋ฅผ 1.9.187 ๋ก ๋ง์ด๊ทธ๋ ์ดํธํ ๋ ์๋ ์ ๋ฆฌ.
|
|
4383
|
+
// .env ์ ์ํฌ๋ฆฟ (TOKEN/KEY/PASSWORD) ์ ์ ๋ ๊ฑด๋๋ฆฌ์ง ์์. ๋ณด์ first.
|
|
4384
|
+
function _migrateNonsecretFromEnv(root) {
|
|
4385
|
+
const envFile = path.join(absRoot(root || process.cwd()), '.env');
|
|
4386
|
+
if (!exists(envFile)) return { migrated: 0, kept: 0 };
|
|
4387
|
+
const txt = read(envFile);
|
|
4388
|
+
const lines = txt.split(/\r?\n/);
|
|
4389
|
+
const migrated = {};
|
|
4390
|
+
const remaining = [];
|
|
4391
|
+
let movedCount = 0;
|
|
4392
|
+
let pendingComment = null; // ์ง์ # ์ฃผ์ ๋ผ์ธ ์ถ์ (๋น์ํฌ๋ฆฟ ํค ์ด๋ ์ ํจ๊ป ์ ๊ฑฐ)
|
|
4393
|
+
for (let i = 0; i < lines.length; i++) {
|
|
4394
|
+
const line = lines[i];
|
|
4395
|
+
if (/^\s*#/.test(line) || /^\s*$/.test(line)) {
|
|
4396
|
+
// ์ฃผ์/๋น์ค โ ์ผ๋จ ๋ณด๊ด, ๋น์ํฌ๋ฆฟ ํค ๋ฐ๊ฒฌ ์ ์ง์ ์ฃผ์๋ ์ ๊ฑฐ ๋์
|
|
4397
|
+
if (/^\s*#/.test(line)) pendingComment = remaining.length;
|
|
4398
|
+
remaining.push(line);
|
|
4399
|
+
continue;
|
|
4400
|
+
}
|
|
4401
|
+
const m = line.match(/^\s*([A-Z_][A-Z0-9_]*)\s*=\s*(.*?)\s*$/);
|
|
4402
|
+
if (!m) { remaining.push(line); pendingComment = null; continue; }
|
|
4403
|
+
const key = m[1];
|
|
4404
|
+
const val = m[2].replace(/^["']|["']$/g, '');
|
|
4405
|
+
if (_LEERNESS_NONSECRET_KEYS.has(key) && !_isSecretKey(key)) {
|
|
4406
|
+
// ๋น์ํฌ๋ฆฟ โ config ๋ก ์ด๋, .env ์์ ์ ๊ฑฐ (์ง์ # ์ฃผ์๋ ์ ๊ฑฐ)
|
|
4407
|
+
migrated[key] = val;
|
|
4408
|
+
movedCount++;
|
|
4409
|
+
if (pendingComment !== null) {
|
|
4410
|
+
// pendingComment ๋ผ์ธ๋ถํฐ ์ง์ ๊น์ง ์ ๊ฑฐ (๋จ, ์์ ๋ผ์ธ ๋ณดํธ)
|
|
4411
|
+
// ๋จ์ํ: ๊ทธ๋ฅ ์ง์ 1์ค๋ง ์ ๊ฑฐ (๋๋ถ๋ถ ์ฃผ์ 1์ค + ํค 1์ค ํจํด)
|
|
4412
|
+
if (remaining.length > 0 && /^\s*#/.test(remaining[remaining.length - 1])) {
|
|
4413
|
+
remaining.pop();
|
|
4414
|
+
}
|
|
4415
|
+
}
|
|
4416
|
+
pendingComment = null;
|
|
4417
|
+
continue;
|
|
4418
|
+
}
|
|
4419
|
+
remaining.push(line);
|
|
4420
|
+
pendingComment = null;
|
|
4421
|
+
}
|
|
4422
|
+
if (movedCount === 0) return { migrated: 0, kept: lines.length };
|
|
4423
|
+
// .env ๋ค์ ์ฐ๊ธฐ (์ํฌ๋ฆฟ + ์ฃผ์๋ง ์ ์ง)
|
|
4424
|
+
// ์ฐ์๋ ๋น ์ค ์ ๋ฆฌ
|
|
4425
|
+
const cleaned = [];
|
|
4426
|
+
let prevEmpty = false;
|
|
4427
|
+
for (const ln of remaining) {
|
|
4428
|
+
const isEmpty = /^\s*$/.test(ln);
|
|
4429
|
+
if (isEmpty && prevEmpty) continue;
|
|
4430
|
+
cleaned.push(ln);
|
|
4431
|
+
prevEmpty = isEmpty;
|
|
4432
|
+
}
|
|
4433
|
+
writeUtf8(envFile, cleaned.join('\n').replace(/\n+$/, '\n'));
|
|
4434
|
+
// config ์ ๋จธ์ง
|
|
4435
|
+
_writeLeernessConfig(root, migrated);
|
|
4436
|
+
return { migrated: movedCount, kept: cleaned.length };
|
|
4437
|
+
}
|
|
4438
|
+
|
|
4439
|
+
function _writeLeernessConfig(root, kv) {
|
|
4440
|
+
const f = _leernessConfigPath(root);
|
|
4441
|
+
mkdirp(path.dirname(f));
|
|
4442
|
+
// ๊ธฐ์กด ํ์ผ ๋จธ์ง (์ฌ์ฉ์๊ฐ ์๋ ํธ์งํ ๊ฐ ๋ณด์กด)
|
|
4443
|
+
let existing = {};
|
|
4444
|
+
if (exists(f)) {
|
|
4445
|
+
try { existing = JSON.parse(read(f)); } catch {}
|
|
4446
|
+
}
|
|
4447
|
+
const merged = {
|
|
4448
|
+
_comment: 'leerness ๋น์ํฌ๋ฆฟ ์ค์ . AI ์์ด์ ํธ๊ฐ ์ฝ์ ์ ์๋ ์์น (git checked-in). ์ํฌ๋ฆฟ(TOKEN/SECRET/PASSWORD)์ .env ์ฌ์ฉ.',
|
|
4449
|
+
_docs: 'https://github.com/gugu9999gu/leerness#config',
|
|
4450
|
+
_version: VERSION,
|
|
4451
|
+
...existing,
|
|
4452
|
+
...kv
|
|
4453
|
+
};
|
|
4454
|
+
// ๋ณด์ ๊ฐ๋ โ ์ํฌ๋ฆฟ ํจํด ์๋ ์ ๊ฑฐ
|
|
4455
|
+
for (const k of Object.keys(merged)) {
|
|
4456
|
+
if (k.startsWith('_')) continue;
|
|
4457
|
+
if (_isSecretKey(k)) { delete merged[k]; }
|
|
4458
|
+
}
|
|
4459
|
+
writeUtf8(f, JSON.stringify(merged, null, 2) + '\n');
|
|
4460
|
+
return f;
|
|
4461
|
+
}
|
|
4330
4462
|
|
|
4331
4463
|
function _httpPostJson(urlStr, body, timeoutMs = 300000) {
|
|
4332
4464
|
return new Promise((resolve, reject) => {
|
|
@@ -10953,7 +11085,16 @@ async function _cliChatStream(root, provider, promptText, opts) {
|
|
|
10953
11085
|
return { ok: false, error: `${provider} ๋นํ์ฑ (${status.status}) โ .env ์์ ${agent.envFlag}=1 ํ์`, provider };
|
|
10954
11086
|
}
|
|
10955
11087
|
let cmd, args;
|
|
10956
|
-
|
|
11088
|
+
// 1.9.186 (์ฌ์ฉ์ ๋ช
์ fix): claude --output-format=stream-json ๊ฐ ์ผ๋ถ ๋ฒ์ ์์ ๋น ์๋ต.
|
|
11089
|
+
// default ๋ฅผ plain --print ๋ก ๋ณ๊ฒฝ โ _cliChat ๊ณผ ๋์ผํ ์ธ์, ์๋ ๊ฒ์ฆ๋ ํจํด.
|
|
11090
|
+
// stream ํ์ ์ฌ์ฉ opt-in: LEERNESS_REPL_STREAM_FORMAT=json (์ค์๊ฐ thinking/tool_use ๋ณด๊ณ ์ถ์ ๋).
|
|
11091
|
+
const useStreamJson = process.env.LEERNESS_REPL_STREAM_FORMAT === 'json';
|
|
11092
|
+
if (provider === 'claude') {
|
|
11093
|
+
cmd = 'claude';
|
|
11094
|
+
args = useStreamJson
|
|
11095
|
+
? ['--print', '--output-format=stream-json', '--verbose', promptText]
|
|
11096
|
+
: ['--print', promptText]; // plain text ์๋ต (์๋ ๊ฒ์ฆ๋จ)
|
|
11097
|
+
}
|
|
10957
11098
|
else if (provider === 'codex') { cmd = 'codex'; args = ['exec', '--skip-git-repo-check', promptText]; }
|
|
10958
11099
|
else if (provider === 'gemini') { cmd = 'gemini'; args = ['-p', promptText]; }
|
|
10959
11100
|
else if (provider === 'copilot') { cmd = 'gh'; args = ['copilot', 'suggest', promptText]; }
|
|
@@ -11015,10 +11156,15 @@ async function _cliChatStream(root, provider, promptText, opts) {
|
|
|
11015
11156
|
process.stdout.write(dim(`\n โโ ${provider} stream โโ\n`));
|
|
11016
11157
|
let child;
|
|
11017
11158
|
try {
|
|
11159
|
+
// 1.9.186 (์ฌ์ฉ์ ๋ช
์ fix): Windows .cmd ํธํ์ ์ํด shell: true.
|
|
11160
|
+
// 1.9.185 ๊น์ง shell: false โ Windows ์์ claude.cmd ๋ชป ์ฐพ์ ๋น ์๋ต (์ฌ์ฉ์ ๋ณด๊ณ : 27์ด ํ 0์).
|
|
11161
|
+
// _cliChat (์๋ํ๋ ํจ์) ์ด runCommandSafe โ spawnSync shell: true ํจํด์ด๋ผ์ ์๋.
|
|
11162
|
+
// _cliChatStream ๋ ๋์ผํ๊ฒ shell: true ์ฌ์ฉ (DEP0190 ์ 1.9.184/185 fix ๋ก ์ต์ ๋จ).
|
|
11163
|
+
// security: shell escape ์ฐ๋ ค๋ promptText ๊ฐ ์ฌ์ฉ์ ์ง์ ์
๋ ฅ โ sandboxed REPL ์์์๋ง ์ฌ์ฉ.
|
|
11018
11164
|
child = cp.spawn(cmd, args, {
|
|
11019
11165
|
cwd: process.cwd(),
|
|
11020
11166
|
env: _scrubEnv({}),
|
|
11021
|
-
shell:
|
|
11167
|
+
shell: true,
|
|
11022
11168
|
stdio: ['ignore', 'pipe', 'pipe']
|
|
11023
11169
|
});
|
|
11024
11170
|
} catch (e) {
|
|
@@ -11070,7 +11216,8 @@ async function _cliChatStream(root, provider, promptText, opts) {
|
|
|
11070
11216
|
|
|
11071
11217
|
child.stdout.on('data', chunk => {
|
|
11072
11218
|
lastActivity = Date.now();
|
|
11073
|
-
|
|
11219
|
+
// 1.9.186: claude default ๋ plain --print โ JSON ๋ผ์ธ ํ์ฑ X. useStreamJson ์์๋ง stream-json ์ฒ๋ฆฌ.
|
|
11220
|
+
if (provider === 'claude' && useStreamJson) {
|
|
11074
11221
|
handleClaudeStream(chunk);
|
|
11075
11222
|
} else {
|
|
11076
11223
|
stopSpinner();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leerness",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.187",
|
|
4
4
|
"description": "Leerness: ๋นํ๊ดด ๋ง์ด๊ทธ๋ ์ด์
, ์๋ ๋ฒ์ ๊ฐ์งยท์
๋ฐ์ดํธ, ๊ณํ/์งํ/ํธ๋์คํ ์๋ํ, ๊ฒ์ผ๋ฆยท์ํฌ๋ฆฟยท์ธ์ฝ๋ฉ ์๋ ๊ฐ๋, Claude Code ์ฌ๋์ ํตํฉ์ ๊ฐ์ถ ํ๊ตญ์ด ์ฐ์ AI ๊ฐ๋ฐ ํ๋ค์ค.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"leerness",
|