reborn-dshkit 1.0.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 REBORN LABS
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.md ADDED
@@ -0,0 +1,67 @@
1
+ # reborn-dshkit
2
+
3
+ **DSH(DeepSeek Harness) 를 켜기 전에 볼 것**을 알려 주는 클로드 코드 스킬 설치기입니다.
4
+
5
+ ```
6
+ npx reborn-dshkit
7
+ ```
8
+
9
+ 깔면 `~/.claude/skills/dsh-guide/` 에 스킬이 들어갑니다.
10
+ 클로드 코드를 껐다 켜면 잡힙니다. 그다음엔 그냥 말하면 됩니다 — **"DSH 켜는 법 알려줘"**
11
+
12
+ ---
13
+
14
+ ## 왜 만들었나
15
+
16
+ DSH 는 딥시크가 낸 오픈소스 에이전트 하네스입니다. MIT 이고, 실제로 좋습니다.
17
+ 그런데 소개 영상과 정리글을 보고 온 사람이 **거의 예외 없이 첫 줄에서 막힙니다.**
18
+
19
+ | 치는 명령 | 실제로 깔리는 것 |
20
+ |---|---|
21
+ | `npx dsh` | **2016년에 올라온 남의 JavaScript 셸** — "A shell written in JavaScript" |
22
+ | `npx deepseek-harness` | **예약된 빈 껍데기** — "The real package will be published here when development completes" |
23
+ | ✅ `npx @deepseek-ai/dsh web` | 진짜입니다 |
24
+
25
+ 약칭이 `dsh` 라서, 제품 이름이 `deepseek-harness` 라서 둘 다 자연스럽게 손이 갑니다.
26
+ **스코프(`@deepseek-ai/`)를 빼면 다른 걸 깝니다.**
27
+
28
+ 그리고 소개글이 잘 말하지 않는 것이 둘 더 있습니다.
29
+
30
+ - **개발자 프리뷰입니다.** README 가 대문자로 적어 뒀습니다 —
31
+ *"THERE WILL BE COMPATIBILITY-BREAKING CHANGES."*
32
+ - **모델은 딸려오지 않습니다.** 하네스는 모델을 굴리는 껍데기입니다.
33
+ 「무료」는 하네스가 MIT 라는 뜻이지 모델이 공짜라는 뜻이 아닙니다.
34
+
35
+ 이 킷은 그 셋을 **좋은 소식보다 먼저** 놓습니다.
36
+
37
+ ## 명령
38
+
39
+ ```
40
+ npx reborn-dshkit 스킬을 깐다
41
+ npx reborn-dshkit run 맞는 실행 명령과 틀린 명령을 나란히 (안 깔고)
42
+ npx reborn-dshkit facts 원장 전체를 찍는다
43
+ npx reborn-dshkit verify 저장소·npm 이 지금 응답하는지 (네트워크)
44
+ npx reborn-dshkit --check 깔렸는지 + 원장이 규칙을 지키는지
45
+ npx reborn-dshkit --self-test 자기시험 (네트워크 안 씀)
46
+ ```
47
+
48
+ ## 사실 원장
49
+
50
+ 스킬 옆에 `dsh.json` 이 함께 깔립니다. 스타 수·버전·도구 수를 **확인일과 함께** 적어 뒀습니다.
51
+ 숫자는 밖에서 바뀌니까요.
52
+
53
+ `--self-test` 는 **양방향**입니다. 정본 원장을 통과시키는 것만 보지 않고,
54
+ 일부러 스코프를 지운 가짜 원장을 만들어 **검사기가 그걸 잡는지**도 봅니다.
55
+ 전건 통과하는 검사기는 검사기가 아닙니다.
56
+
57
+ ## 상표에 대하여
58
+
59
+ 이 프로젝트는 DeepSeek 과 아무 관계가 없습니다. 공인·제휴·승인 받지 않았습니다.
60
+
61
+ "DeepSeek Harness" 는 DeepSeek 의 등록상표입니다.
62
+ `BRAND_GUIDELINES.md` 의 권고에 따라 **패키지 이름에 약칭 `DSH` 를 썼고**,
63
+ 설명문에서만 관계를 사실대로 적었습니다(가이드가 명시적으로 허용하는 범위입니다).
64
+
65
+ ## 라이선스
66
+
67
+ MIT · REBORN LABS
package/cli.mjs ADDED
@@ -0,0 +1,520 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * DSH(DeepSeek Harness) — 켜기 전에 볼 것. 스킬 설치기.
4
+ *
5
+ * npx reborn-dshkit 클로드 코드에 스킬을 깐다
6
+ * npx reborn-dshkit run 맞는 실행 명령과 틀린 명령을 나란히 (안 깔고)
7
+ * npx reborn-dshkit facts 원장 전체를 찍는다
8
+ * npx reborn-dshkit verify 저장소·npm 이 지금 응답하는지 (네트워크)
9
+ * npx reborn-dshkit --check 깔렸는지 + 원장이 규칙을 지키는지
10
+ * npx reborn-dshkit --self-test 자기시험 (네트워크 안 씀)
11
+ *
12
+ * ★★이 설치기가 파는 건 파일이 아니라 **원장의 정직함**이다.
13
+ * 그래서 판정을 "복사가 됐나"로 하지 않는다. **원장이 조건을 숨기지 않았나**로 한다.
14
+ *
15
+ * ① 실행 명령에 **스코프(@deepseek-ai/)** 가 붙어 있는가
16
+ * — `npx dsh` 는 2016년 남의 JS 셸이다. 이 한 줄이 이 킷이 존재하는 이유다.
17
+ * ② **개발자 프리뷰** 경고 원문이 그대로 남아 있는가
18
+ * — 원본 릴스는 「완전 무료·오픈소스」라고만 하고 이 줄을 말하지 않는다.
19
+ * ③ **모델이 딸려오지 않는다**는 사실이 적혀 있는가
20
+ * — 「무료」는 하네스가 MIT 라는 뜻이지 모델이 공짜라는 뜻이 아니다.
21
+ * ④ **상표 규칙**이 적혀 있는가 — 만들어 내놓는 사람에게 이게 먼저다.
22
+ *
23
+ * ★★자기시험은 **양방향**이다. 좋은 원장을 통과시키는 것만으로는 아무것도 증명 못 한다.
24
+ * 일부러 스코프를 지운 가짜 원장을 만들어 검사기가 **그걸 잡는지** 같이 본다.
25
+ * 전건 통과하는 검사기는 검사기가 아니다.
26
+ */
27
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, statSync } from "node:fs";
28
+ import { homedir } from "node:os";
29
+ import { dirname, join, relative } from "node:path";
30
+ import { fileURLToPath } from "node:url";
31
+ import { 링크, TAIL, 마지막 } from "./tail.mjs";
32
+
33
+ const HOME = homedir();
34
+ const 여기 = dirname(fileURLToPath(import.meta.url));
35
+ const c = { g: "\x1b[32m", r: "\x1b[31m", y: "\x1b[33m", d: "\x1b[90m", cy: "\x1b[36m", b: "\x1b[1m", 0: "\x1b[0m" };
36
+
37
+ const 스킬이름 = "dsh-guide";
38
+ const 퍼널 = "dsh";
39
+ const 원본 = join(여기, "skill");
40
+ const 대상 = join(HOME, ".claude", "skills", 스킬이름);
41
+
42
+ /** ★실행 명령은 반드시 이 스코프를 달고 있어야 한다. 빼면 남의 패키지가 깔린다. */
43
+ const 스코프 = "@deepseek-ai/dsh";
44
+
45
+ /** ★반드시 「틀린 명령」으로 적혀 있어야 하는 것들. 이게 이 킷의 존재 이유다. */
46
+ const 반드시틀린명령 = ["npx dsh", "npx deepseek-harness"];
47
+
48
+ /** ★README 가 대문자로 적어 둔 경고. 번역만 남기면 나중에 「몰랐다」가 된다. */
49
+ const 경고핵심 = /COMPATIBILITY-BREAKING/i;
50
+
51
+ /** ★「무료」를 조건 없이 말하면 막는다. 조건은 늘 있다. */
52
+ const 금칙 = [/완전\s*무료/, /무제한/, /제한\s*없이/];
53
+
54
+ function 머리말() {
55
+ console.log(`\n${c.b}DSH — 켜기 전에 볼 것${c[0]} ${c.d}· REBORN LABS · MIT${c[0]}`);
56
+ console.log(`${c.d}맞는 실행 명령 하나와, 「무료」가 어디까지인지 먼저 말합니다.${c[0]}\n`);
57
+ }
58
+
59
+ function 파일목록(뿌리) {
60
+ const 모음 = [];
61
+ for (const 이름 of readdirSync(뿌리)) {
62
+ const p = join(뿌리, 이름);
63
+ if (statSync(p).isDirectory()) 모음.push(...파일목록(p));
64
+ else 모음.push(p);
65
+ }
66
+ return 모음;
67
+ }
68
+
69
+ const 원장읽기 = (뿌리) => {
70
+ const f = join(뿌리, "dsh.json");
71
+ if (!existsSync(f)) return null;
72
+ try { return JSON.parse(readFileSync(f, "utf8")); } catch { return null; }
73
+ };
74
+
75
+ /**
76
+ * ★원장 판정. 여기가 이 설치기의 심장이다.
77
+ * 깔렸는지가 아니라 **깔린 원장이 조건을 숨기지 않았는지**를 본다.
78
+ */
79
+ function 원장검사(d) {
80
+ if (!d) return { 됨: false, 이유: "dsh.json 을 못 읽었습니다" };
81
+ if (!d.확인일) return { 됨: false, 이유: "확인일이 없습니다 — 언제 기준인지 모르면 못 믿습니다" };
82
+ if (!d.확인방법) return { 됨: false, 이유: "확인방법이 없습니다 — 어디서 잰 숫자인지 모르면 못 믿습니다" };
83
+
84
+ /* ── ① ★실행 명령 — 이 킷이 존재하는 이유다 ──────────────────── */
85
+ const E = d["★실행"];
86
+ if (!E) return { 됨: false, 이유: "실행 칸이 통째로 없습니다" };
87
+ if (!E.맞는명령) return { 됨: false, 이유: "맞는 실행 명령이 없습니다" };
88
+ if (!E.맞는명령.includes(스코프))
89
+ return { 됨: false, 이유: `실행 명령에 스코프 「${스코프}」 가 없습니다 — 스코프를 빼면 npm 의 남의 패키지가 깔립니다` };
90
+ if (!Array.isArray(E["★틀린명령"]) || !E["★틀린명령"].length)
91
+ return { 됨: false, 이유: "「틀린명령」이 비었습니다 — 이 킷이 존재하는 이유가 바로 그 칸입니다" };
92
+ for (const 틀 of 반드시틀린명령) {
93
+ const 있 = E["★틀린명령"].find((x) => String(x?.명령).trim() === 틀);
94
+ if (!있) return { 됨: false, 이유: `「${틀}」 이 틀린 명령으로 적혀 있지 않습니다 — 사람들이 실제로 이걸 칩니다` };
95
+ if (!있.무슨일) return { 됨: false, 이유: `「${틀}」 에 실제로 무엇이 깔리는지가 없습니다 — 「쓰지 마라」만으로는 안 지킵니다` };
96
+ }
97
+ if (!E.npm패키지 || !E.npm패키지.startsWith("@deepseek-ai/"))
98
+ return { 됨: false, 이유: "npm 패키지 이름이 스코프 없이 적혀 있습니다" };
99
+
100
+ /* ── ② ★성숙도 — 좋은 소식보다 먼저 놓아야 하는 것 ─────────────── */
101
+ const M = d["★성숙도"];
102
+ if (!M) return { 됨: false, 이유: "성숙도 칸이 없습니다 — 「무료 오픈소스」만 남으면 사고를 파는 것입니다" };
103
+ if (!M.단계 || !/preview|프리뷰/i.test(M.단계))
104
+ return { 됨: false, 이유: "성숙도 단계가 developer preview 라고 적혀 있지 않습니다" };
105
+ if (!경고핵심.test(String(M.경고원문 || "")))
106
+ return { 됨: false, 이유: 'README 경고 원문("…COMPATIBILITY-BREAKING CHANGES")이 없습니다 — 번역만 남기면 나중에 못 다툽니다' };
107
+ if (!M.그래서어떻게)
108
+ return { 됨: false, 이유: "프리뷰라는 사실만 있고 「그래서 어떻게」가 없습니다 — 막기만 하면 아무도 안 씁니다" };
109
+ if (!M.버전) return { 됨: false, 이유: "버전이 없습니다" };
110
+
111
+ /* ── ③ ★모델 — 「무료」의 조건이 여기 있다 ────────────────────── */
112
+ const L = d["★모델"];
113
+ if (!L) return { 됨: false, 이유: "모델 칸이 없습니다" };
114
+ if (L.딸려오나 !== false)
115
+ return { 됨: false, 이유: "「모델이 딸려온다」로 적혀 있습니다 — 하네스는 모델을 싣지 않습니다" };
116
+ if (!L.설명 || !/MIT/.test(L.설명))
117
+ return { 됨: false, 이유: "「무료」가 하네스의 MIT 라이선스를 가리킨다는 설명이 없습니다" };
118
+ if (!L.그래서어떻게)
119
+ return { 됨: false, 이유: "모델을 따로 정해야 한다는 사실만 있고 「그래서 어떻게」가 없습니다" };
120
+
121
+ /* ── ④ ★상표 — 만들어 내놓는 사람에게 이게 먼저다 ────────────── */
122
+ const T = d["★상표"];
123
+ if (!T) return { 됨: false, 이유: "상표 칸이 없습니다 — 플러그인을 내려는 사람에게 이게 먼저입니다" };
124
+ if (!/등록상표/.test(String(T.핵심 || "")))
125
+ return { 됨: false, 이유: "「등록상표」라는 사실이 상표 칸에 없습니다" };
126
+ if (!/DSH/.test(String(T.핵심 || "")))
127
+ return { 됨: false, 이유: "공식이 권하는 약칭(DSH)이 상표 칸에 없습니다 — 막기만 하고 대안을 안 주면 못 지킵니다" };
128
+ if (!T.설명은허용)
129
+ return { 됨: false, 이유: "무엇이 허용되는지가 없습니다 — 금지만 적으면 아무 말도 못 하게 됩니다" };
130
+ if (!T.출처) return { 됨: false, 이유: "상표 규칙의 출처가 없습니다" };
131
+
132
+ /* ── ⑤ 갖춘것 — 「엄청 많다」가 아니라 몇 개인지 ──────────────── */
133
+ const G = d.갖춘것;
134
+ if (!G) return { 됨: false, 이유: "갖춘것 칸이 없습니다" };
135
+ if (!Number.isFinite(G.도구수) || G.도구수 <= 0)
136
+ return { 됨: false, 이유: "도구 수가 숫자가 아닙니다 — 「엄청 많다」는 셀 수 없습니다" };
137
+ if (!Number.isFinite(G.패키지수) || G.패키지수 <= 0)
138
+ return { 됨: false, 이유: "패키지 수가 숫자가 아닙니다" };
139
+ if (!G.출처) return { 됨: false, 이유: "도구 수의 출처가 없습니다 — 어디서 셌는지 없으면 지어낸 것과 구분이 안 됩니다" };
140
+ if (!Array.isArray(G.묶음) || G.묶음.length < 3)
141
+ return { 됨: false, 이유: "도구 묶음이 3개 미만입니다 — 숫자만 있고 무엇인지 없으면 소용없습니다" };
142
+ for (const m of G.묶음)
143
+ if (!m?.이름 || !m?.예) return { 됨: false, 이유: "도구 묶음에 실제 도구 이름(예)이 빠진 항목이 있습니다" };
144
+
145
+ /* ── ⑥ 상호운용 — 사실이지만 단서가 붙어야 한다 ──────────────── */
146
+ const S = d["★상호운용"];
147
+ if (!S) return { 됨: false, 이유: "상호운용 칸이 없습니다" };
148
+ if (S.사실 === true && !S.출처)
149
+ return { 됨: false, 이유: "「클로드 코드 자식을 띄운다」를 사실로 적었는데 출처가 없습니다" };
150
+ if (S.사실 === true && !S.주의)
151
+ return { 됨: false, 이유: "상호운용에 「주의」가 없습니다 — 자식의 설치와 요금이 따라온다는 단서를 빼면 과장입니다" };
152
+
153
+ /* ── ⑦ 인기 — 스타는 관심이지 완성도가 아니다 ────────────────── */
154
+ const P = d.인기;
155
+ if (!P) return { 됨: false, 이유: "인기 칸이 없습니다" };
156
+ if (!Number.isFinite(P.스타)) return { 됨: false, 이유: "스타 수가 숫자가 아닙니다" };
157
+ if (!P.확인일) return { 됨: false, 이유: "스타 수에 확인일이 없습니다 — 숫자는 매일 바뀝니다" };
158
+ if (!P.주의) return { 됨: false, 이유: "스타 수에 「주의」가 없습니다 — 관심의 크기를 완성도로 읽게 두면 안 됩니다" };
159
+
160
+ /* ── ⑧ 금칙어 — 조건 없이 단정하지 않는다 ─────────────────────── */
161
+ const 홍보문 = [
162
+ d.제품?.무엇, d.제품?.무엇이아닌가,
163
+ L.설명, L.그래서어떻게, M.그래서어떻게,
164
+ ...(G.묶음 || []).map((m) => m.예),
165
+ ].join(" ");
166
+ for (const re of 금칙)
167
+ if (re.test(홍보문))
168
+ return { 됨: false, 이유: `설명에 「${re.source}」 에 걸리는 말이 있습니다 — 조건 없이 단정하지 않습니다` };
169
+
170
+ /* ── ⑨ 안 하는 말 — 무엇을 뺐느냐가 원장의 절반이다 ───────────── */
171
+ if (!Array.isArray(d.안하는말) || d.안하는말.length < 3)
172
+ return { 됨: false, 이유: "「안하는말」 이 3개 미만입니다 — 무엇을 안 말하는지가 원장의 절반입니다" };
173
+ for (const x of d.안하는말)
174
+ if (!x?.말 || !x?.이유) return { 됨: false, 이유: "「안하는말」 에 이유가 없는 항목이 있습니다" };
175
+ if (!d.안하는말.some((x) => /npx dsh/.test(x.말)))
176
+ return { 됨: false, 이유: "「안하는말」 에 `npx dsh` 안내 금지가 없습니다 — 이 킷의 첫 번째 이유입니다" };
177
+
178
+ return { 됨: true, 도구: G.도구수, 패키지: G.패키지수, 확인일: d.확인일, 버전: M.버전 };
179
+ }
180
+
181
+ /** 깔린 스킬이 실제로 읽히는지 — 머리말까지 확인한다. */
182
+ function 진짜도나() {
183
+ const md = join(대상, "SKILL.md");
184
+ if (!existsSync(md)) return { 됨: false, 이유: "SKILL.md 가 없습니다" };
185
+ const 본문 = readFileSync(md, "utf8");
186
+ if (!new RegExp("^---[\\s\\S]*?\\bname:\\s*" + 스킬이름 + "\\b", "m").test(본문))
187
+ return { 됨: false, 이유: "SKILL.md 머리말이 깨졌습니다(name 을 못 읽습니다)" };
188
+ return 원장검사(원장읽기(대상));
189
+ }
190
+
191
+ const 원장고르기 = () => 원장읽기(existsSync(join(대상, "dsh.json")) ? 대상 : 원본);
192
+
193
+ /* ── run ────────────────────────────────────────────────────────── */
194
+ function 실행보기() {
195
+ const d = 원장고르기();
196
+ const r = 원장검사(d);
197
+ if (!r.됨) { console.error(`${c.r}✗ 원장이 이상합니다${c[0]} — ${r.이유}`); process.exit(1); }
198
+ 머리말();
199
+ const E = d["★실행"];
200
+
201
+ console.log(`${c.g}${c.b}✅ 맞는 명령${c[0]}`);
202
+ console.log(` ${c.cy}${c.b}${E.맞는명령}${c[0]}`);
203
+ console.log(` ${c.d}${E.무엇이뜨나}${c[0]}`);
204
+ console.log(` ${c.d}필요한 것 — ${E.필요한것}${c[0]}\n`);
205
+
206
+ console.log(`${c.r}${c.b}✗ 이렇게 치면 딴 게 깔립니다${c[0]}`);
207
+ for (const x of E["★틀린명령"]) {
208
+ console.log(` ${c.r}${x.명령}${c[0]}`);
209
+ console.log(` ${c.d}→ ${x.무슨일}${c[0]}`);
210
+ if (x.왜생기나) console.log(` ${c.d} (${x.왜생기나})${c[0]}`);
211
+ }
212
+
213
+ console.log(`\n${c.b}소스에서 돌리려면${c[0]}`);
214
+ console.log(` ${c.cy}${E.소스에서}${c[0]}`);
215
+
216
+ const M = d["★성숙도"];
217
+ console.log(`\n${c.y}${c.b}★ 켜기 전에${c[0]}`);
218
+ console.log(` ${c.d}지금 판은 ${c[0]}${c.y}${M.버전}${c[0]}${c.d} · ${M.단계} 입니다.${c[0]}`);
219
+ console.log(` ${c.d}README 원문 — ${c[0]}"${M.경고원문.replace(/\*\*/g, "")}"`);
220
+ console.log(` ${c.d}${M.그래서어떻게}${c[0]}\n`);
221
+ console.log(`${c.d}확인일 ${d.확인일}. 버전과 스타 수는 밖에서 바뀝니다.${c[0]}\n`);
222
+ }
223
+
224
+ /* ── facts ──────────────────────────────────────────────────────── */
225
+ function 원장보기() {
226
+ const d = 원장고르기();
227
+ const r = 원장검사(d);
228
+ if (!r.됨) { console.error(`${c.r}✗ 원장이 이상합니다${c[0]} — ${r.이유}`); process.exit(1); }
229
+ 머리말();
230
+
231
+ const P = d.제품;
232
+ console.log(`${c.b}${P.정식이름}${c[0]} ${c.d}(약칭 ${P.약칭}) · ${P.만든곳} · ${P.라이선스}${c[0]}`);
233
+ console.log(`${c.d}${P.무엇}${c[0]}`);
234
+ console.log(`${c.y}아닌 것 — ${P.무엇이아닌가}${c[0]}\n`);
235
+
236
+ const E = d["★실행"];
237
+ console.log(`${c.g}${c.b}실행${c[0]} ${c.cy}${E.맞는명령}${c[0]}`);
238
+ for (const x of E["★틀린명령"]) console.log(` ${c.r}✗ ${x.명령.padEnd(24)}${c[0]}${c.d}${x.무슨일.slice(0, 60)}…${c[0]}`);
239
+
240
+ const M = d["★성숙도"];
241
+ console.log(`\n${c.y}${c.b}성숙도${c[0]} ${c.y}${M.버전} · ${M.단계}${c[0]}`);
242
+ console.log(` ${c.d}"${M.경고원문.replace(/\*\*/g, "")}"${c[0]}`);
243
+
244
+ const L = d["★모델"];
245
+ console.log(`\n${c.b}모델${c[0]} ${c.y}딸려오지 않습니다${c[0]}`);
246
+ console.log(` ${c.d}${L.설명}${c[0]}`);
247
+ console.log(` ${c.d}${L.그래서어떻게}${c[0]}`);
248
+
249
+ const G = d.갖춘것;
250
+ console.log(`\n${c.b}갖춘 것${c[0]} ${c.cy}도구 ${G.도구수}종${c[0]} ${c.d}· 플러그인 패키지 ${G.패키지수}개 (${G.출처})${c[0]}`);
251
+ for (const m of G.묶음) console.log(` ${c.d}${m.이름.padEnd(14)}${c[0]}${c.d}${m.예}${c[0]}`);
252
+
253
+ const S = d["★상호운용"];
254
+ console.log(`\n${c.b}다른 에이전트를 자식으로${c[0]}`);
255
+ console.log(` ${c.d}${S.설명}${c[0]}`);
256
+ console.log(` ${c.y}주의 — ${S.주의}${c[0]}`);
257
+
258
+ const T = d["★상표"];
259
+ console.log(`\n${c.b}만들어 내놓으실 거면${c[0]} ${c.d}(${T.출처})${c[0]}`);
260
+ console.log(` ${c.r}${T.핵심}${c[0]}`);
261
+ console.log(` ${c.g}허용 — ${T.설명은허용}${c[0]}`);
262
+ console.log(` ${c.d}${T.그래서어떻게}${c[0]}`);
263
+
264
+ const I = d.인기;
265
+ console.log(`\n${c.b}인기${c[0]} ${c.cy}스타 ${I.스타.toLocaleString()}${c[0]} ${c.d}(${I.확인일} 기준 · ${I.생성일} 생성)${c[0]}`);
266
+ console.log(` ${c.d}${I.주의}${c[0]}`);
267
+
268
+ console.log(`\n${c.b}이 스킬이 하지 않는 말${c[0]}`);
269
+ for (const x of d.안하는말) console.log(` ${c.r}✗${c[0]} ${x.말} ${c.d}— ${x.이유}${c[0]}`);
270
+ console.log(`\n${c.d}확인일 ${d.확인일} · ${d.확인방법}${c[0]}\n`);
271
+ }
272
+
273
+ /* ── verify (네트워크) ──────────────────────────────────────────── */
274
+ async function 확인명령() {
275
+ const d = 원장고르기();
276
+ const r = 원장검사(d);
277
+ if (!r.됨) { console.error(`${c.r}✗ 원장이 이상합니다${c[0]} — ${r.이유}`); process.exit(1); }
278
+ 머리말();
279
+ console.log(`${c.d}주소가 지금 응답하는지 봅니다. 응답한다고 안정판이라는 뜻은 아닙니다 —${c[0]}`);
280
+ console.log(`${c.d}그건 성숙도 이야기이고, HTTP 로는 알 수 없습니다.${c[0]}\n`);
281
+
282
+ const 볼곳 = [
283
+ ["저장소", d.제품.저장소],
284
+ ["홈페이지", d.제품.홈페이지],
285
+ ["npm(진짜)", `https://registry.npmjs.org/${d["★실행"].npm패키지.replace("/", "%2F")}`],
286
+ ];
287
+ let 끊김 = 0;
288
+ for (const [이름, 주소] of 볼곳) {
289
+ let 표, 말;
290
+ try {
291
+ const res = await fetch(주소, { redirect: "follow", signal: AbortSignal.timeout(20000) });
292
+ if (res.ok) { 표 = `${c.g}✓${c[0]}`; 말 = String(res.status); }
293
+ else { 표 = `${c.y}!${c[0]}`; 말 = String(res.status); 끊김++; }
294
+ } catch (e) { 표 = `${c.r}✗${c[0]}`; 말 = String(e.message).slice(0, 40); 끊김++; }
295
+ console.log(` ${표} ${이름.padEnd(12)} ${c.d}${말}${c[0]}`);
296
+ }
297
+ console.log(끊김
298
+ ? `\n${c.y}${끊김}건이 응답하지 않습니다 — 원장을 다시 봐야 합니다.${c[0]}\n`
299
+ : `\n${c.g}전부 응답합니다.${c[0]} ${c.d}(안정판이라는 증명은 아닙니다 — ${d["★성숙도"].버전} 입니다)${c[0]}\n`);
300
+ process.exit(끊김 ? 1 : 0);
301
+ }
302
+
303
+ /* ── --check ────────────────────────────────────────────────────── */
304
+ function 확인만() {
305
+ 머리말();
306
+ const r = 진짜도나();
307
+ if (r.됨) {
308
+ console.log(`${c.g}✓ 설치되어 있고 원장이 규칙을 지킵니다${c[0]} ${c.d}(${대상})${c[0]}`);
309
+ console.log(`${c.d} 도구 ${r.도구}종 · 패키지 ${r.패키지}개 · 대상 판 ${r.버전} · 확인일 ${r.확인일}${c[0]}\n`);
310
+ } else console.log(`${c.y}· 아직 없습니다${c[0]} ${c.d}— ${r.이유}${c[0]}\n`);
311
+ process.exit(r.됨 ? 0 : 1);
312
+ }
313
+
314
+ /* ── 자기시험 (네트워크 안 씀) ─────────────────────────────────── */
315
+ function 자기시험() {
316
+ const 결과 = [];
317
+ const T = (n, ok) => 결과.push([n, !!ok]);
318
+ const 원 = 원장읽기(원본);
319
+ const 복 = () => JSON.parse(JSON.stringify(원));
320
+ const 스킬본문 = () => readFileSync(join(원본, "SKILL.md"), "utf8");
321
+
322
+ T("패키지 안에 skill/dsh.json 이 있다", !!원);
323
+ T("★정본 원장이 검사를 통과한다", 원장검사(원).됨);
324
+ T("★맞는 실행 명령에 스코프가 붙어 있다", String(원?.["★실행"]?.맞는명령).includes(스코프));
325
+ T("★`npx dsh` 가 틀린 명령으로 적혀 있다",
326
+ (원?.["★실행"]?.["★틀린명령"] || []).some((x) => x.명령 === "npx dsh"));
327
+ T("★`npx deepseek-harness` 도 틀린 명령으로 적혀 있다",
328
+ (원?.["★실행"]?.["★틀린명령"] || []).some((x) => x.명령 === "npx deepseek-harness"));
329
+ T("★프리뷰 경고 원문이 그대로 남아 있다", 경고핵심.test(원?.["★성숙도"]?.경고원문 || ""));
330
+ T("★모델이 딸려오지 않는다고 적혀 있다", 원?.["★모델"]?.딸려오나 === false);
331
+ T("★상표 칸에 등록상표와 DSH 약칭이 있다",
332
+ /등록상표/.test(원?.["★상표"]?.핵심 || "") && /DSH/.test(원?.["★상표"]?.핵심 || ""));
333
+ T("★도구 수와 출처가 숫자·문자로 있다",
334
+ Number.isFinite(원?.갖춘것?.도구수) && !!원?.갖춘것?.출처);
335
+ T("★스타 수에 확인일과 주의가 붙어 있다", !!원?.인기?.확인일 && !!원?.인기?.주의);
336
+ T("★상호운용에 출처와 주의가 붙어 있다",
337
+ !!원?.["★상호운용"]?.출처 && !!원?.["★상호운용"]?.주의);
338
+ T("★「안하는말」 에 이유가 전부 적혀 있다", (원?.안하는말 || []).every((x) => x.말 && x.이유));
339
+ T("★SKILL.md 가 옆에 있고 name 이 맞다", (() => {
340
+ const f = join(원본, "SKILL.md");
341
+ return existsSync(f) && new RegExp("^---[\\s\\S]*?\\bname:\\s*" + 스킬이름 + "\\b", "m").test(readFileSync(f, "utf8"));
342
+ })());
343
+ T("★SKILL.md 가 dsh.json 을 읽으라고 못 박는다", 스킬본문().includes("dsh.json"));
344
+ T("★SKILL.md 가 스코프 붙은 명령을 적어 뒀다", 스킬본문().includes(스코프));
345
+ T("★SKILL.md 가 `npx dsh` 를 틀린 것으로 적어 뒀다", /npx dsh\b/.test(스킬본문()) && /남의/.test(스킬본문()));
346
+ T("★package.json files 에 skill/ 과 안내스킬.md 가 들어 있다", (() => {
347
+ const pj = join(여기, "package.json");
348
+ if (!existsSync(pj)) return false;
349
+ const f = JSON.parse(readFileSync(pj, "utf8")).files || [];
350
+ return f.includes("skill/") && f.includes("안내스킬.md");
351
+ })());
352
+ T("★패키지 이름에 상표 전체 이름을 안 썼다(가이드 준수)", (() => {
353
+ const pj = join(여기, "package.json");
354
+ if (!existsSync(pj)) return false;
355
+ return !/deepseek-harness/i.test(JSON.parse(readFileSync(pj, "utf8")).name || "");
356
+ })());
357
+ T("★안내스킬.md 가 스킬팩을 말한다", (() => {
358
+ const f = join(여기, "안내스킬.md");
359
+ return existsSync(f) && /스킬팩|rebornlabs\.kr\/skillpack/.test(readFileSync(f, "utf8"));
360
+ })());
361
+ T("★안내스킬.md 가 클로드킷을 말하지 않는다(계단 유지)", (() => {
362
+ const f = join(여기, "안내스킬.md");
363
+ if (!existsSync(f)) return false;
364
+ return !/클로드킷|자비스\s*풀스택|rebornlabs\.kr\/(claudekit|kit|jarvis)/.test(readFileSync(f, "utf8"));
365
+ })());
366
+
367
+ /* ── ★역시험 — 일부러 깨뜨려 본다. 여기서 통과하면 검사기가 고장난 것이다. ── */
368
+ T("★★실행 명령에서 스코프를 빼면 잡는다", (() => {
369
+ const d = 복(); d["★실행"].맞는명령 = "npx dsh web"; return !원장검사(d).됨;
370
+ })());
371
+ T("★★`npx dsh` 경고를 지우면 잡는다", (() => {
372
+ const d = 복();
373
+ d["★실행"]["★틀린명령"] = d["★실행"]["★틀린명령"].filter((x) => x.명령 !== "npx dsh");
374
+ return !원장검사(d).됨;
375
+ })());
376
+ T("★★틀린 명령에서 「무슨일」을 지우면 잡는다", (() => {
377
+ const d = 복(); delete d["★실행"]["★틀린명령"][0].무슨일; return !원장검사(d).됨;
378
+ })());
379
+ T("★★틀린 명령을 통째로 비우면 잡는다", (() => {
380
+ const d = 복(); d["★실행"]["★틀린명령"] = []; return !원장검사(d).됨;
381
+ })());
382
+ T("★★npm 패키지를 스코프 없이 적으면 잡는다", (() => {
383
+ const d = 복(); d["★실행"].npm패키지 = "dsh"; return !원장검사(d).됨;
384
+ })());
385
+ T("★★성숙도 칸을 통째로 지우면 잡는다", (() => {
386
+ const d = 복(); delete d["★성숙도"]; return !원장검사(d).됨;
387
+ })());
388
+ T("★★프리뷰 경고 원문을 번역만 남기면 잡는다", (() => {
389
+ const d = 복(); d["★성숙도"].경고원문 = "호환성이 깨질 수 있습니다"; return !원장검사(d).됨;
390
+ })());
391
+ T("★★단계를 「정식판」으로 바꾸면 잡는다", (() => {
392
+ const d = 복(); d["★성숙도"].단계 = "stable"; return !원장검사(d).됨;
393
+ })());
394
+ T("★★「그래서어떻게」를 지우면 잡는다", (() => {
395
+ const d = 복(); delete d["★성숙도"].그래서어떻게; return !원장검사(d).됨;
396
+ })());
397
+ T("★★모델이 딸려온다고 바꾸면 잡는다", (() => {
398
+ const d = 복(); d["★모델"].딸려오나 = true; return !원장검사(d).됨;
399
+ })());
400
+ T("★★모델 설명에서 MIT 근거를 빼면 잡는다", (() => {
401
+ const d = 복(); d["★모델"].설명 = "모델은 따로 넣습니다"; return !원장검사(d).됨;
402
+ })());
403
+ T("★★상표 칸을 지우면 잡는다", (() => {
404
+ const d = 복(); delete d["★상표"]; return !원장검사(d).됨;
405
+ })());
406
+ T("★★상표에서 DSH 대안을 빼면 잡는다", (() => {
407
+ const d = 복(); d["★상표"].핵심 = "DeepSeek Harness 는 등록상표입니다. 쓰지 마세요.";
408
+ return !원장검사(d).됨;
409
+ })());
410
+ T("★★상표에서 「허용되는 것」을 지우면 잡는다", (() => {
411
+ const d = 복(); delete d["★상표"].설명은허용; return !원장검사(d).됨;
412
+ })());
413
+ T("★★도구 수의 출처를 지우면 잡는다", (() => {
414
+ const d = 복(); delete d.갖춘것.출처; return !원장검사(d).됨;
415
+ })());
416
+ T("★★도구 수를 숫자 아닌 말로 바꾸면 잡는다", (() => {
417
+ const d = 복(); d.갖춘것.도구수 = "엄청 많음"; return !원장검사(d).됨;
418
+ })());
419
+ T("★★도구 묶음에서 실제 도구 이름을 지우면 잡는다", (() => {
420
+ const d = 복(); delete d.갖춘것.묶음[0].예; return !원장검사(d).됨;
421
+ })());
422
+ T("★★상호운용에서 「주의」를 지우면 잡는다", (() => {
423
+ const d = 복(); delete d["★상호운용"].주의; return !원장검사(d).됨;
424
+ })());
425
+ T("★★상호운용에서 출처를 지우면 잡는다", (() => {
426
+ const d = 복(); delete d["★상호운용"].출처; return !원장검사(d).됨;
427
+ })());
428
+ T("★★스타 수의 확인일을 지우면 잡는다", (() => {
429
+ const d = 복(); delete d.인기.확인일; return !원장검사(d).됨;
430
+ })());
431
+ T("★★스타 수의 「주의」를 지우면 잡는다", (() => {
432
+ const d = 복(); delete d.인기.주의; return !원장검사(d).됨;
433
+ })());
434
+ T("★★설명에 「완전 무료」를 넣으면 잡는다", (() => {
435
+ const d = 복(); d.제품.무엇 = "완전 무료로 쓰는 에이전트 하네스"; return !원장검사(d).됨;
436
+ })());
437
+ T("★★설명에 「무제한」을 넣으면 잡는다", (() => {
438
+ const d = 복(); d["★모델"].그래서어떻게 = "무제한으로 쓸 수 있습니다"; return !원장검사(d).됨;
439
+ })());
440
+ T("★★「안하는말」 을 비우면 잡는다", (() => { const d = 복(); d.안하는말 = []; return !원장검사(d).됨; })());
441
+ T("★★「안하는말」 에서 npx dsh 항목을 빼면 잡는다", (() => {
442
+ const d = 복(); d.안하는말 = d.안하는말.filter((x) => !/npx dsh/.test(x.말));
443
+ while (d.안하는말.length < 3) d.안하는말.push({ 말: "채움", 이유: "채움" });
444
+ return !원장검사(d).됨;
445
+ })());
446
+ T("★★확인일을 지우면 잡는다", (() => { const d = 복(); delete d.확인일; return !원장검사(d).됨; })());
447
+ T("★★확인방법을 지우면 잡는다", (() => { const d = 복(); delete d.확인방법; return !원장검사(d).됨; })());
448
+ T("★없는 원장은 통과하지 않는다", !원장검사(null).됨);
449
+
450
+ for (const [n, ok] of 결과) console.log(` ${ok ? c.g + "✓" : c.r + "✗"}${c[0]} ${n}`);
451
+ const 실패 = 결과.filter(([, ok]) => !ok).length;
452
+ console.log(실패 ? `\n${c.r}✗ 자기시험 ${실패}건 실패${c[0]}` : `\n${c.g}✓ 자기시험 ${결과.length}건 통과${c[0]}`);
453
+ return 실패 ? 1 : 0;
454
+ }
455
+
456
+ /* ── 설치 ───────────────────────────────────────────────────────── */
457
+ function 설치() {
458
+ 머리말();
459
+ if (!existsSync(원본)) {
460
+ console.error(`${c.r}✗ 패키지 안에 skill/ 폴더가 없습니다${c[0]} — 배포가 잘못된 것입니다.`);
461
+ process.exit(2);
462
+ }
463
+ let 쓴수 = 0;
464
+ for (const src of 파일목록(원본)) {
465
+ const 상대 = relative(원본, src);
466
+ const dst = join(대상, 상대);
467
+ mkdirSync(dirname(dst), { recursive: true });
468
+ writeFileSync(dst, readFileSync(src));
469
+ 쓴수++;
470
+ console.log(` ${c.d}·${c[0]} ${상대}`);
471
+ }
472
+ console.log(`\n${c.d}${쓴수}개 파일 → ${대상}${c[0]}`);
473
+ console.log(`${c.d}원장이 조건을 숨기지 않았는지 다시 확인하는 중…${c[0]}`);
474
+
475
+ const r = 진짜도나();
476
+ if (!r.됨) {
477
+ console.error(`\n${c.r}✗ 깔았는데 확인에서 걸렸습니다${c[0]} — ${r.이유}\n`);
478
+ process.exit(1);
479
+ }
480
+
481
+ const d = 원장읽기(대상);
482
+ const E = d["★실행"], M = d["★성숙도"];
483
+ console.log(`\n${c.g}${c.b}✓ 설치 완료${c[0]} ${c.d}— 도구 ${r.도구}종 · 패키지 ${r.패키지}개 (확인일 ${r.확인일})${c[0]}`);
484
+
485
+ console.log(`\n${c.g}${c.b}★ 실행은 이 한 줄입니다${c[0]}`);
486
+ console.log(` ${c.cy}${c.b}${E.맞는명령}${c[0]}`);
487
+ console.log(` ${c.d}${E.무엇이뜨나}${c[0]}`);
488
+ console.log(`\n${c.r}${c.b}★ 이렇게 치면 딴 게 깔립니다${c[0]}`);
489
+ for (const x of E["★틀린명령"]) console.log(` ${c.r}${x.명령.padEnd(24)}${c[0]}${c.d}${x.무슨일.slice(0, 62)}…${c[0]}`);
490
+ console.log(`\n${c.y}${c.b}★ 그리고 지금 판은 ${M.버전} · ${M.단계} 입니다.${c[0]}`);
491
+ console.log(` ${c.d}${M.그래서어떻게}${c[0]}`);
492
+
493
+ console.log(`\n${c.y}★ 클로드 코드를 껐다 켜야 스킬이 잡힙니다.${c[0]}`);
494
+ console.log(`\n${c.b}쓰는 법${c[0]}`);
495
+ console.log(` 클로드에게 그냥 말하세요 — ${c.cy}"DSH 켜는 법 알려줘"${c[0]}`);
496
+ console.log(` 터미널에서 실행 명령만: ${c.cy}npx reborn-dshkit run${c[0]}`);
497
+ console.log(` 원장 전체: ${c.cy}npx reborn-dshkit facts${c[0]}`);
498
+ console.log(` 주소가 아직 사는지: ${c.cy}npx reborn-dshkit verify${c[0]}`);
499
+ console.log(` ${c.d}버전·스타 수는 밖에서 바뀝니다. 확인일을 같이 적어 둔 이유입니다.${c[0]}\n`);
500
+ console.log(`${c.d}설명 · https://rebornlabs.kr/dsh?utm_source=installer&utm_medium=cli&utm_campaign=${퍼널}${c[0]}`);
501
+
502
+ /* ★끝난 뒤 딱 한 칸. 못 깔았으면 여기까지 오지 않는다(위에서 exit 1).
503
+ ★가리키는 곳은 스킬팩(8,900원)이다. 클로드킷은 여기서 말하지 않는다 — 계단이 무너진다. */
504
+ const t = TAIL[퍼널];
505
+ console.log("");
506
+ for (const 줄 of t.이음) console.log(`${c.d}${줄.replace(/\*\*(.+?)\*\*/g, `${c[0]}${c.b}$1${c[0]}${c.d}`)}${c[0]}`);
507
+ console.log(`\n${c.b}${t.제목}${c[0]}${c.d} — ${t.한줄}${c[0]}`);
508
+ console.log(`${c.cy}${링크(퍼널)}${c[0]}`);
509
+ console.log(`${c.d}${마지막}${c[0]}\n`);
510
+ }
511
+
512
+ /* ══ 실행 ═══════════════════════════════════════════════════════ */
513
+ const 인자 = process.argv.slice(2);
514
+ const 첫 = 인자[0];
515
+ if (인자.includes("--self-test")) process.exit(자기시험());
516
+ else if (첫 === "run" || 첫 === "실행") 실행보기();
517
+ else if (첫 === "facts" || 첫 === "원장") 원장보기();
518
+ else if (첫 === "verify" || 첫 === "확인") await 확인명령();
519
+ else if (인자.includes("--check")) 확인만();
520
+ else 설치();
package/package.json ADDED
@@ -0,0 +1,44 @@
1
+ {
2
+ "name": "reborn-dshkit",
3
+ "version": "1.0.0",
4
+ "description": "DSH(DeepSeek Harness) — 딥시크가 낸 오픈소스 에이전트 하네스를 지금 어떻게 실행하고 무엇을 조심할지 알려 주는 클로드 코드 스킬. 사람들이 가장 많이 틀리는 실행 명령, 개발자 프리뷰라는 성숙도, 모델이 딸려오지 않는다는 사실, 상표 사용 규칙까지 먼저 말한다.",
5
+ "keywords": [
6
+ "claude",
7
+ "claude-code",
8
+ "agent-skills",
9
+ "skill",
10
+ "dsh",
11
+ "agent-harness",
12
+ "ai-agent",
13
+ "open-source-agent",
14
+ "plugin-architecture",
15
+ "cordis"
16
+ ],
17
+ "homepage": "https://rebornlabs.kr/dsh?utm_source=installer&utm_medium=cli&utm_campaign=dsh",
18
+ "bugs": {
19
+ "url": "https://mobility.rebornlabs.kr/cs"
20
+ },
21
+ "license": "MIT",
22
+ "author": "REBORN LABS",
23
+ "type": "module",
24
+ "bin": {
25
+ "reborn-dshkit": "cli.mjs"
26
+ },
27
+ "files": [
28
+ "cli.mjs",
29
+ "tail.mjs",
30
+ "skill/",
31
+ "README.md",
32
+ "안내스킬.md"
33
+ ],
34
+ "engines": {
35
+ "node": ">=18"
36
+ },
37
+ "scripts": {
38
+ "test": "node cli.mjs --self-test",
39
+ "check": "node cli.mjs --check"
40
+ },
41
+ "publishConfig": {
42
+ "access": "public"
43
+ }
44
+ }
package/skill/SKILL.md ADDED
@@ -0,0 +1,101 @@
1
+ ---
2
+ name: dsh-guide
3
+ description: >
4
+ DSH(DeepSeek Harness) — 딥시크가 낸 오픈소스 에이전트 하네스를 **지금 어떻게 실행하고
5
+ 무엇을 조심할지** 알려 준다. 사람들이 가장 많이 틀리는 실행 명령(`npx dsh` 는 남의
6
+ 패키지다), 개발자 프리뷰라는 성숙도, 모델이 딸려오지 않는다는 사실, 상표 사용 규칙까지
7
+ 먼저 말한다.
8
+ "DSH", "딥시크 하네스", "DeepSeek Harness", "딥시크가 낸 클로드 코드",
9
+ "클로드 코드 오픈소스 대체", "무료 에이전트 하네스", "에이전트 직접 만들기",
10
+ "플러그인으로 에이전트 조립", "deepseek harness 설치", "dsh 실행 안 됨"
11
+ 같은 이야기가 나올 때 쓴다.
12
+ ---
13
+
14
+ # DSH(DeepSeek Harness) — 켜기 전에 볼 것
15
+
16
+ > 이 스킬은 **리본랩스(REBORN LABS)** 의 무료 설치기가 넣은 것입니다.
17
+ > 필요 없으시면 이 폴더를 지우세요.
18
+
19
+ 사실 정본은 옆의 **`dsh.json`** 입니다. 답할 때는 **반드시 그 파일을 읽고** 말하세요.
20
+ 스타 수·버전·도구 수는 밖에서 바뀝니다. 원장에 **확인일**과 함께 적어 두었습니다.
21
+
22
+ ---
23
+
24
+ ## 이 스킬이 지키는 것 다섯 가지
25
+
26
+ ### ① ★실행 명령을 **정확히** 말한다
27
+
28
+ 맞는 명령은 하나입니다.
29
+
30
+ ```
31
+ npx @deepseek-ai/dsh web
32
+ ```
33
+
34
+ `http://127.0.0.1:3080` 에 웹 UI 가 뜹니다. `--no-open` 을 주면 브라우저를 안 엽니다.
35
+
36
+ **절대 이렇게 안내하지 마세요.**
37
+
38
+ | 틀린 명령 | 실제로 깔리는 것 |
39
+ |---|---|
40
+ | `npx dsh` | **2016년에 올라온 남의 JavaScript 셸.** 딥시크와 무관합니다 |
41
+ | `npx deepseek-harness` | **예약된 빈 껍데기.** "The real package will be published here when development completes" |
42
+
43
+ 약칭이 `dsh` 라서, 그리고 제품 이름이 `deepseek-harness` 라서 둘 다 자연스럽게 나옵니다.
44
+ 그래서 **스코프까지 붙여** 말해야 합니다 — `@deepseek-ai/dsh`.
45
+
46
+ ### ② ★「개발자 프리뷰」를 **좋은 소식보다 먼저** 말한다
47
+
48
+ README 가 대문자로 이렇게 적어 두었습니다.
49
+
50
+ > **THERE WILL BE COMPATIBILITY-BREAKING CHANGES.**
51
+
52
+ 현재 `0.1.1-rc.2` 입니다. 저장소가 만들어진 지 얼마 안 됐습니다.
53
+ **구경하고 실험하기엔 지금이 좋고, 업무를 옮겨 태우기엔 이릅니다.**
54
+
55
+ 「스타 몇십만」을 먼저 말하고 이 줄을 나중에 말하면, 사람은 이미 파이프라인을
56
+ 옮긴 뒤에 읽습니다. **순서를 뒤집지 마세요.**
57
+
58
+ ### ③ ★모델은 딸려오지 않는다
59
+
60
+ 하네스는 **모델을 굴리는 껍데기**입니다. 모델이 아닙니다.
61
+ 자격증명은 환경변수나 로컬 파일로 각자 넣습니다(`credentials-local`).
62
+
63
+ 그래서 「완전 무료」는 **하네스가 MIT 라는 뜻**이지 모델이 공짜라는 뜻이 아닙니다.
64
+ 로컬 모델을 붙이면 실제로 돈이 안 들고, 상용 API 를 붙이면 그 요금이 그대로 나갑니다.
65
+ **「무료」라는 말을 쓸 때 이 조건을 같이 붙이세요.**
66
+
67
+ ### ④ 무엇이 좋은지는 **셀 수 있는 것**으로 말한다
68
+
69
+ 원장의 `갖춘것` 에 도구 수와 묶음이 출처(`docs/tool-catalog.md`)와 함께 있습니다.
70
+ 「엄청 많다」가 아니라 **몇 개인지** 말하세요.
71
+
72
+ 특히 물어보면 이건 사실입니다 — **다른 에이전트를 자식으로 띄웁니다.**
73
+ `subagent-claude-code` 는 공식 Claude Agent SDK 로 진짜 클로드 코드 자식을,
74
+ `subagent-codex` 는 진짜 Codex 자식을 띄웁니다.
75
+ 다만 **각 자식의 설치와 요금은 그대로 따라옵니다** — 이 단서를 빼지 마세요.
76
+
77
+ ### ⑤ 상표 규칙을 알려 준다
78
+
79
+ 사용자가 DSH 로 뭔가 만들어 내놓겠다고 하면 **먼저** 말해 주세요.
80
+
81
+ - "DeepSeek Harness" 는 **등록상표**입니다. 프로젝트 이름에 전체 이름을 쓰지 마세요.
82
+ - 공식 권장은 약칭 **`DSH`** 입니다. 저장소에 `dsh-plugin` 토픽을 다세요.
83
+ - 설명문에서 "built on DeepSeek Harness" 라고 **관계를 사실대로 적는 건 허용**됩니다.
84
+ - 공식 브랜드 자료를 **공인·제휴로 오해될 방식**으로 홍보에 쓰지 마세요.
85
+
86
+ ---
87
+
88
+ ## 답하는 순서
89
+
90
+ 1. `dsh.json` 을 읽는다
91
+ 2. 사용자가 **뭘 하려는지** 먼저 가른다 — 구경인가, 붙여 쓸 것인가, 만들어 낼 것인가
92
+ 3. 구경이면 → `npx @deepseek-ai/dsh web` 한 줄. 프리뷰라는 것만 붙인다
93
+ 4. 붙여 쓸 것이면 → **③ 모델 이야기를 먼저** 한다. 그다음 프리뷰 경고
94
+ 5. 만들어 낼 것이면 → **⑤ 상표**를 먼저 한다
95
+ 6. 숫자를 말할 때는 **확인일을 같이** 말한다
96
+
97
+ ## 하지 않는 말
98
+
99
+ 원장의 `안하는말` 에 이유와 함께 있습니다. 그대로 지키세요.
100
+ 「클로드 코드를 대체한다」 · 「완전 무료」 · 「깔면 바로 된다」 ·
101
+ 「스타가 많으니 검증됐다」 · 「`npx dsh` 로 실행」 — 다섯 개 전부 하지 않습니다.
package/skill/dsh.json ADDED
@@ -0,0 +1,132 @@
1
+ {
2
+ "확인일": "2026-08-27",
3
+ "확인방법": "GitHub REST API(저장소·도구 카탈로그) + npm registry 를 직접 조회했습니다. 릴스나 정리글을 옮긴 것이 아닙니다.",
4
+ "제품": {
5
+ "정식이름": "DeepSeek Harness",
6
+ "약칭": "DSH",
7
+ "만든곳": "DeepSeek AI",
8
+ "저장소": "https://github.com/deepseek-ai/deepseek-harness",
9
+ "홈페이지": "https://deepseek.com/harness",
10
+ "라이선스": "MIT",
11
+ "무엇": "에이전트 하네스(agent harness)입니다. 모델이 아니라 모델을 굴리는 껍데기이고, 「모든 것이 플러그인」이라는 구조로 짜여 있습니다. Cordis 라는 조합 프레임워크 위에 올라가 있습니다.",
12
+ "무엇이아닌가": "언어 모델이 아닙니다. 클로드 코드를 대체하는 완성품도 아직 아닙니다. 모델·도구·메모리·에이전트 루프를 각자 갈아 끼워 자기 에이전트를 짜는 뼈대입니다."
13
+ },
14
+ "★실행": {
15
+ "맞는명령": "npx @deepseek-ai/dsh web",
16
+ "무엇이뜨나": "http://127.0.0.1:3080 에 웹 UI 가 뜨고 기본 브라우저가 열립니다. --no-open 을 주면 서버만 띄웁니다.",
17
+ "필요한것": "Node.js",
18
+ "소스에서": "git clone → pnpm install → pnpm run build → pnpm dsh web",
19
+ "★틀린명령": [
20
+ {
21
+ "명령": "npx dsh",
22
+ "무슨일": "npm 의 `dsh` 는 **2016년에 올라온 남의 JavaScript 셸**입니다(\"DSH - pronounced dash - is a full featured shell written in JavaScript\"). DeepSeek 과 아무 관계가 없고, 십 년 동안 그 자리에 있었습니다.",
23
+ "왜생기나": "약칭이 `dsh` 라서 사람들이 그대로 칩니다.",
24
+ "최초공개": "2016-02-01"
25
+ },
26
+ {
27
+ "명령": "npx deepseek-harness",
28
+ "무슨일": "npm 의 `deepseek-harness` 는 **예약된 빈 껍데기**입니다. 설명이 그대로 이렇습니다 — \"Reserved package name for the DeepSeek Harness TUI project (dsh-tui). The real package will be published here when development completes.\"",
29
+ "왜생기나": "제품 이름을 그대로 치면 나오기 때문입니다."
30
+ }
31
+ ],
32
+ "npm패키지": "@deepseek-ai/dsh",
33
+ "npm최신": "0.1.1-rc.2",
34
+ "npm공개일": "2026-08-21"
35
+ },
36
+ "★성숙도": {
37
+ "단계": "developer preview",
38
+ "버전": "0.1.1-rc.2",
39
+ "경고원문": "DeepSeek Harness is currently in _developer preview_ and is iterating rapidly. **THERE WILL BE COMPATIBILITY-BREAKING CHANGES.**",
40
+ "그래서어떻게": "지금 쓰는 건 구경과 실험까지입니다. 회사 업무 파이프라인을 여기에 얹으면 다음 판에서 깨질 수 있다고 README 가 대문자로 미리 말하고 있습니다. 놀아 보기엔 지금이 좋고, 옮겨 태우기엔 이릅니다.",
41
+ "저장소생성일": "2026-08-13"
42
+ },
43
+ "★모델": {
44
+ "딸려오나": false,
45
+ "설명": "하네스는 모델을 싣지 않습니다. 자격증명은 환경변수나 로컬 파일 제공자(credentials-local)로 각자 넣습니다. 즉 「무료」는 **하네스가 MIT 라는 뜻**이지, 모델을 공짜로 쓴다는 뜻이 아닙니다.",
46
+ "그래서어떻게": "쓰려면 돌릴 모델을 따로 정해야 합니다. 로컬 모델을 붙이면 실제로 돈이 안 들고, 상용 API 를 붙이면 그 API 요금이 그대로 나갑니다."
47
+ },
48
+ "갖춘것": {
49
+ "도구수": 58,
50
+ "패키지수": 26,
51
+ "출처": "docs/tool-catalog.md",
52
+ "묶음": [
53
+ {
54
+ "이름": "셸·파일",
55
+ "예": "bash · pwsh · read · write · edit · glob · grep · str_replace_editor"
56
+ },
57
+ {
58
+ "이름": "에이전트 위임",
59
+ "예": "subagent · spawn_teammate · list_agents · wait_agent · interrupt_agent · send_message"
60
+ },
61
+ {
62
+ "이름": "팀 작업",
63
+ "예": "team_task_create · team_task_update · team_task_list · team_task_get"
64
+ },
65
+ {
66
+ "이름": "터미널",
67
+ "예": "terminal_open · terminal_send · terminal_read · terminal_close · terminal_signal"
68
+ },
69
+ {
70
+ "이름": "세션 되짚기",
71
+ "예": "session_search · session_trace · session_event_search · session_event_trace"
72
+ },
73
+ {
74
+ "이름": "예약·작업",
75
+ "예": "schedule_create · schedule_list · job_list · job_output · job_kill"
76
+ },
77
+ {
78
+ "이름": "웹",
79
+ "예": "web_search · web_fetch"
80
+ },
81
+ {
82
+ "이름": "구조 조작",
83
+ "예": "cordis_define · cordis_run · cordis_inspect_query · cordis_undefine"
84
+ },
85
+ {
86
+ "이름": "계획·목표",
87
+ "예": "exit_plan_mode · create_goal · update_goal · todo_write · workflow · skill"
88
+ }
89
+ ]
90
+ },
91
+ "★상호운용": {
92
+ "사실": true,
93
+ "설명": "다른 에이전트를 자식으로 띄울 수 있습니다. `subagent-claude-code` 는 **공식 Claude Agent SDK 를 통해 진짜 클로드 코드 자식**을 띄우고, `subagent-codex` 는 진짜 Codex app-server 자식을 띄웁니다. `subagent-acp` 는 ACP 로 프로세스 밖 자식을 띄웁니다.",
94
+ "출처": "packages/subagent/README.md",
95
+ "주의": "각 자식은 그 도구의 설치와 요금을 그대로 따릅니다. 클로드 코드 자식을 띄우면 클로드 코드 구독이 필요합니다."
96
+ },
97
+ "★상표": {
98
+ "출처": "BRAND_GUIDELINES.md",
99
+ "핵심": "\"DeepSeek Harness\" 는 DeepSeek 의 등록상표입니다. 프로젝트 이름에 전체 이름을 직접 쓰지 말고 약칭 **DSH** 를 쓰라고 권합니다.",
100
+ "설명은허용": "프로젝트 설명문에서 \"built on DeepSeek Harness\" · \"compatible with DeepSeek Harness\" 처럼 관계를 사실대로 적는 것은 명시적으로 허용합니다.",
101
+ "금지": "공식 브랜드 자료를 공인·제휴·승인으로 오해될 방식으로 홍보에 쓰지 마십시오.",
102
+ "그래서어떻게": "플러그인이나 파생 프로젝트를 내신다면 이름에 DSH 를 쓰고, 저장소에 `dsh-plugin` 토픽을 다세요. 로고를 표지 전면에 세우는 건 피하십시오."
103
+ },
104
+ "인기": {
105
+ "스타": 198779,
106
+ "확인일": "2026-08-27",
107
+ "생성일": "2026-08-13",
108
+ "주의": "스타는 관심의 크기이지 완성도가 아닙니다. 생성 14일 만의 숫자이고, 같은 날 developer preview 라고 적혀 있습니다. 둘을 같이 보세요."
109
+ },
110
+ "안하는말": [
111
+ {
112
+ "말": "「클로드 코드를 대체한다」",
113
+ "이유": "developer preview 이고 호환성 파괴를 예고한 상태입니다. 대체는 지금 할 수 있는 말이 아닙니다."
114
+ },
115
+ {
116
+ "말": "「완전 무료」",
117
+ "이유": "하네스가 MIT 인 것이지 모델이 공짜인 게 아닙니다. 붙이는 모델의 요금은 그대로 나갑니다."
118
+ },
119
+ {
120
+ "말": "「설치하면 바로 된다」",
121
+ "이유": "돌릴 모델과 자격증명을 따로 정해야 합니다."
122
+ },
123
+ {
124
+ "말": "「스타 18만이니 검증됐다」",
125
+ "이유": "스타는 관심의 크기입니다. 14일 된 프리뷰입니다."
126
+ },
127
+ {
128
+ "말": "「npx dsh 로 실행」",
129
+ "이유": "그건 2016년에 올라온 남의 패키지입니다. 이 킷이 존재하는 이유입니다."
130
+ }
131
+ ]
132
+ }
package/tail.mjs ADDED
@@ -0,0 +1,28 @@
1
+ /**
2
+ * 「끝난 뒤 한 칸」 문안 — **정본은 `skillpack/tail_copy.mjs` 다.**
3
+ *
4
+ * 여기 사본이 있는 이유는 하나뿐이다: npm 패키지는 폴더 밖을 import 할 수 없다.
5
+ * 문구를 고칠 일이 생기면 **정본을 먼저 고치고** 이 파일에 옮긴다. 반대로 하지 마라.
6
+ *
7
+ * 규율(정본에서 그대로 온다):
8
+ * ① 끝난 뒤 딱 한 칸 · ② 못 깔았으면 안 띄운다 · ③ 파일을 남기지 않는다
9
+ * ④ 고지 블록 금지 — 대신 마지막 줄에서 목록과 직접 까는 법을 알려 준다
10
+ * ⑤ 첫 줄은 **방금 한 일에서 이어져야** 한다.
11
+ */
12
+ export const 랜딩 = "https://rebornlabs.kr/skillpack";
13
+
14
+ export const 링크 = (퍼널) =>
15
+ `${랜딩}?utm_source=installer&utm_medium=cli&utm_campaign=${퍼널}`;
16
+
17
+ export const TAIL = {
18
+ dsh: {
19
+ 이음: [
20
+ "방금 드린 건 **켜기 전에 볼 눈**입니다 — 맞는 실행 명령 하나와, 「무료」가 어디까지인지.",
21
+ "하네스를 켜고 나면 다음 질문은 대개 **\"그래서 이 절차를 매번 누가 기억하지\"** 입니다.",
22
+ ],
23
+ 제목: "리본랩스 스킬팩",
24
+ 한줄: "같은 방식으로 골라 담은 48종을 한 줄로. 8,900원",
25
+ },
26
+ };
27
+
28
+ export const 마지막 = "목록과 직접 까는 명령까지 그 페이지에 그대로 적어 두었습니다. 안 사셔도 됩니다.";
@@ -0,0 +1,95 @@
1
+ ---
2
+ name: reborn-dsh-guide
3
+ description: >
4
+ DSH(DeepSeek Harness) 를 켜기 전에 무엇을 확인해야 하는지, 사람들이 왜 실행 명령을
5
+ 틀리는지, 「무료·오픈소스」가 어디까지를 뜻하는지 알려 준다.
6
+ "DSH", "딥시크 하네스", "DeepSeek Harness", "dsh 실행이 안 된다",
7
+ "딥시크가 클로드 코드 냈다던데", "무료 에이전트 하네스" 이야기가 나올 때 쓴다.
8
+ ---
9
+
10
+ # DSH — 켜기 전에 볼 것 세 가지
11
+
12
+ > 이 파일은 **리본랩스(REBORN LABS)** 의 무료 설치기가 넣은 것입니다.
13
+ > 필요 없으시면 이 폴더를 지우세요. 지워도 `dsh-guide` 스킬은 그대로 동작합니다.
14
+
15
+ ## 방금 깐 것
16
+
17
+ `dsh-guide` — DSH 를 **지금 어떻게 켜고 무엇을 조심할지** 알려 주는 스킬입니다.
18
+ 사실 원장(`dsh.json`)에 실행 명령·성숙도·도구 수를 **확인일과 함께** 적어 두었습니다.
19
+
20
+ ```
21
+ npx reborn-dshkit run 맞는 실행 명령과 틀린 명령을 나란히 본다
22
+ npx reborn-dshkit facts 원장 전체를 터미널에 찍는다
23
+ npx reborn-dshkit verify 저장소와 npm 이 지금 응답하는지 본다
24
+ ```
25
+
26
+ 클로드에게는 그냥 말하면 됩니다 — **"DSH 켜는 법 알려줘"**
27
+
28
+ ## ① ★`npx dsh` 는 딥시크가 아닙니다
29
+
30
+ 이게 제일 많이 틀리는 지점이고, 이 킷이 존재하는 이유입니다.
31
+
32
+ | 치는 명령 | 실제로 깔리는 것 |
33
+ |---|---|
34
+ | `npx dsh` | **2016년에 올라온 남의 JavaScript 셸** — "A shell written in JavaScript" |
35
+ | `npx deepseek-harness` | **예약된 빈 껍데기** — "The real package will be published here when development completes" |
36
+ | ✅ `npx @deepseek-ai/dsh web` | 진짜입니다. `http://127.0.0.1:3080` 에 웹 UI 가 뜹니다 |
37
+
38
+ 약칭이 `dsh` 라서, 제품 이름이 `deepseek-harness` 라서 둘 다 자연스럽게 손이 갑니다.
39
+ **스코프(`@deepseek-ai/`)를 빼면 다른 걸 깝니다.**
40
+
41
+ 소스에서 돌리시려면 — `git clone` → `pnpm install` → `pnpm run build` → `pnpm dsh web`
42
+
43
+ ## ② 「완전 무료」는 하네스 이야기입니다
44
+
45
+ 라이선스는 **MIT** 가 맞습니다. 그런데 **하네스는 모델이 아닙니다.**
46
+
47
+ 모델을 굴리는 껍데기이고, 돌릴 모델과 자격증명은 **각자 넣습니다**(환경변수나 로컬 파일).
48
+ 그러니까 —
49
+
50
+ - 로컬 모델을 붙이면 → 실제로 돈이 안 듭니다
51
+ - 상용 API 를 붙이면 → **그 API 요금이 그대로 나갑니다**
52
+
53
+ 「무료 오픈소스」만 듣고 켜면 다음 화면에서 「어느 모델을 쓸까요」를 만납니다.
54
+
55
+ ## ③ 지금은 **개발자 프리뷰**입니다
56
+
57
+ README 가 대문자로 적어 둔 문장입니다.
58
+
59
+ > **THERE WILL BE COMPATIBILITY-BREAKING CHANGES.**
60
+
61
+ 현재 판은 `0.1.1-rc.2` 입니다. **구경하고 실험하기엔 지금이 좋습니다.**
62
+ 다만 회사 업무 파이프라인을 여기에 옮겨 태우면 다음 판에서 깨질 수 있다고
63
+ 만든 쪽이 미리 말하고 있습니다. 스타 수보다 이 줄을 먼저 보세요.
64
+
65
+ ## 그래서 뭘 할 수 있나
66
+
67
+ 「모든 것이 플러그인」 구조라 **모델·도구·메모리·에이전트 루프를 갈아 끼웁니다.**
68
+ 도구는 원장 기준 **58종 / 26개 플러그인 패키지**(`docs/tool-catalog.md`)입니다.
69
+
70
+ 셸·파일 · 에이전트 위임 · 팀 작업 · 터미널 · 세션 되짚기 · 예약 · 웹 · 계획.
71
+
72
+ 특히 이건 사실입니다 — **다른 에이전트를 자식으로 띄웁니다.**
73
+ `subagent-claude-code` 가 **공식 Claude Agent SDK 로 진짜 클로드 코드 자식**을 띄우고,
74
+ `subagent-codex` 가 진짜 Codex 자식을 띄웁니다.
75
+ 단, **각 자식의 설치와 요금은 그대로 따라옵니다.**
76
+
77
+ ## 만들어서 내놓으실 거면
78
+
79
+ `BRAND_GUIDELINES.md` 에 상표 규칙이 있습니다.
80
+
81
+ - "DeepSeek Harness" 는 **등록상표**입니다. **프로젝트 이름에 전체 이름을 쓰지 마세요**
82
+ - 공식 권장은 약칭 **`DSH`**. 저장소에 `dsh-plugin` 토픽을 달면 찾기 쉬워집니다
83
+ - 설명문에 "built on DeepSeek Harness" 라고 **관계를 사실대로 적는 건 허용**됩니다
84
+ - 공식 브랜드 자료를 공인·제휴로 **오해될 방식으로** 홍보에 쓰지 마세요
85
+
86
+ ## 켜 보고 나면
87
+
88
+ 켜는 건 한 번이면 끝납니다. 그다음에 남는 질문은 대개 이것입니다 —
89
+ **"그래서 이걸 매번 누가 돌리지."** 하네스가 좋아도 사람이 매번 열어서
90
+ 같은 절차를 다시 설명해야 하면, 일이 준 게 아니라 하나 늘어난 것입니다.
91
+
92
+ 그 자리를 메우는 게 **스킬**입니다. 클로드가 절차를 기억하고 대신 밟습니다.
93
+ 리본랩스가 골라 담은 스킬 48종이 있습니다 —
94
+ <https://rebornlabs.kr/skillpack?utm_source=installer&utm_medium=guide&utm_campaign=dsh>
95
+ 목록과 직접 까는 명령까지 그 페이지에 그대로 적어 두었습니다. 안 사셔도 됩니다.