@c4a/context 0.6.1 → 0.6.2
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 +17 -6
- package/README.zh-CN.md +121 -0
- package/contracts.d.ts +5 -0
- package/docs/README.md +11 -8
- package/docs/getting-started.md +63 -28
- package/docs/guides/agent-dialogue.md +38 -350
- package/docs/guides/agent-guide.md +143 -93
- package/docs/guides/package-outputs.md +87 -25
- package/docs/reference/package-templates.md +121 -32
- package/docs/reference/project-api.md +225 -84
- package/docs/reference/template-variables.md +32 -15
- package/index.d.ts +13 -5
- package/index.js +99 -28
- package/package.json +1 -1
- package/phases.d.ts +65 -6
- package/templates/package-templates/kb/AGENTS.md +13 -16
- package/templates/package-templates/kb/skills/knowledge-query/SKILL.md +101 -177
- package/templates/package-templates/kb/skills/knowledge-query/scripts/search.mjs +264 -0
- package/templates/package-templates/kb/wikis/index.md +8 -8
- package/templates/package-templates.zh-CN/kb/AGENTS.md +30 -0
- package/templates/package-templates.zh-CN/kb/skills/knowledge-query/SKILL.md +99 -0
- package/templates/package-templates.zh-CN/kb/skills/knowledge-query/scripts/search.mjs +264 -0
- package/templates/package-templates.zh-CN/kb/wikis/index.md +39 -0
- package/templates/package-templates.zh-CN/llms/llms.txt +8 -0
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: {{skillName}}
|
|
3
|
+
description: 查询 {{displayName}} 中经过审核、可追溯来源的知识。用于查找包内实体、API、行为、流程、约束、决策、排障信息、关系和覆盖范围。
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# 知识查询
|
|
7
|
+
|
|
8
|
+
只根据本包包含的已批准知识回答。先通过索引缩小范围,再打开具体页面;正文是事实证据,frontmatter 摘要只用于导航。
|
|
9
|
+
|
|
10
|
+
## 查询步骤
|
|
11
|
+
|
|
12
|
+
1. 判断请求属于实体查找、解释、流程、规则、关系、细节还是覆盖检查。
|
|
13
|
+
2. 从最相关的根索引开始,例如 `{{wikisRoot}}/index.md`,再沿链接进入具体页面。
|
|
14
|
+
3. 只读取回答所需的章节;frontmatter 用来选择范围,不作为事实依据。
|
|
15
|
+
4. 查询关系或影响范围时,先检查 `context-build-inventory.json` 中的 `structure.edge_records`,再读取两端页面。
|
|
16
|
+
5. 结论只能来自可见章节正文和来源支持的边记录,并引用对应页面或章节。
|
|
17
|
+
6. 如果包内没有证据,明确报告缺口和已经检查的范围,不要根据邻近内容推断。
|
|
18
|
+
|
|
19
|
+
## 知识根目录
|
|
20
|
+
|
|
21
|
+
| 根目录 | 用途 |
|
|
22
|
+
|---|---|
|
|
23
|
+
| `{{wikisRoot}}/` | 来自 codegraph、business、product 的结构化实体和关系。 |
|
|
24
|
+
| `{{guidesRoot}}/` | 架构、流程、FAQ、决策、故障和排障说明。 |
|
|
25
|
+
| `{{rulesRoot}}/` | 标准、约束、验收条件和测试场景。 |
|
|
26
|
+
| `{{featsRoot}}/` | 被选择进入包内的功能知识。 |
|
|
27
|
+
|
|
28
|
+
缺少某个根目录表示当前知识包没有选择该类别。
|
|
29
|
+
|
|
30
|
+
## 按意图导航
|
|
31
|
+
|
|
32
|
+
| 意图 | 第一步 |
|
|
33
|
+
|---|---|
|
|
34
|
+
| 模糊主题或未知名称 | 打开可能相关的根索引,从分组中选择候选页面。 |
|
|
35
|
+
| 指定实体、API、领域或动作 | 打开匹配页面或最近的分组索引;有歧义时列出候选。 |
|
|
36
|
+
| 架构、流程、FAQ、决策或故障 | 从 `{{guidesRoot}}/index.md` 开始。 |
|
|
37
|
+
| 标准、约束、验收或测试 | 从 `{{rulesRoot}}/index.md` 开始。 |
|
|
38
|
+
| 关系或影响范围 | 检查类型化边,再读取两端页面。 |
|
|
39
|
+
| 已知页面中的细节 | 读取对应 `context:section`。 |
|
|
40
|
+
| 覆盖范围或缺口 | 检查根索引和 `context-build-inventory.json`。 |
|
|
41
|
+
|
|
42
|
+
## 证据契约
|
|
43
|
+
|
|
44
|
+
| 证据 | 可支持的内容 |
|
|
45
|
+
|---|---|
|
|
46
|
+
| 页面路径 | 页面身份和引用位置。 |
|
|
47
|
+
| frontmatter 的标题、描述、稳定节点标识和标签 | 导航和范围选择。 |
|
|
48
|
+
| `context:section` 的 id、kind、`source_ref` | 章节身份、引用位置和来源边界。 |
|
|
49
|
+
| 读者可见的章节正文 | 事实结论的主要依据。 |
|
|
50
|
+
| `context-build-inventory.json` 的边记录 | 类型化关系证据。 |
|
|
51
|
+
| 根索引和构建清单 | 包的范围和覆盖情况。 |
|
|
52
|
+
|
|
53
|
+
不要根据页面同时出现来推断关系。如果 `source_ref` 指向未随包分发的来源,只能引用已经过审核的可见章节,不能扩展到正文没有表达的内容。
|
|
54
|
+
|
|
55
|
+
## 搜索兜底
|
|
56
|
+
|
|
57
|
+
只有索引和页面结构无法确定范围,或候选页面过大不适合整页读取时才搜索。精确名称、API、配置键、路径或错误字符串可先用 `rg`;包含多个关键词、中文短语或多个大型索引时,执行本 Skill 随附的 [`scripts/search.mjs`](scripts/search.mjs) 做 BM25 排序:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
node <当前 knowledge-query Skill 目录>/scripts/search.mjs --query '<关键词>' --limit 8
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
脚本会从原始包或 TTKB 安装目录定位 `{{packageName}}`;无法唯一定位时,增加 `--root <包含 context-build-inventory.json 的包目录>`。它按 Markdown 标题和固定行块机械切分,返回路径、行号、标题和短预览,不判断内容语义。
|
|
64
|
+
|
|
65
|
+
搜索命中只是线索。回答前必须打开命中的页面和章节;关系或影响范围仍以 `context-build-inventory.json` 的类型化边为准,不能用 BM25 分数或文本共现替代关系证据。
|
|
66
|
+
|
|
67
|
+
## 引用与缺口
|
|
68
|
+
|
|
69
|
+
引用应紧跟结论:
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
页面: <结论> [<root>/path/page.md]
|
|
73
|
+
章节: <结论> [<root>/path/page.md#section-id]
|
|
74
|
+
来源绑定: <结论> [<root>/path/page.md#section-id, source_ref]
|
|
75
|
+
关系: <结论> [context-build-inventory.json#structure.edge_records edge:<type>]
|
|
76
|
+
覆盖: <结论> [context-build-inventory.json]
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
没有证据时返回:
|
|
80
|
+
|
|
81
|
+
```text
|
|
82
|
+
缺口:本知识包没有包含 <缺失点> 的证据。
|
|
83
|
+
已检查:<索引、页面或构建产物>。
|
|
84
|
+
下一步来源:<已知时填写来源、页面或 source_ref>。
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
“本包没有证据”不等于“事实不成立”。禁止使用模型记忆、旧对话或包外源码填补缺口。
|
|
88
|
+
|
|
89
|
+
## 包边界
|
|
90
|
+
|
|
91
|
+
- 包内兼容 OKF 的根目录是本 Skill 的事实来源。
|
|
92
|
+
- 本包只包含 Context 工作区选择并批准的知识,不声明完整覆盖底层产品或代码库。
|
|
93
|
+
- 已有更窄索引范围可以回答时,不要扫描所有页面或关系边。
|
|
94
|
+
|
|
95
|
+
已批准知识文件:`{{knowledgeCount}}`
|
|
96
|
+
|
|
97
|
+
## 模板作者建议
|
|
98
|
+
|
|
99
|
+
这是可直接工作的通用查询 Skill。正式发布前,如果知识包存在专用术语、常见用户意图、推荐入口、已知边界或固定任务流程,作者应修改 description、路由表和包边界说明;如果通用行为已经足够,应在 Context 的包模板 Review 中明确接受未修改模板。
|
|
@@ -0,0 +1,264 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
4
|
+
import { dirname, join, relative, resolve } from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const PACKAGE_NAME = "{{packageName}}";
|
|
8
|
+
const STANDARD_ROOTS = ["wikis", "guides", "rules", "feats"];
|
|
9
|
+
const MAX_CHUNK_LINES = 80;
|
|
10
|
+
const CHUNK_OVERLAP = 5;
|
|
11
|
+
|
|
12
|
+
function usage() {
|
|
13
|
+
return [
|
|
14
|
+
"Search an approved knowledge package with deterministic BM25 ranking.",
|
|
15
|
+
"",
|
|
16
|
+
"Usage:",
|
|
17
|
+
" node search.mjs --query <text> [--root <package-root>] [--limit <n>] [--json]",
|
|
18
|
+
" node search.mjs <text> [--root <package-root>] [--limit <n>] [--json]",
|
|
19
|
+
"",
|
|
20
|
+
"The package root is detected from context-build-inventory.json when possible.",
|
|
21
|
+
].join("\n");
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function parseArgs(argv) {
|
|
25
|
+
const positional = [];
|
|
26
|
+
const options = { root: undefined, query: undefined, limit: 8, json: false };
|
|
27
|
+
for (let index = 0; index < argv.length; index++) {
|
|
28
|
+
const arg = argv[index];
|
|
29
|
+
if (arg === "--help" || arg === "-h") return { help: true };
|
|
30
|
+
if (arg === "--json") {
|
|
31
|
+
options.json = true;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
if (arg === "--root" || arg === "--query" || arg === "--limit") {
|
|
35
|
+
const value = argv[++index];
|
|
36
|
+
if (value === undefined) throw new Error(`missing value for ${arg}`);
|
|
37
|
+
if (arg === "--root") options.root = value;
|
|
38
|
+
else if (arg === "--query") options.query = value;
|
|
39
|
+
else options.limit = Number(value);
|
|
40
|
+
continue;
|
|
41
|
+
}
|
|
42
|
+
positional.push(arg);
|
|
43
|
+
}
|
|
44
|
+
if (!Number.isInteger(options.limit) || options.limit < 1 || options.limit > 50) {
|
|
45
|
+
throw new Error("--limit must be an integer between 1 and 50");
|
|
46
|
+
}
|
|
47
|
+
options.query ??= positional.join(" ");
|
|
48
|
+
if (options.query.trim().length === 0) throw new Error("query must not be empty");
|
|
49
|
+
return options;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function isPackageRoot(directory) {
|
|
53
|
+
return existsSync(join(directory, "context-build-inventory.json"));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function ancestorPackageRoot(start) {
|
|
57
|
+
let current = resolve(start);
|
|
58
|
+
while (true) {
|
|
59
|
+
if (isPackageRoot(current)) return current;
|
|
60
|
+
const parent = dirname(current);
|
|
61
|
+
if (parent === current) return undefined;
|
|
62
|
+
current = parent;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function findInventories(directory, depth = 0) {
|
|
67
|
+
if (!existsSync(directory) || depth > 5) return [];
|
|
68
|
+
const inventory = join(directory, "context-build-inventory.json");
|
|
69
|
+
if (existsSync(inventory)) return [inventory];
|
|
70
|
+
const matches = [];
|
|
71
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
72
|
+
if (!entry.isDirectory()) continue;
|
|
73
|
+
matches.push(...findInventories(join(directory, entry.name), depth + 1));
|
|
74
|
+
}
|
|
75
|
+
return matches;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
function inventoryPackageName(inventoryPath) {
|
|
79
|
+
try {
|
|
80
|
+
return JSON.parse(readFileSync(inventoryPath, "utf8"))?.package?.name;
|
|
81
|
+
} catch {
|
|
82
|
+
return undefined;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function resolvePackageRoot(explicitRoot) {
|
|
87
|
+
if (explicitRoot !== undefined) {
|
|
88
|
+
const root = resolve(explicitRoot);
|
|
89
|
+
if (!isPackageRoot(root)) throw new Error(`package root has no context-build-inventory.json: ${root}`);
|
|
90
|
+
return root;
|
|
91
|
+
}
|
|
92
|
+
const scriptRoot = ancestorPackageRoot(dirname(fileURLToPath(import.meta.url)));
|
|
93
|
+
if (scriptRoot !== undefined) return scriptRoot;
|
|
94
|
+
const cwdRoot = ancestorPackageRoot(process.cwd());
|
|
95
|
+
if (cwdRoot !== undefined) return cwdRoot;
|
|
96
|
+
const inventories = findInventories(join(process.cwd(), ".ttkb", "packages"));
|
|
97
|
+
const matching = inventories.filter((path) => inventoryPackageName(path) === PACKAGE_NAME);
|
|
98
|
+
if (matching.length === 1) return dirname(matching[0]);
|
|
99
|
+
if (matching.length > 1) {
|
|
100
|
+
throw new Error(`multiple installed packages named ${PACKAGE_NAME}; pass --root explicitly`);
|
|
101
|
+
}
|
|
102
|
+
throw new Error("cannot locate the knowledge package; pass --root <package-root>");
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function knowledgeRoots(packageRoot) {
|
|
106
|
+
const inventoryPath = join(packageRoot, "context-build-inventory.json");
|
|
107
|
+
let declared = [];
|
|
108
|
+
try {
|
|
109
|
+
const inventory = JSON.parse(readFileSync(inventoryPath, "utf8"));
|
|
110
|
+
declared = Object.values(inventory?.package?.distribution?.roots ?? {})
|
|
111
|
+
.filter((value) => typeof value === "string");
|
|
112
|
+
} catch {
|
|
113
|
+
declared = [];
|
|
114
|
+
}
|
|
115
|
+
return [...new Set([...declared, ...STANDARD_ROOTS])]
|
|
116
|
+
.map((root) => join(packageRoot, root))
|
|
117
|
+
.filter((root) => existsSync(root) && statSync(root).isDirectory());
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function markdownFiles(directory) {
|
|
121
|
+
const files = [];
|
|
122
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
123
|
+
const path = join(directory, entry.name);
|
|
124
|
+
if (entry.isDirectory()) files.push(...markdownFiles(path));
|
|
125
|
+
else if (entry.isFile() && entry.name.endsWith(".md")) files.push(path);
|
|
126
|
+
}
|
|
127
|
+
return files;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function headingChunks(content) {
|
|
131
|
+
const lines = content.split(/\r?\n/u);
|
|
132
|
+
const starts = [];
|
|
133
|
+
for (let index = 0; index < lines.length; index++) {
|
|
134
|
+
if (/^#{1,6}\s+\S/u.test(lines[index] ?? "")) starts.push(index);
|
|
135
|
+
}
|
|
136
|
+
if (starts.length === 0) starts.push(0);
|
|
137
|
+
const chunks = [];
|
|
138
|
+
for (let headingIndex = 0; headingIndex < starts.length; headingIndex++) {
|
|
139
|
+
const start = starts[headingIndex] ?? 0;
|
|
140
|
+
const end = starts[headingIndex + 1] ?? lines.length;
|
|
141
|
+
const heading = (lines[start] ?? "").replace(/^#{1,6}\s+/u, "").trim() || "Document";
|
|
142
|
+
for (let offset = start; offset < end; offset += MAX_CHUNK_LINES - CHUNK_OVERLAP) {
|
|
143
|
+
const chunkEnd = Math.min(end, offset + MAX_CHUNK_LINES);
|
|
144
|
+
chunks.push({
|
|
145
|
+
heading,
|
|
146
|
+
startLine: offset + 1,
|
|
147
|
+
endLine: chunkEnd,
|
|
148
|
+
text: lines.slice(offset, chunkEnd).join("\n"),
|
|
149
|
+
});
|
|
150
|
+
if (chunkEnd === end) break;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
return chunks;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function tokens(text) {
|
|
157
|
+
const normalized = text.toLocaleLowerCase();
|
|
158
|
+
const result = normalized.match(/[a-z0-9@._:/-]+|\p{Script=Han}+/gu) ?? [];
|
|
159
|
+
const expanded = [];
|
|
160
|
+
for (const token of result) {
|
|
161
|
+
expanded.push(token);
|
|
162
|
+
if (/^\p{Script=Han}+$/u.test(token)) {
|
|
163
|
+
for (let index = 0; index < token.length - 1; index++) expanded.push(token.slice(index, index + 2));
|
|
164
|
+
} else {
|
|
165
|
+
expanded.push(...token.split(/[^a-z0-9]+/u).filter((part) => part.length > 1));
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
return expanded.filter((token) => token.length > 0);
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
function termFrequency(items) {
|
|
172
|
+
const counts = new Map();
|
|
173
|
+
for (const item of items) counts.set(item, (counts.get(item) ?? 0) + 1);
|
|
174
|
+
return counts;
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function preview(text, queryTerms, query) {
|
|
178
|
+
const lines = text.split(/\r?\n/u).map((line) => line.trim()).filter(Boolean);
|
|
179
|
+
const loweredQuery = query.toLocaleLowerCase().trim();
|
|
180
|
+
const matching = [...lines].sort((left, right) => {
|
|
181
|
+
const leftText = left.toLocaleLowerCase();
|
|
182
|
+
const rightText = right.toLocaleLowerCase();
|
|
183
|
+
const lineScore = (value) =>
|
|
184
|
+
(value.includes(loweredQuery) ? queryTerms.length + 2 : 0)
|
|
185
|
+
+ queryTerms.filter((term) => value.includes(term)).length;
|
|
186
|
+
return lineScore(rightText) - lineScore(leftText);
|
|
187
|
+
})[0];
|
|
188
|
+
return (matching ?? lines[0] ?? "").replace(/\s+/gu, " ").slice(0, 240);
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function search(packageRoot, query, limit) {
|
|
192
|
+
const documents = [];
|
|
193
|
+
for (const root of knowledgeRoots(packageRoot)) {
|
|
194
|
+
for (const file of markdownFiles(root)) {
|
|
195
|
+
const path = relative(packageRoot, file).split("\\").join("/");
|
|
196
|
+
const content = readFileSync(file, "utf8");
|
|
197
|
+
for (const chunk of headingChunks(content)) {
|
|
198
|
+
const weightedText = `${path} ${chunk.heading} ${chunk.heading} ${chunk.text}`;
|
|
199
|
+
const chunkTokens = tokens(weightedText);
|
|
200
|
+
documents.push({ ...chunk, path, tokens: chunkTokens, tf: termFrequency(chunkTokens) });
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
const queryTerms = [...new Set(tokens(query))];
|
|
205
|
+
const documentFrequency = new Map();
|
|
206
|
+
for (const term of queryTerms) {
|
|
207
|
+
documentFrequency.set(term, documents.filter((document) => document.tf.has(term)).length);
|
|
208
|
+
}
|
|
209
|
+
const averageLength = documents.reduce((sum, document) => sum + document.tokens.length, 0)
|
|
210
|
+
/ Math.max(documents.length, 1);
|
|
211
|
+
const loweredQuery = query.toLocaleLowerCase().trim();
|
|
212
|
+
const scored = documents.map((document) => {
|
|
213
|
+
let score = 0;
|
|
214
|
+
for (const term of queryTerms) {
|
|
215
|
+
const frequency = document.tf.get(term) ?? 0;
|
|
216
|
+
if (frequency === 0) continue;
|
|
217
|
+
const matches = documentFrequency.get(term) ?? 0;
|
|
218
|
+
const idf = Math.log(1 + (documents.length - matches + 0.5) / (matches + 0.5));
|
|
219
|
+
const denominator = frequency + 1.2 * (0.25 + 0.75 * document.tokens.length / Math.max(averageLength, 1));
|
|
220
|
+
score += idf * frequency * 2.2 / denominator;
|
|
221
|
+
}
|
|
222
|
+
const searchable = `${document.path}\n${document.heading}\n${document.text}`.toLocaleLowerCase();
|
|
223
|
+
if (searchable.includes(loweredQuery)) score += 4;
|
|
224
|
+
if (`${document.path} ${document.heading}`.toLocaleLowerCase().includes(loweredQuery)) score += 2;
|
|
225
|
+
return { ...document, score };
|
|
226
|
+
}).filter((document) => document.score > 0);
|
|
227
|
+
scored.sort((left, right) =>
|
|
228
|
+
right.score - left.score
|
|
229
|
+
|| left.path.localeCompare(right.path)
|
|
230
|
+
|| left.startLine - right.startLine
|
|
231
|
+
);
|
|
232
|
+
return scored.slice(0, limit).map((document) => ({
|
|
233
|
+
score: Number(document.score.toFixed(4)),
|
|
234
|
+
path: document.path,
|
|
235
|
+
heading: document.heading,
|
|
236
|
+
start_line: document.startLine,
|
|
237
|
+
end_line: document.endLine,
|
|
238
|
+
preview: preview(document.text, queryTerms, query),
|
|
239
|
+
}));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
try {
|
|
243
|
+
const options = parseArgs(process.argv.slice(2));
|
|
244
|
+
if (options.help) {
|
|
245
|
+
process.stdout.write(`${usage()}\n`);
|
|
246
|
+
process.exit(0);
|
|
247
|
+
}
|
|
248
|
+
const root = resolvePackageRoot(options.root);
|
|
249
|
+
const results = search(root, options.query, options.limit);
|
|
250
|
+
if (options.json) {
|
|
251
|
+
process.stdout.write(`${JSON.stringify({ package: PACKAGE_NAME, root, query: options.query, results }, null, 2)}\n`);
|
|
252
|
+
} else if (results.length === 0) {
|
|
253
|
+
process.stdout.write(`No matching knowledge found for: ${options.query}\n`);
|
|
254
|
+
} else {
|
|
255
|
+
for (const [index, result] of results.entries()) {
|
|
256
|
+
process.stdout.write(`${index + 1}. ${result.path}:${result.start_line}-${result.end_line} [${result.score}] ${result.heading}\n`);
|
|
257
|
+
process.stdout.write(` ${result.preview}\n`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
} catch (error) {
|
|
261
|
+
process.stderr.write(`knowledge-search: ${error instanceof Error ? error.message : String(error)}\n`);
|
|
262
|
+
process.stderr.write(`${usage()}\n`);
|
|
263
|
+
process.exit(2);
|
|
264
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
---
|
|
2
|
+
type: Knowledge Bundle
|
|
3
|
+
title: "{{displayName}}"
|
|
4
|
+
description: "由 Context 工作区生成的已批准知识包。"
|
|
5
|
+
tags:
|
|
6
|
+
- context
|
|
7
|
+
- knowledge-base
|
|
8
|
+
timestamp: "{{knowledgeTimestamp}}"
|
|
9
|
+
resource: "context://package/{{packageName}}/{{wikisRoot}}"
|
|
10
|
+
package: "{{packageName}}"
|
|
11
|
+
package_kind: "{{packageKind}}"
|
|
12
|
+
knowledge_count: {{knowledgeCount}}
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
<!-- context:template
|
|
16
|
+
这是一个起始模板。`context build` 渲染后会将其复制到
|
|
17
|
+
`dist/<package-name>/{{wikisRoot}}/index.md`。
|
|
18
|
+
|
|
19
|
+
以 context:template 开头的模板注释不会进入构建产物。
|
|
20
|
+
修改前请阅读模板变量手册:
|
|
21
|
+
node_modules/@c4a/context/docs/reference/template-variables.md
|
|
22
|
+
|
|
23
|
+
正式发布前,应补充知识包范围、目标读者、推荐阅读顺序、已知缺口、使用场景或任务入口。
|
|
24
|
+
小目录会直接列出内容,超过导航阈值时会链接到子目录 index.md。
|
|
25
|
+
-->
|
|
26
|
+
|
|
27
|
+
# {{displayName}}
|
|
28
|
+
|
|
29
|
+
本知识包包含 `{{knowledgeCount}}` 个已批准知识页面。请先使用本索引定位范围,再打开具体知识页获取可追溯来源的细节。
|
|
30
|
+
|
|
31
|
+
## 内容
|
|
32
|
+
|
|
33
|
+
{{knowledgeGroupsMarkdown}}
|
|
34
|
+
|
|
35
|
+
## 使用方式
|
|
36
|
+
|
|
37
|
+
- 从上方导航开始,再进入知识页或子目录索引。
|
|
38
|
+
- 安装为 Agent 知识包后,可以使用随包提供的 knowledge-query Skill。
|
|
39
|
+
- 如果包需要专用范围、已知缺口、阅读路径或任务入口,作者应替换或编辑本通用索引;否则应在 Context 包模板 Review 中明确接受未修改模板。
|