leerness 1.9.186 โ 1.9.188
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 +210 -0
- package/README.md +3 -3
- package/bin/harness.js +238 -62
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,215 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 1.9.188 โ 2026-05-21
|
|
4
|
+
|
|
5
|
+
**๐ REPL ํ๊ธ prompt ์ ๋ฌ BUG fix (stdin) + ์ธ๋ถ ๋ชจ๋ธ ํ์ + ์
๋ ฅ ๊ตฌ๋ถ์ (์ฌ์ฉ์ ๋ช
์ 3์ข
).**
|
|
6
|
+
|
|
7
|
+
์์จ ๋ชจ๋ 118 ๋ผ์ด๋. ์ฌ์ฉ์ ๋ช
์ ํต์ฌ ๋ฒ๊ทธ:
|
|
8
|
+
```
|
|
9
|
+
agent[claude/actor/โถ]> ์ด ํด๋์ ํ์ด์ฌ ํ๋ก๊ทธ๋จ์ ํ๋ ์ ์ํด์ค
|
|
10
|
+
โโ claude stream โโ
|
|
11
|
+
์ ๋ ์ด ํ๋ก์ ํธ์ **์์ ๊ฐ๋ฐ์์ด์ ํ๋ก์ ํธ ๋งค๋์ **์
๋๋ค.
|
|
12
|
+
...
|
|
13
|
+
๋ฉ์์ง๊ฐ "์ญํ :"๋ก๋ง ๋๋์ ์๋๋ฅผ ํ์ ํ๊ธฐ ์ด๋ ต์ต๋๋ค.
|
|
14
|
+
```
|
|
15
|
+
์ฌ์ฉ์ ์๋์ ๋ฌด๊ดํ ์๋ต โ **claude๊ฐ ํ๊ธ prompt ๋๋ถ๋ถ("์ญํ :") fragment๋ง ๋ฐ์**.
|
|
16
|
+
|
|
17
|
+
### ๊ทผ๋ณธ ์์ธ (์ฝ๋ audit)
|
|
18
|
+
|
|
19
|
+
`cp.spawn(cmd, args, { shell: true })` + Windows cmd.exe + ํ๊ธ/ํน์๋ฌธ์ promptText:
|
|
20
|
+
- shell:true ์ผ ๋ args ๊ฐ join๋์ด shell command line ํ์ฑ
|
|
21
|
+
- Windows cmd.exe๊ฐ ํ๊ธ/๊ณต๋ฐฑ escape ์คํจ โ claude์ ์ผ๋ถ fragment๋ง ์ ๋ฌ
|
|
22
|
+
|
|
23
|
+
### Fix #1 โ prompt๋ฅผ stdin์ผ๋ก ์ ๋ฌ (shell escape ์ฐํ)
|
|
24
|
+
|
|
25
|
+
```diff
|
|
26
|
+
- if (provider === 'claude') { cmd = 'claude'; args = ['--print', promptText]; }
|
|
27
|
+
+ // 1.9.188: promptText ๋ stdin ์ผ๋ก โ args ์์ ์ ๊ฑฐ (ํ๊ธ/ํน์๋ฌธ์ ์์ )
|
|
28
|
+
+ let useStdinForPrompt = false;
|
|
29
|
+
+ if (provider === 'claude') {
|
|
30
|
+
+ cmd = 'claude';
|
|
31
|
+
+ args = ['--print']; // promptText ์ ๊ฑฐ
|
|
32
|
+
+ useStdinForPrompt = true;
|
|
33
|
+
+ }
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
spawn ์:
|
|
37
|
+
```js
|
|
38
|
+
child = cp.spawn(cmd, args, {
|
|
39
|
+
stdio: [useStdinForPrompt ? 'pipe' : 'ignore', 'pipe', 'pipe'],
|
|
40
|
+
shell: true
|
|
41
|
+
});
|
|
42
|
+
if (useStdinForPrompt && child.stdin) {
|
|
43
|
+
child.stdin.write(promptText);
|
|
44
|
+
child.stdin.end();
|
|
45
|
+
}
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`_cliChat` ๋ ๋์ผํ๊ฒ `runCommandSafe(..., { input: stdinInput })` ์ผ๋ก ๋ณ๊ฒฝ.
|
|
49
|
+
|
|
50
|
+
### Live ๊ฒ์ฆ (ํ๊ธ prompt ์ ํ ์ ๋ฌ)
|
|
51
|
+
```bash
|
|
52
|
+
$ leerness agents multi "์ด ํด๋์ ํ์ด์ฌ hello world ํ๋ก๊ทธ๋จ ์ ์ (์๋ต๋ง ์ฝ๋)" --only claude --execute
|
|
53
|
+
โ claude ยท 8279ms ยท 9 ํ ํฐ
|
|
54
|
+
--- ์ฒ์ 600์ ---
|
|
55
|
+
`hello.py` ์์ฑ ๊ถํ์ด ํ์ํฉ๋๋ค. ์น์ธํด ์ฃผ์ธ์.
|
|
56
|
+
```
|
|
57
|
+
**1.9.187๊น์ง**: "์์ ๊ฐ๋ฐ์/ํ๋ก์ ํธ ๋งค๋์ ..." ๊ฐ์ ๋ฌด๊ดํ ์๋ต (์ญํ fragment๋ง ๋ฐ์)
|
|
58
|
+
**1.9.188**: `hello.py ์์ฑ ๊ถํ ํ์` โ ์ ํํ ์์ฒญ ์ดํด โ
|
|
59
|
+
|
|
60
|
+
### Fix #2 โ ์ธ๋ถ ๋ชจ๋ธ ํ์
|
|
61
|
+
**Before**: `โก provider=claude ยท model=(๊ธฐ๋ณธ)`
|
|
62
|
+
**After**: `โก provider=claude ยท model=claude-opus-4-7`
|
|
63
|
+
|
|
64
|
+
```js
|
|
65
|
+
// 1.9.188 (์ฌ์ฉ์ ๋ช
์): state.model ์๋ default (catalog ์ฒซ ๋ชจ๋ธ)
|
|
66
|
+
if (!state.model) {
|
|
67
|
+
const cat = _PROVIDER_MODEL_CATALOG[state.provider];
|
|
68
|
+
if (cat && cat.length) state.model = cat[0].id;
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
prompt ๋ ์ธ๋ถ ๋ชจ๋ธ ๋ช
์:
|
|
73
|
+
```
|
|
74
|
+
Before: agent[claude/actor/โถ]>
|
|
75
|
+
After: agent[claude ยท opus-4-7/actor/โถ]>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
### Fix #3 โ ์
๋ ฅ ๊ตฌ๋ถ์ (Hermes UX ์๊ฐ)
|
|
79
|
+
๊ฐ ์๋ต ๋์ ๊ฐ๋ก ๋๋ฐ์ด๋ ์๋ ์ถ๋ ฅ โ ์
๋ ฅ ์์ญ ์๊ฐ ๋ช
ํ:
|
|
80
|
+
|
|
81
|
+
```
|
|
82
|
+
[assistant: claude/claude-opus-4-7, role=actor, 4598ms ยท 425์]
|
|
83
|
+
|
|
84
|
+
โโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ...
|
|
85
|
+
agent[claude ยท opus-4-7/actor/โถ]> _
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
`_printInputDivider()` ํจ์ ๋์
โ ํฐ๋ฏธ๋ width ๊ธฐ์ค ์๋ ๊ธธ์ด.
|
|
89
|
+
|
|
90
|
+
### Verified
|
|
91
|
+
- stress-v133: **15/15 PASS** (์ฌ์ฉ์ ๋ช
์ 6 + live 2 + ๋์ 7)
|
|
92
|
+
- e2e 217/217 baseline ์ ์ง
|
|
93
|
+
- live ๊ฒ์ฆ:
|
|
94
|
+
- ํ๊ธ prompt "์ด ํด๋์ ํ์ด์ฌ hello world ์ ์" โ claude ์ ํ ์๋ต (12.5์ด)
|
|
95
|
+
- ์๋ฌธ prompt "1+1=?" โ claude ์๋ต (9.3์ด, ํ๊ท ์์)
|
|
96
|
+
- VERSION = 1.9.188 ยท autonomous-rounds = 118 ยท main ์๋ push 49 ๋ผ์ด๋ ์ฐ์
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## 1.9.187 โ 2026-05-21
|
|
101
|
+
|
|
102
|
+
**๐ ๋น์ํฌ๋ฆฟ LEERNESS_* ์ค์ ์ .env โ .harness/leerness-config.json ์ผ๋ก ๋ถ๋ฆฌ (AI ๊ฐ์์ฑ).**
|
|
103
|
+
|
|
104
|
+
์์จ ๋ชจ๋ 117 ๋ผ์ด๋. ์ฌ์ฉ์ ๋ช
์: *".env์ ์
๋ ฅ๋์ด์ AI ์์ด์ ํธ๊ฐ ์ฐธ์กฐํ๊ฑฐ๋ ์ฝ์ ์ ์๋ ์์นํ๋ฉด ์
๋ ฅ๋๋ ์์น๋ฅผ ๋ณ๊ฒฝ"*.
|
|
105
|
+
|
|
106
|
+
### ๋ฐฐ๊ฒฝ & ๋ถ๋ฆฌ ์ ์ฑ
|
|
107
|
+
|
|
108
|
+
| ์์น | ๋ณด์ ์ ์ฑ
| ์ฉ๋ |
|
|
109
|
+
|---|---|---|
|
|
110
|
+
| `.env` (.gitignore) | ์ํฌ๋ฆฟ only | NPM_TOKEN, GITHUB_TOKEN, *_API_KEY |
|
|
111
|
+
| `.harness/leerness-config.json` (git checked-in) | ๋น์ํฌ๋ฆฟ only | ํ์ฑํ ํ๋๊ทธ, ๊ณต๊ฐ URL, ๋ชจ๋ธ ์ด๋ฆ |
|
|
112
|
+
|
|
113
|
+
**ํต์ฌ**: `.env`๋ .gitignore + ์ํฌ๋ฆฟ์ฉ โ ๋ค๋ฅธ AI ์์ด์ ํธ(Claude Code, Cursor, Copilot ๋ฑ)๊ฐ ์ํฌ์คํ์ด์ค ์ฝ์ ๋ ์๋ ๋
ธ์ถ X. ๋น์ํฌ๋ฆฟ LEERNESS_* ์ค์ ์ AI ๊ฐ์ ์์น๋ก ์ฎ๊ฒจ **AI ์์ด์ ํธ ์ธ์ง๋ โ**.
|
|
114
|
+
|
|
115
|
+
### ๋ถ๋ฆฌ ๋์ (11๊ฐ ๋น์ํฌ๋ฆฟ ํค)
|
|
116
|
+
```js
|
|
117
|
+
const _LEERNESS_NONSECRET_KEYS = new Set([
|
|
118
|
+
'LEERNESS_OLLAMA_BASE_URL', // localhost URL โ ๋น๋ฐ X
|
|
119
|
+
'LEERNESS_OLLAMA_MODEL', // ๋ชจ๋ธ ์ด๋ฆ โ ๋น๋ฐ X
|
|
120
|
+
'LEERNESS_ENABLE_CLAUDE', // ํ์ฑํ ํ๋๊ทธ
|
|
121
|
+
'LEERNESS_ENABLE_CODEX',
|
|
122
|
+
'LEERNESS_ENABLE_GEMINI',
|
|
123
|
+
'LEERNESS_ENABLE_COPILOT',
|
|
124
|
+
'LEERNESS_ENABLE_OLLAMA',
|
|
125
|
+
'LEERNESS_SKILL_DISCOVER_URL', // ๊ณต๊ฐ URL
|
|
126
|
+
'LEERNESS_SKILL_AUTO_DISCOVER',
|
|
127
|
+
'LEERNESS_SKILL_AUTO_INSTALL',
|
|
128
|
+
'LEERNESS_SKILL_AUTO_PRESETS'
|
|
129
|
+
]);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### .harness/leerness-config.json ์์
|
|
133
|
+
```json
|
|
134
|
+
{
|
|
135
|
+
"_comment": "leerness ๋น์ํฌ๋ฆฟ ์ค์ . AI ์์ด์ ํธ๊ฐ ์ฝ์ ์ ์๋ ์์น (git checked-in). ์ํฌ๋ฆฟ(TOKEN/SECRET/PASSWORD)์ .env ์ฌ์ฉ.",
|
|
136
|
+
"_docs": "https://github.com/gugu9999gu/leerness#config",
|
|
137
|
+
"_version": "1.9.187",
|
|
138
|
+
"LEERNESS_OLLAMA_BASE_URL": "http://localhost:11434",
|
|
139
|
+
"LEERNESS_OLLAMA_MODEL": "llama3",
|
|
140
|
+
"LEERNESS_ENABLE_CLAUDE": "1",
|
|
141
|
+
"LEERNESS_ENABLE_CODEX": "1",
|
|
142
|
+
"LEERNESS_ENABLE_GEMINI": "0",
|
|
143
|
+
"LEERNESS_ENABLE_COPILOT": "0",
|
|
144
|
+
"LEERNESS_ENABLE_OLLAMA": "0",
|
|
145
|
+
"LEERNESS_SKILL_DISCOVER_URL": "",
|
|
146
|
+
"LEERNESS_SKILL_AUTO_DISCOVER": "0",
|
|
147
|
+
"LEERNESS_SKILL_AUTO_INSTALL": "0",
|
|
148
|
+
"LEERNESS_SKILL_AUTO_PRESETS": "vercel,anthropic"
|
|
149
|
+
}
|
|
150
|
+
```
|
|
151
|
+
|
|
152
|
+
### .env (simplified)
|
|
153
|
+
```
|
|
154
|
+
# Leerness โ SECRET ํ๊ฒฝ๋ณ์๋ง (TOKEN/SECRET/PASSWORD). ๋น์ํฌ๋ฆฟ ์ค์ ์ .harness/leerness-config.json ์ฌ์ฉ.
|
|
155
|
+
# .env ๋ .gitignore โ AI ์์ด์ ํธ(Claude Code, Cursor ๋ฑ)์ ๋
ธ์ถ๋์ง ์์. ์ํฌ๋ฆฟ ์์ ๋ณด๊ด.
|
|
156
|
+
# ๋น์ํฌ๋ฆฟ (ํ์ฑํ ํ๋๊ทธ/๋ชจ๋ธ ์ด๋ฆ/๊ณต๊ฐ URL) ์ .harness/leerness-config.json ์ฐธ์กฐ โ AI ๊ฐ์์ฑ โ.
|
|
157
|
+
|
|
158
|
+
# === ์ํฌ๋ฆฟ (TOKEN/KEY) ===
|
|
159
|
+
LEERNESS_NPM_TOKEN=
|
|
160
|
+
LEERNESS_GITHUB_TOKEN=
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### ๋ณด์ ๊ฐ๋ (์ด์ค ์์ ๋ง)
|
|
164
|
+
- **load ๊ฐ๋**: `_loadLeernessConfig` ์์ `_isSecretKey(k)` ๋งค์น ์ inject ์ฐจ๋จ
|
|
165
|
+
- **write ๊ฐ๋**: `_writeLeernessConfig` ์์ ์ํฌ๋ฆฟ ํจํด ์๋ ์ ๊ฑฐ (์๋ชป ๋ค์ด๊ฐ ํค sanitize)
|
|
166
|
+
- **์ ๊ท์**: `/TOKEN|SECRET|PASSWORD|API_KEY|PRIVATE/i`
|
|
167
|
+
|
|
168
|
+
### ์๋ ๋ง์ด๊ทธ๋ ์ด์
(1.9.186 ์ด์ .env)
|
|
169
|
+
`_migrateNonsecretFromEnv(root)` โ ๊ธฐ์กด .env ์ ๋น์ํฌ๋ฆฟ LEERNESS_* ๊ฐ ์์ผ๋ฉด:
|
|
170
|
+
1. config ํ์ผ๋ก ์๋ ์ด๋
|
|
171
|
+
2. .env ์์ ์ ๊ฑฐ (์ง์ # ์ฃผ์๋ ํจ๊ป)
|
|
172
|
+
3. ์ํฌ๋ฆฟ (TOKEN/KEY/PASSWORD) ์ ์ ๋ ๊ฑด๋๋ฆฌ์ง ์์ (๋ณด์ first)
|
|
173
|
+
|
|
174
|
+
### ์ฐ์ ์์ (ํธํ์ฑ)
|
|
175
|
+
```
|
|
176
|
+
1. process.env (์ด๋ฏธ ์ค์ ๋ ํ๊ฒฝ๋ณ์)
|
|
177
|
+
2. .env ํ์ผ
|
|
178
|
+
3. .harness/leerness-config.json
|
|
179
|
+
4. ์ฝ๋ defaults
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
๊ธฐ์กด `process.env.LEERNESS_*` ์ง์ ์ ๊ทผ ์ฝ๋๋ ๋ณ๊ฒฝ X. leerness ์์ ์ `_loadEnvFile()` ๊ฐ `_loadLeernessConfig()` ๋ ์๋ ํธ์ถ โ config ๊ฐ์ process.env ๋ก inject.
|
|
183
|
+
|
|
184
|
+
### Live ๊ฒ์ฆ (์ฌ์ฉ์ ์ํฌ์คํ์ด์ค ์๋ฎฌ๋ ์ด์
)
|
|
185
|
+
```bash
|
|
186
|
+
# Before (1.9.186 .env)
|
|
187
|
+
LEERNESS_NPM_TOKEN=test-token-12345
|
|
188
|
+
LEERNESS_OLLAMA_BASE_URL=http://localhost:11434
|
|
189
|
+
LEERNESS_OLLAMA_MODEL=llama3
|
|
190
|
+
LEERNESS_ENABLE_CLAUDE=1
|
|
191
|
+
...
|
|
192
|
+
|
|
193
|
+
# After leerness init (1.9.187)
|
|
194
|
+
.env:
|
|
195
|
+
LEERNESS_NPM_TOKEN=test-token-12345 โ ์ํฌ๋ฆฟ ๋ณด์กด
|
|
196
|
+
LEERNESS_GITHUB_TOKEN=
|
|
197
|
+
|
|
198
|
+
.harness/leerness-config.json:
|
|
199
|
+
LEERNESS_OLLAMA_BASE_URL: "http://localhost:11434"
|
|
200
|
+
LEERNESS_OLLAMA_MODEL: "llama3"
|
|
201
|
+
LEERNESS_ENABLE_CLAUDE: "1"
|
|
202
|
+
...
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
### Verified
|
|
206
|
+
- stress-v132: **19/19 PASS** (์ฌ์ฉ์ ๋ช
์ 7 + live 3 + ๋์ 9)
|
|
207
|
+
- e2e 217/217 baseline ์ ์ง
|
|
208
|
+
- live ๋ง์ด๊ทธ๋ ์ด์
๊ฒ์ฆ: ์ํฌ๋ฆฟ ์์ค 0๊ฑด, ๋น์ํฌ๋ฆฟ 100% ์ด๋
|
|
209
|
+
- VERSION = 1.9.187 ยท autonomous-rounds = 117 ยท main ์๋ push 48 ๋ผ์ด๋ ์ฐ์
|
|
210
|
+
|
|
211
|
+
---
|
|
212
|
+
|
|
3
213
|
## 1.9.186 โ 2026-05-21
|
|
4
214
|
|
|
5
215
|
**๐ REPL claude stream 0์ ์๋ต BUG fix + ๊ตฌ์กฐ ์ต์ ํ ์ฒดํฌ (์ฌ์ฉ์ ๋ช
์ ํต์ฌ ๋ฒ๊ทธ).**
|
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.188 AI Agent Reliability Harness + Sandbox โ
|
|
16
16
|
โ verify ยท remember ยท orchestrate ยท audit ยท sandbox ยท drift โ
|
|
17
|
-
โ ๐
|
|
17
|
+
โ ๐ ํ๊ธ prompt stdin ์ ๋ฌ ยท ์ธ๋ถ๋ชจ๋ธ ํ์ ยท ์
๋ ฅ ๊ตฌ๋ถ์ โ
|
|
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.188';
|
|
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) => {
|
|
@@ -10916,10 +11048,11 @@ async function _cliChat(root, provider, prompt, opts) {
|
|
|
10916
11048
|
return { ok: false, error: `${provider} ๋นํ์ฑ (${status.status}) โ .env ์์ ${agent.envFlag}=1 + CLI ์ค์น ํ์`, provider };
|
|
10917
11049
|
}
|
|
10918
11050
|
// CLI ๋ณ ๋น-์ธํฐ๋ํฐ๋ธ ํธ์ถ ์ธ์ ๋งคํ (read-only ๋ชจ๋ โ REPL ์์์ ํ์ผ ์์ X)
|
|
10919
|
-
|
|
10920
|
-
|
|
10921
|
-
|
|
10922
|
-
else if (provider === '
|
|
11051
|
+
// 1.9.188 (์ฌ์ฉ์ ๋ช
์ fix): ํ๊ธ/ํน์๋ฌธ์ prompt ์ shell escape ์คํจ ํํผ โ stdin ์ผ๋ก ์ ๋ฌ.
|
|
11052
|
+
let cmd, args, stdinInput = null;
|
|
11053
|
+
if (provider === 'claude') { cmd = 'claude'; args = ['--print']; stdinInput = prompt; }
|
|
11054
|
+
else if (provider === 'codex') { cmd = 'codex'; args = ['exec', '--skip-git-repo-check', '-']; stdinInput = prompt; }
|
|
11055
|
+
else if (provider === 'gemini') { cmd = 'gemini'; args = ['-p', prompt]; } // gemini ๋ ์ธ์ only
|
|
10923
11056
|
else if (provider === 'copilot') { cmd = 'gh'; args = ['copilot', 'suggest', prompt]; }
|
|
10924
11057
|
else return { ok: false, error: `provider ${provider} ๋ฏธ์ง์`, provider };
|
|
10925
11058
|
// runCommandSafe โ env scrub + observability ์๋
|
|
@@ -10927,7 +11060,8 @@ async function _cliChat(root, provider, prompt, opts) {
|
|
|
10927
11060
|
cwd: process.cwd(), root,
|
|
10928
11061
|
timeout: opts.timeout || 60000,
|
|
10929
11062
|
allowOutsideCwd: true, // CLI ๊ฐ cwd ๋ฐ์์ ์คํ๋ ์ ์์
|
|
10930
|
-
kind: 'agent_repl_cli', label: `repl-${provider}
|
|
11063
|
+
kind: 'agent_repl_cli', label: `repl-${provider}`,
|
|
11064
|
+
input: stdinInput // 1.9.188: claude/codex ๋ stdin ์ ๋ฌ
|
|
10931
11065
|
});
|
|
10932
11066
|
if (r.status === 0) {
|
|
10933
11067
|
return { ok: true, response: (r.stdout || '').trim(), provider, model: provider };
|
|
@@ -10956,15 +11090,25 @@ async function _cliChatStream(root, provider, promptText, opts) {
|
|
|
10956
11090
|
// 1.9.186 (์ฌ์ฉ์ ๋ช
์ fix): claude --output-format=stream-json ๊ฐ ์ผ๋ถ ๋ฒ์ ์์ ๋น ์๋ต.
|
|
10957
11091
|
// default ๋ฅผ plain --print ๋ก ๋ณ๊ฒฝ โ _cliChat ๊ณผ ๋์ผํ ์ธ์, ์๋ ๊ฒ์ฆ๋ ํจํด.
|
|
10958
11092
|
// stream ํ์ ์ฌ์ฉ opt-in: LEERNESS_REPL_STREAM_FORMAT=json (์ค์๊ฐ thinking/tool_use ๋ณด๊ณ ์ถ์ ๋).
|
|
11093
|
+
// 1.9.188 (์ฌ์ฉ์ ๋ช
์ fix): shell:true + Windows cmd.exe + ํ๊ธ/ํน์๋ฌธ์ promptText โ args escape ์คํจ.
|
|
11094
|
+
// ์ฌ์ฉ์ ๋ณด๊ณ : "์ด ํด๋์ ํ์ด์ฌ ํ๋ก๊ทธ๋จ ์ ์" ๋ณด๋์ผ๋ claude๋ "์ญํ :" fragment๋ง ๋ฐ์.
|
|
11095
|
+
// ํด๊ฒฐ: promptText ๋ฅผ args ์์ ์ ๊ฑฐํ๊ณ stdin ์ผ๋ก ์ ๋ฌ (shell escape ์ฐํ).
|
|
10959
11096
|
const useStreamJson = process.env.LEERNESS_REPL_STREAM_FORMAT === 'json';
|
|
11097
|
+
let useStdinForPrompt = false; // 1.9.188: claude/codex ๋ stdin ์ผ๋ก prompt ์ ๋ฌ (shell escape ์ฐํ)
|
|
10960
11098
|
if (provider === 'claude') {
|
|
10961
11099
|
cmd = 'claude';
|
|
11100
|
+
// 1.9.188: promptText ๋ stdin ์ผ๋ก โ args ์์ ์ ๊ฑฐ (ํ๊ธ/ํน์๋ฌธ์ ์์ )
|
|
10962
11101
|
args = useStreamJson
|
|
10963
|
-
? ['--print', '--output-format=stream-json', '--verbose'
|
|
10964
|
-
: ['--print'
|
|
11102
|
+
? ['--print', '--output-format=stream-json', '--verbose']
|
|
11103
|
+
: ['--print'];
|
|
11104
|
+
useStdinForPrompt = true;
|
|
11105
|
+
}
|
|
11106
|
+
else if (provider === 'codex') {
|
|
11107
|
+
cmd = 'codex';
|
|
11108
|
+
args = ['exec', '--skip-git-repo-check', '-']; // - = stdin from claude/codex convention
|
|
11109
|
+
useStdinForPrompt = true;
|
|
10965
11110
|
}
|
|
10966
|
-
else if (provider === '
|
|
10967
|
-
else if (provider === 'gemini') { cmd = 'gemini'; args = ['-p', promptText]; }
|
|
11111
|
+
else if (provider === 'gemini') { cmd = 'gemini'; args = ['-p', promptText]; } // gemini -p ๋ ์ธ์ ๋ชจ๋๋ง ์ง์
|
|
10968
11112
|
else if (provider === 'copilot') { cmd = 'gh'; args = ['copilot', 'suggest', promptText]; }
|
|
10969
11113
|
else return { ok: false, error: `provider ${provider} ๋ฏธ์ง์`, provider };
|
|
10970
11114
|
const t0 = Date.now();
|
|
@@ -11024,17 +11168,26 @@ async function _cliChatStream(root, provider, promptText, opts) {
|
|
|
11024
11168
|
process.stdout.write(dim(`\n โโ ${provider} stream โโ\n`));
|
|
11025
11169
|
let child;
|
|
11026
11170
|
try {
|
|
11027
|
-
// 1.9.186 (์ฌ์ฉ์ ๋ช
์ fix):
|
|
11028
|
-
//
|
|
11029
|
-
//
|
|
11030
|
-
//
|
|
11031
|
-
//
|
|
11171
|
+
// 1.9.186 + 1.9.188 (์ฌ์ฉ์ ๋ช
์ fix):
|
|
11172
|
+
// Windows .cmd ํธํ์ ์ํด shell: true ์ ์ง.
|
|
11173
|
+
// 1.9.188: promptText ๊ฐ ํ๊ธ/ํน์๋ฌธ์์ผ ๋ shell escape ์คํจ โ stdin ์ผ๋ก ์ ๋ฌ.
|
|
11174
|
+
// useStdinForPrompt=true ์ stdio[0]='pipe' + child.stdin.write(promptText) + end().
|
|
11175
|
+
// shell ์ธ์์๋ escape-safe flags ๋ง ๋จ๊น ('--print' ๋ฑ).
|
|
11032
11176
|
child = cp.spawn(cmd, args, {
|
|
11033
11177
|
cwd: process.cwd(),
|
|
11034
11178
|
env: _scrubEnv({}),
|
|
11035
11179
|
shell: true,
|
|
11036
|
-
stdio: ['ignore', 'pipe', 'pipe']
|
|
11180
|
+
stdio: [useStdinForPrompt ? 'pipe' : 'ignore', 'pipe', 'pipe']
|
|
11037
11181
|
});
|
|
11182
|
+
// 1.9.188: promptText ๋ฅผ stdin ์ผ๋ก ์์ ์ ๋ฌ
|
|
11183
|
+
if (useStdinForPrompt && child.stdin) {
|
|
11184
|
+
try {
|
|
11185
|
+
child.stdin.write(promptText);
|
|
11186
|
+
child.stdin.end();
|
|
11187
|
+
} catch (e) {
|
|
11188
|
+
// stdin ์ฐ๊ธฐ ์คํจ ์ child ๊ฐ stdin ์ ๋ฐ์ ์๋ โ ๋ฌด์ํ๊ณ ์งํ
|
|
11189
|
+
}
|
|
11190
|
+
}
|
|
11038
11191
|
} catch (e) {
|
|
11039
11192
|
if (spinnerInterval) clearInterval(spinnerInterval);
|
|
11040
11193
|
return resolve({ ok: false, error: 'spawn ์คํจ: ' + e.message, provider });
|
|
@@ -11439,10 +11592,16 @@ async function _agentRepl(root, opts) {
|
|
|
11439
11592
|
log('');
|
|
11440
11593
|
log(C.dim(' โจ Tab=provider cycle ยท Shift+Tab=model ยท Ctrl+C=quit'));
|
|
11441
11594
|
log('');
|
|
11442
|
-
// 1.9.
|
|
11595
|
+
// 1.9.188 (์ฌ์ฉ์ ๋ช
์): state.model ์ด ๋น์ด์์ผ๋ฉด catalog ์ฒซ ๋ชจ๋ธ ์๋ ์ค์ โ ์ธ๋ถ ๋ชจ๋ธ ํ์.
|
|
11596
|
+
// "model=(๊ธฐ๋ณธ)" ๊ฐ์ ๋ชจํธํ ํ์ ๋์ ์ค์ ์ฌ์ฉ๋ ๋ชจ๋ธ ์ด๋ฆ (์: claude-opus-4-7) ๋ช
์.
|
|
11597
|
+
if (!state.model) {
|
|
11598
|
+
const cat = _PROVIDER_MODEL_CATALOG[state.provider];
|
|
11599
|
+
if (cat && cat.length) state.model = cat[0].id;
|
|
11600
|
+
}
|
|
11601
|
+
// 1.9.179+1.9.188: ์ํ๋ฐ ํ ์ค โ ์ธ๋ถ ๋ชจ๋ธ + ์๊น/๊ตฌ๋ถ์ ๊ฐํ
|
|
11443
11602
|
const permMode = _readPermissions(root).mode || 'basic';
|
|
11444
11603
|
log(' ' + C.bold('โก ') + C.cy(`provider=${state.provider}`) + ' ยท '
|
|
11445
|
-
+ C.mag(`model=${state.model || '(
|
|
11604
|
+
+ C.mag(`model=${state.model || '(unknown)'}`) + ' ยท '
|
|
11446
11605
|
+ C.green(`role=${state.role}`) + ' ยท '
|
|
11447
11606
|
+ C.yel(`perms=${permMode}`) + ' ยท '
|
|
11448
11607
|
+ (state.streamMode ? C.green('โถ stream=on') : C.dim('โก stream=off')));
|
|
@@ -11466,7 +11625,24 @@ async function _agentRepl(root, opts) {
|
|
|
11466
11625
|
}
|
|
11467
11626
|
} catch {}
|
|
11468
11627
|
log('');
|
|
11469
|
-
|
|
11628
|
+
// 1.9.188 (์ฌ์ฉ์ ๋ช
์): prompt ์/์๋ ๊ตฌ๋ถ์ + ์ธ๋ถ ๋ชจ๋ธ ๋ช
์ (์ฌ์ฉ์ ์
๋ ฅ์นธ ์๊ฐ ๊ฐ์กฐ).
|
|
11629
|
+
// ๋งค ์๋ต ์งํ readline.question ํธ์ถ ์ ์ _printInputDivider() ํธ์ถํ์ฌ ์
๋ ฅ ์์ญ ๋ช
ํํ.
|
|
11630
|
+
const _termCols = () => {
|
|
11631
|
+
try { return Math.max(40, (process.stdout.columns || 80)); } catch { return 80; }
|
|
11632
|
+
};
|
|
11633
|
+
const _printInputDivider = () => {
|
|
11634
|
+
if (!isTty) return;
|
|
11635
|
+
const cols = _termCols();
|
|
11636
|
+
const line = 'โ'.repeat(Math.min(cols - 4, 120));
|
|
11637
|
+
process.stdout.write('\n' + C.dim(' ' + line) + '\n');
|
|
11638
|
+
};
|
|
11639
|
+
// prompt ์ ์ธ๋ถ ๋ชจ๋ธ ํ์ (์ฌ์ฉ์ ๋ช
์): agent[claude ยท claude-opus-4-7 / actor / โถ]>
|
|
11640
|
+
const prompt = () => {
|
|
11641
|
+
if (!isTty) return 'agent> ';
|
|
11642
|
+
const modelShort = (state.model || '').replace(/^(claude|gpt|gemini)-/, '').slice(0, 18);
|
|
11643
|
+
const modelTag = modelShort ? C.dim(' ยท ') + C.mag(modelShort) : '';
|
|
11644
|
+
return C.cy(`agent[${state.provider}${modelTag}${C.cy('/' + state.role)}${state.streamMode ? C.cy('/โถ') : ''}${C.cy(']>')} `);
|
|
11645
|
+
};
|
|
11470
11646
|
rl.setPrompt(prompt());
|
|
11471
11647
|
|
|
11472
11648
|
// 1.9.170: Tab cycle โ provider (Tab) / model within provider (Shift+Tab)
|
|
@@ -11880,13 +12056,13 @@ async function _agentRepl(root, opts) {
|
|
|
11880
12056
|
// 1.9.170: stream ๋ชจ๋์์๋ ์ด๋ฏธ ์ค์๊ฐ์ผ๋ก ์ถ๋ ฅ๋์ผ๋ฏ๋ก ํค๋๋ง ํ์ (์๋ต ์ค๋ณต ๋ฐฉ์ง)
|
|
11881
12057
|
if (state.streamMode && ['claude', 'codex', 'gemini', 'copilot'].includes(state.provider)) {
|
|
11882
12058
|
log(C.dim(` [assistant: ${state.provider}/${state.model || 'default'}, role=${state.role}, ${dt}ms ยท ${result.response.length}์]`));
|
|
11883
|
-
log('');
|
|
11884
12059
|
} else {
|
|
11885
12060
|
log('');
|
|
11886
12061
|
log(C.bold(`assistant (${state.model || state.provider}, role=${state.role}, ${dt}ms)`));
|
|
11887
12062
|
log(result.response);
|
|
11888
|
-
log('');
|
|
11889
12063
|
}
|
|
12064
|
+
// 1.9.188 (์ฌ์ฉ์ ๋ช
์): ์๋ต ๋ + ์
๋ ฅ ๊ตฌ๋ถ์ (์
๋ ฅ์นธ ์๊ฐ ๋ช
ํ)
|
|
12065
|
+
_printInputDivider();
|
|
11890
12066
|
if (state.history.length % 6 === 0) saveSession(); // 6ํด๋ง๋ค ์๋ ์ ์ฅ
|
|
11891
12067
|
} else {
|
|
11892
12068
|
// 1.9.185 (์ฌ์ฉ์ ๋ช
์): REPL ํธ์ถ ์คํจ ์น์ ํ ์ง๋จ โ ์ด๋ ๋จ๊ณ์์ ์คํจํ๋์ง + ์ฆ์ ๊ฒ์ฆ ๋ช
๋ น ์๋ด
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "leerness",
|
|
3
|
-
"version": "1.9.
|
|
3
|
+
"version": "1.9.188",
|
|
4
4
|
"description": "Leerness: ๋นํ๊ดด ๋ง์ด๊ทธ๋ ์ด์
, ์๋ ๋ฒ์ ๊ฐ์งยท์
๋ฐ์ดํธ, ๊ณํ/์งํ/ํธ๋์คํ ์๋ํ, ๊ฒ์ผ๋ฆยท์ํฌ๋ฆฟยท์ธ์ฝ๋ฉ ์๋ ๊ฐ๋, Claude Code ์ฌ๋์ ํตํฉ์ ๊ฐ์ถ ํ๊ตญ์ด ์ฐ์ AI ๊ฐ๋ฐ ํ๋ค์ค.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"leerness",
|