@ohos-cpf/3rdloop 0.0.10 → 0.0.11

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/README.md CHANGED
@@ -192,21 +192,19 @@ npm 包内置 MCP Gateway(`vendor/MCP`)、知识库检索引擎(`vendor/Ar
192
192
 
193
193
  ### 场景七:知识回收(把沉淀的知识打包发给维护者)
194
194
 
195
- 任务执行会在本机沉淀知识(kb 文档、BadCase 规则、经验索引)。这些知识默认只在你机器上;想贡献给社区/团队,打包发给维护者即可:
195
+ 任务执行会在本机沉淀知识 JSON(`<dataDir>/Knowledge/` 下的 `index.json` 登记表 + `kn_*.json` 条目)。想贡献给社区/团队,打包发给维护者即可:
196
196
 
197
197
  ```bash
198
198
  # 用户侧:打包(纯本地操作,不上传任何数据)
199
199
  3rdloop knowledge export # → ./3rdloop-knowledge-<日期>-<主机名>.zip
200
- 3rdloop knowledge export --since 2026-09-01 # 只打包该日期后新增的
201
200
  3rdloop knowledge export --note "mp3agic 批次" # 附备注
202
201
 
203
- # 维护者侧:收到 zip 后合入(先预演再落位)
204
- 3rdloop knowledge import <zip> --dry-run # 预览将新增/冲突的文件
205
- 3rdloop knowledge import <zip> # 落位到本地 kb-data 与 BadCase
206
- 3rdloop knowledge import <zip> --allow-overwrite # 覆盖同名文件
202
+ # 维护者侧:收到 zip 后解包到收件箱
203
+ 3rdloop knowledge import <zip> --dry-run # 预览
204
+ 3rdloop knowledge import <zip> # ~/.3lib/3rdloop/knowledge-inbox/<来源主机>-<日期>/
207
205
  ```
208
206
 
209
- 包内容:kb 结构化知识文档 + BadCase 规则/案例 + 经验索引清单(仅人工评估参考)。zip 为标准格式(可用系统 unzip 自查内容);不含检索索引(导入后自动重建)。维护者审核后可把精选内容合入仓库 `Archive/data` 与 `BadCase/`,随 npm 版本分发给所有用户。
207
+ 包内容只有知识 JSON(不含 md / kb 文档 / BadCase)。zip 为标准格式,可先解压自查;多个用户的包在收件箱按 `主机名-日期` 隔离,互不冲突,也不覆盖维护者本地知识库——人工评估后再入库。
210
208
 
211
209
  更多进阶用法(任务管理、`step` 单步执行、`orch` 自定义编排、工作流注册等),见仓库 `cli/README_DEVELOP.md` 或 `3rdloop <子命令> --help`。
212
210
 
package/lib/cli.js CHANGED
@@ -697,9 +697,9 @@ function printHelp() {
697
697
  3rdloop mcp install [--cli opencode|deveco|all] 装配 MCP Gateway(注册到 AI CLI +
698
698
  种子复制知识库数据/BadCase 到 ~/.3lib/3rdloop/,npm 安装态必需)
699
699
  3rdloop mcp uninstall|status 移除注册 / 查看装配状态
700
- 3rdloop knowledge export [--since <date>] [--note <text>]
701
- 打包本地知识(kb 文档 + BadCase)为 zip 发给维护者回收
702
- 3rdloop knowledge import <zip> [--dry-run] 维护者合入收到的知识包
700
+ 3rdloop knowledge export [--note <text>]
701
+ 打包本地知识沉淀 JSON(db/Knowledge)为 zip 发给维护者回收
702
+ 3rdloop knowledge import <zip> [--dry-run] 维护者解包收到的知识包到收件箱
703
703
  3rdloop serve [--port N] [--web-port N] 启动 Server 后端 + Web 前端(同时启动)
704
704
  [--web-root <dir>] [--ext-root <dir>] [--ext <name,...>] [--no-ext] [--open]
705
705
  3rdloop serve install-ext <dir> 安装扩展(tag/issue/prcheck 等二级功能)
package/lib/knowledge.js CHANGED
@@ -1,27 +1,18 @@
1
1
  /**
2
- * knowledge.js —— 3rdloop knowledge:本地知识打包回收(export)与合入(import)
2
+ * knowledge.js —— 3rdloop knowledge:知识沉淀 JSON 打包回收(export)与收件(import)
3
3
  *
4
- * 背景:知识沉淀产物散落在用户数据目录(npm 安装态各机器独立),需要一条
5
- * 人工中转通道实现"用户 维护者 仓库/其他用户"的知识回流:
4
+ * 最简人工中转通道:知识沉淀系统的数据全部是 JSON(<dataDir>/Knowledge/ 下的
5
+ * index.json 登记表 + kn_*.json 条目),export 打包这些 JSON 为 zip 发给维护者;
6
+ * 维护者 import 解包到收件箱目录人工评估,不直接覆盖本地知识库。
6
7
  *
7
- * 用户侧(export,零依赖、离线可用):
8
- * 3rdloop knowledge export [--output <file>] [--since <date>] [--note <text>]
9
- * 打包 ~/.3lib/3rdloop/kb-data/docs 新增文档 + BadCase 新规则/案例 +
10
- * db/Knowledge 索引清单 → zip(含 manifest.json:来源/时间/清单/统计)
11
- * 默认输出 ./3rdloop-knowledge-<日期>-<主机名>.zip
12
- *
13
- * 维护者侧(import,收到 zip 后一键预检 + 合入):
14
- * 3rdloop knowledge import <zip> [--dry-run] [--allow-overwrite]
15
- * 解包校验 manifest → 逐文件落位(docs → kb-data/docs、BadCase → BadCase、
16
- * 索引清单仅汇总展示不入库)→ 重建由 kb 引擎首次检索时自动完成
17
- * --dry-run 只预览将发生的变更,不写任何文件
8
+ * 用户侧: 3rdloop knowledge export [--output <file>] [--note <text>]
9
+ * 维护者侧: 3rdloop knowledge import <zip> [--dry-run]
18
10
  *
19
11
  * 设计约束:
20
- * - export 零网络:纯本地打包,绝不静默上传(隐私合规)
21
- * - 不打 .index/(kb 检索引擎的派生产物,import 后自动重建)
22
- * - BadCase 冲突默认跳过(--allow-overwrite 覆盖);docs 同名默认跳过
23
- * - 索引(db/Knowledge/index.json)只作为"待人工评估清单"随包携带,
24
- * 不自动导入(指针含本机绝对路径,对他机无意义)
12
+ * - 只打 JSON,不碰任何 md / kb-data / BadCase
13
+ * - export 纯本地打包,不上传
14
+ * - import 落位到收件箱 ~/.3lib/3rdloop/knowledge-inbox/<来源>-<日期>/,
15
+ * 多个用户的包互不冲突,也不污染维护者自己的 Knowledge 库(人工评估后自行入库)
25
16
  */
26
17
 
27
18
  import fs from 'node:fs';
@@ -30,31 +21,26 @@ import os from 'node:os';
30
21
  import zlib from 'node:zlib';
31
22
  import process from 'node:process';
32
23
 
33
- // ─── 用户路径 ─────────────────────────────────────────────────────────
24
+ // ─── 路径 ─────────────────────────────────────────────────────────────
34
25
 
35
26
  /**
36
- * 知识相关用户目录。
37
- * @param {string} user3libHome - ~/.3lib
38
- * @param {string} [dataDir] - CLI 解析的数据目录(kb-data/BadCase 固定在 loopHome,
39
- * 仅 Knowledge 索引跟随 dataDir——npm 安装态两者相同,仓库开发态 dataDir 可能被覆盖)
27
+ * 知识目录。
28
+ * @param {string} dataDir - CLI 解析的数据目录(Knowledge = <dataDir>/Knowledge)
40
29
  */
41
- export function knowledgePaths(user3libHome, dataDir = '') {
42
- const loopHome = path.join(user3libHome, '3rdloop');
30
+ export function knowledgePaths(dataDir) {
43
31
  return {
44
- loopHome,
45
- kbDocsDir: path.join(loopHome, 'kb-data', 'docs'),
46
- badcaseDir: path.join(loopHome, 'BadCase'),
47
- knowledgeIndex: dataDir
48
- ? path.join(dataDir, 'Knowledge', 'index.json')
49
- : path.join(loopHome, 'db', 'Knowledge', 'index.json'),
32
+ knowledgeDir: path.join(dataDir, 'Knowledge'),
33
+ inboxRoot: null, // import 侧单独解析(基于 user3libHome)
50
34
  };
51
35
  }
52
36
 
53
- // ─── zip 读写(STORE 打包,纯 Node 无第三方依赖)──────────────────────
54
- // zip 结构说明:为避免实现 zip 目录遍历/central directory 全套规范,
55
- // 本实现采用"单成员 zip"约定:一个 zip 只含一个被 deflate 压缩的
56
- // JSON 载荷文件 payload.json(STORE/DEFLATE 单文件 zip 是稳定子集)。
57
- // payload 内部再携带多文件内容(Base64),兼顾格式标准与实现简洁。
37
+ /** 维护者收件箱根目录。 */
38
+ function inboxRoot(user3libHome) {
39
+ return path.join(user3libHome, '3rdloop', 'knowledge-inbox');
40
+ }
41
+
42
+ // ─── zip 读写(单成员 DEFLATE zip,纯 Node 无第三方依赖)──────────────
43
+ // 一个 zip 只含一个被 deflate 压缩的 JSON 载荷文件 payload.json。
58
44
 
59
45
  const PAYLOAD_NAME = 'payload.json';
60
46
 
@@ -153,133 +139,101 @@ function crc32(buf) {
153
139
  // ─── export ───────────────────────────────────────────────────────────
154
140
 
155
141
  /**
156
- * 递归收集目录下文件相对路径(排除 .index/.DS_Store/node_modules)。
157
- */
158
- function walkFiles(root, rel = '', out = []) {
159
- if (!fs.existsSync(root)) return out;
160
- for (const entry of fs.readdirSync(root, { withFileTypes: true })) {
161
- if (entry.name === '.index' || entry.name === '.DS_Store' || entry.name === 'node_modules') continue;
162
- const relPath = rel ? `${rel}/${entry.name}` : entry.name;
163
- if (entry.isDirectory()) walkFiles(path.join(root, entry.name), relPath, out);
164
- else out.push(relPath);
165
- }
166
- return out;
167
- }
168
-
169
- /**
170
- * 3rdloop knowledge export 实现。
142
+ * 3rdloop knowledge export 实现:打包 <dataDir>/Knowledge/ 下全部 JSON。
171
143
  * @param {object} opts
172
- * @param {string} opts.user3libHome
173
- * @param {string[]} opts.rest - 命令参数(--output/--since/--note)
144
+ * @param {string} opts.dataDir
145
+ * @param {string[]} opts.rest - 命令参数(--output/--note)
174
146
  * @param {boolean} opts.jsonMode
175
147
  * @returns {Promise<number>} 退出码
176
148
  */
177
- export async function knowledgeExport({ user3libHome, dataDir = '', rest = [], jsonMode = false }) {
149
+ export async function knowledgeExport({ dataDir, rest = [], jsonMode = false }) {
178
150
  const out = (data) => { if (jsonMode) process.stdout.write(JSON.stringify(data, null, 2) + '\n'); };
179
151
  const log = (msg) => process.stderr.write(`${msg}\n`);
180
152
 
181
153
  const output = _flagValue(rest, '--output');
182
- const since = _flagValue(rest, '--since'); // ISO 日期:仅收该日期后新增的文件(按 mtime)
183
154
  const note = _flagValue(rest, '--note') || '';
184
- const sinceMs = since ? Date.parse(since) : null;
185
- if (since && Number.isNaN(sinceMs)) {
186
- log(`错误: 非法 --since 日期: ${since}(示例: 2026-09-01)`);
187
- return 1;
188
- }
189
-
190
- const up = knowledgePaths(user3libHome, dataDir);
191
-
192
- // ── 收集三类知识 ──
193
- const docsFiles = walkFiles(up.kbDocsDir);
194
- const badcaseFiles = walkFiles(up.badcaseDir);
195
- const knowledgeIndex = fs.existsSync(up.knowledgeIndex)
196
- ? JSON.parse(fs.readFileSync(up.knowledgeIndex, 'utf8'))
197
- : [];
198
155
 
199
- const sinceFilter = (absPath) => sinceMs == null || fs.statSync(absPath).mtimeMs >= sinceMs;
200
-
201
- // docs:结构化知识文档(export 全量,包小;--since 可裁剪)
202
- const docs = {};
203
- let docsSkipped = 0;
204
- for (const rel of docsFiles) {
205
- const abs = path.join(up.kbDocsDir, rel);
206
- if (!sinceFilter(abs)) { docsSkipped++; continue; }
207
- docs[rel] = fs.readFileSync(abs, 'utf8');
156
+ const knowledgeDir = path.join(dataDir, 'Knowledge');
157
+ if (!fs.existsSync(knowledgeDir)) {
158
+ log(`未找到知识目录: ${knowledgeDir}`);
159
+ log('提示: 知识在任务执行后自动沉淀(db/Knowledge),先跑几个任务再看。');
160
+ return 0;
208
161
  }
209
162
 
210
- // BadCase:规则/案例/日志(排除种子自带内容:仅收 INDEX 之外的新增 + 用户改动,
211
- // 简化策略 = 全量携带,import 端与种子比对去重)
212
- const badcase = {};
213
- let badcaseSkipped = 0;
214
- for (const rel of badcaseFiles) {
215
- const abs = path.join(up.badcaseDir, rel);
216
- if (!sinceFilter(abs)) { badcaseSkipped++; continue; }
217
- badcase[rel] = fs.readFileSync(abs, 'utf8');
163
+ // 收集全部 .json(index.json 登记表 + kn_*.json 知识条目)
164
+ const files = {};
165
+ for (const entry of fs.readdirSync(knowledgeDir, { withFileTypes: true })) {
166
+ if (!entry.isFile() || !entry.name.endsWith('.json')) continue;
167
+ files[entry.name] = fs.readFileSync(path.join(knowledgeDir, entry.name), 'utf8');
218
168
  }
219
-
220
- if (Object.keys(docs).length === 0 && Object.keys(badcase).length === 0 && knowledgeIndex.length === 0) {
221
- log('未发现可导出的知识(kb-data/docs、BadCase 均为空)。');
222
- log('提示: 知识在任务执行后由 knowledge-import SKILL 沉淀,先跑几个任务再看。');
169
+ if (Object.keys(files).length === 0) {
170
+ log(`知识目录为空: ${knowledgeDir}`);
223
171
  return 0;
224
172
  }
225
173
 
226
- // ── 载荷与 manifest ──
227
174
  const manifest = {
228
175
  format: '3rdloop-knowledge/1',
229
176
  exportedAt: new Date().toISOString(),
230
177
  host: os.hostname(),
231
178
  platform: process.platform,
232
179
  note,
233
- since: since || null,
234
180
  stats: {
235
- docs: Object.keys(docs).length,
236
- badcase: Object.keys(badcase).length,
237
- knowledgeIndexEntries: knowledgeIndex.length,
181
+ files: Object.keys(files).length,
182
+ entries: files['index.json']
183
+ ? _countIndexEntries(files['index.json'])
184
+ : Object.keys(files).filter(n => n.startsWith('kn_')).length,
238
185
  },
239
186
  };
240
- const payload = JSON.stringify({ manifest, docs, badcase, knowledgeIndex }, null, 2);
187
+ const payload = JSON.stringify({ manifest, knowledgeFiles: files }, null, 2);
241
188
 
242
- // 默认文件名:./3rdloop-knowledge-<YYYYMMDD>-<hostname>.zip
189
+ // 默认文件名:./3rdloop-knowledge-<YYYYMMDD>-<主机名>.zip
243
190
  const date = new Date().toISOString().slice(0, 10).replace(/-/g, '');
244
191
  const defaultName = `3rdloop-knowledge-${date}-${os.hostname().split('.')[0]}.zip`;
245
- const target = output
246
- ? path.resolve(output)
247
- : path.join(process.cwd(), defaultName);
192
+ const target = output ? path.resolve(output) : path.join(process.cwd(), defaultName);
248
193
 
249
194
  writeSingleFileZip(target, payload);
250
195
 
251
196
  if (jsonMode) {
252
- out({ ok: true, file: target, ...manifest.stats, docsSkippedBySince: docsSkipped });
197
+ out({ ok: true, file: target, ...manifest.stats });
253
198
  } else {
254
199
  log(`[3rdloop knowledge] 知识包已生成: ${target}`);
255
- log(` 结构化知识文档: ${manifest.stats.docs} 篇${docsSkipped ? `(--since 裁剪 ${docsSkipped} 篇)` : ''}`);
256
- log(` BadCase 规则/案例: ${manifest.stats.badcase} 个文件`);
257
- log(` 知识索引条目: ${manifest.stats.knowledgeIndexEntries} 条(仅作人工评估清单)`);
200
+ log(` JSON 文件: ${manifest.stats.files} 个(index.json + kn_*.json)`);
201
+ log(` 知识条目: ${manifest.stats.entries} 条`);
258
202
  if (note) log(` 备注: ${note}`);
259
203
  log(` 大小: ${(fs.statSync(target).size / 1024).toFixed(1)} KB`);
260
- log(`请将 zip 发送给维护者(微信/邮件均可);内容由你本机知识文件打包而成,可先解压自查。`);
204
+ log(`请将 zip 发送给维护者(微信/邮件均可);内容由你本机知识 JSON 打包而成,可先解压自查。`);
261
205
  }
262
206
  return 0;
263
207
  }
264
208
 
209
+ /** 解析 index.json 统计条目数(容错:解析失败返回 0)。 */
210
+ function _countIndexEntries(indexRaw) {
211
+ try {
212
+ const arr = JSON.parse(indexRaw);
213
+ return Array.isArray(arr) ? arr.length : 0;
214
+ } catch {
215
+ return 0;
216
+ }
217
+ }
218
+
265
219
  // ─── import ───────────────────────────────────────────────────────────
266
220
 
267
221
  /**
268
- * 3rdloop knowledge import 实现(维护者侧:收 zip → 校验 → 落位)。
222
+ * 3rdloop knowledge import 实现(维护者侧:收 zip → 解到收件箱目录)。
223
+ * 收件箱按 <来源主机>-<导出时间> 隔离,多包互不冲突;不覆盖本地 Knowledge 库。
269
224
  * @param {object} opts
270
225
  * @param {string} opts.user3libHome
271
- * @param {string[]} opts.rest - <zip> --dry-run --allow-overwrite
226
+ * @param {string[]} opts.rest - <zip> --dry-run
272
227
  * @param {boolean} opts.jsonMode
273
228
  */
274
- export async function knowledgeImport({ user3libHome, dataDir = '', rest = [], jsonMode = false }) {
229
+ export async function knowledgeImport({ user3libHome, rest = [], jsonMode = false }) {
275
230
  const out = (data) => { if (jsonMode) process.stdout.write(JSON.stringify(data, null, 2) + '\n'); };
276
231
  const log = (msg) => process.stderr.write(`${msg}\n`);
277
232
 
278
233
  const dryRun = rest.includes('--dry-run');
279
- const allowOverwrite = rest.includes('--allow-overwrite');
280
234
  const zipPath = rest.find(a => !a.startsWith('--'));
281
235
  if (!zipPath) {
282
- log('缺少 zip 路径。用法: 3rdloop knowledge import <zip> [--dry-run] [--allow-overwrite]');
236
+ log('缺少 zip 路径。用法: 3rdloop knowledge import <zip> [--dry-run]');
283
237
  return 1;
284
238
  }
285
239
  const absZip = path.resolve(zipPath);
@@ -288,7 +242,6 @@ export async function knowledgeImport({ user3libHome, dataDir = '', rest = [], j
288
242
  return 1;
289
243
  }
290
244
 
291
- // ── 解包 ──
292
245
  let payload;
293
246
  try {
294
247
  payload = JSON.parse(readSingleFileZip(absZip));
@@ -296,67 +249,43 @@ export async function knowledgeImport({ user3libHome, dataDir = '', rest = [], j
296
249
  log(`错误: 解析知识包失败: ${err.message}`);
297
250
  return 1;
298
251
  }
299
- const { manifest, docs, badcase, knowledgeIndex } = payload;
252
+ const { manifest, knowledgeFiles } = payload;
300
253
  if (!manifest || manifest.format !== '3rdloop-knowledge/1') {
301
254
  log(`错误: 未知知识包格式: ${manifest?.format || '(缺失 manifest)'}`);
302
255
  return 1;
303
256
  }
304
257
 
305
- const up = knowledgePaths(user3libHome, dataDir);
306
-
307
- // ── 预演:统计冲突 ──
308
- const plan = { docsAdd: [], docsSkip: [], badcaseAdd: [], badcaseSkip: [] };
309
- for (const rel of Object.keys(docs || {})) {
310
- (fs.existsSync(path.join(up.kbDocsDir, rel)) ? plan.docsSkip : plan.docsAdd).push(rel);
311
- }
312
- for (const rel of Object.keys(badcase || {})) {
313
- (fs.existsSync(path.join(up.badcaseDir, rel)) ? plan.badcaseSkip : plan.badcaseAdd).push(rel);
314
- }
258
+ // 收件箱:~/.3lib/3rdloop/knowledge-inbox/<主机名>-<导出日期>/
259
+ const date = String(manifest.exportedAt).slice(0, 10);
260
+ const host = String(manifest.host || 'unknown').split('.')[0];
261
+ const inboxDir = path.join(inboxRoot(user3libHome), `${host}-${date}`);
315
262
 
316
263
  if (dryRun) {
317
- if (jsonMode) out({ ok: true, dryRun: true, manifest, plan });
264
+ if (jsonMode) out({ ok: true, dryRun: true, manifest, inboxDir, files: Object.keys(knowledgeFiles || {}) });
318
265
  else {
319
266
  log(`[3rdloop knowledge] 预演(不写入任何文件)`);
320
267
  log(` 知识包: ${absZip}`);
321
268
  log(` 导出时间: ${manifest.exportedAt} 来源: ${manifest.host}(${manifest.platform})`);
322
269
  if (manifest.note) log(` 备注: ${manifest.note}`);
323
- log(` 将新增 docs: ${plan.docsAdd.length} 篇${plan.docsAdd.length ? `(如 ${plan.docsAdd.slice(0, 3).join(', ')}${plan.docsAdd.length > 3 ? ' ...' : ''})` : ''}`);
324
- log(` 将新增 BadCase: ${plan.badcaseAdd.length} 个${plan.badcaseAdd.length ? `(如 ${plan.badcaseAdd.slice(0, 3).join(', ')}${plan.badcaseAdd.length > 3 ? ' ...' : ''})` : ''}`);
325
- log(` 跳过同名冲突: docs ${plan.docsSkip.length} / BadCase ${plan.badcaseSkip.length}(--allow-overwrite 可覆盖)`);
326
- log(` 知识索引条目: ${(knowledgeIndex || []).length} 条(仅展示,不入库)`);
270
+ log(` JSON 文件: ${Object.keys(knowledgeFiles || {}).length} 收件箱 ${inboxDir}`);
327
271
  }
328
272
  return 0;
329
273
  }
330
274
 
331
- // ── 落位 ──
332
- let added = 0, skipped = 0;
333
- for (const rel of Object.keys(docs || {})) {
334
- const target = path.join(up.kbDocsDir, rel);
335
- if (fs.existsSync(target) && !allowOverwrite) { skipped++; continue; }
336
- fs.mkdirSync(path.dirname(target), { recursive: true });
337
- fs.writeFileSync(target, docs[rel]);
338
- added++;
339
- }
340
- for (const rel of Object.keys(badcase || {})) {
341
- const target = path.join(up.badcaseDir, rel);
342
- if (fs.existsSync(target) && !allowOverwrite) { skipped++; continue; }
343
- fs.mkdirSync(path.dirname(target), { recursive: true });
344
- fs.writeFileSync(target, badcase[rel]);
345
- added++;
346
- }
347
-
348
- // 知识索引:打印摘要供人工评估(不入库:含他机绝对路径指针)
349
- if (knowledgeIndex && knowledgeIndex.length > 0 && !jsonMode) {
350
- log(`[3rdloop knowledge] 携带知识索引 ${knowledgeIndex.length} 条(未入库;含导出者机器的本地路径,仅供参考)`);
275
+ let written = 0;
276
+ for (const [name, content] of Object.entries(knowledgeFiles || {})) {
277
+ fs.mkdirSync(inboxDir, { recursive: true });
278
+ fs.writeFileSync(path.join(inboxDir, name), content);
279
+ written++;
351
280
  }
352
281
 
353
282
  if (jsonMode) {
354
- out({ ok: true, added, skipped, plan });
283
+ out({ ok: true, inboxDir, written, manifest });
355
284
  } else {
356
- log(`[3rdloop knowledge] 导入完成: 新增 ${added} 个文件${skipped ? `,跳过同名冲突 ${skipped} 个` : ''}`);
357
- log(` docs ${up.kbDocsDir}`);
358
- log(` BadCase ${up.badcaseDir}`);
359
- log(` 检索索引将在下次 kb_search 时自动重建(.index/ 为派生产物)`);
285
+ log(`[3rdloop knowledge] 已解包到收件箱: ${inboxDir}`);
286
+ log(` JSON 文件: ${written} 个(含 index.json + kn_*.json)`);
287
+ log(` 来源: ${manifest.host} 导出时间: ${manifest.exportedAt}${manifest.note ? ` 备注: ${manifest.note}` : ''}`);
288
+ log(`人工评估后可将有价值条目拷入仓库或本地 <dataDir>/Knowledge/。`);
360
289
  }
361
290
  return 0;
362
291
  }
@@ -368,31 +297,30 @@ function _flagValue(rest, name) {
368
297
  return idx !== -1 ? rest[idx + 1] : undefined;
369
298
  }
370
299
 
300
+ /** 该 flag 是否携带值。 */
301
+ function _hasValue(flag) {
302
+ return flag === '--output' || flag === '--note';
303
+ }
304
+
371
305
  export function printKnowledgeHelp() {
372
- process.stdout.write(`3rdloop knowledge —— 本地知识打包回收 / 合入(人工中转通道)
306
+ process.stdout.write(`3rdloop knowledge —— 知识沉淀 JSON 打包回收(人工中转通道)
373
307
 
374
308
  用法(用户侧:打包发给维护者):
375
309
  3rdloop knowledge export [选项]
376
- 打包 ~/.3lib/3rdloop/ 下的知识产物为 zip:
377
- - kb-data/docs/ 结构化知识文档(knowledge-import SKILL 的沉淀)
378
- - BadCase/ 已知问题规则/案例(泛化 SKILL 的沉淀)
379
- - db/Knowledge/ 索引清单(仅作人工评估参考,不入库)
310
+ 打包 <dataDir>/Knowledge/ 下的全部 JSON(index.json 登记表 + kn_*.json 知识条目)
380
311
  选项:
381
312
  --output <file> 输出路径(默认 ./3rdloop-knowledge-<日期>-<主机名>.zip)
382
- --since <date> 仅打包该日期后新增的文件(示例: 2026-09-01)
383
313
  --note <text> 备注(写入 manifest,如 "mp3agic 检视批次")
384
314
  --json 结构化输出
385
315
 
386
- 用法(维护者侧:收到 zip 后合入):
387
- 3rdloop knowledge import <zip> [--dry-run] [--allow-overwrite]
388
- --dry-run 仅预览将发生的变更,不写入
389
- --allow-overwrite 覆盖同名文件(默认跳过)
390
- 解包后 docs → ~/.3lib/3rdloop/kb-data/docs、BadCase → ~/.3lib/3rdloop/BadCase
316
+ 用法(维护者侧:收到 zip 后解包到收件箱):
317
+ 3rdloop knowledge import <zip> [--dry-run]
318
+ 解包到 ~/.3lib/3rdloop/knowledge-inbox/<来源主机>-<日期>/
319
+ 多个用户的包互不冲突;不覆盖本地知识库,人工评估后自行入库
391
320
 
392
321
  说明:
393
322
  - export 纯本地打包,不上传任何数据;可先解压自查内容再发送
394
- - 不含检索索引(.index/),导入后首次检索自动重建
395
- - 维护者审核后可把精选内容合入仓库 Archive/data 与 BadCase/,随版本分发
323
+ - 只含知识沉淀 JSON,不含 md / kb 文档 / BadCase
396
324
  `);
397
325
  }
