@aliyunrds/ctxdb 0.0.8-beta.1 → 0.0.8-beta.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.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  configDir
4
- } from "./chunk-U3T5O6NX.js";
4
+ } from "./chunk-BWPFGTF7.js";
5
5
 
6
6
  // src/lib/circuit.ts
7
7
  import { statSync, writeFileSync, unlinkSync, mkdirSync, readdirSync, readFileSync } from "fs";
@@ -6,10 +6,10 @@ import {
6
6
  isConnectionError,
7
7
  resetCircuit,
8
8
  tripCircuit
9
- } from "./chunk-KEQMJ6IO.js";
9
+ } from "./chunk-52IAVJRK.js";
10
10
  import {
11
11
  CtxdbError
12
- } from "./chunk-U3T5O6NX.js";
12
+ } from "./chunk-BWPFGTF7.js";
13
13
 
14
14
  // src/lib/recall-orchestrator.ts
15
15
  import {
@@ -39,23 +39,27 @@ ${s}`;
39
39
  }
40
40
  }
41
41
 
42
- // src/lib/http-client.ts
42
+ // src/lib/package-version.ts
43
43
  import { readFileSync } from "fs";
44
- import { fileURLToPath } from "url";
45
44
  import { dirname as dirname2, join as join2 } from "path";
45
+ import { fileURLToPath } from "url";
46
46
  function findPackageVersion() {
47
47
  let dir = dirname2(fileURLToPath(import.meta.url));
48
48
  for (let i = 0; i < 5; i++) {
49
49
  try {
50
50
  const pkg = JSON.parse(readFileSync(join2(dir, "package.json"), "utf-8"));
51
- if (pkg.name === "@aliyunrds/ctxdb") return pkg.version;
51
+ if (pkg.name === "@aliyunrds/ctxdb" && typeof pkg.version === "string") {
52
+ return pkg.version;
53
+ }
52
54
  } catch {
53
55
  }
54
56
  dir = dirname2(dir);
55
57
  }
56
58
  return "0.0.0";
57
59
  }
58
- var PKG_VERSION = findPackageVersion();
60
+ var PACKAGE_VERSION = findPackageVersion();
61
+
62
+ // src/lib/http-client.ts
59
63
  var DEFAULT_TIMEOUT_MS = 3e4;
60
64
  var CtxdbError = class extends Error {
61
65
  constructor(message) {
@@ -108,7 +112,7 @@ var HttpClient = class {
108
112
  this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
109
113
  this.apiKey = opts.apiKey;
110
114
  this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
111
- this.userAgent = opts.userAgent ?? `ctxdb-cli/${PKG_VERSION}`;
115
+ this.userAgent = opts.userAgent ?? `ctxdb-cli/${PACKAGE_VERSION}`;
112
116
  this.extraHeaders = { ...opts.extraHeaders ?? {} };
113
117
  const f = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
114
118
  this.fetchImpl = f;
@@ -257,7 +261,7 @@ function extractErrorDetail(text, fallback) {
257
261
  // src/lib/agents.ts
258
262
  import { homedir as homedir2 } from "os";
259
263
  import { join as join3 } from "path";
260
- var SUPPORTED_AGENTS = ["qoder", "codex", "claude"];
264
+ var SUPPORTED_AGENTS = ["qoder", "qoderwork", "codex", "claude"];
261
265
  function isBuiltinAgent(v) {
262
266
  return typeof v === "string" && SUPPORTED_AGENTS.includes(v);
263
267
  }
@@ -269,6 +273,8 @@ function agentHomeDir(agent) {
269
273
  switch (agent) {
270
274
  case "qoder":
271
275
  return join3(homedir2(), ".qoder");
276
+ case "qoderwork":
277
+ return join3(homedir2(), ".qoderwork");
272
278
  case "codex":
273
279
  return join3(homedir2(), ".codex");
274
280
  case "claude":
@@ -490,7 +496,9 @@ function writeInstalledPkgVersion(version, path) {
490
496
 
491
497
  export {
492
498
  setDebug,
499
+ isDebug,
493
500
  debug,
501
+ PACKAGE_VERSION,
494
502
  CtxdbError,
495
503
  HttpClient,
496
504
  SUPPORTED_AGENTS,
package/dist/cli/main.js CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  DEFAULT_BASE_URL,
19
19
  DEFAULT_USER_ID,
20
20
  HttpClient,
21
+ PACKAGE_VERSION,
21
22
  SUPPORTED_AGENTS,
22
23
  agentFromEnv,
23
24
  agentHomeDir,
@@ -29,7 +30,7 @@ import {
29
30
  removeAgent,
30
31
  save,
31
32
  writeInstalledPkgVersion
32
- } from "../chunk-U3T5O6NX.js";
33
+ } from "../chunk-BWPFGTF7.js";
33
34
 
34
35
  // src/cli/util.ts
35
36
  var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
@@ -216,7 +217,6 @@ function fail(message, code = 1) {
216
217
  `);
217
218
  process.exit(code);
218
219
  }
219
- var PACKAGE_VERSION = "0.0.7";
220
220
 
221
221
  // src/setup/installer.ts
