@aliyunrds/ctxdb 0.0.5 → 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.
package/dist/cli/main.js CHANGED
@@ -4,8 +4,7 @@ import {
4
4
  DEFAULT_INGEST_TIMEOUT_MS,
5
5
  compactChunk,
6
6
  compactKbQueryResponse,
7
- findKb,
8
- findOrCreateKb,
7
+ createKb,
9
8
  getDocument,
10
9
  listDocuments,
11
10
  listKnowledgeBases,
@@ -14,7 +13,7 @@ import {
14
13
  pollIngest,
15
14
  uploadFile,
16
15
  uploadText
17
- } from "../chunk-HAUTENYD.js";
16
+ } from "../chunk-6S5RJYBC.js";
18
17
  import {
19
18
  DEFAULT_BASE_URL,
20
19
  DEFAULT_USER_ID,
@@ -23,12 +22,14 @@ import {
23
22
  agentFromEnv,
24
23
  agentHomeDir,
25
24
  configuredAgents,
26
- isAgent,
25
+ isAgentSlug,
26
+ isBuiltinAgent,
27
27
  isComplete,
28
28
  load,
29
29
  removeAgent,
30
- save
31
- } from "../chunk-QSSNPN3M.js";
30
+ save,
31
+ writeInstalledPkgVersion
32
+ } from "../chunk-S45GOYUU.js";
32
33
 
33
34
  // src/cli/util.ts
34
35
  var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