398
326
 
@@ -401,6 +329,7 @@ export function printKnowledgeHelp() {
401
329
  * @param {object} opts
402
330
  * @param {string[]} opts.rest
403
331
  * @param {string} opts.user3libHome
332
+ * @param {string} opts.dataDir
404
333
  * @param {boolean} opts.jsonMode
405
334
  * @returns {Promise<number>}
406
335
  */
@@ -413,7 +342,6 @@ export async function cmdKnowledge({ rest = [], user3libHome = '', dataDir = '',
413
342
  let subIdx = -1;
414
343
  for (let i = 0; i < rest.length; i++) {
415
344
  if (rest[i].startsWith('--')) {
416
- // flag 值(如 --note 后面的文本)不是子命令,跳过
417
345
  if (_hasValue(rest[i]) && rest[i + 1] !== undefined && !rest[i + 1].startsWith('--')) i++;
418
346
  continue;
419
347
  }
@@ -427,14 +355,9 @@ export async function cmdKnowledge({ rest = [], user3libHome = '', dataDir = '',
427
355
  return rest.length === 0 ? 0 : 1;
428
356
  }
429
357
  const sub = rest[subIdx];
430
- if (sub === 'export') return knowledgeExport({ user3libHome, dataDir, rest: args, jsonMode });
431
- if (sub === 'import') return knowledgeImport({ user3libHome, dataDir, rest: args, jsonMode });
358
+ if (sub === 'export') return knowledgeExport({ dataDir, rest: args, jsonMode });
359
+ if (sub === 'import') return knowledgeImport({ user3libHome, rest: args, jsonMode });
432
360
  process.stderr.write(`未知子命令: ${sub}\n`);
433
361
  printKnowledgeHelp();
434
362
  return 1;
435
363
  }
436
-
437
- /** 该 flag 是否携带值(--output/--since/--note)。 */
438
- function _hasValue(flag) {
439
- return flag === '--output' || flag === '--since' || flag === '--note';
440
- }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ohos-cpf/3rdloop",
3
- "version": "0.0.10",
3
+ "version": "0.0.11",
4
4
  "description": "3rdLibraryLoop 三方库自动化检视 CLI:嵌入式复用核心引擎,任务生命周期管理(submit/run/wait/progress/abort/list/result/doctor)",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/vendor/VERSION CHANGED
@@ -1,3 +1,3 @@
1
- 0.0.10
2
- built=2026-09-04T07:38:56.069Z
1
+ 0.0.11
2
+ built=2026-09-04T07:50:10.913Z
3
3
  sha=84d14394dbb68bd4