222
222
  import {
@@ -242,6 +242,8 @@ function skillInstallRoot(agent) {
242
242
  switch (agent) {
243
243
  case "qoder":
244
244
  return join(homedir(), ".qoder", "skills");
245
+ case "qoderwork":
246
+ return join(homedir(), ".qoderwork", "skills");
245
247
  case "codex":
246
248
  return join(homedir(), ".codex", "skills");
247
249
  case "claude":
@@ -257,6 +259,8 @@ function hookConfigPath(agent) {
257
259
  switch (agent) {
258
260
  case "qoder":
259
261
  return join(homedir(), ".qoder", "settings.json");
262
+ case "qoderwork":
263
+ return join(homedir(), ".qoderwork", "settings.json");
260
264
  case "codex":
261
265
  return join(homedir(), ".codex", "hooks.json");
262
266
  case "claude":
@@ -306,6 +310,14 @@ async function runSetup(options) {
306
310
  ok: true,
307
311
  detail: `${agent}: ${cfg.baseUrl} (user_id=${cfg.userId})`
308
312
  });
313
+ if (isBuiltinAgent(agent) && !configuredAgents().includes("default")) {
314
+ save(cfg, void 0, { agent: "default" });
315
+ steps.push({
316
+ step: "write-default-config",
317
+ ok: true,
318
+ detail: `default: copied from ${agent}`
319
+ });
320
+ }
309
321
  if (installSkill && isBuiltinAgent(agent)) {
310
322
  const installRoot = skillInstallRoot(agent);
311
323
  const skillResult = installSkillsTo(installRoot, agent);
@@ -628,11 +640,9 @@ function checkHookNodePaths(agent) {
628
640
  nodePaths.add(h.command);
629
641
  } else {
630
642
  const cmd = h.command;
631
- if (cmd.includes("@aliyunrds/ctxdb")) {
632
- const parts = cmd.split(" ");
633
- if (parts.length >= 2 && !parts[0].endsWith(".js") && !parts[0].endsWith(".ts")) {
634
- nodePaths.add(parts[0]);
635
- }
643
+ const parts = cmd.split(" ");
644
+ if (parts.length >= 2 && !parts[0].endsWith(".js") && !parts[0].endsWith(".ts")) {
645
+ nodePaths.add(parts[0]);
636
646
  }
637
647
  }
638
648
  }
@@ -808,6 +818,7 @@ function copySkillDir(src, dest, agent) {
808
818
  }
809
819
  var SKILL_INSTALL_TARGETS = {
810
820
  qoder: join(homedir(), ".qoder", "skills"),
821
+ qoderwork: join(homedir(), ".qoderwork", "skills"),
811
822
  codex: join(homedir(), ".codex", "skills"),
812
823
  claude: join(homedir(), ".claude", "skills"),
813
824
  openclaw: join(homedir(), ".openclaw", "skills"),
@@ -870,14 +881,17 @@ var LEGACY_MARKER_VALUES = ["@aliyunrds/ctxdb-qoder"];
870
881
  var TOOL_SCOPED_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse"]);
871
882
  function appendOne(hooks, event, command, agent) {
872
883
  if (!Array.isArray(hooks[event])) hooks[event] = [];
884
+ const useExecForm = agent === "claude";
885
+ const shellCmd = `${process.execPath} ${command} --agent=${agent}`;
873
886
  const dup = hooks[event].some(
874
887
  (entry2) => Array.isArray(entry2?.hooks) && entry2.hooks.some(
875
- (h) => h?.type === "command" && Array.isArray(h.args) && h.args[0] === command
888
+ (h) => h?.type === "command" && (Array.isArray(h.args) && h.args[0] === command || typeof h.command === "string" && h.command.includes(command))
876
889
  )
877
890
  );
878
891
  if (dup) return;
892
+ const inner = useExecForm ? { type: "command", command: process.execPath, args: [command, `--agent=${agent}`], timeout: 60 } : { type: "command", command: shellCmd, timeout: 60 };
879
893
  const entry = {
880
- hooks: [{ type: "command", command: process.execPath, args: [command, `--agent=${agent}`], timeout: 60 }],
894
+ hooks: [inner],
881
895
  [ENTRY_MARKER_KEY]: ENTRY_MARKER_VALUE,
882
896
  [ENTRY_AGENT_KEY]: agent
883
897
  };
@@ -1240,7 +1254,7 @@ async function ping(args) {
1240
1254
  var HELP = `ctxdb setup \u2014 configure an agent
1241
1255
 
1242
1256
  USAGE
1243
- ctxdb setup [--agent <qoder|codex|claude>]
1257
+ ctxdb setup [--agent <qoder|qoderwork|codex|claude>]
1244
1258
  [--api-key=K] [--base-url=URL] [--user-id=ID]
1245
1259
  [--no-install-skill] [--no-validate] [--json]
1246
1260
 
@@ -1251,7 +1265,7 @@ BEHAVIOR
1251
1265
  specified agent harness.
1252
1266
 
1253
1267
  FLAGS
1254
- --agent <name> Target agent (qoder|codex|claude)
1268
+ --agent <name> Target agent (qoder|qoderwork|codex|claude)
1255
1269
  --api-key <key> API key (required on first setup)
1256
1270
  --base-url <url> Server URL (default: https://context-database.aliyuncs.com)
1257
1271
  --user-id <id> User bucket (default: "default")
@@ -1278,7 +1292,7 @@ async function setup(args) {
1278
1292
  const json = !!args.flags.json;
1279
1293
  if (agent === "default" && !json && !configuredAgents().includes("default")) {
1280
1294
  process.stderr.write(
1281
- `hint: creating CLI-only "default" agent (no hooks/skills). Use --agent <claude|qoder|codex> for full setup.
1295
+ `hint: creating CLI-only "default" agent (no hooks/skills). Use --agent <claude|qoder|qoderwork|codex> for full setup.
1282
1296
  `
1283
1297
  );
1284
1298
  }
@@ -1303,11 +1317,11 @@ ${h}
1303
1317
  var HELP2 = `ctxdb uninstall \u2014 remove hooks + skills
1304
1318
 
1305
1319
  USAGE
1306
- ctxdb uninstall [--agent <qoder|codex|claude>] [--purge-config] [--purge-logs] [--purge-all] [--json]
1320
+ ctxdb uninstall [--agent <qoder|qoderwork|codex|claude>] [--purge-config] [--purge-logs] [--purge-all] [--json]
1307
1321
 
1308
1322
  DEFAULT BEHAVIOR (no --agent, no purge flags)
1309
1323
  Strips ctxdb hook entries and skill directories for ALL agents (qoder,
1310
- codex, claude). Keeps ~/.ctxdb/ctxdb.json (credentials) and ~/.ctxdb/logs/
1324
+ qoderwork, codex, claude). Keeps ~/.ctxdb/ctxdb.json (credentials) and ~/.ctxdb/logs/
1311
1325
  so you can re-run \`ctxdb setup\` later without losing configuration.
1312
1326
 
1313
1327
  WITH --agent <name>
@@ -1315,7 +1329,7 @@ WITH --agent <name>
1315
1329
  Other agents remain untouched.
1316
1330
 
1317
1331
  FLAGS
1318
- --agent <name> Target a single agent (qoder|codex|claude)
1332
+ --agent <name> Target a single agent (qoder|qoderwork|codex|claude)
1319
1333
  --purge-config Also delete ~/.ctxdb/ctxdb.json
1320
1334
  --purge-logs Also delete ~/.ctxdb/logs/
1321
1335
  --purge-all Delete everything under ~/.ctxdb/
@@ -1457,7 +1471,7 @@ function runSelfUpdate(currentVersion, passthroughArgs = []) {
1457
1471
  var HELP3 = `ctxdb upgrade \u2014 refresh skills + hooks
1458
1472
 
1459
1473
  USAGE
1460
- ctxdb upgrade [--agent <qoder|codex|claude>] [--self-update] [--json]
1474
+ ctxdb upgrade [--agent <qoder|qoderwork|codex|claude>] [--self-update] [--json]
1461
1475
 
1462
1476
  BEHAVIOR
1463
1477
  Refreshes skill files and hook entries for configured agents without
@@ -1467,7 +1481,7 @@ BEHAVIOR
1467
1481
  With --agent: upgrades only the specified agent.
1468
1482
 
1469
1483
  FLAGS
1470
- --agent <name> Target a single agent (qoder|codex|claude)
1484
+ --agent <name> Target a single agent (qoder|qoderwork|codex|claude)
1471
1485
  --self-update Also pull latest package from npm before upgrading
1472
1486
  --json Machine-readable JSON output
1473
1487
  `;
@@ -1573,7 +1587,8 @@ function skillInstall(args) {
1573
1587
  }
1574
1588
  targetRoot = resolved;
1575
1589
  }
1576
- const result = installSkillsTo(targetRoot);
1590
+ const templateAgent = target && isBuiltinAgent(target) ? target : "default";
1591
+ const result = installSkillsTo(targetRoot, templateAgent);
1577
1592
  const ok = result.ok;
1578
1593
  const output = {
1579
1594
  ok,
@@ -1829,13 +1844,14 @@ USAGE
1829
1844
  ctxdb <command> [args] [--flags]
1830
1845
 
1831
1846
  COMMANDS
1832
- setup [--agent <qoder|codex|claude>]
1847
+ setup [--agent <qoder|qoderwork|codex|claude>]
1833
1848
  [--api-key=K] [--base-url=URL] [--user-id=ID]
1834
1849
  [--no-install-skill] [--no-validate] [--json]
1835
1850
  Without --agent \u2192 writes agents.default (CLI-only, no hooks/skills)
1836
- Qoder \u2192 writes agents.qoder config + ~/.qoder/settings.json hooks + skill
1837
- Codex \u2192 writes agents.codex config + ~/.codex/hooks.json hooks + skill
1838
- Claude \u2192 writes agents.claude config + ~/.claude/settings.json hooks + skill
1851
+ Qoder \u2192 writes agents.qoder config + ~/.qoder/settings.json hooks + skill
1852
+ QoderWork \u2192 writes agents.qoderwork config + ~/.qoderwork/settings.json hooks + skill
1853
+ Codex \u2192 writes agents.codex config + ~/.codex/hooks.json hooks + skill
1854
+ Claude \u2192 writes agents.claude config + ~/.claude/settings.json hooks + skill
1839
1855
  status [--agent <name>] [--json]
1840
1856
  ping [--agent <name>] [--json]
1841
1857
  uninstall [--agent <name>] [--purge-config] [--purge-logs] [--purge-all] [--json]
@@ -1878,7 +1894,7 @@ COMMANDS
1878
1894
  doc_id?/tags? for citation.
1879
1895
  --raw: server response verbatim (curl parity).
1880
1896
 
1881
- skill install --target <qoder|codex|claude|openclaw|hermes> [--json]
1897
+ skill install --target <qoder|qoderwork|codex|claude|openclaw|hermes> [--json]
1882
1898
  skill install --path <dir> [--json]
1883
1899
  Install skill files (contextdb-memory,
1884
1900
  contextdb-knowledge) to an agent's skill directory.
@@ -1891,7 +1907,7 @@ AGENT RESOLUTION (when --agent is omitted):
1891
1907
  ENV VARS (override selected ~/.ctxdb/ctxdb.json agent config):
1892
1908
  CTXDB_AGENT CTXDB_API_KEY CTXDB_BASE_URL CTXDB_USER_ID
1893
1909
 
1894
- CONFIG: ~/.ctxdb/ctxdb.json (agents.default / agents.qoder / agents.codex / agents.claude)
1910
+ CONFIG: ~/.ctxdb/ctxdb.json (agents.default / agents.qoder / agents.qoderwork / agents.codex / agents.claude)
1895
1911
  LOGS: ~/.ctxdb/logs/ctxdb.log
1896
1912
  `;
1897
1913
  var ROUTES = {
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  fetchKbCatalogBlock,
4
4
  recallTurn
5
- } from "../chunk-C62I23HL.js";
5
+ } from "../chunk-73OY44GY.js";
6
6
  import "../chunk-6S5RJYBC.js";
7
7
  import {
8
8
  isCircuitOpen
9
- } from "../chunk-KEQMJ6IO.js";
9
+ } from "../chunk-52IAVJRK.js";
10
10
  import {
11
11
  HttpClient,
12
12
  agentFromArgvWithFallback,
@@ -14,7 +14,7 @@ import {
14
14
  isComplete,
15
15
  load,
16
16
  setDebug
17
- } from "../chunk-U3T5O6NX.js";
17
+ } from "../chunk-BWPFGTF7.js";
18
18
 
19
19
  // src/hooks/session-start.ts
20
20
  import { pathToFileURL } from "url";
@@ -4,15 +4,16 @@ import {
4
4
  isConnectionError,
5
5
  resetCircuit,
6
6
  tripCircuit
7
- } from "../chunk-KEQMJ6IO.js";
7
+ } from "../chunk-52IAVJRK.js";
8
8
  import {
9
9
  CtxdbError,
10
10
  HttpClient,
11
11
  agentFromArgvWithFallback,
12
12
  debug,
13
+ isDebug,
13
14
  load,
14
15
  setDebug
15
- } from "../chunk-U3T5O6NX.js";
16
+ } from "../chunk-BWPFGTF7.js";
16
17
 
17
18
  // src/lib/capture-orchestrator.ts
18
19
  import {
@@ -122,6 +123,48 @@ function toKbDetectionMessages(rows) {
122
123
  }
123
124
  return out;
124
125
  }
126
+ function selectTurnRows(rows) {
127
+ let turnStart = -1;
128
+ for (let ri = rows.length - 1; ri >= 0; ri--) {
129
+ if (hasExtractableUserText(rows[ri])) {
130
+ turnStart = ri;
131
+ while (turnStart > 0 && hasExtractableUserText(rows[turnStart - 1])) {
132
+ turnStart--;
133
+ }
134
+ break;
135
+ }
136
+ }
137
+ return turnStart >= 0 ? rows.slice(turnStart) : [];
138
+ }
139
+ function hasExtractableUserText(row) {
140
+ if (!row || typeof row !== "object") return false;
141
+ if (row.type === CODEX_RESPONSE_ITEM) {
142
+ const p = row.payload;
143
+ if (!p || typeof p !== "object") return false;
144
+ if (p.type !== "message" || p.role !== "user") return false;
145
+ const content2 = p.content;
146
+ if (!Array.isArray(content2)) return false;
147
+ return content2.some((block) => {
148
+ if (!block || typeof block !== "object") return false;
149
+ if (block.type !== "input_text") return false;
150
+ const text = block.text;
151
+ return typeof text === "string" && text.trim() && !isCodexScaffoldingText(text);
152
+ });
153
+ }
154
+ if (row.isMeta === true) return false;
155
+ const msg = row.message;
156
+ if (!msg || typeof msg !== "object") return false;
157
+ if (msg.role !== "user") return false;
158
+ const content = msg.content;
159
+ if (typeof content === "string") return Boolean(content.trim());
160
+ if (!Array.isArray(content)) return false;
161
+ return content.some((block) => {
162
+ if (!block || typeof block !== "object") return false;
163
+ if (block.type !== "text") return false;
164
+ const text = block.text;
165
+ return typeof text === "string" && Boolean(text.trim());
166
+ });
167
+ }
125
168
  function parseArgs(raw) {
126
169
  if (typeof raw !== "string") return {};
127
170
  try {
@@ -275,15 +318,10 @@ async function captureTurn(transcriptPath, cfg, client, agent = "default") {
275
318
  if (rows.length === 0) {
276
319
  return { captured: false, reason: "empty_transcript", messageCount: 0 };
277
320
  }
278
- const kb = isKnowledgeBaseUploadTurn(toKbDetectionMessages(rows));
279
- if (kb.detected) {
280
- return {
281
- captured: false,
282
- reason: `b3c_skip: ${kb.reason ?? "(no reason)"}`,
283
- messageCount: 0
284
- };
285
- }
321
+ const dbg = isDebug();
322
+ if (dbg) debug("capture", "transcript rows", summarizeTranscriptRows(rows));
286
323
  const parsed = toParsedMessages(rows);
324
+ if (dbg) debug("capture", "parsed messages", summarizeTextMessages(parsed));
287
325
  if (parsed.length === 0) {
288
326
  return {
289
327
  captured: false,
@@ -292,14 +330,26 @@ async function captureTurn(transcriptPath, cfg, client, agent = "default") {
292
330
  };
293
331
  }
294
332
  const turn = selectTurnMessages(parsed);
333
+ if (dbg) debug("capture", "selected turn", summarizeTextMessages(turn));
295
334
  if (turn.length === 0) {
296
335
  return { captured: false, reason: "empty_turn_slice", messageCount: 0 };
297
336
  }
298
337
  if (!turn.some((m) => m.role === "user")) {
299
338
  return { captured: false, reason: "no_user_in_turn", messageCount: 0 };
300
339
  }
340
+ const turnRows = selectTurnRows(rows);
341
+ if (dbg) debug("capture", "current turn rows", summarizeTranscriptRows(turnRows));
342
+ const kb = isKnowledgeBaseUploadTurn(toKbDetectionMessages(turnRows));
343
+ if (kb.detected) {
344
+ return {
345
+ captured: false,
346
+ reason: `b3c_skip: ${kb.reason ?? "(no reason)"}`,
347
+ messageCount: 0
348
+ };
349
+ }
301
350
  const rawMessages = turn.map((m) => ({ role: m.role, content: m.content }));
302
351
  const filtered = filterMessagesForExtraction(rawMessages);
352
+ if (dbg) debug("capture", "filtered messages", summarizeTextMessages(filtered));
303
353
  if (filtered.length === 0) {
304
354
  return { captured: false, reason: "all_filtered", messageCount: 0 };
305
355
  }
@@ -310,7 +360,9 @@ async function captureTurn(transcriptPath, cfg, client, agent = "default") {
310
360
  if (skills.length > 0) bg.skills = skills.map(
311
361
  ({ skill, trigger, args }) => args ? { skill, trigger, args } : { skill, trigger }
312
362
  );
313
- const messages = bg.git || bg.skills ? [{ role: "background", content: JSON.stringify(bg) }, ...filtered] : filtered;
363
+ const hasBackground = Boolean(bg.git || bg.skills);
364
+ if (hasBackground) debug("capture", "background", bg);
365
+ const messages = hasBackground ? [{ role: "background", content: JSON.stringify(bg) }, ...filtered] : filtered;
314
366
  const payload = {
315
367
  messages,
316
368
  user_id: cfg.userId,
@@ -336,6 +388,104 @@ async function captureTurn(transcriptPath, cfg, client, agent = "default") {
336
388
  messageCount: filtered.length
337
389
  };
338
390
  }
391
+ var DEBUG_TAIL_LIMIT = 25;
392
+ var DEBUG_TEXT_LIMIT = 240;
393
+ var DEBUG_BLOCK_LIMIT = 8;
394
+ function summarizeTextMessages(messages) {
395
+ return {
396
+ count: messages.length,
397
+ roles: countRoles(messages),
398
+ tail: tailWithIndex(messages).map(({ index, item }) => ({
399
+ index,
400
+ parsedIndex: typeof item.index === "number" ? item.index : void 0,
401
+ role: item.role ?? "(unknown)",
402
+ content: summarizeText(item.content)
403
+ }))
404
+ };
405
+ }
406
+ function summarizeTranscriptRows(rows) {
407
+ return {
408
+ count: rows.length,
409
+ tail: tailWithIndex(rows).map(({ index, item: row }) => ({
410
+ index,
411
+ type: row.type,
412
+ role: rowRole(row),
413
+ isMeta: row.isMeta === true || void 0,
414
+ payloadType: row.payload?.type,
415
+ content: summarizeRowContent(row)
416
+ }))
417
+ };
418
+ }
419
+ function tailWithIndex(items) {
420
+ const start = Math.max(0, items.length - DEBUG_TAIL_LIMIT);
421
+ return items.slice(start).map((item, offset) => ({ index: start + offset, item }));
422
+ }
423
+ function countRoles(messages) {
424
+ const counts = {};
425
+ for (const msg of messages) {
426
+ const role = msg.role ?? "(unknown)";
427
+ counts[role] = (counts[role] ?? 0) + 1;
428
+ }
429
+ return counts;
430
+ }
431
+ function rowRole(row) {
432
+ const msgRole = row.message && typeof row.message === "object" ? row.message.role : void 0;
433
+ if (typeof msgRole === "string") return msgRole;
434
+ const payloadRole = row.payload && typeof row.payload === "object" ? row.payload.role : void 0;
435
+ return typeof payloadRole === "string" ? payloadRole : void 0;
436
+ }
437
+ function summarizeRowContent(row) {
438
+ if (row.type === "response_item") {
439
+ const p = row.payload;
440
+ if (!p || typeof p !== "object") return { kind: "missing" };
441
+ if (p.type === "function_call") {
442
+ return {
443
+ kind: "function_call",
444
+ name: p.name,
445
+ arguments: summarizeText(p.arguments)
446
+ };
447
+ }
448
+ return summarizeContent(p.content);
449
+ }
450
+ return summarizeContent(row.message?.content);
451
+ }
452
+ function summarizeContent(content) {
453
+ if (typeof content === "string") return summarizeText(content);
454
+ if (!Array.isArray(content)) {
455
+ return { kind: content === void 0 ? "missing" : typeof content };
456
+ }
457
+ return {
458
+ kind: "blocks",
459
+ count: content.length,
460
+ blocks: content.slice(0, DEBUG_BLOCK_LIMIT).map((block) => summarizeBlock(block)),
461
+ truncated: content.length > DEBUG_BLOCK_LIMIT || void 0
462
+ };
463
+ }
464
+ function summarizeBlock(block) {
465
+ if (!block || typeof block !== "object") return { kind: typeof block };
466
+ const b = block;
467
+ const summary = { type: b.type };
468
+ if (typeof b.name === "string") summary.name = b.name;
469
+ if (typeof b.text === "string") summary.text = summarizeText(b.text);
470
+ if (typeof b.thinking === "string") summary.thinking = summarizeText(b.thinking);
471
+ if (typeof b.content === "string") summary.content = summarizeText(b.content);
472
+ const input = b.input;
473
+ if (input && typeof input === "object" && !Array.isArray(input)) {
474
+ const i = input;
475
+ summary.inputKeys = Object.keys(i).sort();
476
+ if (typeof i.command === "string") summary.command = summarizeText(i.command);
477
+ }
478
+ return summary;
479
+ }
480
+ function summarizeText(text) {
481
+ if (typeof text !== "string") return { kind: text === void 0 ? "missing" : typeof text };
482
+ const normalized = text.replace(/\s+/g, " ").trim();
483
+ return {
484
+ kind: "text",
485
+ length: text.length,
486
+ snippet: normalized.length > DEBUG_TEXT_LIMIT ? `${normalized.slice(0, DEBUG_TEXT_LIMIT)}...` : normalized
487
+ };
488
+ }
339
489
 
340
490
  // src/hooks/stop.ts
341
491
  async function readStdinJson() {
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  fetchKbCatalogBlock,
4
4
  recallTurn
5
- } from "../chunk-C62I23HL.js";
5
+ } from "../chunk-73OY44GY.js";
6
6
  import "../chunk-6S5RJYBC.js";
7
7
  import {
8
8
  isCircuitOpen
9
- } from "../chunk-KEQMJ6IO.js";
9
+ } from "../chunk-52IAVJRK.js";
10
10
  import {
11
11
  HttpClient,
12
12
  agentFromArgvWithFallback,
@@ -14,7 +14,7 @@ import {
14
14
  isComplete,
15
15
  load,
16
16
  setDebug
17
- } from "../chunk-U3T5O6NX.js";
17
+ } from "../chunk-BWPFGTF7.js";
18
18
 
19
19
  // src/hooks/user-prompt-submit.ts
20
20
  import { pathToFileURL } from "url";
@@ -18,14 +18,14 @@ description: contextdb 知识库高频操作(查询 / 浏览 KB / 上传文档
18
18
 
19
19
  ## 2. 高频 recipes
20
20
 
21
- > 命令里 `--agent=qoder` 是必填参数。如果用户跑别的 agent(codex / claude),把 `qoder` 替换成对应名字。
21
+ > 命令里的 `--agent={{agent}}` 是当前 skill 安装目标。不要把示例改成 `qoder`;如果读到未替换的双花括号 agent 模板,先按 §6 判定真实 agent,再替换成 `qoder` / `qoderwork` / `codex` / `claude` / `default`。
22
22
 
23
23
  ### Recipe 1:在已知 KB 中查内容(最常见)
24
24
 
25
25
  **场景**:用户问"X KB 里关于 Y 的内容"、"在知识库 Z 查一下 W"。
26
26
 
27
27
  ```sh
28
- ctxdb kb search "<query>" --kb=<kb-name> --top-k=6 --agent=qoder
28
+ ctxdb kb search "<query>" --kb=<kb-name> --top-k=6 --agent={{agent}}
29
29
  ```
30
30
 
31
31
  返回 `chunks` 数组,按 `score` 倒序判断相关性;默认 threshold 0.4,空结果时见 §5 注意事项 4。
@@ -37,7 +37,7 @@ ctxdb kb search "<query>" --kb=<kb-name> --top-k=6 --agent=qoder
37
37
  **场景**:用户问"有哪些知识库"、"列一下 KB",或本身在写 recipe 1 之前需要确认 KB 名。
38
38
 
39
39
  ```sh
40
- ctxdb kb list --agent=qoder
40
+ ctxdb kb list --agent={{agent}}
41
41
  ```
42
42
 
43
43
  返回 `knowledge_bases` 数组,每项含 `name` / `id` / `document_count`。跨多 KB 查 chunk 时把 `name` 拼起来:`--kb=a,b,c`。
@@ -49,7 +49,7 @@ ctxdb kb list --agent=qoder
49
49
  **场景**:用户问"X KB 收了哪些文档"、"X KB 里有几篇"。
50
50
 
51
51
  ```sh
52
- ctxdb kb documents-list <kb-name> --agent=qoder
52
+ ctxdb kb documents-list <kb-name> --agent={{agent}}
53
53
  ```
54
54
 
55
55
  返回 `documents` 数组,每项含 `name` / `id` / `size`。
@@ -61,7 +61,7 @@ ctxdb kb documents-list <kb-name> --agent=qoder
61
61
  **场景**:用户说"把这段记进 X KB"、"上传这段文字到知识库 Y"。
62
62
 
63
63
  ```sh
64
- ctxdb kb upload-text <kb-name> <doc-name> --text="<body>" --agent=qoder
64
+ ctxdb kb upload-text <kb-name> <doc-name> --text="<body>" --agent={{agent}}
65
65
  ```
66
66
 
67
67
  副作用操作。**用户没明示 KB 名前先 recipe 2 列出来确认**(见 §5 注意事项 3)。KB 不存在会直接报错,不自动创建。
@@ -73,7 +73,7 @@ ctxdb kb upload-text <kb-name> <doc-name> --text="<body>" --agent=qoder
73
73
  **场景**:用户说"把 X.pdf 上传到 Y KB"、"灌这个文件进知识库 Z"。
74
74
 
75
75
  ```sh
76
- ctxdb kb upload-file <kb-name> <local-path> --agent=qoder
76
+ ctxdb kb upload-file <kb-name> <local-path> --agent={{agent}}
77
77
  ```
78
78
 
79
79
  支持格式:PDF / DOCX / MD / TXT。默认等待 chunking 完成;想立即返回加 `--no-wait`(见 §6)。
@@ -118,7 +118,7 @@ ctxdb kb upload-file <kb-name> <local-path> --agent=qoder
118
118
 
119
119
  - **现象**:在 shell 里写 `ctxdb kb list | python3 -c "import json,sys; ..."` 经常因为单/双引号嵌套 escape 失败
120
120
  - **根因**:shell + python -c 的引号是两层独立 escape,组合时极容易漏
121
- - **正确做法**:`ctxdb kb list --agent=qoder | jq '.knowledge_bases[].name'`;jq 表达式在单引号里不需要 escape
121
+ - **正确做法**:`ctxdb kb list --agent={{agent}} | jq '.knowledge_bases[].name'`;jq 表达式在单引号里不需要 escape
122
122
 
123
123
  ### 注意事项 3:`upload-*` / `create` 是副作用,没明示 KB 名前必须先确认
124
124
 
@@ -136,7 +136,7 @@ ctxdb kb upload-file <kb-name> <local-path> --agent=qoder
136
136
 
137
137
  - **现象**:`kb upload-text foo bar --text=...` 报错 "kb 'foo' not found"
138
138
  - **根因**:上传命令只写已有 KB,**不会**为了你 implicit 建一个
139
- - **正确做法**:先 `ctxdb kb create foo --agent=qoder`,再 upload
139
+ - **正确做法**:先 `ctxdb kb create foo --agent={{agent}}`,再 upload
140
140
 
141
141
  ## 6. 高级参数
142
142
 
@@ -172,18 +172,20 @@ ctxdb kb upload-file <kb-name> <local-path> --agent=qoder
172
172
 
173
173
  | 参数 | 说明 |
174
174
  |------|------|
175
- | `--agent=<name>` | **必填**。指定操作哪个 agent 的配置桶(`qoder` / `codex` / `claude` / `default`) |
175
+ | `--agent=<name>` | **必填**。指定操作哪个 agent 的配置桶(`qoder` / `qoderwork` / `codex` / `claude` / `default`) |
176
176
 
177
177
  `--agent` 解析顺序:
178
178
  1. 命令行显式 `--agent=<name>` → 用它
179
179
  2. 缺省 → 读 `CTXDB_AGENT` 环境变量
180
180
  3. env 也没有 → 落到 `"default"` 桶
181
- 4. `"default"` 桶未通过 `ctxdb setup --api-key=... --base-url=...` 配置过 exit 2 "config incomplete for agent default"
181
+ 4. 新版本 `ctxdb setup --agent <qoder|qoderwork|codex|claude>` 会在 `agents.default` 缺失时复制当前 agent 配置作为兜底
182
+ 5. 若 `"default"` 桶仍不存在或未配置完整 → exit 2 "config incomplete for agent default"
182
183
 
183
184
  ## 7. 配置
184
185
 
185
- - 配置文件:`~/.ctxdb/ctxdb.json`,分 `agents.<name>` 段(`qoder` / `codex` / `claude` / `default`),互不复用
186
+ - 配置文件:`~/.ctxdb/ctxdb.json`,分 `agents.<name>` 段(`qoder` / `qoderwork` / `codex` / `claude` / `default`),互不复用
186
187
  - 配置命令:`ctxdb setup --agent <name> --api-key=<key> --base-url=<url> [--user-id=<id>]`
187
- - 带 `--agent` 时还会装 hooks + skill;不带 `--agent` 时只写 `agents.default` 段(仅 CLI 用)
188
+ - 带 `--agent` 时还会装 hooks + skill;若 `agents.default` 缺失,会复制当前 agent 配置作为 CLI 兜底
189
+ - `--agent default` 只写 `agents.default` 段(仅 CLI 用,不装 hooks / skill)
188
190
  - 连接检查:`ctxdb ping --agent=<name>`
189
191
  - 状态查看:`ctxdb status --agent=<name>`
@@ -18,14 +18,14 @@ description: contextdb 长期记忆高频操作(主动记忆 / 搜索过往 /
18
18
 
19
19
  ## 2. 高频 recipes
20
20
 
21
- > 命令里 `--agent=qoder` 是必填参数。如果用户跑别的 agent(codex / claude),把 `qoder` 替换成对应名字。
21
+ > 命令里的 `--agent={{agent}}` 是当前 skill 安装目标。不要把示例改成 `qoder`;如果读到未替换的双花括号 agent 模板,先按 §6 判定真实 agent,再替换成 `qoder` / `qoderwork` / `codex` / `claude` / `default`。
22
22
 
23
23
  ### Recipe 1:主动记忆("记住 X")
24
24
 
25
25
  **场景**:用户明确说"记住"、"帮我记一下"、"把这条写进记忆"。
26
26
 
27
27
  ```sh
28
- ctxdb memory add "<text>" --agent=qoder
28
+ ctxdb memory add "<text>" --agent={{agent}}
29
29
  ```
30
30
 
31
31
  默认走 LLM fact-extraction(同步等结果)。想原文存(项目代号 / 精确数值)加 `--no-infer`(见 §6)。
@@ -39,7 +39,7 @@ ctxdb memory add "<text>" --agent=qoder
39
39
  **场景**:用户问"之前我们讨论过 X 吗"、"上次怎么说的"、"我之前提过 Y 没"。
40
40
 
41
41
  ```sh
42
- ctxdb memory search "<query>" --agent=qoder
42
+ ctxdb memory search "<query>" --agent={{agent}}
43
43
  ```
44
44
 
45
45
  返回 `results` 数组,每项含 `id` / `memory` / `score`,按 score 倒序。top-5 默认。想同时搜知识库 chunks 加 `--knowledge`(见 §6)。
@@ -51,7 +51,7 @@ ctxdb memory search "<query>" --agent=qoder
51
51
  **场景**:排查"刚才那条记进去没"、用户想看"系统记了我什么"。
52
52
 
53
53
  ```sh
54
- ctxdb memory list --page-size=20 --agent=qoder
54
+ ctxdb memory list --page-size=20 --agent={{agent}}
55
55
  ```
56
56
 
57
57
  返回 `results` 数组(含 `total` 计数)。按 category 过滤加 `--category=<cat>`(见 §6)。
@@ -64,10 +64,10 @@ ctxdb memory list --page-size=20 --agent=qoder
64
64
 
65
65
  ```sh
66
66
  # 改
67
- ctxdb memory update <memory-id> --text="<new-text>" --agent=qoder
67
+ ctxdb memory update <memory-id> --text="<new-text>" --agent={{agent}}
68
68
 
69
69
  # 删单条
70
- ctxdb memory delete <memory-id> --agent=qoder
70
+ ctxdb memory delete <memory-id> --agent={{agent}}
71
71
  ```
72
72
 
73
73
  `<memory-id>` 必须先用 recipe 2 或 recipe 3 拿到。
@@ -165,18 +165,20 @@ ctxdb memory delete <memory-id> --agent=qoder
165
165
 
166
166
  | 参数 | 说明 |
167
167
  |------|------|
168
- | `--agent=<name>` | **必填**。指定操作哪个 agent 的配置桶(`qoder` / `codex` / `claude` / `default`) |
168
+ | `--agent=<name>` | **必填**。指定操作哪个 agent 的配置桶(`qoder` / `qoderwork` / `codex` / `claude` / `default`) |
169
169
 
170
170
  `--agent` 解析顺序:
171
171
  1. 命令行显式 `--agent=<name>` → 用它
172
172
  2. 缺省 → 读 `CTXDB_AGENT` 环境变量
173
173
  3. env 也没有 → 落到 `"default"` 桶
174
- 4. `"default"` 桶未通过 `ctxdb setup --api-key=... --base-url=...` 配置过 exit 2 "config incomplete for agent default"
174
+ 4. 新版本 `ctxdb setup --agent <qoder|qoderwork|codex|claude>` 会在 `agents.default` 缺失时复制当前 agent 配置作为兜底
175
+ 5. 若 `"default"` 桶仍不存在或未配置完整 → exit 2 "config incomplete for agent default"
175
176
 
176
177
  ## 7. 配置
177
178
 
178
- - 配置文件:`~/.ctxdb/ctxdb.json`,分 `agents.<name>` 段(`qoder` / `codex` / `claude` / `default`),互不复用
179
+ - 配置文件:`~/.ctxdb/ctxdb.json`,分 `agents.<name>` 段(`qoder` / `qoderwork` / `codex` / `claude` / `default`),互不复用
179
180
  - 配置命令:`ctxdb setup --agent <name> --api-key=<key> --base-url=<url> [--user-id=<id>]`
180
- - 带 `--agent` 时还会装 hooks + skill;不带 `--agent` 时只写 `agents.default` 段(仅 CLI 用)
181
+ - 带 `--agent` 时还会装 hooks + skill;若 `agents.default` 缺失,会复制当前 agent 配置作为 CLI 兜底
182
+ - `--agent default` 只写 `agents.default` 段(仅 CLI 用,不装 hooks / skill)
181
183
  - 连接检查:`ctxdb ping --agent=<name>`
182
184
  - 状态查看:`ctxdb status --agent=<name>`
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aliyunrds/ctxdb",
3
- "version": "0.0.8-beta.1",
3
+ "version": "0.0.8-beta.2",
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",