@itradingai/aiwiki 0.2.5 → 0.2.7

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
@@ -1,9 +1,19 @@
1
+ ![AIWiki 宣传图](docs/assets/aiwiki-hero.png)
2
+
1
3
  # AIWiki
2
4
 
3
5
  AIWiki 是一个面向宿主 Agent 的本地知识库生产 CLI。用户把文章链接或正文发给 Codex、QClaw、OpenClaw、Claude Code 等 Agent,并使用“入库”触发词;宿主 Agent 负责读取网页或上下文,AIWiki 负责把内容写入本地知识库,并生成适合 Obsidian + Dataview 审阅的资料结构。
4
6
 
5
7
  AIWiki 的重点不是替代 Agent 抓网页,而是把 Agent 已经读到的内容稳定沉淀为可追踪、可复盘、可继续写作的本地知识库。
6
8
 
9
+ ## 最新动态
10
+
11
+ - `2026-05-09`:完成 npm 公开发布准备,补齐发布前的 README 与交付信息,并让 CLI 版本号与 `package.json` / 发布包保持一致,便于安装、排查与版本确认。
12
+ - `2026-05-08`:完成中文化体验收口,包括默认生成中文 prompt、中文状态输出、中文目标描述,以及 README 和使用文档的中文本地化。
13
+ - `2026-05-08`:强化 Obsidian 工作流,把 Review Queue、Claims Review 等审阅队列提升为一等入口,方便在知识库里持续审阅和回看入库内容。
14
+ - `2026-05-07`:新增 Codex skill 安装能力,并补上 Agent 协议安装引导,让宿主 Agent 在正式入库前更容易完成对接。
15
+ - `2026-05-07`:持续打磨初始化体验,修复 setup 提示问题,避免静默套用默认值,并把首次使用流程改成交互式引导。
16
+
7
17
  ## 安装与初始化
8
18
 
9
19
  一次性运行交互式 setup:
@@ -35,6 +45,34 @@ npm install -g @itradingai/aiwiki@latest
35
45
 
36
46
  要求 Node.js `>=20`。
37
47
 
48
+ ## 直接发给 AI 帮你安装
49
+
50
+ 如果你希望让 Codex、Claude Code、QClaw、OpenClaw 等 AI 直接帮你完成安装和配置,可以把下面这段话原样发给它,注意修改下你的知识库路径:
51
+
52
+ ```text
53
+ 请帮我安装并配置 AIWiki。
54
+
55
+ 要求:
56
+ 1. 先检查本机 Node.js 是否满足 >=20。
57
+ 2. 如果还没安装 AIWiki,就安装最新版 `@itradingai/aiwiki`。
58
+ 3. 执行 `aiwiki setup`,帮我完成知识库初始化;把知识库路径设为 `F:\knowledge_data\aiwiki`。
59
+ 4. 执行 `aiwiki agent list` 检查当前环境支持哪些宿主 Agent。
60
+ 5. 优先为当前 AI/Agent 安装 AIWiki 对接;如果能自动安装,就执行 `aiwiki agent install` 或对应的 `--agent` 命令。
61
+ 6. 如果当前 Agent 不支持自动安装,就执行 `aiwiki prompt agent`,然后把生成的对接协议整理好,告诉我应该粘贴到哪里。
62
+ 7. 完成后,再执行 `aiwiki doctor` 和 `aiwiki status`,确认安装和配置是否正常。
63
+ 8. 最后告诉我:
64
+ - 实际执行了哪些命令
65
+ - 知识库路径是什么
66
+ - Agent 对接是否完成
67
+ - 如果还差手动步骤,明确告诉我下一步怎么做
68
+ ```
69
+
70
+ 如果你已经确定知识库路径,也可以把上面第 3 步改成直接执行:
71
+
72
+ ```bash
73
+ aiwiki setup --path "F:\knowledge_data\aiwiki" --yes
74
+ ```
75
+
38
76
  ## 让宿主 Agent 学会 AIWiki
39
77
 
40
78
  初始化知识库后,先扫描本机支持的宿主 Agent:
package/dist/src/app.js CHANGED
@@ -7,7 +7,7 @@ import { flagBool, flagString, parseArgs } from "./args.js";
7
7
  import { ingestFile, ingestPayload } from "./ingest.js";
8
8
  import { CliError, writeLine } from "./output.js";
9
9
  import { confirmInit, directorySummary, doctor, exists, initWorkspace, promptForSetup, promptForInitPath, readConfig, resolveWorkspace, setDefaultWorkspace, statusSummary } from "./workspace.js";
