@zleap-ai/sag-cli 0.2.1 → 0.3.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/README.en.md +23 -2
- package/README.md +23 -2
- package/dist/cli.js +205 -2
- package/dist/cli.js.map +1 -1
- package/package.json +3 -2
- package/skill/SKILL.md +59 -0
- package/skill/references/citation-rules.md +85 -0
- package/skill/references/cli-reference.md +223 -0
- package/skill/references/search-strategies.md +73 -0
package/README.en.md
CHANGED
|
@@ -99,6 +99,20 @@ sag search "how to wire MCP" --source <source-id> --top-k 5
|
|
|
99
99
|
|
|
100
100
|
A profile stores only `scheme://host[:port]`. The `/api/v1` prefix is added by the CLI — do not put it in the URL yourself.
|
|
101
101
|
|
|
102
|
+
## Use with the Skill
|
|
103
|
+
|
|
104
|
+
The Skill files (`skill/`) ship alongside this CLI. Copy them into your Agent's skills directory:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Claude Code
|
|
108
|
+
SKILL_SRC="$(npm root -g)/@zleap-ai/sag-cli"
|
|
109
|
+
cp -r "$SKILL_SRC/skill" ~/.claude/skills/sag-knowledge
|
|
110
|
+
|
|
111
|
+
# Codex
|
|
112
|
+
SKILL_SRC="$(npm root -g)/@zleap-ai/sag-cli"
|
|
113
|
+
cp -r "$SKILL_SRC/skill" ~/.codex/skills/sag-knowledge
|
|
114
|
+
```
|
|
115
|
+
|
|
102
116
|
## Command reference
|
|
103
117
|
|
|
104
118
|
```text
|
|
@@ -184,10 +198,17 @@ Tokens never appear in JSON, logs, or error output. Full error and exit codes li
|
|
|
184
198
|
|
|
185
199
|
## Further reading
|
|
186
200
|
|
|
201
|
+
### Using the CLI
|
|
202
|
+
|
|
203
|
+
- [CHANGELOG.md](CHANGELOG.md) — release notes
|
|
187
204
|
- Architecture and exit codes: [English](docs/architecture.md) / [简体中文](docs/architecture.zh-CN.md)
|
|
205
|
+
- [SKILL.md](SKILL.md) — teaches an Agent how to explore SAG efficiently
|
|
206
|
+
|
|
207
|
+
### Developing and contributing
|
|
208
|
+
|
|
188
209
|
- Local development and debugging: [English](docs/development.md) / [简体中文](docs/development.zh-CN.md)
|
|
189
210
|
- Compatibility matrix: [English](docs/compatibility.md) / [简体中文](docs/compatibility.zh-CN.md)
|
|
190
211
|
- OAuth and MCP authorization: [English](docs/oauth.md) / [简体中文](docs/oauth.zh-CN.md)
|
|
191
212
|
- Release process: [English](docs/release.md) / [简体中文](docs/release.zh-CN.md)
|
|
192
|
-
-
|
|
193
|
-
-
|
|
213
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
214
|
+
- [SECURITY.md](SECURITY.md)
|
package/README.md
CHANGED
|
@@ -99,6 +99,20 @@ sag search "MCP 如何接入" --source <source-id> --top-k 5
|
|
|
99
99
|
|
|
100
100
|
Profile URL 只保存 `scheme://host[:port]`。API 前缀 `/api/v1` 由 CLI 自动补齐,不要自己写。
|
|
101
101
|
|
|
102
|
+
## 搭配 Skill 使用
|
|
103
|
+
|
|
104
|
+
Skill 文件(`skill/`)随本 CLI 一起发布。安装 CLI 后复制到 Agent 的 skills 目录:
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
# Claude Code
|
|
108
|
+
SKILL_SRC="$(npm root -g)/@zleap-ai/sag-cli"
|
|
109
|
+
cp -r "$SKILL_SRC/skill" ~/.claude/skills/sag-knowledge
|
|
110
|
+
|
|
111
|
+
# Codex
|
|
112
|
+
SKILL_SRC="$(npm root -g)/@zleap-ai/sag-cli"
|
|
113
|
+
cp -r "$SKILL_SRC/skill" ~/.codex/skills/sag-knowledge
|
|
114
|
+
```
|
|
115
|
+
|
|
102
116
|
## 命令一览
|
|
103
117
|
|
|
104
118
|
```text
|
|
@@ -184,10 +198,17 @@ Token 不会出现在 JSON、日志或错误输出里。完整错误码与退出
|
|
|
184
198
|
|
|
185
199
|
## 深入阅读
|
|
186
200
|
|
|
201
|
+
### 使用指南
|
|
202
|
+
|
|
203
|
+
- [CHANGELOG.md](CHANGELOG.md) — 版本变更记录
|
|
187
204
|
- 架构与退出码:[中文](docs/architecture.zh-CN.md) / [English](docs/architecture.md)
|
|
205
|
+
- [SKILL.md](SKILL.md) — 教 Agent 怎么高效探索 SAG 知识库
|
|
206
|
+
|
|
207
|
+
### 开发与贡献
|
|
208
|
+
|
|
188
209
|
- 本地开发与调试:[中文](docs/development.zh-CN.md) / [English](docs/development.md)
|
|
189
210
|
- 兼容性矩阵:[中文](docs/compatibility.zh-CN.md) / [English](docs/compatibility.md)
|
|
190
211
|
- OAuth 与 MCP 授权:[中文](docs/oauth.zh-CN.md) / [English](docs/oauth.md)
|
|
191
212
|
- 发布流程:[中文](docs/release.zh-CN.md) / [English](docs/release.md)
|
|
192
|
-
-
|
|
193
|
-
-
|
|
213
|
+
- [CONTRIBUTING.md](CONTRIBUTING.md)
|
|
214
|
+
- [SECURITY.md](SECURITY.md)
|
package/dist/cli.js
CHANGED
|
@@ -6,7 +6,7 @@ import { confirm, password, select } from "@inquirer/prompts";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@zleap-ai/sag-cli",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.3.0",
|
|
10
10
|
description: "Command-line client and diagnostics for SAG knowledge bases",
|
|
11
11
|
type: "module",
|
|
12
12
|
bin: {
|
|
@@ -16,7 +16,8 @@ var package_default = {
|
|
|
16
16
|
"dist",
|
|
17
17
|
"README.md",
|
|
18
18
|
"README.en.md",
|
|
19
|
-
"LICENSE"
|
|
19
|
+
"LICENSE",
|
|
20
|
+
"skill"
|
|
20
21
|
],
|
|
21
22
|
scripts: {
|
|
22
23
|
build: "tsup",
|
|
@@ -1354,6 +1355,43 @@ var mcpDescriptorSchema = z6.object({
|
|
|
1354
1355
|
headers: z6.record(z6.string(), z6.string())
|
|
1355
1356
|
}).passthrough()
|
|
1356
1357
|
}).passthrough();
|
|
1358
|
+
var outlineItemSchema = z6.object({
|
|
1359
|
+
rank: z6.number().int(),
|
|
1360
|
+
heading: z6.string(),
|
|
1361
|
+
chunk_id: z6.string()
|
|
1362
|
+
}).passthrough();
|
|
1363
|
+
var outlineSchema = z6.object({
|
|
1364
|
+
document_id: z6.string(),
|
|
1365
|
+
filename: z6.string(),
|
|
1366
|
+
outline: z6.array(outlineItemSchema)
|
|
1367
|
+
}).passthrough();
|
|
1368
|
+
var grepMatchSchema = z6.object({
|
|
1369
|
+
chunk_id: z6.string(),
|
|
1370
|
+
heading: z6.string(),
|
|
1371
|
+
snippet: z6.string()
|
|
1372
|
+
}).passthrough();
|
|
1373
|
+
var grepResponseSchema = z6.object({
|
|
1374
|
+
pattern: z6.string(),
|
|
1375
|
+
matches: z6.array(grepMatchSchema),
|
|
1376
|
+
count: z6.number().int().nonnegative()
|
|
1377
|
+
}).passthrough();
|
|
1378
|
+
var readResponseSchema = z6.object({
|
|
1379
|
+
document_id: z6.string(),
|
|
1380
|
+
filename: z6.string(),
|
|
1381
|
+
total_lines: z6.number().int().nonnegative(),
|
|
1382
|
+
offset: z6.number().int().positive(),
|
|
1383
|
+
limit: z6.number().int().positive(),
|
|
1384
|
+
lines: z6.array(z6.string())
|
|
1385
|
+
}).passthrough();
|
|
1386
|
+
var entityContextSchema = z6.object({
|
|
1387
|
+
entity_id: z6.string(),
|
|
1388
|
+
name: z6.string(),
|
|
1389
|
+
type: z6.string(),
|
|
1390
|
+
description: z6.string(),
|
|
1391
|
+
context: z6.string(),
|
|
1392
|
+
source_id: z6.string(),
|
|
1393
|
+
source_name: z6.string()
|
|
1394
|
+
}).passthrough();
|
|
1357
1395
|
|
|
1358
1396
|
// src/api/client.ts
|
|
1359
1397
|
var SagClient = class {
|
|
@@ -1415,6 +1453,42 @@ var SagClient = class {
|
|
|
1415
1453
|
body: input
|
|
1416
1454
|
});
|
|
1417
1455
|
}
|
|
1456
|
+
outline(sourceId, documentId) {
|
|
1457
|
+
const params = new URLSearchParams({ document_id: documentId });
|
|
1458
|
+
return this.#request(
|
|
1459
|
+
`/api/v1/sources/${encodeURIComponent(sourceId)}/outline?${params}`,
|
|
1460
|
+
outlineSchema
|
|
1461
|
+
);
|
|
1462
|
+
}
|
|
1463
|
+
grep(sourceId, pattern, limit) {
|
|
1464
|
+
const params = new URLSearchParams({ pattern });
|
|
1465
|
+
if (limit !== void 0) {
|
|
1466
|
+
params.set("limit", String(limit));
|
|
1467
|
+
}
|
|
1468
|
+
return this.#request(
|
|
1469
|
+
`/api/v1/sources/${encodeURIComponent(sourceId)}/grep?${params}`,
|
|
1470
|
+
grepResponseSchema
|
|
1471
|
+
);
|
|
1472
|
+
}
|
|
1473
|
+
readDocument(sourceId, documentId, offset, limit) {
|
|
1474
|
+
const params = new URLSearchParams();
|
|
1475
|
+
if (offset !== void 0) {
|
|
1476
|
+
params.set("offset", String(offset));
|
|
1477
|
+
}
|
|
1478
|
+
if (limit !== void 0) {
|
|
1479
|
+
params.set("limit", String(limit));
|
|
1480
|
+
}
|
|
1481
|
+
return this.#request(
|
|
1482
|
+
`/api/v1/sources/${encodeURIComponent(sourceId)}/documents/${encodeURIComponent(documentId)}/read?${params}`,
|
|
1483
|
+
readResponseSchema
|
|
1484
|
+
);
|
|
1485
|
+
}
|
|
1486
|
+
getEntityContext(sourceId, name) {
|
|
1487
|
+
return this.#request(
|
|
1488
|
+
`/api/v1/sources/${encodeURIComponent(sourceId)}/entities/${encodeURIComponent(name)}/context`,
|
|
1489
|
+
entityContextSchema
|
|
1490
|
+
);
|
|
1491
|
+
}
|
|
1418
1492
|
async #request(path5, schema, options = {}) {
|
|
1419
1493
|
const authenticated = options.authenticated ?? true;
|
|
1420
1494
|
if (authenticated && !this.#token) {
|
|
@@ -2133,6 +2207,44 @@ async function documentStatus(client, sourceId, documentId) {
|
|
|
2133
2207
|
};
|
|
2134
2208
|
}
|
|
2135
2209
|
|
|
2210
|
+
// src/commands/knowledge.ts
|
|
2211
|
+
function requiredIdentifier2(value, label) {
|
|
2212
|
+
const normalized = value.trim();
|
|
2213
|
+
if (!normalized) {
|
|
2214
|
+
throw new CliError("INVALID_ARGUMENT", `${label} is required`, {
|
|
2215
|
+
exitCode: exitCodes.invalidArgument
|
|
2216
|
+
});
|
|
2217
|
+
}
|
|
2218
|
+
return normalized;
|
|
2219
|
+
}
|
|
2220
|
+
async function outline(client, sourceId, documentId) {
|
|
2221
|
+
return client.outline(
|
|
2222
|
+
requiredIdentifier2(sourceId, "Source ID"),
|
|
2223
|
+
requiredIdentifier2(documentId, "Document ID")
|
|
2224
|
+
);
|
|
2225
|
+
}
|
|
2226
|
+
async function grep(client, sourceId, pattern, limit) {
|
|
2227
|
+
return client.grep(
|
|
2228
|
+
requiredIdentifier2(sourceId, "Source ID"),
|
|
2229
|
+
requiredIdentifier2(pattern, "Pattern"),
|
|
2230
|
+
limit
|
|
2231
|
+
);
|
|
2232
|
+
}
|
|
2233
|
+
async function read(client, sourceId, documentId, offset, limit) {
|
|
2234
|
+
return client.readDocument(
|
|
2235
|
+
requiredIdentifier2(sourceId, "Source ID"),
|
|
2236
|
+
requiredIdentifier2(documentId, "Document ID"),
|
|
2237
|
+
offset,
|
|
2238
|
+
limit
|
|
2239
|
+
);
|
|
2240
|
+
}
|
|
2241
|
+
async function getEntityContext(client, sourceId, name) {
|
|
2242
|
+
return client.getEntityContext(
|
|
2243
|
+
requiredIdentifier2(sourceId, "Source ID"),
|
|
2244
|
+
requiredIdentifier2(name, "Entity name")
|
|
2245
|
+
);
|
|
2246
|
+
}
|
|
2247
|
+
|
|
2136
2248
|
// src/commands/mcp.ts
|
|
2137
2249
|
async function testLocalMcp(runtime2, input) {
|
|
2138
2250
|
const container = await discoverSagContainer({
|
|
@@ -2681,6 +2793,45 @@ function renderLocalMcp(value) {
|
|
|
2681
2793
|
].join("\n")}
|
|
2682
2794
|
`;
|
|
2683
2795
|
}
|
|
2796
|
+
function renderOutline(value) {
|
|
2797
|
+
const filename = typeof value.filename === "string" ? value.filename : "Unknown";
|
|
2798
|
+
const items = Array.isArray(value.outline) ? value.outline.filter(isRecord) : [];
|
|
2799
|
+
const lines = items.map(
|
|
2800
|
+
(item) => `${String(item.rank ?? 0).padStart(3)}. ${item.heading || "(untitled)"} (chunk_id=${item.chunk_id})`
|
|
2801
|
+
);
|
|
2802
|
+
return `${filename}
|
|
2803
|
+
${lines.join("\n")}
|
|
2804
|
+
`;
|
|
2805
|
+
}
|
|
2806
|
+
function renderGrep(value) {
|
|
2807
|
+
const matches = Array.isArray(value.matches) ? value.matches.filter(isRecord) : [];
|
|
2808
|
+
const lines = matches.map(
|
|
2809
|
+
(match, index) => `[${index + 1}] ${match.heading || "Match"}
|
|
2810
|
+
${match.snippet}`
|
|
2811
|
+
);
|
|
2812
|
+
return lines.length ? `${lines.join("\n\n")}
|
|
2813
|
+
` : "No matches.\n";
|
|
2814
|
+
}
|
|
2815
|
+
function renderRead(value) {
|
|
2816
|
+
const filename = typeof value.filename === "string" ? value.filename : "Unknown";
|
|
2817
|
+
const total = typeof value.total_lines === "number" ? value.total_lines : 0;
|
|
2818
|
+
const offset = typeof value.offset === "number" ? value.offset : 1;
|
|
2819
|
+
const lines = Array.isArray(value.lines) ? value.lines.map(String) : [];
|
|
2820
|
+
const header = `${filename} \xB7 lines ${offset}-${offset + lines.length - 1} / ${total}`;
|
|
2821
|
+
return `${header}
|
|
2822
|
+
${lines.join("")}`;
|
|
2823
|
+
}
|
|
2824
|
+
function renderEntityContext(value) {
|
|
2825
|
+
const name = typeof value.name === "string" ? value.name : "Unknown";
|
|
2826
|
+
const type = typeof value.type === "string" ? value.type : "";
|
|
2827
|
+
const source = typeof value.source_name === "string" ? value.source_name : "";
|
|
2828
|
+
const context = typeof value.context === "string" ? value.context : "";
|
|
2829
|
+
return `Entity: ${name}${type ? ` (${type})` : ""}
|
|
2830
|
+
Source: ${source}
|
|
2831
|
+
|
|
2832
|
+
${context}
|
|
2833
|
+
`;
|
|
2834
|
+
}
|
|
2684
2835
|
function renderHuman(value, quiet = false) {
|
|
2685
2836
|
if (quiet) {
|
|
2686
2837
|
if (Array.isArray(value)) {
|
|
@@ -2703,6 +2854,18 @@ function renderHuman(value, quiet = false) {
|
|
|
2703
2854
|
if (isRecord(value) && Array.isArray(value.sections) && "query" in value) {
|
|
2704
2855
|
return renderSearch(value);
|
|
2705
2856
|
}
|
|
2857
|
+
if (isRecord(value) && Array.isArray(value.outline) && "filename" in value) {
|
|
2858
|
+
return renderOutline(value);
|
|
2859
|
+
}
|
|
2860
|
+
if (isRecord(value) && Array.isArray(value.matches) && "pattern" in value) {
|
|
2861
|
+
return renderGrep(value);
|
|
2862
|
+
}
|
|
2863
|
+
if (isRecord(value) && Array.isArray(value.lines) && "total_lines" in value) {
|
|
2864
|
+
return renderRead(value);
|
|
2865
|
+
}
|
|
2866
|
+
if (isRecord(value) && "entity_id" in value && "context" in value) {
|
|
2867
|
+
return renderEntityContext(value);
|
|
2868
|
+
}
|
|
2706
2869
|
if (isRecord(value) && value.provider === "docker-stdio" && "protocolVersion" in value && "container" in value) {
|
|
2707
2870
|
return renderLocalMcp(value);
|
|
2708
2871
|
}
|
|
@@ -2946,6 +3109,46 @@ function addKnowledgeCommands(program, dependencies2) {
|
|
|
2946
3109
|
);
|
|
2947
3110
|
}
|
|
2948
3111
|
);
|
|
3112
|
+
program.command("outline").argument("<document-id>").requiredOption("--source <source-id>").action(async (documentId, options) => {
|
|
3113
|
+
const context = await runtime(program, dependencies2);
|
|
3114
|
+
emit(
|
|
3115
|
+
program,
|
|
3116
|
+
dependencies2,
|
|
3117
|
+
await outline(context.client, options.source, documentId)
|
|
3118
|
+
);
|
|
3119
|
+
});
|
|
3120
|
+
program.command("grep").argument("<pattern>").requiredOption("--source <source-id>").option("--limit <number>", "Maximum result count", parseInteger).action(async (pattern, options) => {
|
|
3121
|
+
const context = await runtime(program, dependencies2);
|
|
3122
|
+
emit(
|
|
3123
|
+
program,
|
|
3124
|
+
dependencies2,
|
|
3125
|
+
await grep(context.client, options.source, pattern, options.limit)
|
|
3126
|
+
);
|
|
3127
|
+
});
|
|
3128
|
+
program.command("read").argument("<document-id>").requiredOption("--source <source-id>").option("--offset <number>", "Starting line (1-based)", parseInteger).option("--limit <number>", "Lines to read", parseInteger).action(
|
|
3129
|
+
async (documentId, options) => {
|
|
3130
|
+
const context = await runtime(program, dependencies2);
|
|
3131
|
+
emit(
|
|
3132
|
+
program,
|
|
3133
|
+
dependencies2,
|
|
3134
|
+
await read(
|
|
3135
|
+
context.client,
|
|
3136
|
+
options.source,
|
|
3137
|
+
documentId,
|
|
3138
|
+
options.offset,
|
|
3139
|
+
options.limit
|
|
3140
|
+
)
|
|
3141
|
+
);
|
|
3142
|
+
}
|
|
3143
|
+
);
|
|
3144
|
+
program.command("get-entity").argument("<name>").requiredOption("--source <source-id>").action(async (name, options) => {
|
|
3145
|
+
const context = await runtime(program, dependencies2);
|
|
3146
|
+
emit(
|
|
3147
|
+
program,
|
|
3148
|
+
dependencies2,
|
|
3149
|
+
await getEntityContext(context.client, options.source, name)
|
|
3150
|
+
);
|
|
3151
|
+
});
|
|
2949
3152
|
}
|
|
2950
3153
|
function addLocalMcpCommands(program, dependencies2) {
|
|
2951
3154
|
const mcp = program.command("mcp").description("Verify local SAG MCP");
|