@@ -192,17 +193,16 @@ function printResult(value, json, command) {
192
193
  function agentFromFlags(flags) {
193
194
  const raw = flags.agent;
194
195
  if (raw === void 0) return agentFromEnv();
195
- if (isAgent(raw)) return raw;
196
- fail(`unknown --agent: ${String(raw)} (expected one of ${SUPPORTED_AGENTS.join(" / ")})`, 2);
196
+ if (isAgentSlug(raw)) return raw;
197
+ fail(`unknown --agent: ${String(raw)} (expected one of ${SUPPORTED_AGENTS.join(" / ")} / default)`, 2);
197
198
  }
198
199
  function buildContext(args) {
199
200
  const agent = agentFromFlags(args?.flags ?? {});
200
201
  const cfg = load({ agent });
201
202
  if (!isComplete(cfg)) {
202
- process.stderr.write(
203
- `config incomplete for agent ${agent}: run \`ctxdb setup --agent ${agent}\` or set CTXDB_API_KEY / CTXDB_BASE_URL / CTXDB_USER_ID env vars
204
- `
205
- );
203
+ const hint = agent === "default" ? "run `ctxdb setup --api-key=<key> --base-url=<url>` or set CTXDB_API_KEY / CTXDB_BASE_URL env vars" : `run \`ctxdb setup --agent ${agent}\` or set CTXDB_API_KEY / CTXDB_BASE_URL / CTXDB_USER_ID env vars`;
204
+ process.stderr.write(`config incomplete for agent ${agent}: ${hint}
205
+ `);
206
206
  process.exit(2);
207
207
  }
208
208
  const client = new HttpClient({
@@ -216,7 +216,7 @@ function fail(message, code = 1) {
216
216
  `);
217
217
  process.exit(code);
218
218
  }
219
- var PACKAGE_VERSION = "0.0.4";
219
+ var PACKAGE_VERSION = "0.0.7";
220
220
 
221
221
  // src/setup/installer.ts
222
222
  import {
@@ -248,15 +248,10 @@ function skillInstallRoot(agent) {
248
248
  return join(homedir(), ".claude", "skills");
249
249
  }
250
250
  }
251
- var SKILL_DIR_NAME = "ctxdb";
252
- function skillResourceDir(agent) {
253
- return agentSupportsHooks(agent) ? "hooks-driven" : "cli-only";
254
- }
255
- function skillInstallDir(agent) {
256
- return join(skillInstallRoot(agent), SKILL_DIR_NAME);
257
- }
251
+ var SKILL_DIRS = ["contextdb-memory", "contextdb-knowledge"];
252
+ var LEGACY_SKILL_DIRS = ["ctxdb"];
258
253
  function agentSupportsHooks(agent) {
259
- return agent === "qoder" || agent === "codex" || agent === "claude";
254
+ return isBuiltinAgent(agent);
260
255
  }
261
256
  function hookConfigPath(agent) {
262
257
  switch (agent) {
@@ -276,54 +271,48 @@ var LEGACY_QODER_SKILL_DIRS = [
276
271
  var HOOK_EVENTS = ["UserPromptSubmit", "Stop", "SessionStart"];
277
272
  async function runSetup(options) {
278
273
  const steps = [];
279
- if (!isAgent(options.agent)) {
274
+ if (!isAgentSlug(options.agent)) {
280
275
  steps.push({
281
276
  step: "validate-agent",
282
277
  ok: false,
283
- detail: `unknown --agent: ${String(options.agent)} (expected one of ${SUPPORTED_AGENTS.join(" / ")})`
278
+ detail: `unknown --agent: ${String(options.agent)} (expected one of ${SUPPORTED_AGENTS.join(" / ")} / default)`
284
279
  });
285
280
  return { ok: false, steps };
286
281
  }
287
282
  const agent = options.agent;
288
283
  const installSkill = options.installSkill !== false;
289
284
  const validate = options.validate !== false;
290
- const homeCheck = checkAgentHome(agent);
291
- steps.push(homeCheck);
292
- if (!homeCheck.ok) return { ok: false, steps };
285
+ if (isBuiltinAgent(agent)) {
286
+ const homeCheck = checkAgentHome(agent);
287
+ steps.push(homeCheck);
288
+ if (!homeCheck.ok) return { ok: false, steps };
289
+ }
293
290
  const cfg = load({ agent });
294
291
  if (options.apiKey) cfg.apiKey = options.apiKey;
295
292
  if (options.baseUrl) cfg.baseUrl = options.baseUrl.replace(/\/+$/, "");
296
293
  else if (!cfg.baseUrl) cfg.baseUrl = DEFAULT_BASE_URL;
297
294
  cfg.userId = options.userId ?? cfg.userId ?? DEFAULT_USER_ID;
295
+ if (!cfg.apiKey) {
296
+ steps.push({
297
+ step: "write-config",
298
+ ok: false,
299
+ detail: `${agent}: --api-key is required (won't write config without a valid API key)`
300
+ });
301
+ return { ok: false, steps };
302
+ }
298
303
  save(cfg, void 0, { agent });
299
304
  steps.push({
300
305
  step: "write-config",
301
306
  ok: true,
302
307
  detail: `${agent}: ${cfg.baseUrl} (user_id=${cfg.userId})`
303
308
  });
304
- if (installSkill) {
305
- try {
306
- const srcDir = locateSkillDir(skillResourceDir(agent));
307
- if (srcDir) {
308
- const dest = skillInstallDir(agent);
309
- copySkillDir(srcDir, dest, agent);
310
- steps.push({ step: "install-skill", ok: true, detail: dest });
311
- } else {
312
- steps.push({
313
- step: "install-skill",
314
- ok: false,
315
- detail: `skill source directory not found (${skillResourceDir(agent)})`
316
- });
317
- }
318
- } catch (err) {
319
- steps.push({
320
- step: "install-skill",
321
- ok: false,
322
- detail: err?.message ?? String(err)
323
- });
324
- }
309
+ if (installSkill && isBuiltinAgent(agent)) {
310
+ const installRoot = skillInstallRoot(agent);
311
+ const skillResult = installSkillsTo(installRoot, agent);
312
+ steps.push(...skillResult.steps);
313
+ if (!skillResult.ok) return { ok: false, steps };
325
314
  }
326
- if (agentSupportsHooks(agent)) {
315
+ if (isBuiltinAgent(agent) && agentSupportsHooks(agent)) {
327
316
  const hookPaths = locateHookPaths();
328
317
  steps.push({
329
318
  step: "locate-hooks",
@@ -406,15 +395,15 @@ async function runSetup(options) {
406
395
  }
407
396
  function runRemove(agent, options = {}) {
408
397
  const steps = [];
409
- if (!isAgent(agent)) {
398
+ if (!isAgentSlug(agent)) {
410
399
  steps.push({
411
400
  step: "validate-agent",
412
401
  ok: false,
413
- detail: `unknown --agent: ${String(agent)} (expected one of ${SUPPORTED_AGENTS.join(" / ")})`
402
+ detail: `unknown --agent: ${String(agent)} (expected one of ${SUPPORTED_AGENTS.join(" / ")} / default)`
414
403
  });
415
404
  return { ok: false, steps };
416
405
  }
417
- if (agentSupportsHooks(agent)) {
406
+ if (isBuiltinAgent(agent) && agentSupportsHooks(agent)) {
418
407
  const path = hookConfigPath(agent);
419
408
  if (!existsSync(path)) {
420
409
  steps.push({
@@ -451,12 +440,12 @@ function runRemove(agent, options = {}) {
451
440
  return { ok: false, steps };
452
441
  }
453
442
  }
454
- const dirs = agent === "qoder" ? [
455
- skillInstallDir("qoder"),
456
- ...LEGACY_QODER_SKILL_DIRS.map(
457
- (d) => join(homedir(), ".qoder", "skills", d)
458
- )
459
- ] : [skillInstallDir(agent)];
443
+ const root = skillInstallRoot(agent);
444
+ const dirs = [
445
+ ...SKILL_DIRS.map((d) => join(root, d)),
446
+ ...LEGACY_SKILL_DIRS.map((d) => join(root, d)),
447
+ ...agent === "qoder" ? LEGACY_QODER_SKILL_DIRS.map((d) => join(root, d)) : []
448
+ ];
460
449
  for (const p of dirs) {
461
450
  if (!existsSync(p)) continue;
462
451
  try {
@@ -470,26 +459,6 @@ function runRemove(agent, options = {}) {
470
459
  });
471
460
  }
472
461
  }
473
- } else {
474
- const dir = skillInstallDir(agent);
475
- if (!existsSync(dir)) {
476
- steps.push({
477
- step: "skill-missing",
478
- ok: true,
479
- detail: `nothing to remove (${dir})`
480
- });
481
- } else {
482
- try {
483
- rmSync(dir, { recursive: true, force: true });
484
- steps.push({ step: "remove-skill-dir", ok: true, detail: dir });
485
- } catch (err) {
486
- steps.push({
487
- step: "remove-skill-dir",
488
- ok: false,
489
- detail: `${dir}: ${err?.message ?? String(err)}`
490
- });
491
- }
492
- }
493
462
  }
494
463
  try {
495
464
  const r = removeAgent(agent, void 0, {
@@ -546,6 +515,12 @@ function runTeardown(options = {}) {
546
515
  steps.push({ ...s, step: `${agent}:${s.step}` });
547
516
  }
548
517
  }
518
+ const defaultSub = runRemove("default", { keepConfigShell });
519
+ if (defaultSub.steps.some((s) => s.step === "remove-config-section" && s.detail && !s.detail.includes("nothing to remove"))) {
520
+ for (const s of defaultSub.steps) {
521
+ steps.push({ ...s, step: `default:${s.step}` });
522
+ }
523
+ }
549
524
  if (purgeConfig) {
550
525
  const cfgPath = join(homedir(), ".ctxdb", "ctxdb.json");
551
526
  if (existsSync(cfgPath)) {
@@ -827,6 +802,44 @@ function copySkillDir(src, dest, agent) {
827
802
  }
828
803
  }
829
804
  }
805
+ var SKILL_INSTALL_TARGETS = {
806
+ qoder: join(homedir(), ".qoder", "skills"),
807
+ codex: join(homedir(), ".codex", "skills"),
808
+ claude: join(homedir(), ".claude", "skills"),
809
+ openclaw: join(homedir(), ".openclaw", "skills"),
810
+ hermes: join(homedir(), ".hermes", "skills")
811
+ };
812
+ function skillInstallTargetPath(target) {
813
+ return SKILL_INSTALL_TARGETS[target] ?? null;
814
+ }
815
+ function installSkillsTo(targetRoot, agent = "default") {
816
+ const steps = [];
817
+ for (const skillName of SKILL_DIRS) {
818
+ try {
819
+ const srcDir = locateSkillDir(skillName);
820
+ if (srcDir) {
821
+ const dest = join(targetRoot, skillName);
822
+ copySkillDir(srcDir, dest, agent);
823
+ steps.push({ step: "install-skill", ok: true, detail: dest });
824
+ } else {
825
+ steps.push({
826
+ step: "install-skill",
827
+ ok: false,
828
+ detail: `skill source directory not found (${skillName})`
829
+ });
830
+ return { ok: false, steps };
831
+ }
832
+ } catch (err) {
833
+ steps.push({
834
+ step: "install-skill",
835
+ ok: false,
836
+ detail: err?.message ?? String(err)
837
+ });
838
+ return { ok: false, steps };
839
+ }
840
+ }
841
+ return { ok: true, steps };
842
+ }
830
843
  function appendHooks(agent, hookPaths) {
831
844
  const path = hookConfigPath(agent);
832
845
  if (!path) return;
@@ -1081,7 +1094,7 @@ function describeCodexFeatureMutation(status2, createdNew) {
1081
1094
  }
1082
1095
  }
1083
1096
  function runUpgrade(options = {}) {
1084
- const agents = options.agent ? [options.agent] : configuredAgents(options.configPath);
1097
+ const agents = options.agent ? [options.agent] : configuredAgents(options.configPath).filter(isBuiltinAgent);
1085
1098
  if (agents.length === 0) {
1086
1099
  return {
1087
1100
  ok: false,
@@ -1113,24 +1126,19 @@ function upgradeOneAgent(agent) {
1113
1126
  if (!homeCheck.ok) {
1114
1127
  return { ok: true, steps, hints: [`${agent}: skipped \u2014 agent home not found`] };
1115
1128
  }
1116
- try {
1117
- const srcDir = locateSkillDir(skillResourceDir(agent));
1118
- if (srcDir) {
1119
- const dest = skillInstallDir(agent);
1120
- copySkillDir(srcDir, dest, agent);
1121
- steps.push({ step: "install-skill", ok: true, detail: dest });
1122
- } else {
1123
- steps.push({
1124
- step: "install-skill",
1125
- ok: false,
1126
- detail: "skill source directory not found"
1127
- });
1128
- return { ok: false, steps };
1129
+ const root = skillInstallRoot(agent);
1130
+ for (const legacy of LEGACY_SKILL_DIRS) {
1131
+ const legacyDir = join(root, legacy);
1132
+ if (!existsSync(legacyDir)) continue;
1133
+ try {
1134
+ rmSync(legacyDir, { recursive: true, force: true });
1135
+ steps.push({ step: "remove-legacy-skill", ok: true, detail: legacyDir });
1136
+ } catch {
1129
1137
  }
1130
- } catch (err) {
1131
- steps.push({ step: "install-skill", ok: false, detail: err?.message ?? String(err) });
1132
- return { ok: false, steps };
1133
1138
  }
1139
+ const skillResult = installSkillsTo(root, agent);
1140
+ steps.push(...skillResult.steps);
1141
+ if (!skillResult.ok) return { ok: false, steps };
1134
1142
  if (agentSupportsHooks(agent)) {
1135
1143
  const hookPaths = locateHookPaths();
1136
1144
  if (!hookPaths) {
@@ -1188,7 +1196,7 @@ async function status(args) {
1188
1196
  pingError = err?.message ?? String(err);
1189
1197
  }
1190
1198
  }
1191
- const hookHealth = checkHookNodePaths(agent);
1199
+ const hookHealth = isBuiltinAgent(agent) ? checkHookNodePaths(agent) : { installed: false, nodePaths: [], nodeOk: true, detail: "no hooks (default agent)" };
1192
1200
  printResult(
1193
1201
  {
1194
1202
  ok: complete,
@@ -1226,25 +1234,51 @@ async function ping(args) {
1226
1234
  }
1227
1235
 
1228
1236
  // src/cli/setup-cli.ts
1237
+ var HELP = `ctxdb setup \u2014 configure an agent
1238
+
1239
+ USAGE
1240
+ ctxdb setup [--agent <qoder|codex|claude>]
1241
+ [--api-key=K] [--base-url=URL] [--user-id=ID]
1242
+ [--no-install-skill] [--no-validate] [--json]
1243
+
1244
+ BEHAVIOR
1245
+ Without --agent Writes agents.default in ~/.ctxdb/ctxdb.json (CLI-only,
1246
+ no hooks or skills installed).
1247
+ With --agent Writes agent config + installs hooks + skills for the
1248
+ specified agent harness.
1249
+
1250
+ FLAGS
1251
+ --agent <name> Target agent (qoder|codex|claude)
1252
+ --api-key <key> API key (required on first setup)
1253
+ --base-url <url> Server URL (default: https://context-database.aliyuncs.com)
1254
+ --user-id <id> User bucket (default: "default")
1255
+ --no-install-skill Skip skill directory installation
1256
+ --no-validate Skip connectivity ping after setup
1257
+ --json Machine-readable JSON output
1258
+ `;
1229
1259
  function parseAgent(args) {
1230
1260
  const raw = args.flags.agent;
1231
- if (typeof raw !== "string") return null;
1232
- return SUPPORTED_AGENTS.includes(raw) ? raw : null;
1233
- }
1234
- function agentError() {
1261
+ if (raw === void 0) return "default";
1262
+ if (typeof raw === "string" && isAgentSlug(raw)) return raw;
1235
1263
  process.stderr.write(
1236
- `ctxdb setup: --agent <${SUPPORTED_AGENTS.join("|")}> is required
1237
- e.g. ctxdb setup --agent qoder --api-key=<key>
1264
+ `unknown --agent: ${String(raw)} (expected one of ${SUPPORTED_AGENTS.join(" / ")} / default)
1238
1265
  `
1239
1266
  );
1267
+ process.exit(2);
1240
1268
  }
1241
1269
  async function setup(args) {
1242
- const agent = parseAgent(args);
1243
- if (!agent) {
1244
- agentError();
1245
- return 2;
1270
+ if (args.flags.help || args.flags.h) {
1271
+ process.stdout.write(HELP);
1272
+ return 0;
1246
1273
  }
1274
+ const agent = parseAgent(args);
1247
1275
  const json = !!args.flags.json;
1276
+ if (agent === "default" && !json && !configuredAgents().includes("default")) {
1277
+ process.stderr.write(
1278
+ `hint: creating CLI-only "default" agent (no hooks/skills). Use --agent <claude|qoder|codex> for full setup.
1279
+ `
1280
+ );
1281
+ }
1248
1282
  const result = await runSetup({
1249
1283
  agent,
1250
1284
  apiKey: typeof args.flags["api-key"] === "string" ? args.flags["api-key"] : void 0,
@@ -1263,7 +1297,7 @@ ${h}
1263
1297
  }
1264
1298
 
1265
1299
  // src/cli/teardown.ts
1266
- var HELP = `ctxdb uninstall \u2014 remove hooks + skills
1300
+ var HELP2 = `ctxdb uninstall \u2014 remove hooks + skills
1267
1301
 
1268
1302
  USAGE
1269
1303
  ctxdb uninstall [--agent <qoder|codex|claude>] [--purge-config] [--purge-logs] [--purge-all] [--json]
@@ -1290,15 +1324,15 @@ NOTE
1290
1324
  `;
1291
1325
  function uninstall(args) {
1292
1326
  if (args.flags.help || args.flags.h) {
1293
- process.stdout.write(HELP);
1327
+ process.stdout.write(HELP2);
1294
1328
  return 0;
1295
1329
  }
1296
1330
  const json = !!args.flags.json;
1297
1331
  const agentFlag = args.flags.agent;
1298
1332
  if (agentFlag !== void 0) {
1299
- if (!isAgent(agentFlag)) {
1333
+ if (!isAgentSlug(agentFlag)) {
1300
1334
  process.stderr.write(
1301
- `unknown --agent: ${String(agentFlag)} (expected one of ${SUPPORTED_AGENTS.join(" / ")})
1335
+ `unknown --agent: ${String(agentFlag)} (expected one of ${SUPPORTED_AGENTS.join(" / ")} / default)
1302
1336
  `
1303
1337
  );
1304
1338
  return 2;
@@ -1326,10 +1360,145 @@ ${h}
1326
1360
  return result.ok ? 0 : 1;
1327
1361
  }
1328
1362
 
1363
+ // src/lib/self-update.ts
1364
+ import { spawnSync } from "child_process";
1365
+ import { fileURLToPath as fileURLToPath2 } from "url";
1366
+ import { dirname as dirname2 } from "path";
1367
+ var PACKAGE_NAME = "@aliyunrds/ctxdb";
1368
+ var NPM_VIEW_TIMEOUT_MS = 1e4;
1369
+ function detectInstallMethod() {
1370
+ const dir = dirname2(fileURLToPath2(import.meta.url));
1371
+ return dir.includes("/node_modules/") ? "npm" : "unknown";
1372
+ }
1373
+ function checkLatestVersion(currentVersion) {
1374
+ const result = spawnSync("npm", ["view", PACKAGE_NAME, "version"], {
1375
+ encoding: "utf-8",
1376
+ timeout: NPM_VIEW_TIMEOUT_MS,
1377
+ stdio: ["ignore", "pipe", "pipe"]
1378
+ });
1379
+ if (result.status !== 0 || !result.stdout?.trim()) {
1380
+ return {
1381
+ latest: null,
1382
+ current: currentVersion,
1383
+ isNewer: false,
1384
+ error: result.stderr?.trim() || `npm view exited with code ${result.status}`
1385
+ };
1386
+ }
1387
+ const latest = result.stdout.trim();
1388
+ return {
1389
+ latest,
1390
+ current: currentVersion,
1391
+ isNewer: isNewerVersion(latest, currentVersion)
1392
+ };
1393
+ }
1394
+ function isNewerVersion(candidate, current) {
1395
+ const parse = (v) => v.replace(/^v/, "").split(".").map(Number);
1396
+ const [cMaj = 0, cMin = 0, cPat = 0] = parse(candidate);
1397
+ const [uMaj = 0, uMin = 0, uPat = 0] = parse(current);
1398
+ if (cMaj !== uMaj) return cMaj > uMaj;
1399
+ if (cMin !== uMin) return cMin > uMin;
1400
+ return cPat > uPat;
1401
+ }
1402
+ function runSelfUpdate(currentVersion, passthroughArgs = []) {
1403
+ const method = detectInstallMethod();
1404
+ if (method !== "npm") {
1405
+ return {
1406
+ ok: false,
1407
+ updated: false,
1408
+ fromVersion: currentVersion,
1409
+ error: "ctxdb was not installed via npm. Update manually with your package manager."
1410
+ };
1411
+ }
1412
+ const check = checkLatestVersion(currentVersion);
1413
+ if (check.error) {
1414
+ process.stderr.write(`ctxdb: npm view failed (${check.error}), skipping self-update
1415
+ `);
1416
+ return { ok: true, updated: false, fromVersion: currentVersion };
1417
+ }
1418
+ if (!check.isNewer) {
1419
+ process.stderr.write(`ctxdb: package already up to date (v${currentVersion})
1420
+ `);
1421
+ return { ok: true, updated: false, fromVersion: currentVersion };
1422
+ }
1423
+ process.stderr.write(`ctxdb: updating ${PACKAGE_NAME} v${currentVersion} \u2192 v${check.latest}...
1424
+ `);
1425
+ const install = spawnSync("npm", ["install", "-g", `${PACKAGE_NAME}@${check.latest}`], {
1426
+ stdio: "inherit",
1427
+ encoding: "utf-8"
1428
+ });
1429
+ if (install.status !== 0) {
1430
+ return {
1431
+ ok: false,
1432
+ updated: false,
1433
+ fromVersion: currentVersion,
1434
+ error: `npm install -g failed with exit code ${install.status}`
1435
+ };
1436
+ }
1437
+ process.stderr.write(`ctxdb: package updated to v${check.latest}, refreshing skills/hooks...
1438
+ `);
1439
+ const upgradeArgs = ["upgrade", ...passthroughArgs];
1440
+ const refresh = spawnSync("ctxdb", upgradeArgs, {
1441
+ stdio: "inherit",
1442
+ encoding: "utf-8"
1443
+ });
1444
+ return {
1445
+ ok: refresh.status === 0,
1446
+ updated: true,
1447
+ fromVersion: currentVersion,
1448
+ toVersion: check.latest,
1449
+ error: refresh.status !== 0 ? `ctxdb upgrade exited with code ${refresh.status}` : void 0
1450
+ };
1451
+ }
1452
+
1329
1453
  // src/cli/upgrade.ts
1454
+ var HELP3 = `ctxdb upgrade \u2014 refresh skills + hooks
1455
+
1456
+ USAGE
1457
+ ctxdb upgrade [--agent <qoder|codex|claude>] [--self-update] [--json]
1458
+
1459
+ BEHAVIOR
1460
+ Refreshes skill files and hook entries for configured agents without
1461
+ touching credentials. Designed for the post-npm-update path.
1462
+
1463
+ Without --agent: upgrades all configured built-in agents.
1464
+ With --agent: upgrades only the specified agent.
1465
+
1466
+ FLAGS
1467
+ --agent <name> Target a single agent (qoder|codex|claude)
1468
+ --self-update Also pull latest package from npm before upgrading
1469
+ --json Machine-readable JSON output
1470
+ `;
1330
1471
  function upgrade(args) {
1472
+ if (args.flags.help || args.flags.h) {
1473
+ process.stdout.write(HELP3);
1474
+ return 0;
1475
+ }
1476
+ const selfUpdate = !!args.flags["self-update"];
1477
+ if (selfUpdate) {
1478
+ const passthrough = [];
1479
+ const agentFlag2 = args.flags.agent;
1480
+ if (agentFlag2) passthrough.push("--agent", agentFlag2);
1481
+ if (args.flags.json) passthrough.push("--json");
1482
+ const result2 = runSelfUpdate(PACKAGE_VERSION, passthrough);
1483
+ if (!result2.ok) {
1484
+ if (args.flags.json) {
1485
+ process.stdout.write(JSON.stringify({ ok: false, error: result2.error }) + "\n");
1486
+ } else {
1487
+ process.stderr.write(`ctxdb: self-update failed: ${result2.error}
1488
+ `);
1489
+ }
1490
+ return 1;
1491
+ }
1492
+ if (result2.updated) {
1493
+ return 0;
1494
+ }
1495
+ }
1331
1496
  const agentFlag = args.flags.agent;
1332
- if (agentFlag && !isAgent(agentFlag)) {
1497
+ if (agentFlag === "default") {
1498
+ process.stderr.write("nothing to upgrade for default agent (no hooks or skills)\n");
1499
+ return 0;
1500
+ }
1501
+ if (agentFlag && !isBuiltinAgent(agentFlag)) {
1333
1502
  process.stderr.write(
1334
1503
  `unknown --agent: ${agentFlag} (expected one of ${SUPPORTED_AGENTS.join(" / ")})
1335
1504
  `
@@ -1337,6 +1506,12 @@ function upgrade(args) {
1337
1506
  return 2;
1338
1507
  }
1339
1508
  const result = runUpgrade({ agent: agentFlag });
1509
+ if (result.ok) {
1510
+ try {
1511
+ writeInstalledPkgVersion(PACKAGE_VERSION);
1512
+ } catch {
1513
+ }
1514
+ }
1340
1515
  const json = !!args.flags.json;
1341
1516
  printResult(result, json);
1342
1517
  if (!json && result.hints) {
@@ -1347,6 +1522,71 @@ ${h}
1347
1522
  return result.ok ? 0 : 1;
1348
1523
  }
1349
1524
 
1525
+ // src/cli/skill-cli.ts
1526
+ var SUPPORTED_TARGETS = Object.keys(SKILL_INSTALL_TARGETS);
1527
+ var HELP4 = `ctxdb skill install \u2014 install skill files to an agent's skill directory
1528
+
1529
+ USAGE
1530
+ ctxdb skill install --target <name>
1531
+ ctxdb skill install --path <dir>
1532
+
1533
+ TARGETS
1534
+ ${SUPPORTED_TARGETS.join(", ")}
1535
+
1536
+ FLAGS
1537
+ --target <name> Install to a built-in agent's skill directory
1538
+ --path <dir> Install to a custom directory
1539
+ --json Machine-readable JSON output
1540
+
1541
+ Note: This command only installs skill files (contextdb, contextdb-memory,
1542
+ contextdb-knowledge). It does not write config or install hooks.
1543
+ To configure server connection, run: ctxdb setup --api-key=<key> --base-url=<url>
1544
+ `;
1545
+ function skillInstall(args) {
1546
+ if (args.flags.help || args.flags.h) {
1547
+ process.stdout.write(HELP4);
1548
+ return 0;
1549
+ }
1550
+ const json = !!args.flags.json;
1551
+ const target = args.flags.target;
1552
+ const customPath = args.flags.path;
1553
+ if (target && customPath) {
1554
+ fail("--target and --path are mutually exclusive", 2);
1555
+ }
1556
+ if (!target && !customPath) {
1557
+ process.stderr.write(HELP4);
1558
+ return 2;
1559
+ }
1560
+ let targetRoot;
1561
+ if (customPath) {
1562
+ targetRoot = customPath;
1563
+ } else {
1564
+ const resolved = skillInstallTargetPath(target);
1565
+ if (!resolved) {
1566
+ fail(
1567
+ `unknown --target: ${target} (supported: ${SUPPORTED_TARGETS.join(", ")})`,
1568
+ 2
1569
+ );
1570
+ }
1571
+ targetRoot = resolved;
1572
+ }
1573
+ const result = installSkillsTo(targetRoot);
1574
+ const ok = result.ok;
1575
+ const output = {
1576
+ ok,
1577
+ target: target ?? null,
1578
+ path: targetRoot,
1579
+ steps: result.steps
1580
+ };
1581
+ printResult(output, json);
1582
+ if (ok && !json) {
1583
+ process.stderr.write(
1584
+ "\nNote: This command only installs skill files.\nTo configure server connection, run:\n ctxdb setup --api-key=<key> --base-url=<url>\n"
1585
+ );
1586
+ }
1587
+ return ok ? 0 : 1;
1588
+ }
1589
+
1350
1590
  // src/cli/memory.ts
1351
1591
  async function memoryAdd(args) {
1352
1592
  const text = args.positional[0];
@@ -1477,25 +1717,20 @@ async function kbUploadText(args) {
1477
1717
  const text = args.flags["text"];
1478
1718
  if (!kbName || !docName || typeof text !== "string") {
1479
1719
  fail(
1480
- "usage: ctxdb kb upload-text <kb-name> <doc-name> --text=<body> [--kb-description=...] [--file-path=<server-logical-path>] [--no-wait]"
1720
+ "usage: ctxdb kb upload-text <kb-name> <doc-name> --text=<body> [--file-path=<server-logical-path>] [--no-wait]"
1481
1721
  );
1482
1722
  }
1483
1723
  const ctx = buildContext(args);
1484
- const description = args.flags["kb-description"] ?? "";
1485
1724
  const filePath = typeof args.flags["file-path"] === "string" ? args.flags["file-path"] : void 0;
1486
- const { kb, created } = await findOrCreateKb(ctx.client, kbName, description);
1487
- const doc = await uploadText(ctx.client, kb.id, docName, text, "text/plain", filePath);
1725
+ const doc = await uploadText(ctx.client, kbName, docName, text, "text/plain", filePath);
1488
1726
  if (args.flags["no-wait"]) {
1489
- printResult({ kb, kb_created: created, document: doc }, !!args.flags.json);
1727
+ printResult({ document: doc }, !!args.flags.json);
1490
1728
  return 0;
1491
1729
  }
1492
- const final = await pollIngest(ctx.client, kb.id, doc.id, {
1730
+ const final = await pollIngest(ctx.client, kbName, doc.id, {
1493
1731
  timeoutMs: DEFAULT_INGEST_TIMEOUT_MS
1494
1732
  });
1495
- printResult(
1496
- { kb, kb_created: created, document: final },
1497
- !!args.flags.json
1498
- );
1733
+ printResult({ document: final }, !!args.flags.json);
1499
1734
  return 0;
1500
1735
  }
1501
1736
  async function kbUploadFile(args) {
@@ -1506,23 +1741,28 @@ async function kbUploadFile(args) {
1506
1741
  );
1507
1742
  }
1508
1743
  const ctx = buildContext(args);
1509
- const { kb, created } = await findOrCreateKb(ctx.client, kbName);
1510
- const doc = await uploadFile(ctx.client, kb.id, localPath, {
1744
+ const doc = await uploadFile(ctx.client, kbName, localPath, {
1511
1745
  docName: typeof args.flags["doc-name"] === "string" ? args.flags["doc-name"] : void 0,
1512
1746
  filePath: typeof args.flags["file-path"] === "string" ? args.flags["file-path"] : void 0,
1513
1747
  timeoutMs: DEFAULT_FILE_INGEST_TIMEOUT_MS
1514
1748
  });
1515
1749
  if (args.flags["no-wait"]) {
1516
- printResult({ kb, kb_created: created, document: doc }, !!args.flags.json);
1750
+ printResult({ document: doc }, !!args.flags.json);
1517
1751
  return 0;
1518
1752
  }
1519
- const final = await pollIngest(ctx.client, kb.id, doc.id, {
1753
+ const final = await pollIngest(ctx.client, kbName, doc.id, {
1520
1754
  timeoutMs: DEFAULT_FILE_INGEST_TIMEOUT_MS
1521
1755
  });
1522
- printResult(
1523
- { kb, kb_created: created, document: final },
1524
- !!args.flags.json
1525
- );
1756
+ printResult({ document: final }, !!args.flags.json);
1757
+ return 0;
1758
+ }
1759
+ async function kbCreate(args) {
1760
+ const kbName = args.positional[0];
1761
+ if (!kbName) fail("usage: ctxdb kb create <kb-name> [--description=<desc>]");
1762
+ const ctx = buildContext(args);
1763
+ const description = typeof args.flags["description"] === "string" ? args.flags["description"] : "";
1764
+ const kb = await createKb(ctx.client, kbName, description);
1765
+ printResult(kb, !!args.flags.json);
1526
1766
  return 0;
1527
1767
  }
1528
1768
  async function kbList(args) {
@@ -1532,28 +1772,24 @@ async function kbList(args) {
1532
1772
  return 0;
1533
1773
  }
1534
1774
  async function kbDocumentsList(args) {
1535
- const kbNameOrId = args.positional[0];
1536
- if (!kbNameOrId) fail("usage: ctxdb kb documents-list <kb-name-or-id>");
1775
+ const kbName = args.positional[0];
1776
+ if (!kbName) fail("usage: ctxdb kb documents-list <kb-name>");
1537
1777
  const ctx = buildContext(args);
1538
- const kb = await findKb(ctx.client, kbNameOrId);
1539
- if (!kb) fail(`KB not found: ${kbNameOrId}`);
1540
- const docs = await listDocuments(ctx.client, kb.id);
1778
+ const docs = await listDocuments(ctx.client, kbName);
1541
1779
  printResult(
1542
- { kb, documents: docs, count: docs.length },
1780
+ { documents: docs, count: docs.length },
1543
1781
  !!args.flags.json
1544
1782
  );
1545
1783
  return 0;
1546
1784
  }
1547
1785
  async function kbDocumentGet(args) {
1548
- const [kbNameOrId, docId] = args.positional;
1549
- if (!kbNameOrId || !docId) {
1550
- fail("usage: ctxdb kb document-get <kb-name-or-id> <doc-id>");
1786
+ const [kbName, docId] = args.positional;
1787
+ if (!kbName || !docId) {
1788
+ fail("usage: ctxdb kb document-get <kb-name> <doc-id>");
1551
1789
  }
1552
1790
  const ctx = buildContext(args);
1553
- const kb = await findKb(ctx.client, kbNameOrId);
1554
- if (!kb) fail(`KB not found: ${kbNameOrId}`);
1555
- const doc = await getDocument(ctx.client, kb.id, docId);
1556
- printResult({ kb, document: doc }, !!args.flags.json);
1791
+ const doc = await getDocument(ctx.client, kbName, docId);
1792
+ printResult({ document: doc }, !!args.flags.json);
1557
1793
  return 0;
1558
1794
  }
1559
1795
  async function kbSearch(args) {
@@ -1564,16 +1800,10 @@ async function kbSearch(args) {
1564
1800
  );
1565
1801
  }
1566
1802
  const ctx = buildContext(args);
1567
- const kbList2 = (typeof args.flags.kb === "string" ? args.flags.kb : "").split(",").map((s) => s.trim()).filter(Boolean);
1568
- const datasetIds = [];
1569
- for (const name of kbList2) {
1570
- const kb = await findKb(ctx.client, name);
1571
- if (!kb) fail(`KB not found: ${name}`);
1572
- datasetIds.push(kb.id);
1573
- }
1803
+ const kbNames = (typeof args.flags.kb === "string" ? args.flags.kb : "").split(",").map((s) => s.trim()).filter(Boolean);
1574
1804
  const body = {
1575
1805
  question: query,
1576
- dataset_ids: datasetIds
1806
+ knowledge_base_names: kbNames
1577
1807
  };
1578
1808
  if (typeof args.flags["top-k"] === "string") {
1579
1809
  const n = Number(args.flags["top-k"]);
@@ -1590,21 +1820,22 @@ async function kbSearch(args) {
1590
1820
  }
1591
1821
 
1592
1822
  // src/cli/main.ts
1593
- var HELP2 = `ctxdb v${PACKAGE_VERSION} \u2014 RDS ContextDatabase CLI (multi-agent)
1823
+ var HELP5 = `ctxdb v${PACKAGE_VERSION} \u2014 RDS ContextDatabase CLI (multi-agent)
1594
1824
 
1595
1825
  USAGE
1596
1826
  ctxdb <command> [args] [--flags]
1597
1827
 
1598
1828
  COMMANDS
1599
- setup --agent <qoder|codex|claude>
1829
+ setup [--agent <qoder|codex|claude>]
1600
1830
  [--api-key=K] [--base-url=URL] [--user-id=ID]
1601
1831
  [--no-install-skill] [--no-validate] [--json]
1832
+ Without --agent \u2192 writes agents.default (CLI-only, no hooks/skills)
1602
1833
  Qoder \u2192 writes agents.qoder config + ~/.qoder/settings.json hooks + skill
1603
1834
  Codex \u2192 writes agents.codex config + ~/.codex/hooks.json hooks + skill
1604
1835
  Claude \u2192 writes agents.claude config + ~/.claude/settings.json hooks + skill
1605
- status [--agent <qoder|codex|claude>] [--json]
1606
- ping [--agent <qoder|codex|claude>] [--json]
1607
- uninstall [--agent <qoder|codex|claude>] [--purge-config] [--purge-logs] [--purge-all] [--json]
1836
+ status [--agent <name>] [--json]
1837
+ ping [--agent <name>] [--json]
1838
+ uninstall [--agent <name>] [--purge-config] [--purge-logs] [--purge-all] [--json]
1608
1839
  teardown (alias for uninstall)
1609
1840
  Without --agent: removes hooks + skills for every agent.
1610
1841
  With --agent: removes only that agent's hooks + skill + config section.
@@ -1612,10 +1843,9 @@ COMMANDS
1612
1843
  --purge-logs also deletes ~/.ctxdb/logs/.
1613
1844
  --purge-all deletes everything under ~/.ctxdb/
1614
1845
  (does not run npm uninstall).
1615
- upgrade [--agent <name>] [--json]
1616
- Refresh skills + hooks for configured agents.
1617
- Run after npm update -g @aliyunrds/ctxdb.
1618
- Does not modify credentials.
1846
+ upgrade|update [--agent <name>] [--self-update] [--json]
1847
+ Refresh skills + hooks. --self-update also
1848
+ pulls latest package from npm.
1619
1849
 
1620
1850
  memory add <text> [--agent=<name>] [--user-id=...] [--metadata=K1=V1,K2=V2] [--no-infer]
1621
1851
  memory search <query> [--agent=<name>] [--top-k=10] [--threshold=0.4] [--knowledge]
@@ -1629,13 +1859,14 @@ COMMANDS
1629
1859
  memory update <memory-id> --text=<new-text> [--agent=<name>]
1630
1860
  memory delete <memory-id> | --all [--agent=<name>]
1631
1861
 
1862
+ kb create <kb-name> [--description=<desc>] [--agent=<name>]
1632
1863
  kb upload-text <kb-name> <doc-name> --text=<body> [--agent=<name>]
1633
- [--kb-description=...] [--file-path=<server-logical-path>] [--no-wait]
1864
+ [--file-path=<server-logical-path>] [--no-wait]
1634
1865
  kb upload-file <kb-name> <local-path> [--agent=<name>] [--doc-name=...]
1635
1866
  [--file-path=<server-logical-path>] [--no-wait]
1636
1867
  kb list [--agent=<name>]
1637
- kb documents-list <kb-name-or-id> [--agent=<name>]
1638
- kb document-get <kb-name-or-id> <doc-id> [--agent=<name>]
1868
+ kb documents-list <kb-name> [--agent=<name>]
1869
+ kb document-get <kb-name> <doc-id> [--agent=<name>]
1639
1870
  kb search <query> [--agent=<name>] [--kb=name1,name2] [--top-k=N] [--threshold=F]
1640
1871
  [--verbose] [--raw]
1641
1872
  Standalone KB recall (independent of memory).
@@ -1644,10 +1875,20 @@ COMMANDS
1644
1875
  doc_id?/tags? for citation.
1645
1876
  --raw: server response verbatim (curl parity).
1646
1877
 
1878
+ skill install --target <qoder|codex|claude|openclaw|hermes> [--json]
1879
+ skill install --path <dir> [--json]
1880
+ Install skill files (contextdb-memory,
1881
+ contextdb-knowledge) to an agent's skill directory.
1882
+ Does not write config or install hooks.
1883
+
1884
+ AGENT RESOLUTION (when --agent is omitted):
1885
+ 1. CTXDB_AGENT env var
1886
+ 2. agents.default section in ~/.ctxdb/ctxdb.json
1887
+
1647
1888
  ENV VARS (override selected ~/.ctxdb/ctxdb.json agent config):
1648
1889
  CTXDB_AGENT CTXDB_API_KEY CTXDB_BASE_URL CTXDB_USER_ID
1649
1890
 
1650
- CONFIG: ~/.ctxdb/ctxdb.json (agents.qoder / agents.codex / agents.claude)
1891
+ CONFIG: ~/.ctxdb/ctxdb.json (agents.default / agents.qoder / agents.codex / agents.claude)
1651
1892
  LOGS: ~/.ctxdb/logs/ctxdb.log
1652
1893
  `;
1653
1894
  var ROUTES = {
@@ -1657,6 +1898,10 @@ var ROUTES = {
1657
1898
  uninstall,
1658
1899
  teardown: uninstall,
1659
1900
  upgrade,
1901
+ update: upgrade,
1902
+ skill: {
1903
+ install: skillInstall
1904
+ },
1660
1905
  memory: {
1661
1906
  add: memoryAdd,
1662
1907
  search: memorySearch,
@@ -1666,6 +1911,7 @@ var ROUTES = {
1666
1911
  delete: memoryDelete
1667
1912
  },
1668
1913
  kb: {
1914
+ create: kbCreate,
1669
1915
  "upload-text": kbUploadText,
1670
1916
  "upload-file": kbUploadFile,
1671
1917
  list: kbList,
@@ -1676,7 +1922,7 @@ var ROUTES = {
1676
1922
  };
1677
1923
  async function main(argv = process.argv.slice(2)) {
1678
1924
  if (argv.length === 0 || argv[0] === "-h" || argv[0] === "--help") {
1679
- process.stdout.write(HELP2);
1925
+ process.stdout.write(HELP5);
1680
1926
  return 0;
1681
1927
  }
1682
1928
  if (argv[0] === "-v" || argv[0] === "--version") {
@@ -1689,7 +1935,7 @@ async function main(argv = process.argv.slice(2)) {
1689
1935
  if (!route) {
1690
1936
  process.stderr.write(`unknown command: ${first}
1691
1937
  `);
1692
- process.stderr.write(HELP2);
1938
+ process.stderr.write(HELP5);
1693
1939
  return 2;
1694
1940
  }
1695
1941
  if (typeof route === "function") {
@@ -1702,6 +1948,13 @@ async function main(argv = process.argv.slice(2)) {
1702
1948
  );
1703
1949
  return 2;
1704
1950
  }
1951
+ if (rest[0] === "-h" || rest[0] === "--help") {
1952
+ process.stdout.write(
1953
+ `usage: ctxdb ${first} <${Object.keys(route).join("|")}> ...
1954
+ `
1955
+ );
1956
+ return 0;
1957
+ }
1705
1958
  const [sub, ...subRest] = rest;
1706
1959
  const handler = route[sub];
1707
1960
  if (!handler) {