@aliyunrds/ctxdb 0.0.4 → 0.0.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.
@@ -1,7 +1,10 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  recallTurn
4
- } from "../chunk-7NOXAU2X.js";
4
+ } from "../chunk-LIC44DR6.js";
5
+ import {
6
+ isCircuitOpen
7
+ } from "../chunk-Q2EEP4CE.js";
5
8
  import {
6
9
  HttpClient,
7
10
  agentFromArgvWithFallback,
@@ -9,7 +12,7 @@ import {
9
12
  isComplete,
10
13
  load,
11
14
  setDebug
12
- } from "../chunk-QSSNPN3M.js";
15
+ } from "../chunk-S45GOYUU.js";
13
16
 
14
17
  // src/lib/warmup-recall.ts
15
18
  import { execSync } from "child_process";
@@ -63,7 +66,7 @@ async function warmupRecall(cwd, cfg, client) {
63
66
  }
64
67
 
65
68
  // src/hooks/session-start.ts
66
- var HOOK_TIMEOUT_MS = 3e4;
69
+ var HOOK_TIMEOUT_MS = 5e3;
67
70
  async function readStdinJson() {
68
71
  let raw = "";
69
72
  for await (const chunk of process.stdin) raw += chunk;
@@ -97,6 +100,10 @@ async function main() {
97
100
  debug("warmup", "skip (config incomplete or warmupRecall=false)");
98
101
  return 0;
99
102
  }
103
+ if (isCircuitOpen(agent, cfg.baseUrl)) {
104
+ debug("warmup", "skip (circuit open)");
105
+ return 0;
106
+ }
100
107
  const client = new HttpClient({
101
108
  baseUrl: cfg.baseUrl,
102
109
  apiKey: cfg.apiKey,
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ isCircuitOpen,
4
+ isConnectionError,
5
+ resetCircuit,
6
+ tripCircuit
7
+ } from "../chunk-Q2EEP4CE.js";
2
8
  import {
3
9
  CtxdbError,
4
10
  HttpClient,
@@ -6,7 +12,7 @@ import {
6
12
  debug,
7
13
  load,
8
14
  setDebug
9
- } from "../chunk-QSSNPN3M.js";
15
+ } from "../chunk-S45GOYUU.js";
10
16
 
11
17
  // src/lib/capture-orchestrator.ts
12
18
  import {
@@ -15,6 +21,26 @@ import {
15
21
  selectTurnMessages
16
22
  } from "@aliyunrds/ctxdb-shared";
17
23
 
24
+ // src/lib/git-context.ts
25
+ import { execSync } from "child_process";
26
+ function getGitContext() {
27
+ try {
28
+ const raw = execSync("git rev-parse --show-toplevel --abbrev-ref HEAD", {
29
+ encoding: "utf-8",
30
+ timeout: 3e3,
31
+ stdio: ["ignore", "pipe", "ignore"]
32
+ }).trim();
33
+ const lines = raw.split("\n");
34
+ if (lines.length < 2) return null;
35
+ const project = lines[0].split("/").pop();
36
+ if (!project) return null;
37
+ const branch = lines[1];
38
+ return { project, branch };
39
+ } catch {
40
+ return null;
41
+ }
42
+ }
43
+
18
44
  // src/lib/transcript.ts
19
45
  import { existsSync, readFileSync } from "fs";
20
46
  var ANTHROPIC_MESSAGE_TYPES = /* @__PURE__ */ new Set(["user", "assistant"]);
@@ -157,21 +183,10 @@ function toParsedMessages(rows) {
157
183
  }
158
184
  continue;
159
185
  }
160
- if (ptype === "reasoning") {
161
- const summary = p.summary;
162
- if (!Array.isArray(summary)) continue;
163
- for (const block of summary) {
164
- if (!block || typeof block !== "object") continue;
165
- const btype = block.type;
166
- if (btype === "summary_text") {
167
- const v = block.text;
168
- if (typeof v === "string") push("assistant", v);
169
- }
170
- }
171
- continue;
172
- }
186
+ if (ptype === "reasoning") continue;
173
187
  continue;
174
188
  }
189
+ if (row.isMeta === true) continue;
175
190
  const msg = row.message;
176
191
  if (!msg || typeof msg !== "object") continue;
177
192
  const role = msg.role;
@@ -189,9 +204,6 @@ function toParsedMessages(rows) {
189
204
  if (btype === "text") {
190
205
  const v = block.text;
191
206
  if (typeof v === "string") text = v;
192
- } else if (btype === "thinking") {
193
- const v = block.thinking;
194
- if (typeof v === "string") text = v;
195
207
  }
196
208
  if (text) push(role, text);
197
209
  }
@@ -199,15 +211,66 @@ function toParsedMessages(rows) {
199
211
  }
200
212
  return parsed;
201
213
  }
