dsh-book-deconstruct 0.0.0-stage → 1.1.3

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 Kang
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 CHANGED
@@ -1,3 +1,64 @@
1
- # Temporary Holding Version
1
+ # dsh-book-deconstruct
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ DeepSeek Harness **Host 插件**(不是 Agent 预设):装进 profile 后会出现在插件列表,**任意会话**都可用:
4
+
5
+ - 斜杠命令:`/book 书名 | 作者`
6
+ - 工具:`book_deconstruct`(智能体在对话里调用)
7
+
8
+ ## 安装
9
+
10
+ 从 npm(推荐):
11
+
12
+ ```sh
13
+ dsh plugin --profile desktop add dsh-book-deconstruct
14
+ ```
15
+
16
+ 从本地目录(开发):
17
+
18
+ ```sh
19
+ dsh plugin --profile desktop add /path/to/book-deconstruct
20
+ ```
21
+
22
+ 从 GitHub(需公开仓库):
23
+
24
+ ```sh
25
+ dsh plugin --profile desktop add github:OWNER/dsh-book-deconstruct
26
+ ```
27
+
28
+ 装完后**完全退出并重新打开** DeepSeek Harness。
29
+
30
+ ## 用法
31
+
32
+ 任意预设下的会话均可:
33
+
34
+ ```text
35
+ /book 思考,快与慢 | 丹尼尔·卡尼曼
36
+ ```
37
+
38
+ 也支持:
39
+
40
+ ```text
41
+ /book 《思考,快与慢》丹尼尔·卡尼曼
42
+ ```
43
+
44
+ 或直接说:「帮我解读《思考,快与慢》,作者丹尼尔·卡尼曼」。
45
+
46
+ 输出只有两部分:把整本书讲透 + 最后一段有深度的思想。不编造不确定的事实、引文、页码。
47
+
48
+ ## 配置(可选)
49
+
50
+ 在 profile 的 `cordis.patch.yml` 按行 id `book-deconstruct` 覆盖:
51
+
52
+ ```yaml
53
+ - id: book-deconstruct
54
+ name: dsh-book-deconstruct
55
+ config:
56
+ provider: '' # 与 model 成对;空则用默认模型
57
+ model: ''
58
+ temperature: 0.5
59
+ maxTokens: 8000
60
+ ```
61
+
62
+ ## Topics(上架市场)
63
+
64
+ 公开 GitHub 仓库后请添加 Topics:`dsh-plugin`、`deepseek-harness`。也可在 [DSH Marketplace](https://dshmarketplace.dev/submit) 提交仓库。
@@ -0,0 +1,5 @@
1
+ # 「书籍拆解」Host 插件:任意会话可用 /book 与 book_deconstruct。
2
+ # 不再注册 Agent 预设;请在插件列表启用本包即可。
3
+ - insert:
4
+ - id: book-deconstruct
5
+ name: dsh-book-deconstruct
package/icon.svg ADDED
@@ -0,0 +1,10 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64" width="64" height="64" role="img" aria-label="Book deconstruct preset">
2
+ <rect x="8" y="10" width="40" height="44" rx="8" fill="#eef4ff" stroke="#2f6fed" stroke-width="3"/>
3
+ <path d="M28 15 V49" stroke="#8fb6f7" stroke-width="2.5" stroke-linecap="round"/>
4
+ <path d="M15 21 H23" stroke="#2f6fed" stroke-width="3" stroke-linecap="round"/>
5
+ <path d="M15 28 H23" stroke="#8fb6f7" stroke-width="3" stroke-linecap="round"/>
6
+ <path d="M15 35 H21" stroke="#8fb6f7" stroke-width="3" stroke-linecap="round"/>
7
+ <path d="M33 21 H41" stroke="#2f6fed" stroke-width="3" stroke-linecap="round"/>
8
+ <path d="M33 28 H41" stroke="#8fb6f7" stroke-width="3" stroke-linecap="round"/>
9
+ <path d="M46 36 L47.8 41.2 L53 43 L47.8 44.8 L46 50 L44.2 44.8 L39 43 L44.2 41.2 Z" fill="#f2b622" stroke="#d19a12" stroke-width="1.5" stroke-linejoin="round"/>
10
+ </svg>
package/index.js ADDED
@@ -0,0 +1,393 @@
1
+ /**
2
+ * book-deconstruct — 输入书名 + 作者,直接输出成品级书籍解读。
3
+ *
4
+ * 输出只保留两部分:
5
+ * 1. 这本书讲的是什么事(主体:详细讲清整本书的内容)
6
+ * 2. 一段深度的思想(最后只写一段,不列条目)
7
+ *
8
+ * 两个入口共用同一套解读逻辑(工具 execute):
9
+ * - `/book …` 斜杠命令:解析后 followup/steer 用户消息,让模型调用 book_deconstruct
10
+ * - `book_deconstruct` 工具(智能体在对话中调用)
11
+ */
12
+
13
+ import { randomUUID } from 'node:crypto';
14
+
15
+ export const inject = ['llm', 'commands', 'tools'];
16
+
17
+ const DEFAULT_TEMPERATURE = 0.5;
18
+ const DEFAULT_MAX_TOKENS = 8000;
19
+
20
+ const USAGE = [
21
+ '用法:/book 书名 | 作者',
22
+ '示例:/book 思考,快与慢 | 丹尼尔·卡尼曼',
23
+ '也支持:/book 《思考,快与慢》丹尼尔·卡尼曼',
24
+ '或:/book 帮我解读《思考,快与慢》,作者丹尼尔·卡尼曼',
25
+ '也可直接说:帮我解读《思考,快与慢》,作者丹尼尔·卡尼曼',
26
+ ].join('\n');
27
+
28
+ // ---------------------------------------------------------------------------
29
+ // 配置
30
+ // ---------------------------------------------------------------------------
31
+
32
+ function clampNumber(value, min, max, fallback) {
33
+ const n = typeof value === 'number' ? value : Number(value);
34
+ if (!Number.isFinite(n)) return fallback;
35
+ return Math.min(max, Math.max(min, n));
36
+ }
37
+
38
+ function clampInt(value, min, max, fallback) {
39
+ return Math.round(clampNumber(value, min, max, fallback));
40
+ }
41
+
42
+ function normalizeConfig(raw) {
43
+ const source = raw !== null && typeof raw === 'object' && !Array.isArray(raw) ? raw : {};
44
+ let provider = typeof source.provider === 'string' ? source.provider.trim() : '';
45
+ let model = typeof source.model === 'string' ? source.model.trim() : '';
46
+ if ((provider === '') !== (model === '')) {
47
+ provider = '';
48
+ model = '';
49
+ }
50
+ return {
51
+ provider,
52
+ model,
53
+ temperature: clampNumber(source.temperature, 0, 2, DEFAULT_TEMPERATURE),
54
+ maxTokens: clampInt(source.maxTokens, 256, 32000, DEFAULT_MAX_TOKENS),
55
+ };
56
+ }
57
+
58
+ // ---------------------------------------------------------------------------
59
+ // 输入解析
60
+ // ---------------------------------------------------------------------------
61
+
62
+ /** 去掉首尾空白、首尾书名号/引号,并压平换行。 */
63
+ function cleanField(value, maxLength) {
64
+ if (typeof value !== 'string') return '';
65
+ const flat = value.replace(/\s+/g, ' ').trim();
66
+ const stripped = flat
67
+ .replace(/^[《「『"'“”]+/u, '')
68
+ .replace(/[》」』"'“”]+$/u, '')
69
+ .trim();
70
+ return stripped.slice(0, maxLength);
71
+ }
72
+
73
+ /** 去掉作者字段前的分隔符与「作者/by」前缀。 */
74
+ function cleanAuthorTail(value) {
75
+ let author = String(value ?? '').trim();
76
+ author = author.replace(/^[\s||/—\-–—,,、:;:]+/u, '').trim();
77
+ author = author.replace(/^(?:作者(?:是)?|by)\s*[:::]?\s*/iu, '').trim();
78
+ return author;
79
+ }
80
+
81
+ /**
82
+ * 解析「书名 + 作者」。支持:
83
+ * 《书名》作者 / 书名 | 作者 / 书名|作者 / 书名 / 作者
84
+ * 自然语言:帮我解读《活着》,作者余华
85
+ * @returns {{ title: string, author: string } | null}
86
+ */
87
+ export function parseBookInput(raw) {
88
+ let text = String(raw === null || raw === undefined ? '' : raw)
89
+ .replace(/\r\n?/gu, '\n')
90
+ .trim();
91
+ if (text === '') return null;
92
+
93
+ text = text.replace(/^\/book\b/i, '').trim();
94
+ if (text === '') return null;
95
+
96
+ const angle = text.match(/《([^》\n]{1,200})》(.*)$/su);
97
+ if (angle !== null) {
98
+ const title = angle[1].trim();
99
+ const author = cleanAuthorTail(angle[2]);
100
+ if (title !== '' && author !== '') return { title, author };
101
+ }
102
+
103
+ const named = text.match(
104
+ /(?:请?帮?我?)?(?:解读|拆解|总结|分析|讲讲|讲一下)?\s*[《「"']?([^》」"'\n||,,]{1,200}?)[》」"']?\s*[,,、]?\s*(?:作者(?:是)?|by)\s*[:::]?\s*(.+)$/iu,
105
+ );
106
+ if (named !== null) {
107
+ const title = cleanField(named[1], 200);
108
+ const author = cleanAuthorTail(named[2]);
109
+ if (title !== '' && author !== '') return { title, author };
110
+ }
111
+
112
+ const pipe = text
113
+ .split(/[||]/u)
114
+ .map((part) => part.trim())
115
+ .filter((part) => part !== '');
116
+ if (pipe.length >= 2) {
117
+ const title = cleanField(pipe[0], 200);
118
+ const author = cleanAuthorTail(pipe.slice(1).join(' '));
119
+ if (title !== '' && author !== '') return { title, author };
120
+ }
121
+
122
+ const slash = text
123
+ .split(/\s*\/\s*/u)
124
+ .map((part) => part.trim())
125
+ .filter((part) => part !== '');
126
+ if (slash.length >= 2) {
127
+ const title = cleanField(slash[0], 200);
128
+ const author = cleanAuthorTail(slash.slice(1).join(' '));
129
+ if (title !== '' && author !== '') return { title, author };
130
+ }
131
+
132
+ const lines = text
133
+ .split('\n')
134
+ .map((line) => line.trim())
135
+ .filter((line) => line !== '');
136
+ if (lines.length >= 2) {
137
+ const title = cleanField(lines[0], 200);
138
+ const author = cleanAuthorTail(lines.slice(1).join(' '));
139
+ if (title !== '' && author !== '') return { title, author };
140
+ }
141
+
142
+ return null;
143
+ }
144
+
145
+ function buildSteerPrompt(parsed, raw) {
146
+ if (parsed !== null) {
147
+ return `请用 book_deconstruct 工具解读《${parsed.title}》,作者${parsed.author}`;
148
+ }
149
+ return `请用 book_deconstruct 工具处理用户的书籍解读请求:${raw}`;
150
+ }
151
+
152
+ /** 不依赖 @deepseek-ai/dsh-llm(本地 file: 插件 realpath 解析不到宿主包)。 */
153
+ function makeUserMessage(text) {
154
+ return Object.freeze({
155
+ id: randomUUID(),
156
+ role: 'user',
157
+ content: Object.freeze([{ type: 'text', text }]),
158
+ source: Object.freeze({ kind: 'user' }),
159
+ });
160
+ }
161
+
162
+ function enqueueUserTurn(agent, message) {
163
+ if (agent !== null && agent !== undefined && typeof agent.followup === 'function') {
164
+ agent.followup(message);
165
+ return true;
166
+ }
167
+ if (agent !== null && agent !== undefined && typeof agent.steer === 'function') {
168
+ agent.steer(message);
169
+ return true;
170
+ }
171
+ return false;
172
+ }
173
+
174
+ // ---------------------------------------------------------------------------
175
+ // 提示词:只产出成品,不产出过程
176
+ // ---------------------------------------------------------------------------
177
+
178
+ const SYSTEM_PROMPT = [
179
+ '你是一位有洞察力的书籍解读者。你的任务不是展示分析过程,而是直接交付高质量的成品:把一本书讲透,并在最后给出一段有深度的思想。',
180
+ '',
181
+ '铁律:',
182
+ '1. 只输出成品内容。不要写你的分析步骤、框架说明、知识边界声明、置信度清单、自我审校或任何元评论。',
183
+ '2. 不编造。对不确定的事实、引文、页码、章节、销量、奖项、时间线,不要写;宁可少写,也不要虚构。',
184
+ '3. 区分「书中明确观点」与「你的推论/评价」。属于你的推论时,用「由此可以推出」「这意味着」等措辞,不要冒充作者原话。',
185
+ '4. 不写目录复述,不写百科词条。要写出这本书的骨架和真正的洞见。',
186
+ '5. 用中文 Markdown,深度优先,语言干净利落,不堆砌空洞形容词。',
187
+ '6. 如果对书名或作者没有可靠信息,直接说明「我无法可靠地解读这本书」,不要编造内容。',
188
+ ].join('\n');
189
+
190
+ function buildAnalysisPrompt(title, author) {
191
+ return `书名:《${title}》
192
+ 作者:${author}
193
+
194
+ 请直接输出成品,只写下面两部分,不要写任何分析过程或元信息。
195
+
196
+ ## 这本书讲的是什么事
197
+ 用 1000–1800 字,把整本书的内容讲透(这是主体部分):
198
+ - 这本书的核心主题,以及它在回答什么问题;
199
+ - 全书的结构与推进方式:大致分几部分/几条主线,每部分主要讲了什么;
200
+ - 关键概念、核心论点、重要方法或案例(用自己的话转述,不编造引文);
201
+ - 作者的基本立场与最终结论。
202
+ 要求:按书的内容实际展开,让人读完这一段就基本掌握这本书讲了什么;不要只写一句概括,也不要写成目录清单。
203
+
204
+ ## 一段深度的思想
205
+ 用 250–400 字写**一整段**(不要分条、不要小标题),从整本书里提炼一个最深的洞见或结论:
206
+ - 它是什么;
207
+ - 为什么成立(结合书中的内容);
208
+ - 对读者意味着什么。
209
+ 只写这一段,不要再列多条结论。
210
+
211
+ 要求:直接给结果,不要写「我如何分析」「我的知识边界」「置信度」这类内容。`;
212
+ }
213
+
214
+ // ---------------------------------------------------------------------------
215
+ // LLM 调用(仅工具路径使用;斜杠命令改走 agent 回合)
216
+ // ---------------------------------------------------------------------------
217
+
218
+ function resolveRoute(ctx, config) {
219
+ if (config.provider !== '' && config.model !== '') {
220
+ return { provider: config.provider, model: config.model };
221
+ }
222
+ const defaultModel = ctx.get('agentDefaultModel');
223
+ const selection =
224
+ defaultModel !== undefined && typeof defaultModel.currentSelection === 'function'
225
+ ? defaultModel.currentSelection()
226
+ : undefined;
227
+ if (selection !== undefined && selection.provider && selection.model) {
228
+ return { provider: selection.provider, model: selection.model };
229
+ }
230
+ throw new Error('没有可用的模型:请在插件配置里设置 provider 和 model,或在设置里选择默认模型。');
231
+ }
232
+
233
+ async function generate(ctx, config, options) {
234
+ const route = resolveRoute(ctx, config);
235
+ const stream = ctx.llm.stream({
236
+ provider: route.provider,
237
+ model: route.model,
238
+ system: options.system,
239
+ messages: [{ role: 'user', content: [{ type: 'text', text: options.user }] }],
240
+ temperature: options.temperature,
241
+ maxTokens: options.maxTokens,
242
+ signal: options.signal,
243
+ });
244
+
245
+ let text = '';
246
+ let sawDelta = false;
247
+ const blockTexts = [];
248
+ let finish;
249
+
250
+ for await (const chunk of stream) {
251
+ if (chunk.type === 'text-delta') {
252
+ sawDelta = true;
253
+ text += chunk.text;
254
+ } else if (chunk.type === 'block-end') {
255
+ if (chunk.block !== null && typeof chunk.block === 'object' && chunk.block.type === 'text') {
256
+ blockTexts.push(chunk.block.text);
257
+ }
258
+ } else if (chunk.type === 'finish') {
259
+ finish = chunk.reason;
260
+ }
261
+ }
262
+
263
+ if (!sawDelta && blockTexts.length > 0) text = blockTexts.join('');
264
+
265
+ if (finish !== undefined && finish.kind === 'error') {
266
+ const message =
267
+ finish.failure !== undefined && finish.failure !== null && finish.failure.message
268
+ ? finish.failure.message
269
+ : '未知错误';
270
+ throw new Error('模型调用失败:' + message);
271
+ }
272
+ if (finish !== undefined && finish.kind === 'aborted') {
273
+ throw new Error('模型调用被取消');
274
+ }
275
+ return { text: text.trim(), finish };
276
+ }
277
+
278
+ async function deconstruct(ctx, config, rawTitle, rawAuthor, options = {}) {
279
+ const title = cleanField(rawTitle, 200);
280
+ const author = cleanField(rawAuthor, 200);
281
+ if (title === '') throw new Error('书名不能为空');
282
+ if (author === '') throw new Error('作者不能为空');
283
+
284
+ const result = await generate(ctx, config, {
285
+ system: SYSTEM_PROMPT,
286
+ user: buildAnalysisPrompt(title, author),
287
+ temperature: config.temperature,
288
+ maxTokens: config.maxTokens,
289
+ signal: options.signal,
290
+ });
291
+ if (result.text === '') throw new Error('模型没有返回内容,请稍后重试。');
292
+
293
+ let output = result.text;
294
+ if (result.finish !== undefined && result.finish.kind === 'max-tokens') {
295
+ output += '\n\n> (输出达到长度上限,可能有截断)';
296
+ }
297
+ return output;
298
+ }
299
+
300
+ // ---------------------------------------------------------------------------
301
+ // 插件入口
302
+ // ---------------------------------------------------------------------------
303
+
304
+ export function apply(ctx, rawConfig) {
305
+ const config = normalizeConfig(rawConfig);
306
+ const disposers = [];
307
+
308
+ disposers.push(
309
+ ctx.commands.register({
310
+ // 挂 input.hint:从 / 菜单选 book 时保留输入框,继续填「书名 | 作者」。
311
+ // 也可整行输入 /book 书名 | 作者 后直接发送。
312
+ name: 'book',
313
+ description: '深度解读一本书。用法:/book 书名 | 作者',
314
+ input: { hint: '书名 | 作者' },
315
+ handler: async (invocation) => {
316
+ const raw = String(invocation.rawInput ?? '').trim();
317
+ if (raw === '') return { kind: 'error', text: USAGE };
318
+
319
+ const parsed = parseBookInput(raw);
320
+ if (parsed === null && !/作者|by|《.|》/u.test(raw) && !/[||]/u.test(raw)) {
321
+ return { kind: 'error', text: USAGE };
322
+ }
323
+
324
+ const prompt = buildSteerPrompt(parsed, raw);
325
+ const agent = invocation.agent;
326
+ const ok = enqueueUserTurn(agent, makeUserMessage(prompt));
327
+ if (ok) {
328
+ if (parsed !== null) {
329
+ return {
330
+ kind: 'success',
331
+ text: `已提交解读:《${parsed.title}》 / ${parsed.author}(将调用 book_deconstruct)`,
332
+ };
333
+ }
334
+ return { kind: 'success', text: '已提交书籍解读请求(将调用 book_deconstruct)' };
335
+ }
336
+
337
+ // followup/steer 不可用时,直接跑工具逻辑,保证斜杠有反馈
338
+ if (parsed === null) {
339
+ return { kind: 'error', text: USAGE };
340
+ }
341
+ try {
342
+ const text = await deconstruct(ctx, config, parsed.title, parsed.author, {
343
+ signal: invocation.signal,
344
+ });
345
+ return { kind: 'success', text };
346
+ } catch (error) {
347
+ const message = error !== null && error !== undefined && error.message ? error.message : String(error);
348
+ return { kind: 'error', text: '解读失败:' + message };
349
+ }
350
+ },
351
+ }),
352
+ );
353
+
354
+ disposers.push(
355
+ ctx.tools.register({
356
+ name: 'book_deconstruct',
357
+ description:
358
+ '深度解读一本书:输入书名与作者,先详细讲清整本书的内容,最后给出一段有深度的思想。当用户要求「解读/总结/拆解某本书」时使用。',
359
+ parameters: {
360
+ type: 'object',
361
+ properties: {
362
+ title: { type: 'string', description: '书名,例如《思考,快与慢》或“思考,快与慢”' },
363
+ author: { type: 'string', description: '作者,例如“丹尼尔·卡尼曼”' },
364
+ },
365
+ required: ['title', 'author'],
366
+ additionalProperties: false,
367
+ },
368
+ output: {
369
+ schema: { type: 'string' },
370
+ render(_args, value) {
371
+ return [{ type: 'text', text: String(value) }];
372
+ },
373
+ },
374
+ async execute(args, exec) {
375
+ const payload = args !== null && typeof args === 'object' ? args : {};
376
+ const title = cleanField(payload.title, 200);
377
+ const author = cleanField(payload.author, 200);
378
+ if (title === '' || author === '') throw new Error('title 和 author 必填');
379
+ return deconstruct(ctx, config, title, author, { signal: exec.signal });
380
+ },
381
+ }),
382
+ );
383
+
384
+ return () => {
385
+ for (const dispose of disposers.reverse()) {
386
+ try {
387
+ dispose();
388
+ } catch {
389
+ /* ignore disposer errors */
390
+ }
391
+ }
392
+ };
393
+ }
package/locale/en.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "Book Deconstruct",
4
+ "description": "Host plugin: /book and book_deconstruct in any session. Give a title and author for a full walkthrough plus one deep insight."
5
+ }
6
+ }
package/locale/zh.json ADDED
@@ -0,0 +1,6 @@
1
+ {
2
+ "meta": {
3
+ "title": "书籍拆解",
4
+ "description": "Host 插件:任意会话可用 /book 与 book_deconstruct。输入书名与作者,把整本书讲透,最后给出一段有深度的思想。"
5
+ }
6
+ }
package/package.json CHANGED
@@ -1,6 +1,47 @@
1
1
  {
2
2
  "name": "dsh-book-deconstruct",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
3
+ "version": "1.1.3",
4
+ "description": "DSH Host plugin: /book slash command + book_deconstruct tool for full book walkthroughs.",
5
+ "keywords": [
6
+ "dsh",
7
+ "dsh-plugin",
8
+ "deepseek-harness",
9
+ "book",
10
+ "reading"
11
+ ],
12
+ "license": "MIT",
13
+ "author": "Kang",
14
+ "type": "module",
15
+ "main": "index.js",
16
+ "exports": {
17
+ ".": "./index.js",
18
+ "./locale/*.json": "./locale/*.json",
19
+ "./icon": "./icon.svg",
20
+ "./cordis.patch.yml": "./cordis.patch.yml"
21
+ },
22
+ "files": [
23
+ "index.js",
24
+ "cordis.patch.yml",
25
+ "locale/en.json",
26
+ "locale/zh.json",
27
+ "icon.svg",
28
+ "README.md",
29
+ "LICENSE"
30
+ ],
31
+ "engines": {
32
+ "dsh": ">=0.2.0"
33
+ },
34
+ "dsh": {
35
+ "bundle": {
36
+ "patch": "./cordis.patch.yml"
37
+ }
38
+ },
39
+ "repository": {
40
+ "type": "git",
41
+ "url": "https://github.com/zmz123612/dsh-book-deconstruct.git"
42
+ },
43
+ "bugs": {
44
+ "url": "https://github.com/zmz123612/dsh-book-deconstruct/issues"
45
+ },
46
+ "homepage": "https://github.com/zmz123612/dsh-book-deconstruct#readme"
47
+ }