10
- export const VERSION = "0.2.2";
10
+ export const VERSION = "0.2.7";
11
11
  export async function runCli(argv, streams = { stdout: process.stdout, stderr: process.stderr }) {
12
12
  try {
13
13
  const args = parseArgs(argv);
@@ -13,7 +13,10 @@ export function normalizePayload(raw, runStartedAt) {
13
13
  const legacyContent = isRecord(raw.content) ? stringValue(raw.content.text) : undefined;
14
14
  const legacyMetadata = isRecord(raw.metadata) ? raw.metadata : undefined;
15
15
  const warnings = [];
16
- const content = stringValue(sourceRaw.content) ?? legacyContent;
16
+ const contentRepair = repairMojibake(stringValue(sourceRaw.content) ?? legacyContent);
17
+ const titleRepair = repairMojibake(stringValue(sourceRaw.title));
18
+ const fetchNotesRepair = repairMojibake(stringValue(sourceRaw.fetch_notes));
19
+ const content = contentRepair.value;
17
20
  const fetcher = stringValue(sourceRaw.fetcher) ?? (legacyMetadata ? stringValue(legacyMetadata.fetcher) : undefined);
18
21
  const legacyCapturedAt = legacyMetadata ? stringValue(legacyMetadata.captured_at) : undefined;
19
22
  const capturedAt = stringValue(sourceRaw.captured_at) ?? legacyCapturedAt ?? runStartedAt;
@@ -42,6 +45,15 @@ export function normalizePayload(raw, runStartedAt) {
42
45
  if (typeof raw.target_kb === "string" && raw.target_kb.trim()) {
43
46
  warnings.push(`target_kb=${raw.target_kb} 已被单知识库流程忽略。`);
44
47
  }
48
+ if (contentRepair.repaired) {
49
+ warnings.push("source.content 检测到疑似 UTF-8 mojibake,已自动修复。");
50
+ }
51
+ if (titleRepair.repaired) {
52
+ warnings.push("source.title 检测到疑似 UTF-8 mojibake,已自动修复。");
53
+ }
54
+ if (fetchNotesRepair.repaired) {
55
+ warnings.push("source.fetch_notes 检测到疑似 UTF-8 mojibake,已自动修复。");
56
+ }
45
57
  if (fetchStatus === "failed" && content?.trim()) {
46
58
  throw new Error("source.content must be empty when source.fetch_status is failed");
47
59
  }
@@ -51,14 +63,14 @@ export function normalizePayload(raw, runStartedAt) {
51
63
  source: {
52
64
  kind,
53
65
  url: stringValue(sourceRaw.url),
54
- title: stringValue(sourceRaw.title),
66
+ title: titleRepair.value,
55
67
  author: stringValue(sourceRaw.author),
56
68
  platform: stringValue(sourceRaw.platform),
57
69
  content_format: stringValue(sourceRaw.content_format),
58
70
  content,
59
71
  fetcher,
60
72
  fetch_status: fetchStatus,
61
- fetch_notes: stringValue(sourceRaw.fetch_notes),
73
+ fetch_notes: fetchNotesRepair.value,
62
74
  captured_at: capturedAt,
63
75
  language: stringValue(sourceRaw.language) ?? (legacyMetadata ? stringValue(legacyMetadata.language) : undefined)
64
76
  },
@@ -93,3 +105,79 @@ function stringValue(value) {
93
105
  function isRecord(value) {
94
106
  return typeof value === "object" && value !== null && !Array.isArray(value);
95
107
  }
108
+ function repairMojibake(value) {
109
+ if (!value || !looksLikeUtf8Mojibake(value)) {
110
+ return { value, repaired: false };
111
+ }
112
+ const repaired = decodeUtf8BytesFromLatin1(value) ?? decodeUtf8BytesFromCp1252(value);
113
+ if (!repaired || scoreText(repaired) <= scoreText(value)) {
114
+ return { value, repaired: false };
115
+ }
116
+ return { value: repaired, repaired: true };
117
+ }
118
+ function looksLikeUtf8Mojibake(value) {
119
+ return /(?:Ã|Â|â€|“|”|’|å|ç|è|é|ä|æ|ï¼|ã€)/.test(value);
120
+ }
121
+ function decodeUtf8BytesFromLatin1(value) {
122
+ try {
123
+ return Buffer.from(value, "latin1").toString("utf8");
124
+ }
125
+ catch {
126
+ return undefined;
127
+ }
128
+ }
129
+ function decodeUtf8BytesFromCp1252(value) {
130
+ const bytes = [];
131
+ for (const char of value) {
132
+ const code = char.codePointAt(0);
133
+ if (code === undefined) {
134
+ continue;
135
+ }
136
+ const mapped = cp1252ReverseMap.get(code);
137
+ if (mapped !== undefined) {
138
+ bytes.push(mapped);
139
+ }
140
+ else if (code <= 0xff) {
141
+ bytes.push(code);
142
+ }
143
+ else {
144
+ return undefined;
145
+ }
146
+ }
147
+ return Buffer.from(bytes).toString("utf8");
148
+ }
149
+ function scoreText(value) {
150
+ const cjk = [...value].filter((char) => /[\u4e00-\u9fff]/u.test(char)).length;
151
+ const mojibake = (value.match(/(?:Ã|Â|â€|“|”|’|å|ç|è|é|ä|æ|ï¼|ã€)/g) ?? []).length;
152
+ const replacement = (value.match(/\uFFFD/g) ?? []).length;
153
+ return cjk * 4 - mojibake * 20 - replacement * 50;
154
+ }
155
+ const cp1252ReverseMap = new Map([
156
+ [0x20ac, 0x80],
157
+ [0x201a, 0x82],
158
+ [0x0192, 0x83],
159
+ [0x201e, 0x84],
160
+ [0x2026, 0x85],
161
+ [0x2020, 0x86],
162
+ [0x2021, 0x87],
163
+ [0x02c6, 0x88],
164
+ [0x2030, 0x89],
165
+ [0x0160, 0x8a],
166
+ [0x2039, 0x8b],
167
+ [0x0152, 0x8c],
168
+ [0x017d, 0x8e],
169
+ [0x2018, 0x91],
170
+ [0x2019, 0x92],
171
+ [0x201c, 0x93],
172
+ [0x201d, 0x94],
173
+ [0x2022, 0x95],
174
+ [0x2013, 0x96],
175
+ [0x2014, 0x97],
176
+ [0x02dc, 0x98],
177
+ [0x2122, 0x99],
178
+ [0x0161, 0x9a],
179
+ [0x203a, 0x9b],
180
+ [0x0153, 0x9c],
181
+ [0x017e, 0x9e],
182
+ [0x0178, 0x9f]
183
+ ]);
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@itradingai/aiwiki",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "type": "module",
5
5
  "description": "Agent-first local knowledge production CLI for a single knowledge base.",
6
6
  "license": "MIT",