214
+ var COMMAND_MESSAGE_RE = /<command-message>([\s\S]*?)<\/command-message>/;
215
+ var COMMAND_ARGS_RE = /<command-args>([\s\S]*?)<\/command-args>/;
216
+ function extractSkillSignals(rows) {
217
+ const signals = [];
218
+ const seen = /* @__PURE__ */ new Set();
219
+ const add = (s) => {
220
+ const key = `${s.trigger}:${s.skill}`;
221
+ if (seen.has(key)) return;
222
+ seen.add(key);
223
+ signals.push(s);
224
+ };
225
+ for (const row of rows) {
226
+ if (!row || typeof row !== "object") continue;
227
+ if (row.type === "user") {
228
+ const msg = row.message;
229
+ if (!msg || typeof msg !== "object") continue;
230
+ const content = msg.content;
231
+ if (typeof content === "string") {
232
+ const m = COMMAND_MESSAGE_RE.exec(content);
233
+ if (m) {
234
+ const argsMatch = COMMAND_ARGS_RE.exec(content);
235
+ add({
236
+ skill: m[1].trim(),
237
+ args: argsMatch?.[1]?.trim() || void 0,
238
+ trigger: "command"
239
+ });
240
+ }
241
+ }
242
+ }
243
+ if (row.type === "assistant") {
244
+ const msg = row.message;
245
+ if (!msg || typeof msg !== "object") continue;
246
+ const content = msg.content;
247
+ if (!Array.isArray(content)) continue;
248
+ for (const block of content) {
249
+ if (block && typeof block === "object" && block.type === "tool_use" && block.name === "Skill") {
250
+ const input = block.input;
251
+ if (input && typeof input === "object") {
252
+ const skill = typeof input.skill === "string" ? input.skill : "";
253
+ const args = typeof input.args === "string" ? input.args : void 0;
254
+ if (skill) add({ skill, args, trigger: "agent" });
255
+ }
256
+ }
257
+ }
258
+ }
259
+ }
260
+ return signals;
261
+ }
202
262
 
203
263
  // src/lib/capture-orchestrator.ts
