deel-local-cli 0.5.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 yunseok
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.en.md ADDED
@@ -0,0 +1,246 @@
1
+ # deel-local-cli
2
+
3
+ A coding agent CLI that runs entirely on **local models or your own private gateway**.
4
+ **Zero dependencies** — nothing but Node's own standard library.
5
+
6
+ > The interface and source comments are in Korean. This file is the English guide.
7
+
8
+ ---
9
+
10
+ ## Why zero dependencies
11
+
12
+ This was built for an offline corporate network where installing third-party
13
+ software requires review. `dependencies` is empty and stays empty — that fact is
14
+ the argument you hand to a security team.
15
+
16
+ ```
17
+ npm ls → no dependencies
18
+ cat package.json → "dependencies": {}
19
+ ```
20
+
21
+ All you need is **Node 20+**. There is no `npm install` step.
22
+
23
+ ---
24
+
25
+ ## Install
26
+
27
+ ```bash
28
+ npm install -g deel-local-cli
29
+ # or run without installing
30
+ npx deel-local-cli setup
31
+ ```
32
+
33
+ For an air-gapped machine, copy the folder across and run `node bin/deel.js`
34
+ directly. Nothing is installed on the target machine.
35
+
36
+ ---
37
+
38
+ ## Connect
39
+
40
+ ```bash
41
+ deel setup
42
+ ```
43
+
44
+ It asks for a name, base URL and API key, probes the endpoint, lists the models
45
+ it found, and saves the profile to `~/.deel/config.json`.
46
+
47
+ Point it at anything OpenAI-compatible:
48
+
49
+ | | Example URL |
50
+ |---|---|
51
+ | Private AI gateway | `https://ai-gw.example.corp/v1` |
52
+ | Ollama | `http://localhost:11434` |
53
+ | LM Studio | `http://localhost:1234/v1` |
54
+ | vLLM / LiteLLM | `http://host:port/v1` |
55
+
56
+ The auth style is detected automatically — `Authorization: Bearer`, `x-api-key`,
57
+ `api-key` (Azure-style), or none.
58
+
59
+ ### Keeping the key out of the config file
60
+
61
+ ```bash
62
+ export DEEL_API_KEY=sk-xxxx # takes precedence over the file
63
+ deel diagnose --url https://ai-gw.example.corp/v1 --model sec-llm-01
64
+ ```
65
+
66
+ Other environment variables: `NODE_EXTRA_CA_CERTS` (corporate CA),
67
+ `HTTPS_PROXY`, `DEEL_DEBUG=1` (full stack traces).
68
+
69
+ ---
70
+
71
+ ## Diagnose a gateway
72
+
73
+ Before trusting an endpoint, find out what it actually supports:
74
+
75
+ ```bash
76
+ deel diagnose --url <base-url> --key <key> --model <model> --out report.txt
77
+ ```
78
+
79
+ | Check | Why it matters |
80
+ |---|---|
81
+ | Basic chat | URL, key and model name are right |
82
+ | System message | Whether rules and skills take effect |
83
+ | Streaming | Whether output can arrive token by token |
84
+ | **Tool calling** | **Whether files can be read and edited — the critical one** |
85
+ | **Tool result round-trip** | **Whether multi-turn works — the agent loop depends on it** |
86
+ | Structured output | Whether edit formats can be schema-enforced |
87
+ | Reasoning effort | Whether `/think` reaches the model |
88
+ | Context length | How many files fit at once |
89
+
90
+ It ends with a verdict: **ready / limited / blocked / unreachable**, plus a
91
+ plain-text report you can hand to whoever runs the gateway.
92
+
93
+ Reasoning models are handled correctly: if all output lands in `thinking` and
94
+ the body gets truncated, it retries with thinking disabled instead of reporting
95
+ a false failure.
96
+
97
+ ---
98
+
99
+ ## Chat
100
+
101
+ Run it inside the folder you want to work in. That folder becomes the **scope** —
102
+ nothing outside it can be read or written, even if the model asks.
103
+
104
+ ```
105
+ deel sec-llm-01 · /home/you/project
106
+ skills: 337 · commands: 127 (42 plugins)
107
+
108
+ › unify the log format
109
+
110
+ ⏺ Grep(console.log)
111
+ └ 1 file · 1 match
112
+
113
+ ⏺ Read(src/runner.js)
114
+ └ 5 lines
115
+
116
+ ⏺ Edit(src/runner.js)
117
+ └ 1 occurrence
118
+
119
+ Unified the log calls to the logger format. One change in runner.js.
120
+
121
+ ─ 4.2s · 3 tools · 180 tokens
122
+ ```
123
+
124
+ ### Slash commands
125
+
126
+ Names follow the Claude Code / Codex convention.
127
+
128
+ | Command | |
129
+ |---|---|
130
+ | `/help` | list commands |
131
+ | `/context` | context usage — what is taking up room |
132
+ | `/compact` · `/clear` | shrink · wipe the conversation |
133
+ | `/model` | switch connection or model (conversation continues) |
134
+ | `/think off\|low\|medium\|high\|max` | reasoning effort |
135
+ | `/mode auto\|confirm\|strict` | execution mode |
136
+ | `/undo [n]` | roll back the last n turns |
137
+ | `/tools` · `/skills` | what is available |
138
+ | `/cost` · `/status` | usage · connection |
139
+ | `/init` | create a `DEEL.md` rules file |
140
+
141
+ ### Tools
142
+
143
+ Names and arguments match Claude Code, so skills and commands written for that
144
+ convention work unchanged.
145
+
146
+ `Read` · `Write` · `Edit` · `Glob` · `Grep` · `Bash` · `Skill`
147
+
148
+ ---
149
+
150
+ ## Edits survive sloppy models
151
+
152
+ Models routinely get whitespace, indentation and line endings slightly wrong.
153
+ `Edit` relaxes matching in stages — but **refuses outright when ambiguous**,
154
+ because silently editing the wrong place is far worse than not finding it.
155
+
156
+ ```
157
+ exact → trailing space & CRLF → indentation → all whitespace
158
+ ```
159
+
160
+ Measured by `npm run bench`:
161
+
162
+ ```
163
+ should-fix cases exact-only 2/10 (20%) → staged 10/10 (100%)
164
+ should-refuse 5/5 (100%) · wrong-place edits: 0
165
+ ```
166
+
167
+ When it fails, it points at the closest real line so the model can correct itself:
168
+
169
+ ```
170
+ Not found.
171
+ Line 2 of the file is closest:
172
+ console.log("start: " + id);
173
+ Copy that line verbatim and try again.
174
+ ```
175
+
176
+ ---
177
+
178
+ ## Skills and commands come from the host machine
179
+
180
+ Nothing is bundled. On startup it scans:
181
+
182
+ ```
183
+ project ./.deel/skills ./.claude/skills ./.deel/commands ./.claude/commands
184
+ user ~/.deel/skills ~/.claude/skills ~/.claude/commands
185
+ plugins ~/.claude/plugins/** (any folder with .claude-plugin/plugin.json)
186
+ ```
187
+
188
+ It reads the Claude Code format: `SKILL.md` with YAML frontmatter,
189
+ `commands/*.md` with `$ARGUMENTS` substitution.
190
+
191
+ ### Loaded in three stages
192
+
193
+ Listing everything would blow the context window. So:
194
+
195
+ | Stage | What | Cost |
196
+ |---|---|---|
197
+ | 1 | name + one-line description in the system prompt | ~1,800 tokens for 40 |
198
+ | 2 | the model calls `Skill` for the one body it wants | one at a time |
199
+ | 3 | files that body references, via `Read` | only if needed |
200
+
201
+ On a machine with 337 skills, listing 40 costs about 1,800 tokens instead of
202
+ well over 100,000.
203
+
204
+ Not supported on purpose: **hooks** (executable scripts — a review problem and an
205
+ extra failure path for autonomous execution), **subagents** (doubles model calls
206
+ against a gateway quota), and **MCP** (a separate protocol, a project of its own).
207
+
208
+ ---
209
+
210
+ ## Safety without approval prompts
211
+
212
+ The default mode is `auto`: it edits files and runs commands without asking.
213
+ The safety net is that everything is **reversible**, not that everything is gated.
214
+
215
+ | | |
216
+ |---|---|
217
+ | **Undo** | every file is snapshotted before it changes; `/undo` restores by turn |
218
+ | **Scope** | the starting folder is a hard boundary |
219
+ | **Blocked commands** | only irreversible ones — disk format, recursive delete, force-push, piping downloads into a shell. Ordinary commands pass |
220
+ | **No re-run** | a mutating command is never retried after failure — running it twice is the accident |
221
+ | **Audit log** | everything lands in `.deel/audit.jsonl` |
222
+
223
+ `/mode confirm` asks before irreversible commands; `/mode strict` asks before
224
+ every file change and command.
225
+
226
+ ---
227
+
228
+ ## Development
229
+
230
+ ```bash
231
+ npm run check # syntax across every module
232
+ npm test # 20 tool checks + 16 engine checks + the edit benchmark
233
+ npm run demo # see the chat screen, driven by a fake gateway
234
+ npm run bench # edit reliability numbers
235
+ ```
236
+
237
+ The engine tests spin up a **fake OpenAI-compatible gateway** over HTTP. No real
238
+ model is involved, so the loop, streaming parser, tool execution and undo are all
239
+ verified deterministically — including the case where a gateway splits tool-call
240
+ arguments across streaming chunks.
241
+
242
+ ---
243
+
244
+ ## License
245
+
246
+ MIT
package/README.md ADDED
@@ -0,0 +1,322 @@
1
+ # deel-local-cli
2
+
3
+ 로컬 모델·사내 게이트웨이 전용 코딩 에이전트. **외부 패키지를 하나도 쓰지 않습니다.**
4
+
5
+ > English guide: [README.en.md](README.en.md)
6
+
7
+ **3단계(편집 신뢰성)·5단계(스킬)까지 되어 있습니다.** 실제로 파일을 읽고 고치며, 그 PC 에 있는 스킬·명령을 찾아 씁니다.
8
+
9
+ ---
10
+
11
+ ## 왜 의존성이 0개인가
12
+
13
+ 사내 반입 심사에서 "미승인 소프트웨어"로 걸리지 않기 위해서입니다.
14
+ `package.json`의 `dependencies`가 비어 있고, Node에 원래 들어 있는 기능만 씁니다.
15
+
16
+ ```
17
+ 확인 방법: npm ls → 의존성 없음
18
+ cat package.json → "dependencies": {}
19
+ ```
20
+
21
+ 필요한 것은 **Node 20 이상**뿐입니다. `npm install`을 하지 않습니다.
22
+
23
+ ---
24
+
25
+ ## 대화 시작하기
26
+
27
+ 작업할 폴더에서:
28
+
29
+ ```
30
+ deel # 또는 설치 없이: node <이폴더>/bin/deel.js
31
+ ```
32
+
33
+ 그 폴더가 **작업 범위**가 됩니다. 밖의 파일은 읽지도 쓰지도 못합니다.
34
+
35
+ ```
36
+ deel sec-llm-01 · C:\work\myproject
37
+ /help 로 명령 목록. Ctrl+C 로 끝냅니다.
38
+
39
+ › 로그 형식 통일해줘
40
+
41
+ ⏺ Grep(console.log)
42
+ └ 1개 파일 · 1건
43
+
44
+ ⏺ Read(src/runner.js)
45
+ └ 5줄
46
+
47
+ ⏺ Edit(src/runner.js)
48
+ └ 1군데
49
+
50
+ 로그 호출을 logger 형식으로 통일했습니다.
51
+
52
+ ─ 4.2초 · 도구 3회 · 180토큰
53
+ ```
54
+
55
+ ### 슬래시 명령
56
+
57
+ Claude Code / Codex 와 같은 이름을 씁니다.
58
+
59
+ | 명령 | 하는 일 |
60
+ |---|---|
61
+ | `/help` | 명령 목록 |
62
+ | `/context` | 컨텍스트 사용량 — 무엇이 자리를 먹는지 |
63
+ | `/compact` | 오래된 대화 줄이기 |
64
+ | `/clear` | 대화 비우기 |
65
+ | `/model` | 연결·모델 바꾸기 (대화는 이어짐) |
66
+ | `/think off\|low\|medium\|high\|max` | 추론 강도 |
67
+ | `/mode auto\|confirm\|strict` | 실행 모드 |
68
+ | `/undo [턴수]` | 되돌리기 |
69
+ | `/tools` | 도구 목록 |
70
+ | `/cost` | 이번 세션 사용량 |
71
+ | `/status` | 연결 상태 |
72
+ | `/init` | `DEEL.md` 규칙 파일 만들기 |
73
+ | `/exit` | 끝내기 |
74
+
75
+ ### 도구 6종
76
+
77
+ 이름과 인자를 Claude Code 와 같게 맞췄습니다. 그 관례로 쓰인 스킬·명령이 그대로 먹습니다.
78
+
79
+ | 도구 | 하는 일 |
80
+ |---|---|
81
+ | `Read` | 파일 읽기 (줄 번호 붙음) |
82
+ | `Write` | 파일 쓰기·덮어쓰기 |
83
+ | `Edit` | 정확한 문자열 하나 바꾸기 |
84
+ | `Glob` | 이름 패턴으로 파일 찾기 |
85
+ | `Grep` | 내용 정규식 검색 |
86
+ | `Bash` | 명령 실행 |
87
+ | `Skill` | 스킬 본문 펼쳐 읽기 (스킬이 있을 때만 모델에게 보임) |
88
+
89
+ #### 편집이 조금 틀려도 찾아냅니다
90
+
91
+ 모델은 공백·들여쓰기·줄바꿈을 자주 틀립니다. 단계적으로 완화해 찾되, **모호하면 무조건 거부**합니다 —
92
+ 엉뚱한 곳을 조용히 고치는 것이 못 찾는 것보다 훨씬 나쁘기 때문입니다.
93
+
94
+ ```
95
+ 정확히 일치 → 줄 끝 공백·CRLF 무시 → 들여쓰기 무시 → 모든 공백 무시
96
+ ```
97
+
98
+ `npm run bench` 로 잰 결과: **정확히 일치만 쓰면 20%, 지금은 100%. 엉뚱한 곳을 고친 경우 0건.**
99
+
100
+ 못 찾으면 파일에서 가장 비슷한 줄을 짚어 줍니다:
101
+
102
+ ```
103
+ 찾지 못했습니다.
104
+ 파일의 2번 줄이 가장 비슷합니다:
105
+ console.log("실행 시작: " + id);
106
+ 이 줄을 그대로 옮겨 담아 다시 시도하세요.
107
+ ```
108
+
109
+ ---
110
+
111
+ ## 스킬·명령 — 그 PC 에 있는 것을 씁니다
112
+
113
+ deel 는 스킬을 품고 다니지 않습니다. 켜질 때 아래를 훑어 **있는 것을 그대로** 씁니다.
114
+
115
+ ```
116
+ 프로젝트 ./.deel/skills ./.claude/skills ./.deel/commands ./.claude/commands
117
+ 사용자 ~/.deel/skills ~/.claude/skills ~/.claude/commands
118
+ 플러그인 ~/.claude/plugins/** (.claude-plugin/plugin.json 이 있는 폴더)
119
+ ```
120
+
121
+ Claude Code 와 같은 형식(`SKILL.md` + YAML 앞머리, `commands/*.md`, `$ARGUMENTS`)을 읽습니다.
122
+
123
+ ### 3단계로 나눠 올립니다
124
+
125
+ 전부 올리면 컨텍스트가 죽습니다. 그래서:
126
+
127
+ | 단계 | 무엇을 | 비용 |
128
+ |---|---|---|
129
+ | 1 | 이름 + 설명 한 줄만 프롬프트에 | 40개 기준 약 1,800토큰 |
130
+ | 2 | 모델이 `Skill` 도구로 고른 것의 본문만 | 필요할 때 1개씩 |
131
+ | 3 | 본문이 가리키는 파일은 `Read` 로 | 그때 또 |
132
+
133
+ `/context` 에서 스킬 목록이 얼마나 먹는지 바로 보입니다.
134
+
135
+ ```
136
+ /skills 지금 올라간 것 보기
137
+ /skills <검색어> 찾아보기
138
+ /skills on <검색어> 걸리는 것만 올리기
139
+ /skills all | off 전부 올리기 | 내리기
140
+ ```
141
+
142
+ ### 슬래시 명령도 그대로
143
+
144
+ 찾은 명령은 `/<플러그인>:<이름>` 으로 부릅니다. `$ARGUMENTS` 가 치환됩니다.
145
+
146
+ ```
147
+ › /ecc:code-review src/app.js
148
+ ⌘ ecc:code-review plugin
149
+ ```
150
+
151
+ ### 안 넣은 것
152
+
153
+ | | 이유 |
154
+ |---|---|
155
+ | hooks | 실행 스크립트라 사내 반입 심사에 걸리고, 자율 실행에 사고 경로를 늘립니다 |
156
+ | 서브에이전트 | 모델 호출이 배로 늘어 게이트웨이 할당량을 먹습니다 |
157
+ | MCP | 별도 프로토콜이라 그 자체로 하나의 프로젝트입니다 |
158
+
159
+ ### 안전망
160
+
161
+ 승인 프롬프트 대신 **되돌릴 수 있게** 만들었습니다. 기본 모드는 `auto` — 묻지 않고 알아서 합니다.
162
+
163
+ | 장치 | 내용 |
164
+ |---|---|
165
+ | **되돌리기** | 파일을 고치기 전 항상 스냅샷. `/undo` 로 턴 단위 복구 |
166
+ | **작업 범위** | 시작한 폴더 밖은 모델이 시켜도 거부 |
167
+ | **위험 명령 차단** | 되돌릴 수 없는 것만 (디스크 포맷, 재귀 삭제, `--force` 푸시 등). 평범한 명령은 통과 |
168
+ | **재실행 금지** | 변경성 명령은 실패해도 다시 실행하지 않음 — 두 번 돌면 사고 |
169
+ | **감사 로그** | `.deel/audit.jsonl` 에 전부 기록 |
170
+
171
+ `/mode confirm` 은 되돌릴 수 없는 명령만, `/mode strict` 는 파일 변경·명령을 전부 물어봅니다.
172
+
173
+ ---
174
+
175
+ ## 사내망에서 진단 돌리기
176
+
177
+ 압축을 풀고 그 폴더에서:
178
+
179
+ ```
180
+ node bin/deel.js diagnose --url <게이트웨이주소> --key <키> --model <모델> --out report.txt
181
+ ```
182
+
183
+ 예시:
184
+
185
+ ```
186
+ node bin/deel.js diagnose --url https://ai-gw.example.corp/v1 --key sk-xxxx --model sec-llm-01 --out report.txt
187
+ ```
188
+
189
+ `report.txt` 파일 하나만 가져오시면 됩니다. 색 없는 평문이라 그대로 붙여넣을 수 있습니다.
190
+
191
+ ### 키를 파일에 안 남기고 싶으면
192
+
193
+ ```
194
+ set DEEL_API_KEY=sk-xxxx
195
+ node bin/deel.js diagnose --url https://ai-gw.example.corp/v1 --model sec-llm-01 --out report.txt
196
+ ```
197
+
198
+ 환경변수가 설정 파일보다 우선합니다.
199
+
200
+ ---
201
+
202
+ ## 대화형으로 설정하기
203
+
204
+ ```
205
+ node bin/deel.js setup
206
+ ```
207
+
208
+ 이름 → 주소 → 키를 물어보고, 붙어보고, 모델 목록을 띄워 고르게 한 뒤,
209
+ 진단까지 돌리고 저장합니다. 저장 위치는 `~/.deel/config.json`입니다.
210
+
211
+ 이후에는:
212
+
213
+ ```
214
+ node bin/deel.js 연결 상태 보기
215
+ node bin/deel.js diagnose 저장된 연결로 진단 다시 돌리기
216
+ ```
217
+
218
+ ---
219
+
220
+ ## 무엇을 검사하는가
221
+
222
+ | 검사 | 왜 보는가 |
223
+ |---|---|
224
+ | 기본 대화 | 주소·키·모델 이름이 맞는지 |
225
+ | 시스템 메시지 | 규칙(DEEL.md)과 스킬이 먹는지 |
226
+ | 스트리밍 | 화면이 한 글자씩 흐를 수 있는지 |
227
+ | **도구 호출** | **파일을 읽고 고칠 수 있는지 — 가장 중요** |
228
+ | **도구 결과 되돌리기** | **여러 턴이 이어지는지 — 에이전트 루프의 전제** |
229
+ | 구조적 출력 | 편집 형식을 스키마로 강제할 수 있는지 |
230
+ | 추론 강도 조절 | `/think` 가 모델 층에서 먹는지 |
231
+ | 컨텍스트 길이 | 파일을 몇 개까지 한 번에 읽힐 수 있는지 |
232
+
233
+ 마지막에 **판정**이 나옵니다.
234
+
235
+ | 판정 | 뜻 |
236
+ |---|---|
237
+ | 준비됨 | 에이전트 루프를 그대로 올릴 수 있음 |
238
+ | 제한적 | 돌아가지만 편집 신뢰성 보강이 필요 |
239
+ | 막힘 | 도구 호출이 안 됨 — 게이트웨이 설정을 확인해야 함 |
240
+ | 연결실패 | 주소·키·인증서·프록시 문제 |
241
+
242
+ ---
243
+
244
+ ## 붙는 서버
245
+
246
+ 주소만 넣으면 규격을 알아서 찾습니다.
247
+
248
+ | | 주소 예 |
249
+ |---|---|
250
+ | 사내 AI 게이트웨이 (OpenAI 호환) | `https://ai-gw.example.corp/v1` |
251
+ | Ollama | `http://localhost:11434` |
252
+ | LM Studio | `http://localhost:1234/v1` |
253
+ | vLLM · LiteLLM | `http://호스트:포트/v1` |
254
+
255
+ 인증 방식도 자동으로 맞춥니다 — `Authorization: Bearer`, `x-api-key`, `api-key`(Azure 계열), 인증 없음 순으로 시도합니다.
256
+
257
+ ---
258
+
259
+ ## 연결이 안 될 때
260
+
261
+ | 증상 | 확인할 것 |
262
+ |---|---|
263
+ | `주소를 찾을 수 없습니다` | 주소 오타, DNS, 사내망 접속 여부 |
264
+ | `연결이 거부되었습니다` | 서버가 꺼져 있거나 포트가 다름 |
265
+ | `인증서 문제` | `set NODE_EXTRA_CA_CERTS=C:\경로\사내CA.pem` |
266
+ | 프록시를 거쳐야 함 | `set HTTPS_PROXY=http://프록시:포트` |
267
+ | 401 / 403 | 키가 틀렸거나 인증 헤더 형식이 다름 (진단이 4가지를 자동 시도합니다) |
268
+
269
+ 자세한 오류를 보려면 `set DEEL_DEBUG=1`.
270
+
271
+ ---
272
+
273
+ ## 폴더 구조
274
+
275
+ ```
276
+ bin/deel.js 진입점
277
+ src/
278
+ ui/ 색·한글 폭·입력·스피너
279
+ config.js 연결 프로필 저장/읽기
280
+ backend/http.js HTTP 한 겹 + 인증 방식 4종
281
+ backend/detect.js 규격·인증 자동 판별
282
+ backend/adapter.js OpenAI/Ollama 차이 흡수 + 스트리밍 파서
283
+ backend/probe.js 진단 검사 8종
284
+ tools/index.js 도구 6종
285
+ tools/fsutil.js glob·파일 훑기 (직접 구현)
286
+ safety/guard.js 작업 범위 + 위험 명령 차단
287
+ safety/undo.js 스냅샷·되돌리기
288
+ safety/audit.js 감사 로그
289
+ agent/session.js 대화 상태 + 컨텍스트 셈
290
+ agent/loop.js 에이전트 루프
291
+ commands.js 슬래시 명령
292
+ repl.js 대화 화면
293
+ report.js 진단 표 + 판정
294
+ setup.js 마법사
295
+ test/ 검증 (배포 zip 에서 뺀다)
296
+ ```
297
+
298
+ ## 검증
299
+
300
+ ```
301
+ npm test 도구 20건 + 엔진 16건
302
+ npm run demo 화면이 어떻게 보이는지 실제로 돌려 보기
303
+ ```
304
+
305
+ 엔진 검증은 **가짜 게이트웨이**를 띄워서 합니다. 실제 모델 없이 OpenAI 호환 규격 그대로
306
+ 루프·스트리밍·도구 실행·되돌리기를 결정적으로 확인합니다.
307
+
308
+ ---
309
+
310
+ ## 다음 단계
311
+
312
+ | 단계 | 내용 | 상태 |
313
+ |---|---|---|
314
+ | 1 | 연결 설정 + 진단 | 됨 |
315
+ | 2 | 엔진 — 루프 · 도구 6종 · 되돌리기 · 감사로그 | 됨 |
316
+ | 4 | 화면 — 스트리밍 · 도구 배지 · 슬래시 명령 · `/context` | 됨 (2단계에서 같이) |
317
+ | 7 | 추론 조절 — `/think`, `/mode`, 도구 호출 상한 | 반쯤 (모델 층·루프 층까지) |
318
+ | 3 | 편집 신뢰성 — 단계별 완화 매칭 · 실패 안내 · 성공률 측정 | 됨 (20%→100%) |
319
+ | 5 | 스킬·명령 발견 + 3단계 적재 | 됨 |
320
+ | **6** | **플러그인 — `/plugin install` · `/plugin pack`** | **다음** |
321
+ | 7 | 추론 조절 — 작업 층 (단계별 다른 모델) | 남음 |
322
+ | 8 | 반입 패키징 + 외부통신 0건 검증 | 남음 |
package/bin/deel.js ADDED
@@ -0,0 +1,94 @@
1
+ #!/usr/bin/env node
2
+ // deel 진입점. 외부 의존성 없음 — Node 표준 기능만 씁니다.
3
+ import { c, say } from '../src/ui/ansi.js';
4
+ import { runSetup, runDiagnose, showStatus, banner } from '../src/setup.js';
5
+ import { chatLoop } from '../src/repl.js';
6
+
7
+ const MIN_NODE = 20;
8
+
9
+ function parse(argv) {
10
+ const flags = {};
11
+ const args = [];
12
+ for (let i = 0; i < argv.length; i++) {
13
+ const a = argv[i];
14
+ if (a === '-h') { flags.help = true; continue; }
15
+ if (a.startsWith('--')) {
16
+ const [k, inline] = a.slice(2).split('=');
17
+ if (inline !== undefined) flags[k] = inline;
18
+ else if (argv[i + 1] && !argv[i + 1].startsWith('-')) flags[k] = argv[++i];
19
+ else flags[k] = true;
20
+ } else args.push(a);
21
+ }
22
+ // 명령은 플래그가 아닌 첫 낱말. 없으면 상태 보기.
23
+ return { cmd: args[0] ?? '', args: args.slice(1), flags };
24
+ }
25
+
26
+ function help() {
27
+ banner();
28
+ say(` ${c.bold('사용법')}`);
29
+ say('');
30
+ say(` ${c.cyan('deel')} 대화 시작 (이 폴더에서)`);
31
+ say(` ${c.cyan('deel setup')} 연결 설정 (주소·키·모델)`);
32
+ say(` ${c.cyan('deel status')} 연결 상태 보기`);
33
+ say(` ${c.cyan('deel diagnose')} 저장된 연결로 진단 다시 돌리기`);
34
+ say('');
35
+ say(` ${c.bold('대화 시작 옵션')}`);
36
+ say('');
37
+ say(` ${c.gray('--root <폴더>')} 작업 범위. 기본은 지금 폴더`);
38
+ say(` ${c.gray('--mode <모드>')} auto(기본) / confirm / strict`);
39
+ say(` ${c.gray('--think <수준>')} off / low / medium(기본) / high / max`);
40
+ say('');
41
+ say(` ${c.bold('진단 직접 지정')} ${c.gray('— 설정을 남기지 않고 확인만 할 때')}`);
42
+ say('');
43
+ say(` deel diagnose --url <주소> --key <키> --model <모델> --out report.txt`);
44
+ say('');
45
+ say(` ${c.bold('환경변수')}`);
46
+ say('');
47
+ say(` ${c.gray('DEEL_API_KEY')} 키를 파일에 안 남기고 싶을 때 (파일보다 우선)`);
48
+ say(` ${c.gray('NODE_EXTRA_CA_CERTS')} 사내 인증서를 쓰는 게이트웨이일 때`);
49
+ say(` ${c.gray('HTTPS_PROXY')} 프록시를 거쳐야 할 때`);
50
+ say('');
51
+ }
52
+
53
+ async function main() {
54
+ const major = parseInt(process.versions.node.split('.')[0], 10);
55
+ if (major < MIN_NODE) {
56
+ say(` Node ${MIN_NODE} 이상이 필요합니다. 지금은 ${process.versions.node} 입니다.`);
57
+ process.exit(1);
58
+ }
59
+
60
+ const { cmd, flags } = parse(process.argv.slice(2));
61
+ if (flags.help || cmd === 'help') { help(); return 0; }
62
+
63
+ switch (cmd) {
64
+ case '':
65
+ case 'chat':
66
+ return chatLoop({
67
+ root: flags.root ? String(flags.root) : undefined,
68
+ mode: flags.mode ? String(flags.mode) : undefined,
69
+ think: flags.think ? String(flags.think) : undefined,
70
+ });
71
+ case 'status':
72
+ return showStatus();
73
+ case 'setup':
74
+ return runSetup();
75
+ case 'diagnose':
76
+ case 'doctor':
77
+ return runDiagnose(flags);
78
+ default:
79
+ say('');
80
+ say(` ${c.red('모르는 명령')} ${c.bold(cmd)}`);
81
+ help();
82
+ return 1;
83
+ }
84
+ }
85
+
86
+ main()
87
+ .then((code) => process.exit(code ?? 0))
88
+ .catch((err) => {
89
+ say('');
90
+ say(` ${c.red('오류')} ${err?.message ?? err}`);
91
+ if (process.env.DEEL_DEBUG) say(c.gray(err?.stack ?? ''));
92
+ say('');
93
+ process.exit(1);
94
+ });