204
- async function captureTurn(transcriptPath, cfg, client) {
264
+ async function captureTurn(transcriptPath, cfg, client, agent = "default") {
205
265
  if (!cfg.apiKey || !cfg.baseUrl) {
206
266
  return { captured: false, reason: "config_incomplete", messageCount: 0 };
207
267
  }
208
268
  if (!cfg.autoCapture) {
209
269
  return { captured: false, reason: "auto_capture_disabled", messageCount: 0 };
210
270
  }
271
+ if (isCircuitOpen(agent, cfg.baseUrl)) {
272
+ return { captured: false, reason: `circuit_open: ${cfg.baseUrl}`, messageCount: 0 };
273
+ }
211
274
  const rows = readTranscript(transcriptPath);
212
275
  if (rows.length === 0) {
213
276
  return { captured: false, reason: "empty_transcript", messageCount: 0 };
@@ -240,16 +303,32 @@ async function captureTurn(transcriptPath, cfg, client) {
240
303
  if (filtered.length === 0) {
241
304
  return { captured: false, reason: "all_filtered", messageCount: 0 };
242
305
  }
306
+ const git = getGitContext();
307
+ const skills = extractSkillSignals(rows);
308
+ const bgParts = [];
309
+ if (git) {
310
+ bgParts.push(`This conversation took place in project "${git.project}", branch "${git.branch}".`);
311
+ }
312
+ if (skills.length > 0) {
313
+ const descs = skills.map((s) => {
314
+ const label = s.trigger === "command" ? "user-invoked" : "agent-invoked";
315
+ return s.args ? `${s.skill} (${label}, args: "${s.args}")` : `${s.skill} (${label})`;
316
+ });
317
+ bgParts.push(`Skills used: ${descs.join(", ")}.`);
318
+ }
319
+ const messages = bgParts.length > 0 ? [{ role: "background", content: bgParts.join("\n") }, ...filtered] : filtered;
243
320
  const payload = {
244
- messages: filtered,
321
+ messages,
245
322
  user_id: cfg.userId,
246
323
  async_mode: true
247
324
  };
248
325
  let resp;
249
326
  try {
250
327
  resp = await client.postJson("/v3/memories/add/", payload);
328
+ resetCircuit(agent);
251
329
  } catch (err) {
252
330
  const msg = err instanceof CtxdbError ? err.message : String(err);
331
+ if (isConnectionError(err)) tripCircuit(agent, cfg.baseUrl, msg);
253
332
  return {
254
333
  captured: false,
255
334
  reason: `http_error: ${msg}`,
@@ -295,8 +374,8 @@ async function main() {
295
374
  debug("capture", "skip (config incomplete)");
296
375
  return 0;
297
376
  }
298
- const client = new HttpClient({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey });
299
- const result = await captureTurn(transcriptPath, cfg, client);
377
+ const client = new HttpClient({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey, timeoutMs: 5e3 });
378
+ const result = await captureTurn(transcriptPath, cfg, client, agent);
300
379
  if (result.captured) {
301
380
  debug("capture", `ok (${result.messageCount} msgs)`, result);
302
381
  process.stderr.write(
@@ -1,7 +1,13 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ listKnowledgeBases
4
+ } from "../chunk-6S5RJYBC.js";
2
5
  import {
3
6
  recallTurn
4
- } from "../chunk-7NOXAU2X.js";
7
+ } from "../chunk-LIC44DR6.js";
8
+ import {
9
+ isCircuitOpen
10
+ } from "../chunk-Q2EEP4CE.js";
5
11
  import {
6
12
  HttpClient,
7
13
  agentFromArgvWithFallback,
@@ -9,7 +15,21 @@ import {
9
15
  isComplete,
10
16
  load,
11
17
  setDebug
12
- } from "../chunk-QSSNPN3M.js";
18
+ } from "../chunk-S45GOYUU.js";
19
+
20
+ // src/lib/kb-catalog.ts
21
+ async function fetchKbCatalogBlock(client, agent) {
22
+ const kbs = await listKnowledgeBases(client);
23
+ const active = kbs.filter((kb) => kb.status === "active");
24
+ if (active.length === 0) return "";
25
+ const lines = active.map((kb) => `\xB7 ${kb.name}`);
26
+ return [
27
+ "<available-knowledge-bases>",
28
+ `When you identify that relevant information may exist in the knowledge bases below, you MUST run \`ctxdb kb search "<query>" --kb=<name> --agent=${agent}\` with targeted keywords after initial analysis to supplement and correct your approach. Knowledge bases:`,
29
+ ...lines,
30
+ "</available-knowledge-bases>"
31
+ ].join("\n");
32
+ }
13
33
 
14
34
  // src/hooks/user-prompt-submit.ts
15
35
  async function readStdinJson() {
@@ -42,9 +62,18 @@ async function main() {
42
62
  debug("recall", "skip (config incomplete or autoRecall=false)");
43
63
  return 0;
44
64
  }
45
- const client = new HttpClient({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey });
46
- const result = await recallTurn(prompt, cfg, client);
47
- if (!result.ok || !result.additionalContext) {
65
+ if (isCircuitOpen(agent, cfg.baseUrl)) {
66
+ debug("recall", "skip (circuit open)");
67
+ process.stderr.write(`ctxdb recall: skip (circuit_open: ${cfg.baseUrl})
68
+ `);
69
+ return 0;
70
+ }
71
+ const client = new HttpClient({ baseUrl: cfg.baseUrl, apiKey: cfg.apiKey, timeoutMs: 5e3 });
72
+ const [result, kbBlock] = await Promise.all([
73
+ recallTurn(prompt, cfg, client, agent),
74
+ fetchKbCatalogBlock(client, agent).catch(() => "")
75
+ ]);
76
+ if (!result.ok && !result.additionalContext && !kbBlock) {
48
77
  const reason = result.reason ?? "no_context";
49
78
  debug("recall", `no result: ${reason}`, result);
50
79
  if (result.reason && result.reason.startsWith("http_error:")) {
@@ -53,14 +82,19 @@ async function main() {
53
82
  }
54
83
  return 0;
55
84
  }
56
- debug("recall", `ok, additionalContext length=${result.additionalContext.length}`);
85
+ let ctx = result.additionalContext || "";
86
+ if (kbBlock) ctx = ctx ? `${ctx}
87
+
88
+ ${kbBlock}` : kbBlock;
89
+ if (!ctx) return 0;
90
+ debug("recall", `ok, additionalContext length=${ctx.length}`);
57
91
  if (agent === "codex") {
58
- process.stdout.write(result.additionalContext + "\n");
92
+ process.stdout.write(ctx + "\n");
59
93
  } else {
60
94
  const out = {
61
95
  hookSpecificOutput: {
62
96
  hookEventName: "UserPromptSubmit",
63
- additionalContext: result.additionalContext
97
+ additionalContext: ctx
64
98
  }
65
99
  };
66
100
  process.stdout.write(JSON.stringify(out) + "\n");
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: contextdb-knowledge
3
+ description: RDS contextdb 知识库操作全量参考。覆盖 kb create / search / upload-text / upload-file / list / documents-list / document-get 的完整参数和 JSON 输出结构。当用户提到"查知识库"、"查 KB"、"上传到 KB"、"灌进知识库"、"列出知识库"、"看文档"、"建知识库"等场景时使用。
4
+ ---
5
+
6
+ # contextdb Knowledge Base 操作参考
7
+
8
+ ## 配置
9
+
10
+ - 配置文件:`~/.ctxdb/ctxdb.json`
11
+ - 配置命令:`ctxdb setup --api-key=<key> --base-url=<url> [--user-id=<id>]`
12
+ - 带 `--agent <name>` 时写入指定 agent 的配置段并安装 hooks + skill
13
+ - 不带 `--agent` 时写入 `agents.default` 配置段(仅 CLI 使用)
14
+ - 连接检查:`ctxdb ping`,状态查看:`ctxdb status`
15
+
16
+ ## kb search
17
+
18
+ 搜索知识库。
19
+
20
+ ```sh
21
+ ctxdb kb search "<query>" [--kb=<name1,name2>] [--top-k=N] [--threshold=F] [--verbose] [--raw] [--agent=<name>]
22
+ ```
23
+
24
+ | 参数 | 说明 | 默认值 |
25
+ |------|------|--------|
26
+ | `<query>` | 搜索文本(必填) | |
27
+ | `--kb` | 指定搜索的知识库名称,逗号分隔 | 搜索所有 KB |
28
+ | `--top-k` | 返回 chunk 数上限 | 6 |
29
+ | `--threshold` | 相关度阈值 | 0.4 |
30
+ | `--verbose` | 每个 chunk 增加 `doc_name` / `kb_id` / `doc_id` / `tags` 字段 | false |
31
+ | `--raw` | 服务端原始响应(13+ 字段,含 tokenizer 细节) | false |
32
+ | `--agent` | 指定操作哪个 agent 的配置 | agents.default |
33
+
34
+ **输出 JSON 结构**(默认):
35
+
36
+ ```json
37
+ {
38
+ "chunks": [
39
+ {
40
+ "content": "chunk 文本内容",
41
+ "score": 0.82
42
+ }
43
+ ]
44
+ }
45
+ ```
46
+
47
+ `--verbose` 模式增加字段:
48
+
49
+ ```json
50
+ {
51
+ "chunks": [
52
+ {
53
+ "content": "chunk 文本内容",
54
+ "score": 0.82,
55
+ "doc_name": "zircodb-overview",
56
+ "kb_id": "kb-uuid",
57
+ "doc_id": "doc-uuid",
58
+ "tags": ["architecture"]
59
+ }
60
+ ]
61
+ }
62
+ ```
63
+
64
+ ## kb create
65
+
66
+ 创建知识库。
67
+
68
+ ```sh
69
+ ctxdb kb create <kb-name> [--description=<desc>] [--agent=<name>]
70
+ ```
71
+
72
+ | 参数 | 说明 |
73
+ |------|------|
74
+ | `<kb-name>` | 知识库名称(必填) |
75
+ | `--description` | 知识库描述 |
76
+
77
+ 上传前须先创建 KB。`upload-text` / `upload-file` 不会自动创建 KB,KB 不存在时会报错。
78
+
79
+ ## kb upload-file
80
+
81
+ 上传本地文件到知识库。
82
+
83
+ ```sh
84
+ ctxdb kb upload-file <kb-name> <local-path> [--doc-name=<name>] [--file-path=<server-logical-path>] [--no-wait] [--agent=<name>]
85
+ ```
86
+
87
+ | 参数 | 说明 |
88
+ |------|------|
89
+ | `<kb-name>` | 目标知识库名称(须已存在) |
90
+ | `<local-path>` | 本机文件路径 |
91
+ | `--doc-name` | 服务端文档名(默认取文件名) |
92
+ | `--file-path` | 服务端逻辑路径(归档/分类用) |
93
+ | `--no-wait` | 不等待 chunking 完成,立即返回 |
94
+
95
+ 支持格式:PDF、DOCX、MD、TXT。
96
+
97
+ ## kb upload-text
98
+
99
+ 上传文本内容到知识库。
100
+
101
+ ```sh
102
+ ctxdb kb upload-text <kb-name> <doc-name> --text="<body>" [--file-path=<server-logical-path>] [--no-wait] [--agent=<name>]
103
+ ```
104
+
105
+ | 参数 | 说明 |
106
+ |------|------|
107
+ | `<kb-name>` | 目标知识库名称(须已存在) |
108
+ | `<doc-name>` | 文档名称 |
109
+ | `--text` | 文本内容(必填) |
110
+ | `--file-path` | 服务端逻辑路径 |
111
+ | `--no-wait` | 不等待 chunking 完成 |
112
+
113
+ ## kb list
114
+
115
+ 列出所有知识库。
116
+
117
+ ```sh
118
+ ctxdb kb list [--agent=<name>]
119
+ ```
120
+
121
+ 返回 `knowledge_bases` 数组,每项含知识库名称、ID、文档数等。
122
+
123
+ ## kb documents-list
124
+
125
+ 列出知识库中的文档。
126
+
127
+ ```sh
128
+ ctxdb kb documents-list <kb-name> [--agent=<name>]
129
+ ```
130
+
131
+ ## kb document-get
132
+
133
+ 查看文档详情。
134
+
135
+ ```sh
136
+ ctxdb kb document-get <kb-name> <doc-id> [--agent=<name>]
137
+ ```
138
+
139
+ ## 写操作注意事项
140
+
141
+ `kb create`、`kb upload-text`、`kb upload-file` 是副作用操作(创建 KB / 写入文档)。执行前须明确目标 KB 名称:
142
+
143
+ - 用户指定了 KB 名称 → 直接使用
144
+ - 用户未指定 → 先用 `kb list` 查看现有知识库,向用户确认目标 KB 后再操作
145
+ - KB 不存在时 upload 命令会自动创建,需确认用户意图是新建还是写入已有 KB
146
+
147
+ ## --agent 参数
148
+
149
+ 所有 kb 命令支持 `--agent <name>` 指定操作目标。知识库归属于 workspace,不同 agent 若共享同一 workspace 则访问相同的知识库。
150
+
151
+ 不带 `--agent` 时的解析顺序:`CTXDB_AGENT` 环境变量 → `agents.default` 配置段。
152
+
153
+ ## Hooks 感知
154
+
155
+ **KB 目录注入**:如果对话中出现 `<available-knowledge-bases>` 块,表示 hooks 已启用,该块列出可用知识库名称,可直接用于 `--kb=<name>` 参数。
156
+
157
+ **B-3c 守卫**:包含 `kb upload-text` 或 `kb upload-file` 操作的对话轮次,其内容不会被 autoCapture 写入记忆(防止文档原文混入记忆)。
158
+
159
+ 如果对话中没有 `<available-knowledge-bases>`,表示当前为纯 CLI 模式,用 `kb list` 查看可用知识库。
160
+
161
+ 所有命令输出 JSON 到 stdout,错误输出到 stderr 并返回非零退出码。
@@ -0,0 +1,142 @@
1
+ ---
2
+ name: contextdb-memory
3
+ description: RDS contextdb 记忆操作全量参考。覆盖 memory add / search / list / get / update / delete 的完整参数、JSON 输出结构和 hooks 感知机制。当用户提到"记住"、"记一下"、"查记忆"、"删记忆"、"改记忆"、"之前说过"等场景时使用。
4
+ ---
5
+
6
+ # contextdb Memory 操作参考
7
+
8
+ ## 配置
9
+
10
+ - 配置文件:`~/.ctxdb/ctxdb.json`
11
+ - 配置命令:`ctxdb setup --api-key=<key> --base-url=<url> [--user-id=<id>]`
12
+ - 带 `--agent <name>` 时写入指定 agent 的配置段并安装 hooks + skill
13
+ - 不带 `--agent` 时写入 `agents.default` 配置段(仅 CLI 使用)
14
+ - 连接检查:`ctxdb ping`,状态查看:`ctxdb status`
15
+
16
+ ## memory add
17
+
18
+ 将文本写入长期记忆。
19
+
20
+ ```sh
21
+ ctxdb memory add "<text>" [--no-infer] [--user-id=<id>] [--metadata=K1=V1,K2=V2] [--agent=<name>]
22
+ ```
23
+
24
+ | 参数 | 说明 |
25
+ |------|------|
26
+ | `<text>` | 要记忆的文本内容(必填) |
27
+ | `--no-infer` | 跳过 LLM fact-extraction,原文直存 |
28
+ | `--user-id` | 覆盖配置中的 user_id |
29
+ | `--metadata` | 附加键值对元数据,逗号分隔 |
30
+ | `--agent` | 指定操作哪个 agent 的记忆桶(默认使用 agents.default 配置) |
31
+
32
+ 服务端默认走同步模式(`async_mode: false`),等待 LLM fact-extraction 完成后返回结果。`--no-infer` 跳过提炼,适用于需要原文保留的场景(如项目代号、精确数值)。
33
+
34
+ **输出 JSON 结构**(同步模式):
35
+
36
+ ```json
37
+ {
38
+ "results": [
39
+ {
40
+ "id": "mem-uuid",
41
+ "memory": "提炼后的事实文本",
42
+ "event": "ADD"
43
+ }
44
+ ]
45
+ }
46
+ ```
47
+
48
+ `event` 可能的值:`ADD`(新增)、`UPDATE`(更新已有记忆)、`NONE`(无新事实提取)。`results` 为空数组表示服务端未从输入中提取出新事实。
49
+
50
+ ## memory search
51
+
52
+ 搜索长期记忆。
53
+
54
+ ```sh
55
+ ctxdb memory search "<query>" [--top-k=N] [--threshold=F] [--knowledge] [--verbose] [--raw] [--agent=<name>]
56
+ ```
57
+
58
+ | 参数 | 说明 | 默认值 |
59
+ |------|------|--------|
60
+ | `<query>` | 搜索文本(必填) | |
61
+ | `--top-k` | 返回条数上限 | 5 |
62
+ | `--threshold` | 相关度阈值 | 0.4 |
63
+ | `--knowledge` | 同时搜索知识库 chunks | false |
64
+ | `--verbose` | 增加 doc_name/kb_id 等字段(knowledge 模式) | false |
65
+ | `--raw` | 服务端原始响应 | false |
66
+ | `--agent` | 指定操作哪个 agent 的记忆桶 | agents.default |
67
+
68
+ **输出 JSON 结构**:
69
+
70
+ ```json
71
+ {
72
+ "results": [
73
+ {
74
+ "id": "mem-uuid",
75
+ "memory": "事实文本",
76
+ "score": 0.85
77
+ }
78
+ ]
79
+ }
80
+ ```
81
+
82
+ `results` 为空数组表示未找到匹配记忆。
83
+
84
+ 带 `--knowledge` 时额外返回 `knowledge` 数组(每项含 `content` / `score`,`--verbose` 增加 `doc_name` / `kb_id` / `doc_id` / `tags`)。
85
+
86
+ ## memory list
87
+
88
+ 列出记忆。
89
+
90
+ ```sh
91
+ ctxdb memory list [--page-size=N] [--category=<cat>] [--agent=<name>]
92
+ ```
93
+
94
+ | 参数 | 说明 | 默认值 |
95
+ |------|------|--------|
96
+ | `--page-size` | 每页条数 | 100 |
97
+ | `--category` | 按分类过滤 | |
98
+
99
+ ## memory get
100
+
101
+ 查看单条记忆。
102
+
103
+ ```sh
104
+ ctxdb memory get <memory-id> [--agent=<name>]
105
+ ```
106
+
107
+ ## memory update
108
+
109
+ 修改记忆内容。
110
+
111
+ ```sh
112
+ ctxdb memory update <memory-id> --text="<new-text>" [--agent=<name>]
113
+ ```
114
+
115
+ ## memory delete
116
+
117
+ 删除记忆。
118
+
119
+ ```sh
120
+ ctxdb memory delete <memory-id> [--agent=<name>]
121
+ ctxdb memory delete --all [--agent=<name>]
122
+ ```
123
+
124
+ `--all` 删除当前 user_id 下的所有记忆。
125
+
126
+ ## --agent 参数
127
+
128
+ 所有 memory 命令支持 `--agent <name>` 指定操作目标。不同 agent 的记忆存储在各自的配置桶中(由 `~/.ctxdb/ctxdb.json` 的 `agents.<name>.user_id` 决定)。
129
+
130
+ 不带 `--agent` 时的解析顺序:`CTXDB_AGENT` 环境变量 → `agents.default` 配置段。
131
+
132
+ ## Hooks 感知
133
+
134
+ **autoCapture**:如果对话中出现 `<recalled-memories>` 块,表示 Stop hook 在每轮对话结束后自动提取事实写入记忆。`memory add` 在此基础上用于用户主动强调的内容(加强记忆)。
135
+
136
+ **autoRecall**:`<recalled-memories>` 块内容是 UserPromptSubmit hook 自动搜索当前 prompt 相关记忆的结果。
137
+
138
+ **B-3c 守卫**:包含 `kb upload-text` 或 `kb upload-file` 操作的对话轮次,其内容不会被 autoCapture 写入记忆(防止文档原文混入记忆)。
139
+
140
+ 如果对话中没有 `<recalled-memories>`,表示当前为纯 CLI 模式,所有操作需主动调用。
141
+
142
+ 所有命令输出 JSON 到 stdout,错误输出到 stderr 并返回非零退出码。
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliyunrds/ctxdb",
3
- "version": "0.0.4",
3
+ "version": "0.0.7",
4
4
  "type": "module",
5
5
  "description": "Unified access layer for RDS ContextDatabase: `ctxdb` CLI (memory + KB ops), one-shot `setup --agent <qoder|codex|claude>` installer, per-agent config, hooks, and SKILL.md.",
6
6
  "license": "Apache-2.0",
@@ -28,7 +28,7 @@
28
28
  "node": ">=22"
29
29
  },
30
30
  "dependencies": {
31
- "@aliyunrds/ctxdb-shared": "~0.0.2"
31
+ "@aliyunrds/ctxdb-shared": "~0.0.3"
32
32
  },
33
33
  "devDependencies": {
34
34
  "@types/node": "^22.15.0",