@aliyunrds/ctxdb 1.0.9-beta.1 → 1.0.10-beta.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/dist/cli/main.js CHANGED
@@ -1,4 +1,9 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ beginUpdateNotification,
4
+ completeUpdateNotification,
5
+ runSelfUpdate
6
+ } from "../chunk-QUPLZ7KF.js";
2
7
  import {
3
8
  DEFAULT_FILE_INGEST_TIMEOUT_MS,
4
9
  DEFAULT_FILE_UPLOAD_TIMEOUT_MS,
@@ -31,13 +36,14 @@ import {
31
36
  uploadFile,
32
37
  uploadText,
33
38
  validateLocalFile
34
- } from "../chunk-T5ZJEPR4.js";
39
+ } from "../chunk-SZ3AZDMU.js";
35
40
  import {
36
41
  AGENT_CHOICES,
37
42
  CtxdbError,
38
43
  DEFAULT_BASE_URL,
39
44
  DEFAULT_USER_ID,
40
45
  HttpClient,
46
+ MAX_ZIP,
41
47
  PACKAGE_NAME,
42
48
  PACKAGE_VERSION,
43
49
  SUPPORTED_AGENTS,
@@ -46,36 +52,61 @@ import {
46
52
  agentHomeDir,
47
53
  agentHomeDirs,
48
54
  agentPlatformSupport,
55
+ autoCollectAllowed,
56
+ bindSession,
57
+ bindingPath,
58
+ businessEvents,
59
+ catalogContext,
60
+ configFileName,
49
61
  configuredAgents,
50
62
  defaultConfigPath,
51
63
  detectInstalledAgents,
64
+ expireSessionEvidence,
65
+ getBinding,
52
66
  hasConfiguredAgent,
53
67
  inspectAgentHomes,
54
68
  inspectConfig,
69
+ inspectZip,
55
70
  isAgentSlug,
56
71
  isBuiltinAgent,
57
72
  isDataApiReady,
58
73
  load,
74
+ loadSkill,
75
+ privatePath,
76
+ publishPackage,
77
+ readPackage,
78
+ readPrivate,
79
+ readSpool,
80
+ readState,
81
+ refreshManifest,
59
82
  removeAgent,
83
+ requireUuid,
60
84
  resolveDataApiCredential,
85
+ resumeSubmission,
86
+ retryFeedback,
61
87
  save,
88
+ sha256,
89
+ skillRoot,
90
+ spoolDirectory,
91
+ threshold,
92
+ unbox,
93
+ updateAutoSkillEvolution,
62
94
  updateConfiguredDebug,
63
- writeInstalledPkgVersion
64
- } from "../chunk-2U7G6UXE.js";
65
- import {
66
- beginUpdateNotification,
67
- completeUpdateNotification,
68
- runSelfUpdate
69
- } from "../chunk-3MEBQDGU.js";
95
+ validateEvidence,
96
+ verifiedClient,
97
+ withStateLock,
98
+ writeInstalledPkgVersion,
99
+ writeState
100
+ } from "../chunk-TZFDXKLJ.js";
70
101
  import {
71
102
  DISTRIBUTION_MANIFEST
72
103
  } from "../chunk-5ITT5GSP.js";
73
104
  import "../chunk-PI2SUS3M.js";
74
105
 
75
106
  // src/cli/main.ts
76
- import { realpathSync } from "fs";
77
- import { basename as basename3, resolve as resolve3 } from "path";
78
- import { fileURLToPath as fileURLToPath2 } from "url";
107
+ import { realpathSync } from "node:fs";
108
+ import { basename as basename3, resolve as resolve4 } from "node:path";
109
+ import { fileURLToPath as fileURLToPath2 } from "node:url";
79
110
 
80
111
  // src/cli/util.ts
81
112
  var REPEATABLE_FLAGS = /* @__PURE__ */ new Set([
@@ -100,6 +131,13 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
100
131
  "purge-config",
101
132
  "purge-logs",
102
133
  "purge-all",
134
+ // skills
135
+ "local",
136
+ "editable",
137
+ "remote",
138
+ "resume",
139
+ "apply",
140
+ "cache",
103
141
  // memory
104
142
  "no-infer",
105
143
  // memory add
@@ -155,10 +193,10 @@ function parseArgs(argv, booleanFlags = BOOLEAN_FLAGS) {
155
193
  const positional2 = [];
156
194
  const flags = {};
157
195
  const repeatedFlags = {};
158
- const setFlag = (name, value) => {
159
- flags[name] = value;
196
+ const setFlag = (name, value2) => {
197
+ flags[name] = value2;
160
198
  if (REPEATABLE_FLAGS.has(name)) {
161
- (repeatedFlags[name] ??= []).push(value);
199
+ (repeatedFlags[name] ??= []).push(value2);
162
200
  }
163
201
  };
164
202
  let separatorSeen = false;
@@ -231,8 +269,8 @@ function isUpgradeResult(v) {
231
269
  }
232
270
  function isMultiAgentResult(v) {
233
271
  if (typeof v !== "object" || v === null) return false;
234
- const value = v;
235
- return value.mode === "all" && (value.operation === "setup" || value.operation === "status") && typeof value.agents === "object" && value.agents !== null && Array.isArray(value.skipped) && typeof value.summary === "object" && value.summary !== null;
272
+ const value2 = v;
273
+ return value2.mode === "all" && (value2.operation === "setup" || value2.operation === "status") && typeof value2.agents === "object" && value2.agents !== null && Array.isArray(value2.skipped) && typeof value2.summary === "object" && value2.summary !== null;
236
274
  }
237
275
  function isStatusResult(v) {
238
276
  if (typeof v !== "object" || v === null) return false;
@@ -257,6 +295,7 @@ function formatUpgradeResult(r) {
257
295
  for (const [name, result] of Object.entries(r.agents)) {
258
296
  const icon = result.ok ? CHECK : CROSS;
259
297
  lines.push(` ${icon} ${BOLD}${name}${RESET}`);
298
+ if (result.hook_activation) lines.push(` ${highlightInlineCode(result.hook_activation.next_action)}`);
260
299
  for (const s of result.steps) {
261
300
  const sIcon = s.ok ? CHECK : CROSS;
262
301
  const detail = s.detail ? ` ${dim(s.detail)}` : "";
@@ -293,6 +332,7 @@ function formatMultiAgentResult(r) {
293
332
  if (result.hooks_node !== void 0) {
294
333
  lines.push(` ${DIM}hooks${RESET} ${result.hooks_node}`);
295
334
  }
335
+ if (result.hook_activation) lines.push(` ${highlightInlineCode(result.hook_activation.next_action)}`);
296
336
  if (result.ping_error) {
297
337
  lines.push(` ${DIM}ping_error${RESET} ${result.ping_error}`);
298
338
  }
@@ -316,6 +356,7 @@ function formatStatusResult(r) {
316
356
  ["base_url", r.base_url],
317
357
  ["user_id", r.user_id],
318
358
  ["api_key", r.api_key_set ? "set" : "not set"],
359
+ ["auto_skill_evolution", r.auto_skill_evolution],
319
360
  ["access_token", r.access_token_set ? "set" : "not set"],
320
361
  ["credential_type", r.credential_type ?? "none"],
321
362
  ["auto_capture", r.auto_capture],
@@ -334,6 +375,12 @@ function formatStatusResult(r) {
334
375
  display.push(["hooks_node", r.hooks_node]);
335
376
  display.push(["hooks_node_ok", r.hooks_node_ok]);
336
377
  }
378
+ if (r.hook_activation) {
379
+ const activation = r.hook_activation;
380
+ display.push(["hook_trust", activation.trust], ["runtime_ready", "\u5F85\u786E\u8BA4"], ["hook_config", activation.config_path]);
381
+ lines.push(`${BOLD}${highlightInlineCode(String(activation.next_action))}${RESET}
382
+ `);
383
+ }
337
384
  if (r.ping_error) display.push(["ping_error", r.ping_error]);
338
385
  const maxKey = Math.max(...display.map(([k]) => k.length));
339
386
  for (const [k, v] of display) {
@@ -342,19 +389,19 @@ function formatStatusResult(r) {
342
389
  }
343
390
  return lines.join("\n");
344
391
  }
345
- function printResult(value, json, command) {
392
+ function printResult(value2, json, command) {
346
393
  if (json) {
347
- process.stdout.write(JSON.stringify(value, null, 2) + "\n");
348
- } else if (isMultiAgentResult(value)) {
349
- process.stdout.write(formatMultiAgentResult(value) + "\n");
350
- } else if (isUpgradeResult(value)) {
351
- process.stdout.write(formatUpgradeResult(value) + "\n");
352
- } else if (isSetupResult(value)) {
353
- process.stdout.write(formatSetupResult(value, command ?? "setup") + "\n");
354
- } else if (isStatusResult(value)) {
355
- process.stdout.write(formatStatusResult(value) + "\n");
394
+ process.stdout.write(JSON.stringify(value2, null, 2) + "\n");
395
+ } else if (isMultiAgentResult(value2)) {
396
+ process.stdout.write(formatMultiAgentResult(value2) + "\n");
397
+ } else if (isUpgradeResult(value2)) {
398
+ process.stdout.write(formatUpgradeResult(value2) + "\n");
399
+ } else if (isSetupResult(value2)) {
400
+ process.stdout.write(formatSetupResult(value2, command ?? "setup") + "\n");
401
+ } else if (isStatusResult(value2)) {
402
+ process.stdout.write(formatStatusResult(value2) + "\n");
356
403
  } else {
357
- process.stdout.write(summarize(value) + "\n");
404
+ process.stdout.write(summarize(value2) + "\n");
358
405
  }
359
406
  }
360
407
  function agentFromFlags(flags) {
@@ -395,11 +442,11 @@ import {
395
442
  rmdirSync,
396
443
  unlinkSync,
397
444
  statSync
398
- } from "fs";
399
- import { homedir } from "os";
400
- import { join, dirname, resolve } from "path";
401
- import { fileURLToPath } from "url";
402
- import { execFileSync } from "child_process";
445
+ } from "node:fs";
446
+ import { homedir } from "node:os";
447
+ import { join, dirname, resolve } from "node:path";
448
+ import { fileURLToPath } from "node:url";
449
+ import { execFileSync } from "node:child_process";
403
450
  import { parse as parseYaml, stringify as stringifyYaml } from "yaml";
404
451
  import { setupAuthentication, oauthProfileStatus, validateDataBaseUrl } from "@aliyunrds/ctxdb-shared";
405
452
 
@@ -440,7 +487,7 @@ function skillInstallRoot(agent) {
440
487
  return join(homedir(), ".workbuddy-ai", "skills");
441
488
  }
442
489
  }
443
- var SKILL_DIRS = ["contextdb-memory", "contextdb-knowledge"];
490
+ var SKILL_DIRS = ["contextdb-memory", "contextdb-knowledge", "contextdb-skills"];
444
491
  var LEGACY_SKILL_DIRS = ["ctxdb"];
445
492
  function agentSupportsHooks(agent) {
446
493
  if (agent === "opencode" || agent === "hermes") return false;
@@ -470,7 +517,23 @@ var LEGACY_QODER_SKILL_DIRS = [
470
517
  "rds-ctxdb-qoder",
471
518
  "qoder-ctxdb"
472
519
  ];
473
- var HOOK_EVENTS = ["UserPromptSubmit", "Stop", "SessionStart"];
520
+ var HOOK_EVENTS = ["UserPromptSubmit", "Stop", "SessionStart", "SessionEnd"];
521
+ var CODEX_HOOKS_TRUST_HINT = "\u5F85\u786E\u8BA4\uFF1ACodex Hook \u4FE1\u4EFB\u5C1A\u672A\u6838\u5B9E\uFF0C\u5B89\u88C5\u6210\u529F\u548C\u8FDE\u63A5\u6210\u529F\u4E0D\u4EE3\u8868\u81EA\u52A8\u63D0\u70BC\u5DF2\u5C31\u7EEA\u3002CLI \u8F93\u5165 `/hooks`\uFF1B\u684C\u9762\u7248\u8FDB\u5165 \u8BBE\u7F6E \u2192 `Hooks\uFF08\u94A9\u5B50\uFF09` \u2192 `\u7528\u6237\u914D\u7F6E`\uFF0C\u4EC5\u68C0\u67E5\u5E76\u4FE1\u4EFB `@aliyunrds/ctxdb` \u7684 `SessionStart`\u3001`UserPromptSubmit`\u3001`Stop`\u3001`SessionEnd`\u3002\u91CD\u542F\u5BA2\u6237\u7AEF\u540E\u65B0\u5EFA\u4F1A\u8BDD\u5E76\u53D1\u9001\u666E\u901A\u6D88\u606F\uFF0C\u518D\u8FD0\u884C `ctxdb skill status --agent codex --json` \u6838\u5BF9\u8BE5\u4F1A\u8BDD\u7684\u91C7\u96C6\u8BB0\u5F55\u3002";
522
+ function codexHookActivation() {
523
+ return {
524
+ trust: "unverified",
525
+ runtime_ready: null,
526
+ config_path: hookConfigPath("codex"),
527
+ required_events: [...HOOK_EVENTS],
528
+ next_action: CODEX_HOOKS_TRUST_HINT
529
+ };
530
+ }
531
+ function codexHookDefinitions() {
532
+ const file = hookConfigPath("codex");
533
+ if (!existsSync(file)) return "[]";
534
+ const data = JSON.parse(readFileSync(file, "utf8"));
535
+ return JSON.stringify(Object.entries(data.hooks ?? {}).sort(([a], [b]) => a.localeCompare(b)).flatMap(([event, entries]) => Array.isArray(entries) ? entries.filter(entryIsCtxdb).map((entry) => [event, entry]) : []));
536
+ }
474
537
  var MIN_NODE_MAJOR = 20;
475
538
  function parseNodeMajor(version) {
476
539
  const match = /^v?(\d+)/.exec(version.trim());
@@ -501,6 +564,7 @@ async function runSetup(options) {
501
564
  return { ok: false, steps };
502
565
  }
503
566
  const agent = options.agent;
567
+ let codexDefinitionsChanged;
504
568
  const installSkill = options.installSkill !== false;
505
569
  const validate = options.validate !== false;
506
570
  const nodeVersionCheck = checkCurrentNodeVersion();
@@ -629,6 +693,7 @@ async function runSetup(options) {
629
693
  if (!nodeCheck.ok) return { ok: false, steps };
630
694
  if (primaryHomeExists) {
631
695
  try {
696
+ const before = agent === "codex" ? codexHookDefinitions() : void 0;
632
697
  backupSettingsJson(agent);
633
698
  steps.push({ step: "backup-settings", ok: true });
634
699
  const removed = stripCtxdbHooks(agent);
@@ -640,6 +705,7 @@ async function runSetup(options) {
640
705
  });
641
706
  }
642
707
  appendHooks(agent, hookPaths);
708
+ if (agent === "codex") codexDefinitionsChanged = before !== codexHookDefinitions();
643
709
  steps.push({ step: "append-hooks", ok: true });
644
710
  } catch (err) {
645
711
  steps.push({
@@ -654,6 +720,7 @@ async function runSetup(options) {
654
720
  chmodSync(hookPaths.userPromptSubmit, 493);
655
721
  chmodSync(hookPaths.stop, 493);
656
722
  chmodSync(hookPaths.sessionStart, 493);
723
+ if (hookPaths.sessionEnd) chmodSync(hookPaths.sessionEnd, 493);
657
724
  if (hookPaths.preToolUse && existsSync(hookPaths.preToolUse)) {
658
725
  chmodSync(hookPaths.preToolUse, 493);
659
726
  }
@@ -816,11 +883,14 @@ async function runSetup(options) {
816
883
  }
817
884
  const failedSteps = steps.filter((s) => !s.ok);
818
885
  const ok = failedSteps.length === 0;
819
- const actionRequired = agent === "hermes" && failedSteps.length === 1 && failedSteps[0]?.step === "check-hermes-hook-allowlist" ? "hermes-hook-allowlist" : void 0;
886
+ const actionRequired = agent === "hermes" && failedSteps.length === 1 && failedSteps[0]?.step === "check-hermes-hook-allowlist" ? "hermes-hook-allowlist" : agent === "codex" && failedSteps.length === 0 && codexDefinitionsChanged !== void 0 ? "review-codex-hook-trust" : void 0;
820
887
  const hints = ok ? [UNINSTALL_ORDER_HINT] : [];
821
- if (actionRequired) {
888
+ if (actionRequired === "hermes-hook-allowlist") {
822
889
  hints.unshift(HERMES_ALLOWLIST_ACTION_HINT, HERMES_WEBUI_GATEWAY_HINT);
823
890
  }
891
+ if (actionRequired === "review-codex-hook-trust") {
892
+ hints.unshift(CODEX_HOOKS_TRUST_HINT, codexDefinitionsChanged ? "ContextDB Hook \u5B9A\u4E49\u5DF2\u65B0\u589E\u6216\u53D8\u66F4\uFF1BCodex \u6309\u5B9A\u4E49\u54C8\u5E0C\u8BB0\u5F55\u4FE1\u4EFB\uFF0C\u8BF7\u91CD\u65B0\u68C0\u67E5\u6388\u6743\u3002" : "ContextDB Hook \u5B9A\u4E49\u672A\u53D8\u5316\uFF1B\u8FD9\u4E0D\u8BC1\u660E\u5DF2\u6388\u6743\uFF0C\u4E5F\u4E0D\u4F1A\u4FEE\u6539\u5BBF\u4E3B\u7684\u4FE1\u4EFB\u8BB0\u5F55\u3002");
893
+ }
824
894
  if (ok && agent === "opencode") {
825
895
  hints.unshift(
826
896
  `OpenCode plugin shim: ${opencodeShimPath()}`,
@@ -838,6 +908,7 @@ async function runSetup(options) {
838
908
  ok,
839
909
  steps,
840
910
  actionRequired,
911
+ ...actionRequired === "review-codex-hook-trust" ? { hook_activation: { ...codexHookActivation(), definitions_changed: codexDefinitionsChanged } } : {},
841
912
  hints: hints.length > 0 ? hints : void 0
842
913
  };
843
914
  }
@@ -1500,6 +1571,7 @@ function hookPathsFromDir(dir, ext) {
1500
1571
  userPromptSubmit: ups,
1501
1572
  stop: stp,
1502
1573
  sessionStart: ss,
1574
+ sessionEnd: existsSync(join(dir, `session-end.${ext}`)) ? join(dir, `session-end.${ext}`) : void 0,
1503
1575
  preToolUse: existsSync(ptu) ? ptu : void 0,
1504
1576
  hermesPreLlmCall: existsSync(hpl) ? hpl : void 0,
1505
1577
  hermesPostLlmCall: existsSync(hpost) ? hpost : void 0
@@ -1616,6 +1688,7 @@ function appendHooksToFile(path, agent, hookPaths) {
1616
1688
  appendOne(data.hooks, "UserPromptSubmit", hookPaths.userPromptSubmit, agent);
1617
1689
  appendOne(data.hooks, "Stop", hookPaths.stop, agent);
1618
1690
  appendOne(data.hooks, "SessionStart", hookPaths.sessionStart, agent);
1691
+ if ((agent === "codex" || agent === "claude" || agent === "workbuddy") && hookPaths.sessionEnd) appendOne(data.hooks, "SessionEnd", hookPaths.sessionEnd, agent);
1619
1692
  mkdirSync(dirname(path), { recursive: true });
1620
1693
  writeFileSync(path, JSON.stringify(data, null, 2) + "\n", "utf-8");
1621
1694
  }
@@ -1684,7 +1757,7 @@ function appendOne(hooks, event, command, agent) {
1684
1757
  )
1685
1758
  );
1686
1759
  if (dup) return;
1687
- const inner = useExecForm ? { type: "command", command: process.execPath, args: [command, `--agent=${agent}`], timeout: 60 } : { type: "command", command: shellCmd, timeout: 60 };
1760
+ const inner = useExecForm ? { type: "command", command: process.execPath, args: [command, `--agent=${agent}`], timeout: 60 } : { type: "command", command: shellCmd, timeout: event === "SessionEnd" ? 3 : 60 };
1688
1761
  const entry = {
1689
1762
  hooks: [inner],
1690
1763
  [ENTRY_MARKER_KEY]: ENTRY_MARKER_VALUE,
@@ -1753,8 +1826,8 @@ function hermesHooksMap(data) {
1753
1826
  }
1754
1827
  return data.hooks;
1755
1828
  }
1756
- function quoteHermesCommandPart(value) {
1757
- return `'${value.replace(/'/g, `'\\''`)}'`;
1829
+ function quoteHermesCommandPart(value2) {
1830
+ return `'${value2.replace(/'/g, `'\\''`)}'`;
1758
1831
  }
1759
1832
  function hermesHookCommand(scriptPath, nodePath) {
1760
1833
  return `${quoteHermesCommandPart(nodePath)} ${quoteHermesCommandPart(scriptPath)} --agent=hermes`;
@@ -1960,11 +2033,11 @@ function inspectCodexHooksFeature(rawContent) {
1960
2033
  };
1961
2034
  }
1962
2035
  if (/^features\.hooks\s*=/.test(stripped)) {
1963
- const value = stripped.split("=", 2)[1]?.trim();
1964
- if (value === "true") return { kind: "already-enabled" };
2036
+ const value2 = stripped.split("=", 2)[1]?.trim();
2037
+ if (value2 === "true") return { kind: "already-enabled" };
1965
2038
  return {
1966
2039
  kind: "ambiguous",
1967
- reason: `features.hooks already set to '${value}' via dotted-key \u2014 please set it to true manually`
2040
+ reason: `features.hooks already set to '${value2}' via dotted-key \u2014 please set it to true manually`
1968
2041
  };
1969
2042
  }
1970
2043
  }
@@ -1989,12 +2062,12 @@ function inspectCodexHooksFeature(rawContent) {
1989
2062
  if (!inFeatures) continue;
1990
2063
  const kv = stripped.match(/^hooks\s*=\s*(.+)$/);
1991
2064
  if (kv) {
1992
- const value = kv[1].trim();
1993
- if (value === "true") return { kind: "already-enabled" };
1994
- if (value === "false") return { kind: "features-with-hooks-false" };
2065
+ const value2 = kv[1].trim();
2066
+ if (value2 === "true") return { kind: "already-enabled" };
2067
+ if (value2 === "false") return { kind: "features-with-hooks-false" };
1995
2068
  return {
1996
2069
  kind: "ambiguous",
1997
- reason: `[features].hooks already set to '${value}' \u2014 please set it to true manually`
2070
+ reason: `[features].hooks already set to '${value2}' \u2014 please set it to true manually`
1998
2071
  };
1999
2072
  }
2000
2073
  }
@@ -2210,10 +2283,6 @@ FLAGS
2210
2283
  --no-validate Skip connectivity ping after setup
2211
2284
  --json Machine-readable JSON output
2212
2285
  `;
2213
- var CODEX_HOOKS_TRUST_REMINDER = [
2214
- "codex hooks \u914D\u7F6E\u540E\uFF0C\u9700\u8981\u91CD\u542F Codex \u5BF9\u8BDD\uFF0C\u5728\u5F39\u51FA\u7684 hooks \u786E\u8BA4\u754C\u9762\u9009\u62E9`\u63A5\u6536\u6240\u6709 hooks(\u94A9\u5B50)`\u9009\u9879\u3002",
2215
- "\u5982\u679C\u4F7F\u7528\u7684\u662F Codex App \u684C\u9762\u7248\uFF0C\u64CD\u4F5C\u8DEF\u5F84\u662F\uFF1A\u5DE6\u4E0B\u89D2\u70B9\u51FB\u7528\u6237\u540D \u2192 \u70B9\u51FB\u8BBE\u7F6E \u2192 \u70B9\u51FB\u4FA7\u8FB9\u680F\u7684 `Hooks\uFF08\u94A9\u5B50\uFF09`\u2192 \u70B9\u51FB`\u7528\u6237\u914D\u7F6E`\uFF0C\u4FE1\u4EFB `UserPromptSubmit`\u3001`SessionStart` \u548C `Stop` \u91CC\u7684 `aliyunrds/ctxdb` \u94A9\u5B50\u3002"
2216
- ].join("\n");
2217
2286
  var FULL_APP_RESTART_REMINDERS = {
2218
2287
  qoder: "Next action: \u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u65B0\u542F\u52A8 Qoder\uFF08\u4E0D\u662F\u53EA\u5173\u95ED\u5F53\u524D\u5BF9\u8BDD\uFF09\uFF0C\u4F7F\u65B0\u7684 hooks \u548C skill \u751F\u6548\u3002",
2219
2288
  qoderwork: "Next action: \u8BF7\u5B8C\u5168\u9000\u51FA\u5E76\u91CD\u65B0\u542F\u52A8 QoderWork\uFF08\u4E0D\u662F\u53EA\u5173\u95ED\u5F53\u524D\u5BF9\u8BDD\uFF09\uFF0C\u4F7F\u65B0\u7684 hooks \u548C skill \u751F\u6548\u3002",
@@ -2221,7 +2290,7 @@ var FULL_APP_RESTART_REMINDERS = {
2221
2290
  };
2222
2291
  function postSetupReminders(agent) {
2223
2292
  const reminder = FULL_APP_RESTART_REMINDERS[agent];
2224
- return [...reminder ? [reminder] : [], ...agent === "codex" ? [CODEX_HOOKS_TRUST_REMINDER] : []];
2293
+ return [...reminder ? [reminder] : [], ...agent === "codex" ? [CODEX_HOOKS_TRUST_HINT] : []];
2225
2294
  }
2226
2295
  function printPostSetupReminder(agent) {
2227
2296
  for (const reminder of postSetupReminders(agent)) process.stderr.write(`
@@ -2387,6 +2456,8 @@ async function inspectStatus(agent) {
2387
2456
  base_url: cfg.baseUrl,
2388
2457
  user_id: cfg.userId,
2389
2458
  api_key_set: Boolean(cfg.apiKey),
2459
+ auto_skill_evolution: cfg.autoSkillEvolution,
2460
+ ...agent === "codex" ? { hook_activation: codexHookActivation() } : {},
2390
2461
  access_token_set: credential?.type === "access-token",
2391
2462
  credential_type: credential?.type ?? null,
2392
2463
  auto_capture: cfg.autoCapture,
@@ -2665,7 +2736,7 @@ FLAGS
2665
2736
  --json Machine-readable JSON output
2666
2737
 
2667
2738
  Note: This command only installs skill files (contextdb, contextdb-memory,
2668
- contextdb-knowledge). It does not write config or install hooks.
2739
+ contextdb-knowledge, contextdb-skills). It does not write config or install hooks.
2669
2740
  To configure server connection, run: ctxdb setup --api-key=<key> --base-url=<url>
2670
2741
  `;
2671
2742
  function skillInstall(args) {
@@ -2719,6 +2790,603 @@ function skillInstall(args) {
2719
2790
  return ok ? 0 : 1;
2720
2791
  }
2721
2792
 
2793
+ // src/skills/session-selector.ts
2794
+ import { homedir as homedir2 } from "node:os";
2795
+ import { join as join3, win32 } from "node:path";
2796
+ import { existsSync as existsSync3 } from "node:fs";
2797
+
2798
+ // src/skills/status.ts
2799
+ import { existsSync as existsSync2, readdirSync as readdirSync2, lstatSync } from "node:fs";
2800
+ import "node:path";
2801
+ function statusBindings(root, agent) {
2802
+ const dir = privatePath(root, "sessions", "bindings");
2803
+ if (!existsSync2(dir)) return [];
2804
+ return readdirSync2(dir).filter((name) => /^[a-f0-9-]{36}\.json$/.test(name)).map((name) => getBinding(name.slice(0, -5), root)).filter((bound) => !agent || bound.agent === agent).sort((a, b) => Number(b.active === true) - Number(a.active === true));
2805
+ }
2806
+ function localSkillStatus(bound, root) {
2807
+ const spool = readSpool(bound, root), business = businessEvents(spool);
2808
+ const calls = new Set(business.filter((e) => e.type === "tool_call").map((e) => e.tool_call_id));
2809
+ const results = new Set(spool.events.filter((e) => e.type === "tool_result").map((e) => e.tool_call_id));
2810
+ const unpaired = spool.events.filter((e) => e.type === "tool_call" && !results.has(e.tool_call_id)).map((e) => e.tool_call_id);
2811
+ const path = (...parts) => privatePath(root, ...parts);
2812
+ const skills = path("skills", bound.scope_key), evolution = path("skill-evolution", bound.scope_key);
2813
+ const directory = path(spoolDirectory(bound));
2814
+ const list = (dir) => existsSync2(dir) ? readdirSync2(dir) : [];
2815
+ const dispositions = list(path(directory, "dispositions")).filter((n) => /^\d+-\d+\.json$/.test(n)).map((name) => {
2816
+ const file = path(directory, "dispositions", name);
2817
+ const d = readState(root, file);
2818
+ return {
2819
+ path: file,
2820
+ status: d.status,
2821
+ reason: d.reason,
2822
+ from_sequence: d.from_sequence,
2823
+ through_sequence: d.through_sequence,
2824
+ event_count: d.event_count,
2825
+ buffer_bytes: d.buffer_bytes,
2826
+ created_at: d.created_at
2827
+ };
2828
+ });
2829
+ const publications = [], evaluations = [];
2830
+ for (const name of list(path(evolution, "artifacts"))) {
2831
+ if (/^upload-[a-f0-9-]{36}$/.test(name)) {
2832
+ const file = path(evolution, "artifacts", name, "publication.json");
2833
+ const p = readState(root, file);
2834
+ if (p.binding_id !== bound.id) continue;
2835
+ publications.push({
2836
+ evaluation_id: p.evaluation_id,
2837
+ request_id: p.input.client_request_id,
2838
+ source: p.input.source,
2839
+ result: p.result,
2840
+ verified: p.verified ?? false,
2841
+ manifest_refreshed: p.manifest_refreshed,
2842
+ active_advanced: p.active_advanced,
2843
+ receipt_path: file,
2844
+ bundle_path: path(evolution, "artifacts", name, "bundle.zip")
2845
+ });
2846
+ } else if (/^evaluation-[a-f0-9-]{36}\.json$/.test(name)) {
2847
+ const file = path(evolution, "artifacts", name), e = readState(root, file);
2848
+ if (e.binding_id === bound.id) evaluations.push({ request_id: e.evidence.client_request_id, result: e.result, path: file });
2849
+ }
2850
+ }
2851
+ const handoffs = list(path(directory, "handoffs")).filter((n) => /^[a-zA-Z0-9_-]{1,128}\.json$/.test(n)).map((name) => {
2852
+ const file = path(directory, "handoffs", name), h = readState(root, file);
2853
+ const related = publications.filter((p) => p.evaluation_id === h.event_id);
2854
+ return {
2855
+ event_id: h.event_id,
2856
+ delivery: h.delivery,
2857
+ recorded_at: h.recorded_at,
2858
+ completion: related.some((p) => p.verified) ? "published" : related.length ? "publication_unconfirmed" : "unconfirmed",
2859
+ automatic_redelivery: false,
2860
+ path: file
2861
+ };
2862
+ });
2863
+ const loaded = (bound.loaded_versions ?? []).map((ref) => ({
2864
+ ...ref,
2865
+ path: path(skills, "cache", ref.skill_id, ref.version_id, "bundle", "SKILL.md")
2866
+ }));
2867
+ const manifestPath = path(skills, "manifest.json");
2868
+ const snapshot = existsSync2(manifestPath) ? readState(root, manifestPath) : void 0;
2869
+ const drafts = list(path(skills, "drafts")).flatMap((name) => {
2870
+ if (!lstatSync(path(skills, "drafts", name)).isDirectory()) return [];
2871
+ const direct = path(skills, "drafts", name, "SKILL.md"), bundle = path(skills, "drafts", name, "bundle", "SKILL.md");
2872
+ return [direct, bundle].filter((file) => existsSync2(file));
2873
+ });
2874
+ const submissions = list(path(directory, "submissions")).filter((n) => /^[a-f0-9-]{36}\.json$/.test(n)).map((name) => {
2875
+ const file = path(directory, "submissions", name);
2876
+ const bytes = readPrivate(root, file, 65536), e = JSON.parse(bytes.toString("utf8"));
2877
+ return {
2878
+ path: file,
2879
+ request_id: e.client_request_id,
2880
+ trigger: e.trigger,
2881
+ request_bytes: bytes.length,
2882
+ distinct_tool_calls: new Set(e.events.filter((event) => event.type === "tool_call").map((event) => event.tool_call_id)).size,
2883
+ event_count: e.events.length,
2884
+ goal_characters: Array.from(e.goal ?? "").length,
2885
+ max_summary_characters: Math.max(0, ...e.events.map((event) => Array.from(event.summary ?? "").length))
2886
+ };
2887
+ });
2888
+ const trigger = threshold(spool);
2889
+ const waiting = bound.active === false ? "inactive_session" : spool.maintenance?.active ? "agent_maintenance" : spool.current ? "evaluation_in_progress_or_awaiting_delivery" : !autoCollectAllowed(bound, root) ? "automatic_capture_disabled" : !trigger ? "below_threshold" : "awaiting_complete_host_checkpoint";
2890
+ return {
2891
+ binding_id: bound.id,
2892
+ session_id: bound.session_id,
2893
+ agent: bound.agent,
2894
+ endpoint: bound.endpoint,
2895
+ auth_scope: bound.auth_scope,
2896
+ scope_key: bound.scope_key,
2897
+ active: bound.active,
2898
+ verified: bound.verified,
2899
+ auto_evolution_allowed: bound.auto_evolution_allowed,
2900
+ current: spool.current,
2901
+ error: spool.error,
2902
+ maintenance: spool.maintenance,
2903
+ buffered_events: spool.events.length,
2904
+ buffer_bytes: spool.buffer_bytes,
2905
+ buffer: {
2906
+ business_events: business.length,
2907
+ distinct_tool_calls: calls.size,
2908
+ bytes: spool.buffer_bytes,
2909
+ threshold_reached: !!trigger,
2910
+ trigger,
2911
+ tool_call_threshold: 10,
2912
+ bytes_threshold: 40960,
2913
+ max_request_bytes: 65536,
2914
+ max_events: 200,
2915
+ max_summary_characters: 2e3,
2916
+ unpaired_call_ids: unpaired,
2917
+ has_final_answer_hash: !!spool.last_answer_hash,
2918
+ waiting_reason: waiting
2919
+ },
2920
+ ...bound.agent === "codex" ? { hook_activation: {
2921
+ trust: "unverified",
2922
+ runtime_ready: null,
2923
+ binding_record: "present",
2924
+ transcript_checkpoint: spool.cursor > 0 ? "present" : "not_observed",
2925
+ evidence_capture: spool.events.length > 0 || submissions.some((s) => s.event_count > 0) || dispositions.some((d) => Number(d.event_count) > 0) ? "present" : "not_observed",
2926
+ evaluation_submission: spool.current?.event_id ? "accepted" : submissions.length ? "prepared" : "not_observed",
2927
+ last_checkpoint_at: spool.updated_at ?? null,
2928
+ note: "Local records are not Codex trust receipts. A binding can be created by CLI; historical capture or submission does not prove current hooks are trusted. Match this session to your new conversation. Missing records can mean no event yet, an unloaded hook, missing trust, or execution failure."
2929
+ } } : {},
2930
+ state_exists: existsSync2(path(directory, "state.json")),
2931
+ cursor: spool.cursor,
2932
+ submitted_through_sequence: spool.submitted_through_sequence,
2933
+ processed_through_sequence: spool.processed_through_sequence,
2934
+ updated_at: spool.updated_at,
2935
+ dispositions,
2936
+ submissions,
2937
+ publications,
2938
+ evaluations,
2939
+ handoffs,
2940
+ scope_drafts: drafts,
2941
+ loaded_versions: loaded,
2942
+ manifest: {
2943
+ path: manifestPath,
2944
+ exists: !!snapshot,
2945
+ status: bound.manifest_status,
2946
+ fetched_at: snapshot?.fetched_at,
2947
+ skills: snapshot?.manifest.skills ?? []
2948
+ },
2949
+ paths: {
2950
+ binding: path(bindingPath(bound)),
2951
+ state: path(directory, "state.json"),
2952
+ skills,
2953
+ drafts: path(skills, "drafts"),
2954
+ cache: path(skills, "cache"),
2955
+ evolution,
2956
+ submissions: path(directory, "submissions"),
2957
+ dispositions: path(directory, "dispositions"),
2958
+ current_submission: spool.current ? path(directory, "submissions", `${spool.current.request_id}.json`) : null
2959
+ },
2960
+ notes: [
2961
+ "Buffer counts only unsubmitted evidence and clears after accepted submission; submissions retain the submitted call/event/byte counts.",
2962
+ "Inactive sessions may be host initialization sessions; match session_id to the actual conversation before interpreting counters.",
2963
+ "A delivered handoff is not completion. Unconfirmed includes ignored, interrupted or permission-blocked work; absence of a receipt cannot identify the reason. No automatic redelivery.",
2964
+ "Drafts and manifest are scope-wide; draft ownership is not inferred from directory existence.",
2965
+ "Loaded versions may come from publication verification or manual Load; they do not prove business reuse.",
2966
+ "Local current is a checkpoint snapshot; use --remote for server status. A final-answer hash alone does not certify a complete boundary."
2967
+ ]
2968
+ };
2969
+ }
2970
+
2971
+ // src/skills/session-selector.ts
2972
+ function qoderWorkDatabase(platform2 = process.platform, home = homedir2(), appData = process.env.APPDATA) {
2973
+ if (platform2 === "darwin") return join3(home, "Library", "Application Support", "QoderWork", "data", "agents.db");
2974
+ if (platform2 === "win32") return win32.join(appData || win32.join(home, "AppData", "Roaming"), "QoderWork", "data", "agents.db");
2975
+ throw new Error("qoderwork_chat_lookup_platform_unsupported_use_binding_id");
2976
+ }
2977
+ async function selectSkillSessions(id, agent, root) {
2978
+ if (!id.trim() || id.length > 256) throw new Error("invalid_session_id");
2979
+ const bindings = statusBindings(root, agent);
2980
+ const direct = bindings.filter((b) => b.session_id === id);
2981
+ if (direct.length) return { bindings: direct, host_chat_id: void 0, unbound_session_ids: [] };
2982
+ if (agent !== "qoderwork") throw new Error("session_binding_not_found");
2983
+ const database = qoderWorkDatabase();
2984
+ if (!existsSync3(database)) throw new Error("qoderwork_database_not_found");
2985
+ const [major, minor] = process.versions.node.split(".").map(Number);
2986
+ if (major < 22 || major === 22 && minor < 13 || major === 23 && minor < 4) {
2987
+ throw new Error("qoderwork_chat_lookup_requires_node_22_13_or_newer_use_binding_id");
2988
+ }
2989
+ let sqlite;
2990
+ try {
2991
+ sqlite = await import("node:sqlite");
2992
+ } catch {
2993
+ throw new Error("qoderwork_chat_lookup_requires_node_22_13_or_newer_use_binding_id");
2994
+ }
2995
+ let rows;
2996
+ try {
2997
+ const db = new sqlite.DatabaseSync(database, { readOnly: true });
2998
+ try {
2999
+ rows = db.prepare("SELECT s.session_id FROM chats c LEFT JOIN sub_chats s ON s.chat_id=c.id WHERE c.id=? ORDER BY s.session_id").all(id);
3000
+ if (rows.some((r) => !(r.session_id === null || typeof r.session_id === "string"))) throw new Error();
3001
+ } finally {
3002
+ db.close();
3003
+ }
3004
+ } catch {
3005
+ throw new Error("qoderwork_database_unreadable_or_schema_unsupported_use_binding_id");
3006
+ }
3007
+ if (!rows.length) throw new Error("qoderwork_chat_not_found");
3008
+ const ids = [...new Set(rows.map((r) => r.session_id).filter((s) => !!s))];
3009
+ if (!ids.length) throw new Error("qoderwork_session_not_started");
3010
+ const matched = bindings.filter((b) => ids.includes(b.session_id));
3011
+ const unbound = ids.filter((id2) => !matched.some((b) => b.session_id === id2));
3012
+ return { bindings: matched, host_chat_id: id, unbound_session_ids: unbound };
3013
+ }
3014
+
3015
+ // src/skills/evaluation.ts
3016
+ import { randomUUID } from "node:crypto";
3017
+ import { existsSync as existsSync4 } from "node:fs";
3018
+ import { join as join4 } from "node:path";
3019
+ async function submitManualEvaluation(binding2, client, input, source, requestId, root = skillRoot()) {
3020
+ const { client_request_id: ignored, ...rest } = input;
3021
+ const body = { ...rest, submission_mode: "manual", trigger: "manual" };
3022
+ const artifacts = join4("skill-evolution", binding2.scope_key, "artifacts");
3023
+ const index = join4(artifacts, `manual-${sha256(JSON.stringify([source, body]))}.json`);
3024
+ const id = requestId ? requireUuid(requestId) : await withStateLock(root, index, async () => {
3025
+ if (existsSync4(privatePath(root, index))) return requireUuid(readState(root, index).request_id);
3026
+ const request_id = randomUUID();
3027
+ writeState(root, index, { request_id });
3028
+ return request_id;
3029
+ });
3030
+ const evidence = { ...body, client_request_id: id };
3031
+ validateEvidence(evidence);
3032
+ const path = join4(artifacts, `evaluation-${id}.json`);
3033
+ return withStateLock(root, path, async () => {
3034
+ let state;
3035
+ if (existsSync4(privatePath(root, path))) {
3036
+ state = readState(root, path);
3037
+ if (JSON.stringify(state.evidence) !== JSON.stringify(evidence)) throw new Error("idempotency_payload_mismatch");
3038
+ } else {
3039
+ state = { binding_id: binding2.id, evidence };
3040
+ writeState(root, path, state);
3041
+ }
3042
+ if (!state.result) {
3043
+ const result = unbox(await client.postJson("/v1/skill-evolution/evaluations", evidence));
3044
+ if (!result || !/^[a-zA-Z0-9_-]{1,128}$/.test(result.event_id) || typeof result.status !== "string") throw new Error("invalid_evaluation_response");
3045
+ state.result = result;
3046
+ writeState(root, path, state);
3047
+ }
3048
+ return state.result;
3049
+ });
3050
+ }
3051
+
3052
+ // src/cli/skill-storage-cli.ts
3053
+ import { randomUUID as randomUUID2 } from "node:crypto";
3054
+ import { join as join5, resolve as resolve2 } from "node:path";
3055
+ import { existsSync as existsSync5, readdirSync as readdirSync3, rmSync as rmSync2 } from "node:fs";
3056
+ function value(args, name) {
3057
+ const item = args.flags[name];
3058
+ if (item === true) throw new Error(`--${name} requires a value`);
3059
+ return item;
3060
+ }
3061
+ async function binding(args) {
3062
+ const id = value(args, "session");
3063
+ if (id) return getBinding(id);
3064
+ if (process.env.CODEX_THREAD_ID || process.env.CODEX_SESSION_ID || process.env.CLAUDE_CODE_ENTRYPOINT || process.env.CTXDB_SKILL_REQUIRE_SESSION === "1") throw new Error("session_binding_required");
3065
+ return bindSession(agentFromFlags(args.flags), `terminal-${randomUUID2()}`, skillRoot(), 3e4);
3066
+ }
3067
+ var skillBind = async (args) => {
3068
+ const session = value(args, "session-id");
3069
+ if (!session) throw new Error("--session-id is required");
3070
+ const result = await bindSession(agentFromFlags(args.flags), session, skillRoot(), 3e4);
3071
+ const client = await verifiedClient(result);
3072
+ await refreshManifest(result, client);
3073
+ printResult({ binding_id: result.id, auth_scope: result.auth_scope }, !!args.flags.json);
3074
+ return 0;
3075
+ };
3076
+ var skillManifest = async (args) => {
3077
+ const bound = await binding(args), client = await verifiedClient(bound);
3078
+ const snapshot = await refreshManifest(bound, client);
3079
+ printResult({ ...snapshot, catalog_context: catalogContext(bound) }, !!args.flags.json);
3080
+ return 0;
3081
+ };
3082
+ var skillList = async (args) => {
3083
+ const bound = await binding(args), client = await verifiedClient(bound);
3084
+ const snapshot = await refreshManifest(bound, client);
3085
+ const result = {
3086
+ fetched_at: snapshot.fetched_at,
3087
+ auth_scope: snapshot.manifest.auth_scope,
3088
+ skills: [...snapshot.manifest.skills].sort((left, right) => left.display_name.localeCompare(right.display_name) || left.skill_id.localeCompare(right.skill_id))
3089
+ };
3090
+ if (args.flags.json) printResult(result, true);
3091
+ else if (result.skills.length === 0) process.stdout.write("No active Skills.\n");
3092
+ else process.stdout.write(`${result.skills.length} active Skill${result.skills.length === 1 ? "" : "s"}:
3093
+ ${result.skills.map((entry) => `- ${entry.display_name} (${entry.category})
3094
+ skill_id: ${entry.skill_id}
3095
+ active_version_id: ${entry.active_version_id}
3096
+ updated_at: ${entry.updated_at}`).join("\n")}
3097
+ `);
3098
+ return 0;
3099
+ };
3100
+ async function effectiveSkillConfig(args) {
3101
+ const bound = await binding(args), client = await verifiedClient(bound);
3102
+ const remote = unbox(await client.get("/v1/skill-evolution/config"));
3103
+ const localEnabled = load({ agent: bound.agent, path: join5(skillRoot(), configFileName()) }).autoSkillEvolution !== false;
3104
+ return {
3105
+ ...remote,
3106
+ local_auto_evolution_enabled: localEnabled,
3107
+ effective_auto_evolution_enabled: localEnabled && remote.auto_evolution_enabled === true
3108
+ };
3109
+ }
3110
+ function localConfigAgent(args, requireExplicit = false) {
3111
+ const session = value(args, "session");
3112
+ if (session) {
3113
+ if (requireExplicit) throw new Error("--session cannot be used for local config changes");
3114
+ const bound = getBinding(session);
3115
+ const requested = args.flags.agent === void 0 ? void 0 : agentFromFlags(args.flags);
3116
+ if (requested && requested !== bound.agent) throw new Error("session_agent_mismatch");
3117
+ return bound.agent;
3118
+ }
3119
+ if (requireExplicit && typeof args.flags.agent !== "string") throw new Error("--agent is required for local config changes");
3120
+ return agentFromFlags(args.flags);
3121
+ }
3122
+ function localAutoEvolution(agent) {
3123
+ return load({ agent, path: join5(skillRoot(), configFileName()) }).autoSkillEvolution !== false;
3124
+ }
3125
+ var skillConfig = async (args) => {
3126
+ const [action, key, rawValue, ...extra] = args.positional;
3127
+ if (!action || action === "get" && !key) {
3128
+ if (rawValue || extra.length) throw new Error("invalid_skill_config_command");
3129
+ printResult(await effectiveSkillConfig(args), !!args.flags.json);
3130
+ return 0;
3131
+ }
3132
+ if (!["get", "set", "unset"].includes(action) || key !== "auto-skill-evolution" || extra.length) {
3133
+ throw new Error("invalid_skill_config_command");
3134
+ }
3135
+ if (action === "get") {
3136
+ if (rawValue) throw new Error("invalid_skill_config_command");
3137
+ const agent2 = localConfigAgent(args);
3138
+ printResult({ key, agent: agent2, value: localAutoEvolution(agent2), source: "local" }, !!args.flags.json);
3139
+ return 0;
3140
+ }
3141
+ const agent = localConfigAgent(args, true);
3142
+ let enabled;
3143
+ if (action === "set") {
3144
+ if (rawValue !== "on" && rawValue !== "off") throw new Error("auto-skill-evolution value must be on or off");
3145
+ enabled = rawValue === "on";
3146
+ } else if (rawValue) throw new Error("invalid_skill_config_command");
3147
+ if (!updateAutoSkillEvolution(agent, enabled, join5(skillRoot(), configFileName()))) throw new Error("agent_config_not_found");
3148
+ printResult({ key, agent, value: localAutoEvolution(agent), source: enabled === void 0 ? "default" : "local" }, !!args.flags.json);
3149
+ return 0;
3150
+ };
3151
+ var skillSearch = async (args) => {
3152
+ const query = args.positional.join(" ");
3153
+ const topK = Number(value(args, "top-k") ?? 5);
3154
+ if (!query || Array.from(query).length > 512 || !Number.isInteger(topK) || topK < 1 || topK > 10) throw new Error("invalid_search");
3155
+ if (args.flags.local) throw new Error("local_search_removed_use_manifest_or_remote_search");
3156
+ const bound = await binding(args);
3157
+ const result = unbox(await (await verifiedClient(bound)).get("/v1/skills/search", { query, top_k: topK }));
3158
+ printResult(result, !!args.flags.json);
3159
+ return 0;
3160
+ };
3161
+ var skillLoad = async (args) => {
3162
+ const id = value(args, "skill-id");
3163
+ if (!id) throw new Error("--skill-id is required");
3164
+ const bound = await binding(args), client = await verifiedClient(bound);
3165
+ printResult(await loadSkill(bound, client, id, value(args, "version-id"), !!args.flags.editable), !!args.flags.json);
3166
+ return 0;
3167
+ };
3168
+ var skillDelete = (args) => skillDisable(args, true);
3169
+ var skillDisable = async (args, deleteHistory = false) => {
3170
+ const id = value(args, "skill-id");
3171
+ if (!id) throw new Error("--skill-id is required");
3172
+ requireUuid(id);
3173
+ const bound = await binding(args);
3174
+ if (readSpool(bound).maintenance?.active) throw new Error("management_requires_user_instruction");
3175
+ const client = await verifiedClient(bound);
3176
+ const result = unbox(await client.delete("/v1/skills", {
3177
+ skill_id: id,
3178
+ ...deleteHistory ? { delete_history: true } : {}
3179
+ }));
3180
+ if (result.skill_id !== id || result.deleted !== true) throw new Error("invalid_disable_response");
3181
+ await refreshManifest(bound, client);
3182
+ printResult(result, !!args.flags.json);
3183
+ return 0;
3184
+ };
3185
+ var skillRollback = async (args) => {
3186
+ const id = value(args, "skill-id"), version = value(args, "version-id"), base = value(args, "base-version-id");
3187
+ if (!id || !version || !base) throw new Error("rollback_requires_skill_version_and_base");
3188
+ [id, version, base].forEach(requireUuid);
3189
+ const reason = value(args, "revoke-reason");
3190
+ if (reason !== void 0 && (!reason.trim() || reason.length > 1e3)) throw new Error("invalid_revoke_reason");
3191
+ const bound = await binding(args);
3192
+ if (readSpool(bound).maintenance?.active) throw new Error("management_requires_user_instruction");
3193
+ const client = await verifiedClient(bound);
3194
+ const result = unbox(await client.postJson("/v1/skills/rollback", {
3195
+ skill_id: id,
3196
+ version_id: version,
3197
+ base_version_id: base,
3198
+ ...reason ? { revoke_reason: reason } : {}
3199
+ }));
3200
+ if (result.skill_id !== id || result.version_id !== version || result.action !== "rollback") throw new Error("invalid_rollback_response");
3201
+ await refreshManifest(bound, client);
3202
+ printResult(result, !!args.flags.json);
3203
+ return 0;
3204
+ };
3205
+ var skillValidate = async (args) => {
3206
+ const path = args.positional[0];
3207
+ if (!path) throw new Error("package path is required");
3208
+ const pkg = await readPackage(resolve2(path));
3209
+ printResult({
3210
+ valid: true,
3211
+ validation_scope: "static_package",
3212
+ scripts_executed: false,
3213
+ bundle_hash: pkg.hash,
3214
+ bundle_size_bytes: pkg.zip.length,
3215
+ file_count: pkg.files.size,
3216
+ name: pkg.metadata.name,
3217
+ version: pkg.metadata.version
3218
+ }, !!args.flags.json);
3219
+ return 0;
3220
+ };
3221
+ var skillUpload = async (args) => {
3222
+ const path = args.positional[0];
3223
+ if (!path) throw new Error("package path is required");
3224
+ const skillId = value(args, "skill-id"), base = value(args, "base-version-id");
3225
+ if (!!skillId !== !!base) throw new Error("update_requires_skill_and_base_version");
3226
+ if (skillId) {
3227
+ requireUuid(skillId);
3228
+ requireUuid(base);
3229
+ }
3230
+ const pkg = await readPackage(resolve2(path));
3231
+ const bound = await binding(args), client = await verifiedClient(bound), root = skillRoot();
3232
+ const source = value(args, "source") ?? "import";
3233
+ if (source !== "import" && source !== "evolution") throw new Error("invalid_upload_source");
3234
+ if (readSpool(bound, root).maintenance?.active && source !== "evolution") throw new Error("maintenance_upload_requires_evolution_source");
3235
+ const body = {
3236
+ operation: skillId ? "update" : "create",
3237
+ bundle_hash: pkg.hash,
3238
+ bundle_size_bytes: pkg.zip.length,
3239
+ source,
3240
+ ...skillId ? { skill_id: skillId, base_version_id: base } : {}
3241
+ };
3242
+ const index = join5("skill-evolution", bound.scope_key, "artifacts", `draft-${sha256(resolve2(path))}.json`);
3243
+ const result = await withStateLock(root, index, async () => {
3244
+ const previous = existsSync5(privatePath(root, index)) ? readState(root, index) : void 0;
3245
+ const same = previous && JSON.stringify(previous.body) === JSON.stringify(body);
3246
+ if (previous && !same) {
3247
+ const publicationPath = join5("skill-evolution", bound.scope_key, "artifacts", `upload-${previous.request_id}`, "publication.json");
3248
+ if (existsSync5(privatePath(root, publicationPath)) && !readState(root, publicationPath).result)
3249
+ throw new Error("previous_upload_unresolved_run_status_resume_before_changing_draft");
3250
+ }
3251
+ const requested = value(args, "request-id");
3252
+ if (same && requested && requested !== previous.request_id) throw new Error("retry_requires_original_request_id");
3253
+ const requestId = same ? previous.request_id : requested ?? randomUUID2();
3254
+ requireUuid(requestId);
3255
+ const receipt = join5("skill-evolution", bound.scope_key, "artifacts", `upload-${requestId}`, "publication.json");
3256
+ if (existsSync5(privatePath(root, receipt))) {
3257
+ const original = readState(root, receipt).input;
3258
+ if (JSON.stringify(original) !== JSON.stringify({ ...body, client_request_id: requestId })) throw new Error("idempotency_payload_mismatch");
3259
+ }
3260
+ writeState(root, index, { request_id: requestId, body });
3261
+ return publishPackage(bound, client, pkg, { ...body, client_request_id: requestId }, root);
3262
+ });
3263
+ printResult(result, !!args.flags.json);
3264
+ return 0;
3265
+ };
3266
+ var skillEvaluate = async (args) => {
3267
+ const file = args.positional[0];
3268
+ if (!file) throw new Error("evidence JSON path is required");
3269
+ const root = skillRoot(), bytes = readPrivate(resolve2(file, ".."), resolve2(file), 65536);
3270
+ const evidence = JSON.parse(bytes.toString("utf8"));
3271
+ if (!evidence || typeof evidence !== "object" || Array.isArray(evidence)) throw new Error("invalid_evidence");
3272
+ const bound = await binding(args), client = await verifiedClient(bound);
3273
+ const result = await submitManualEvaluation(bound, client, evidence, resolve2(file), value(args, "request-id"), root);
3274
+ printResult(result, !!args.flags.json);
3275
+ return 0;
3276
+ };
3277
+ var skillStatus = async (args) => {
3278
+ const root = skillRoot();
3279
+ let selected = value(args, "session");
3280
+ const sessionId = value(args, "session-id");
3281
+ if (sessionId !== void 0 && selected) throw new Error("session_and_session_id_are_mutually_exclusive");
3282
+ if (sessionId !== void 0 && !value(args, "agent")) throw new Error("--session-id requires --agent");
3283
+ const selection = sessionId !== void 0 ? await selectSkillSessions(sessionId, agentFromFlags(args.flags), root) : void 0;
3284
+ if (selection && (args.flags.remote || args.flags.resume || args.flags["event-id"])) {
3285
+ if (selection.bindings.length !== 1 || selection.unbound_session_ids.length) throw new Error("session_id_requires_unique_binding_use_session");
3286
+ selected = selection.bindings[0].id;
3287
+ args = { ...args, flags: { ...args.flags, session: selected } };
3288
+ }
3289
+ const event = value(args, "event-id");
3290
+ if (args.flags.remote && args.flags.resume) throw new Error("remote_and_resume_are_mutually_exclusive");
3291
+ if (!args.flags.resume && !event) {
3292
+ if (args.flags.remote && !selected) throw new Error("--remote requires --session");
3293
+ const bounds = selection?.bindings ?? (selected ? [getBinding(selected, root)] : statusBindings(root, value(args, "agent")));
3294
+ const sessions = bounds.map((bound2) => ({ ...localSkillStatus(bound2, root), ...selection?.host_chat_id ? { host_chat_id: selection.host_chat_id } : {} }));
3295
+ if (args.flags.remote) {
3296
+ const id = sessions[0]?.current?.event_id;
3297
+ if (id) Object.assign(sessions[0], { evaluation: unbox(await (await verifiedClient(bounds[0])).get("/v1/skill-evolution/evaluations", { event_id: id })) });
3298
+ else if (sessions[0]) Object.assign(sessions[0], { remote_status: "no_event_id" });
3299
+ }
3300
+ const result = { auth_scope: selected ? bounds[0].auth_scope : void 0, sessions, ...selection ? { requested_session_id: sessionId, unbound_session_ids: selection.unbound_session_ids } : {} };
3301
+ if (args.flags.json) printResult(result, true);
3302
+ else {
3303
+ if (!sessions.length) console.log("No Skill sessions found. Start a bound Agent conversation first.");
3304
+ if (selection?.host_chat_id) console.log(`QoderWork chat: ${selection.host_chat_id}`);
3305
+ for (const id of selection?.unbound_session_ids ?? []) console.log(`No ContextDB binding observed for session: ${id}`);
3306
+ for (const s of sessions) {
3307
+ console.log(`${s.binding_id} ${s.agent} session=${s.session_id}`);
3308
+ console.log(` Session: ${s.active === false ? "inactive" : s.active === true ? "active" : "unknown"}`);
3309
+ console.log(` Workspace: ${s.auth_scope.workspace_id} Member: ${s.auth_scope.member_id}`);
3310
+ console.log(` Pending buffer (clears after submission): ${s.buffer.distinct_tool_calls}/10 calls, ${s.buffer.bytes}/40960 bytes, ${s.buffered_events} events; ${s.buffer.waiting_reason}`);
3311
+ console.log(` Evaluate: ${s.current?.event_id ?? "no_event_id"}; request=${s.current?.request_id ?? "none"}; delivery=${s.current?.delivery ?? "none"}`);
3312
+ if ("evaluation" in s) console.log(` Server: ${JSON.stringify(s.evaluation)}`);
3313
+ for (const h of s.handoffs) console.log(` Handoff: ${h.event_id}; delivery=${h.delivery}; completion=${h.completion}; automatic_redelivery=false`);
3314
+ if (s.error) console.log(` Error: ${s.error}`);
3315
+ for (const d of s.dispositions) console.log(` Not evaluated: ${d.reason}; events=${d.event_count}, bytes=${d.buffer_bytes}; ${d.path}`);
3316
+ for (const p of s.publications) console.log(` Publication: ${p.request_id}; verified=${p.verified}; ${JSON.stringify(p.result ?? null)}; ${p.receipt_path}`);
3317
+ console.log(` Drafts (scope-wide): ${s.paths.drafts}`);
3318
+ for (const file of s.scope_drafts) console.log(` Scope draft: ${file}`);
3319
+ for (const e of s.submissions) console.log(` Frozen request: ${e.request_id}; ${e.distinct_tool_calls} calls, ${e.request_bytes} bytes, ${e.event_count} events; ${e.path}`);
3320
+ console.log(` Manifest: ${s.manifest.path} (${s.manifest.status})`);
3321
+ for (const v of s.loaded_versions) console.log(` Loaded: ${v.skill_id}@${v.version_id}; ${v.path}`);
3322
+ console.log(` State: ${s.paths.state}`);
3323
+ }
3324
+ }
3325
+ return 0;
3326
+ }
3327
+ if (args.flags.resume && !selected && !event) throw new Error("--resume requires --session");
3328
+ const bound = await binding(args);
3329
+ if (event) {
3330
+ if (!/^[a-zA-Z0-9_-]{1,128}$/.test(event)) throw new Error("invalid_event_id");
3331
+ printResult(unbox(await (await verifiedClient(bound)).get("/v1/skill-evolution/evaluations", { event_id: event })), !!args.flags.json);
3332
+ return 0;
3333
+ }
3334
+ const artifacts = join5("skill-evolution", bound.scope_key, "artifacts");
3335
+ if (existsSync5(privatePath(root, artifacts))) for (const name of readdirSync3(privatePath(root, artifacts))) {
3336
+ if (/^evaluation-[a-f0-9-]{36}\.json$/.test(name)) {
3337
+ const state = readState(root, join5(artifacts, name));
3338
+ if (state.binding_id === bound.id && !state.result) await submitManualEvaluation(
3339
+ bound,
3340
+ await verifiedClient(bound),
3341
+ state.evidence,
3342
+ "",
3343
+ state.evidence.client_request_id,
3344
+ root
3345
+ );
3346
+ } else if (/^upload-[a-f0-9-]{36}$/.test(name)) {
3347
+ const dir = join5(artifacts, name), state = readState(root, join5(dir, "publication.json"));
3348
+ if (state.binding_id !== bound.id || state.verified) continue;
3349
+ const client = await verifiedClient(bound);
3350
+ const pkg = await inspectZip(readPrivate(root, join5(dir, "bundle.zip"), MAX_ZIP), state.input.bundle_hash, state.input.bundle_size_bytes);
3351
+ await publishPackage(bound, client, pkg, state.input, root);
3352
+ }
3353
+ }
3354
+ await expireSessionEvidence(bound, root);
3355
+ await resumeSubmission(bound, root);
3356
+ return skillStatus({ positional: [], flags: { session: bound.id, ...args.flags.json ? { json: true } : {} } });
3357
+ };
3358
+ var skillPurge = async (args) => {
3359
+ const bound = await binding(args), root = skillRoot();
3360
+ if (args.flags.cache) {
3361
+ const cache = join5("skills", bound.scope_key, "cache");
3362
+ const eligible2 = existsSync5(privatePath(root, cache)) ? [cache] : [];
3363
+ if (args.flags.apply) await withStateLock(root, cache, async () => {
3364
+ rmSync2(privatePath(root, cache), { recursive: true, force: true });
3365
+ });
3366
+ printResult({ applied: !!args.flags.apply, eligible: eligible2, prerequisite: "Stop related sessions and transfers before --apply; drafts are preserved." }, !!args.flags.json);
3367
+ return 0;
3368
+ }
3369
+ const artifacts = join5("skill-evolution", bound.scope_key, "artifacts"), eligible = [];
3370
+ if (existsSync5(privatePath(root, artifacts))) for (const name of readdirSync3(privatePath(root, artifacts))) {
3371
+ if (!/^upload-[a-f0-9-]{36}$/.test(name)) continue;
3372
+ const dir = join5(artifacts, name), state = readState(root, join5(dir, "publication.json"));
3373
+ if (state.verified && existsSync5(privatePath(root, dir, "bundle.zip"))) eligible.push(name);
3374
+ }
3375
+ if (args.flags.apply) for (const name of eligible) await withStateLock(root, join5(artifacts, name), async () => {
3376
+ if (readState(root, join5(artifacts, name, "publication.json")).verified)
3377
+ rmSync2(privatePath(root, artifacts, name, "bundle.zip"), { force: true });
3378
+ });
3379
+ printResult({ applied: !!args.flags.apply, scope_key: bound.scope_key, eligible }, !!args.flags.json);
3380
+ return 0;
3381
+ };
3382
+ var skillRetryFeedback = async (args) => {
3383
+ const id = value(args, "session"), event = value(args, "event-id");
3384
+ if (!id || !event) throw new Error("retry-feedback requires --session and --event-id");
3385
+ await retryFeedback(getBinding(id), event);
3386
+ printResult({ event_id: event, queued_for_next_user_prompt: true, published: false }, !!args.flags.json);
3387
+ return 0;
3388
+ };
3389
+
2722
3390
  // src/cli/debug-cli.ts
2723
3391
  import {
2724
3392
  readRecallTraces
@@ -2880,12 +3548,12 @@ async function debugRecall(args) {
2880
3548
  import {
2881
3549
  accessSync,
2882
3550
  constants as fsConstants,
2883
- existsSync as existsSync2,
3551
+ existsSync as existsSync6,
2884
3552
  openAsBlob,
2885
3553
  statSync as statSync2
2886
- } from "fs";
2887
- import { homedir as homedir2 } from "os";
2888
- import { basename, extname, resolve as resolve2 } from "path";
3554
+ } from "node:fs";
3555
+ import { homedir as homedir3 } from "node:os";
3556
+ import { basename, extname, resolve as resolve3 } from "node:path";
2889
3557
 
2890
3558
  // src/lib/review.ts
2891
3559
  function unwrapReviewData(response, operation) {
@@ -2901,31 +3569,31 @@ function unwrapReviewData(response, operation) {
2901
3569
  }
2902
3570
  return box.data ?? null;
2903
3571
  }
2904
- function asRecord(value) {
2905
- return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
3572
+ function asRecord(value2) {
3573
+ return value2 && typeof value2 === "object" && !Array.isArray(value2) ? value2 : void 0;
2906
3574
  }
2907
3575
  function pickString(record, field) {
2908
- const value = record[field];
2909
- return typeof value === "string" ? value : "";
3576
+ const value2 = record[field];
3577
+ return typeof value2 === "string" ? value2 : "";
2910
3578
  }
2911
3579
  function pickNumber(record, field) {
2912
- const value = record[field];
2913
- return typeof value === "number" && Number.isFinite(value) ? value : void 0;
3580
+ const value2 = record[field];
3581
+ return typeof value2 === "number" && Number.isFinite(value2) ? value2 : void 0;
2914
3582
  }
2915
3583
  function pickBoolean(record, field) {
2916
- const value = record[field];
2917
- return typeof value === "boolean" ? value : void 0;
3584
+ const value2 = record[field];
3585
+ return typeof value2 === "boolean" ? value2 : void 0;
2918
3586
  }
2919
- function parseEmbeddedJson(value) {
2920
- if (typeof value !== "string" || value.trim() === "") return void 0;
3587
+ function parseEmbeddedJson(value2) {
3588
+ if (typeof value2 !== "string" || value2.trim() === "") return void 0;
2921
3589
  try {
2922
- return JSON.parse(value);
3590
+ return JSON.parse(value2);
2923
3591
  } catch {
2924
3592
  return void 0;
2925
3593
  }
2926
3594
  }
2927
- function countArrayEntries(value) {
2928
- return Array.isArray(value) ? value.length : void 0;
3595
+ function countArrayEntries(value2) {
3596
+ return Array.isArray(value2) ? value2.length : void 0;
2929
3597
  }
2930
3598
  function readReviewListData(data) {
2931
3599
  const record = asRecord(data);
@@ -3000,8 +3668,8 @@ function minimalReviewDetail(raw) {
3000
3668
  "human_action",
3001
3669
  "created_at"
3002
3670
  ]) {
3003
- const value = pickString(r, field);
3004
- if (value) out[field] = value;
3671
+ const value2 = pickString(r, field);
3672
+ if (value2) out[field] = value2;
3005
3673
  }
3006
3674
  for (const field of [
3007
3675
  "confidence_score",
@@ -3009,12 +3677,12 @@ function minimalReviewDetail(raw) {
3009
3677
  "noise_score",
3010
3678
  "contradiction_score"
3011
3679
  ]) {
3012
- const value = pickNumber(r, field);
3013
- if (value !== void 0) out[field] = value;
3680
+ const value2 = pickNumber(r, field);
3681
+ if (value2 !== void 0) out[field] = value2;
3014
3682
  }
3015
3683
  for (const field of ["has_contradictions", "rewrite_needed"]) {
3016
- const value = pickBoolean(r, field);
3017
- if (value !== void 0) out[field] = value;
3684
+ const value2 = pickBoolean(r, field);
3685
+ if (value2 !== void 0) out[field] = value2;
3018
3686
  }
3019
3687
  const conflictCount = countArrayEntries(parseEmbeddedJson(r.conflicts));
3020
3688
  if (conflictCount !== void 0) out.conflict_count = conflictCount;
@@ -3174,7 +3842,7 @@ async function memoryAdd(args) {
3174
3842
  fail(`memory add cannot use --image with --no-infer; image memory requires inference
3175
3843
  ${usage2}`, 2);
3176
3844
  }
3177
- const images = imageFlags.length > 0 ? await Promise.all(imageFlags.map((value) => loadImagePart(value, usage2))) : [];
3845
+ const images = imageFlags.length > 0 ? await Promise.all(imageFlags.map((value2) => loadImagePart(value2, usage2))) : [];
3178
3846
  const ctx = buildContext(args);
3179
3847
  const userId = args.flags["user-id"] ?? ctx.cfg.userId;
3180
3848
  const metadata = parseMetadataFlags(args.flags["metadata"]);
@@ -3225,12 +3893,12 @@ async function memorySearch(args) {
3225
3893
  const ctx = buildContext(args);
3226
3894
  const userId = args.flags["user-id"] ?? ctx.cfg.userId;
3227
3895
  const topK = numFlag(args.flags["top-k"], ctx.cfg.topK);
3228
- const threshold = numFlag(args.flags["threshold"], ctx.cfg.threshold);
3896
+ const threshold2 = numFlag(args.flags["threshold"], ctx.cfg.threshold);
3229
3897
  const body = {
3230
3898
  query,
3231
3899
  user_id: userId,
3232
3900
  top_k: topK,
3233
- threshold
3901
+ threshold: threshold2
3234
3902
  };
3235
3903
  if (args.flags.knowledge) {
3236
3904
  body.knowledge = { enable: true, top_k: ctx.cfg.knowledgeTopK };
@@ -3352,7 +4020,7 @@ async function memoryEntityCreate(args) {
3352
4020
  };
3353
4021
  addOptionalEntityFields(body, args);
3354
4022
  const ctx = buildContext(args);
3355
- if (![body.user_id, body.agent_id, body.app_id, body.run_id].some((value) => typeof value === "string" && value.trim() !== "")) {
4023
+ if (![body.user_id, body.agent_id, body.app_id, body.run_id].some((value2) => typeof value2 === "string" && value2.trim() !== "")) {
3356
4024
  body.user_id = ctx.cfg.userId;
3357
4025
  }
3358
4026
  const resp = await ctx.client.postJson("/v1/memories/entities", body);
@@ -3417,18 +4085,18 @@ function parseMetadataFlags(raw) {
3417
4085
  }
3418
4086
  return out;
3419
4087
  }
3420
- function stringFlag(value) {
3421
- return typeof value === "string" ? value : void 0;
4088
+ function stringFlag(value2) {
4089
+ return typeof value2 === "string" ? value2 : void 0;
3422
4090
  }
3423
- function csvFlag(value) {
3424
- return typeof value === "string" ? value.split(",").map((item) => item.trim()).filter(Boolean) : [];
4091
+ function csvFlag(value2) {
4092
+ return typeof value2 === "string" ? value2.split(",").map((item) => item.trim()).filter(Boolean) : [];
3425
4093
  }
3426
- function jsonStringArray(value, name) {
3427
- if (value === void 0) return [];
3428
- if (typeof value !== "string") fail(`--${name} must be a JSON array of strings`, 2);
4094
+ function jsonStringArray(value2, name) {
4095
+ if (value2 === void 0) return [];
4096
+ if (typeof value2 !== "string") fail(`--${name} must be a JSON array of strings`, 2);
3429
4097
  let parsed;
3430
4098
  try {
3431
- parsed = JSON.parse(value);
4099
+ parsed = JSON.parse(value2);
3432
4100
  } catch {
3433
4101
  fail(`--${name} must be a JSON array of strings`, 2);
3434
4102
  }
@@ -3439,8 +4107,8 @@ function jsonStringArray(value, name) {
3439
4107
  }
3440
4108
  function addOptionalScopeFields(body, args) {
3441
4109
  for (const [flag, field] of [["user-id", "user_id"], ["agent-id", "agent_id"], ["app-id", "app_id"], ["run-id", "run_id"]]) {
3442
- const value = stringFlag(args.flags[flag]);
3443
- if (value !== void 0) body[field] = value;
4110
+ const value2 = stringFlag(args.flags[flag]);
4111
+ if (value2 !== void 0) body[field] = value2;
3444
4112
  }
3445
4113
  }
3446
4114
  function addOptionalEntityFields(body, args) {
@@ -3452,8 +4120,8 @@ function addOptionalEntityFields(body, args) {
3452
4120
  function repeatedFlagValues(args, name) {
3453
4121
  const repeated = args.repeatedFlags?.[name];
3454
4122
  if (repeated) return repeated;
3455
- const value = args.flags[name];
3456
- return value === void 0 ? [] : [value];
4123
+ const value2 = args.flags[name];
4124
+ return value2 === void 0 ? [] : [value2];
3457
4125
  }
3458
4126
  async function loadImagePart(rawPath, usage2) {
3459
4127
  if (typeof rawPath !== "string" || rawPath.trim() === "") {
@@ -3461,7 +4129,7 @@ async function loadImagePart(rawPath, usage2) {
3461
4129
  ${usage2}`, 2);
3462
4130
  }
3463
4131
  const path = expandLocalPath(rawPath);
3464
- if (!existsSync2(path)) fail(`image file not found: ${path}`, 2);
4132
+ if (!existsSync6(path)) fail(`image file not found: ${path}`, 2);
3465
4133
  const stat = statSync2(path);
3466
4134
  if (!stat.isFile()) fail(`image path is not a file: ${path}`, 2);
3467
4135
  try {
@@ -3482,9 +4150,9 @@ ${usage2}`, 2);
3482
4150
  }
3483
4151
  function expandLocalPath(path) {
3484
4152
  if (path === "~" || path.startsWith("~/")) {
3485
- return resolve2(homedir2(), path === "~" ? "" : path.slice(2));
4153
+ return resolve3(homedir3(), path === "~" ? "" : path.slice(2));
3486
4154
  }
3487
- return resolve2(path);
4155
+ return resolve3(path);
3488
4156
  }
3489
4157
  function imageMimeType(path) {
3490
4158
  switch (extname(path).toLowerCase()) {
@@ -3501,7 +4169,7 @@ function imageMimeType(path) {
3501
4169
  }
3502
4170
 
3503
4171
  // src/cli/kb-cli.ts
3504
- import { basename as basename2 } from "path";
4172
+ import { basename as basename2 } from "node:path";
3505
4173
  var UPLOAD_TEXT_USAGE = "usage: ctxdb kb upload-text <kb-name> <doc-name> --text=<body> [--file-path=<server-logical-path>] [--review-enabled|--no-review-enabled] [--wait|--no-wait]";
3506
4174
  var UPLOAD_FILE_USAGE = "usage: ctxdb kb upload-file <kb-name> <local-path> [--doc-name=...] [--file-path=<server-logical-path>] [--review-enabled|--no-review-enabled] [--wait|--no-wait]";
3507
4175
  var UPDATE_TEXT_USAGE = "usage: ctxdb kb update-text <document-id> --text=<body> [--wait|--no-wait]\n or: ctxdb kb update-text --kb-name=<kb-name> --doc-name=<logical-name> --text=<body> [--file-path=<server-logical-path>] [--wait|--no-wait]";
@@ -3521,17 +4189,17 @@ var ENTITY_CHUNKS_USAGE = "usage: ctxdb kb entity-chunks --kb=<kb-id> --entity-i
3521
4189
  var RELATIONS_USAGE = "usage: ctxdb kb relations [--kb=<kb-id>] [--entity-id=<id> | --entity-ids=<a,b>]\n [--relation-type=<t>] [--page=N] [--page-size=N]\n [--max-nodes=N] [--max-edges=N] [--verbose] [--raw] [--json]";
3522
4190
  var UPDATE_LOCATOR_FLAGS = ["kb-name", "doc-name", "file-path"];
3523
4191
  function nonEmptyFlag(args, name) {
3524
- const value = args.flags[name];
3525
- return typeof value === "string" && value.trim() !== "" ? value : void 0;
4192
+ const value2 = args.flags[name];
4193
+ return typeof value2 === "string" && value2.trim() !== "" ? value2 : void 0;
3526
4194
  }
3527
4195
  function optionalNonEmptyFlag(args, name, usage2) {
3528
4196
  if (!Object.hasOwn(args.flags, name)) return void 0;
3529
- const value = nonEmptyFlag(args, name);
3530
- if (value === void 0) {
4197
+ const value2 = nonEmptyFlag(args, name);
4198
+ if (value2 === void 0) {
3531
4199
  fail(`--${name} must have a non-empty value
3532
4200
  ${usage2}`, 2);
3533
4201
  }
3534
- return value;
4202
+ return value2;
3535
4203
  }
3536
4204
  function hasAnyLocatorFlag(args) {
3537
4205
  return UPDATE_LOCATOR_FLAGS.some((name) => Object.hasOwn(args.flags, name));
@@ -3603,10 +4271,10 @@ function parseKbLocator(args, usage2) {
3603
4271
  function optionalNonNegativeInteger(args, name, usage2) {
3604
4272
  const raw = args.flags[name];
3605
4273
  if (raw === void 0) return void 0;
3606
- const value = typeof raw === "string" ? Number(raw) : Number.NaN;
3607
- if (!Number.isInteger(value) || value < 0) fail(`--${name} must be a non-negative integer
4274
+ const value2 = typeof raw === "string" ? Number(raw) : Number.NaN;
4275
+ if (!Number.isInteger(value2) || value2 < 0) fail(`--${name} must be a non-negative integer
3608
4276
  ${usage2}`, 2);
3609
- return value;
4277
+ return value2;
3610
4278
  }
3611
4279
  function requiredIds(args, usage2) {
3612
4280
  const raw = args.flags.ids;
@@ -3916,10 +4584,10 @@ function limitGraphRelations(result, maxNodes, maxEdges) {
3916
4584
  if (!Array.isArray(graph.relations)) return result;
3917
4585
  const nodeIds = /* @__PURE__ */ new Set();
3918
4586
  const relations = [];
3919
- for (const value of graph.relations) {
4587
+ for (const value2 of graph.relations) {
3920
4588
  if (maxEdges !== void 0 && relations.length >= maxEdges) break;
3921
- if (!value || typeof value !== "object") continue;
3922
- const relation = value;
4589
+ if (!value2 || typeof value2 !== "object") continue;
4590
+ const relation = value2;
3923
4591
  const source = relation.source_entity_id;
3924
4592
  const target = relation.target_entity_id;
3925
4593
  if (typeof source !== "string" || typeof target !== "string") continue;
@@ -3927,7 +4595,7 @@ function limitGraphRelations(result, maxNodes, maxEdges) {
3927
4595
  nextNodes.add(source);
3928
4596
  nextNodes.add(target);
3929
4597
  if (maxNodes !== void 0 && nextNodes.size > maxNodes) continue;
3930
- relations.push(value);
4598
+ relations.push(value2);
3931
4599
  nodeIds.add(source);
3932
4600
  nodeIds.add(target);
3933
4601
  }
@@ -3987,10 +4655,10 @@ async function kbSearch(args) {
3987
4655
  return 0;
3988
4656
  }
3989
4657
  function positiveIntFlag(args, flagName, usage2) {
3990
- const value = args.flags[flagName];
3991
- if (value === void 0) return void 0;
3992
- if (typeof value !== "string") fail(usage2, 2);
3993
- const parsed = Number(value);
4658
+ const value2 = args.flags[flagName];
4659
+ if (value2 === void 0) return void 0;
4660
+ if (typeof value2 !== "string") fail(usage2, 2);
4661
+ const parsed = Number(value2);
3994
4662
  if (!Number.isInteger(parsed) || parsed < 1) {
3995
4663
  fail(`${flagName} must be a positive integer
3996
4664
  ${usage2}`, 2);
@@ -4109,11 +4777,11 @@ async function kbAcl(operation, args) {
4109
4777
  throw new Error(`exactly one ${operation === "delete" ? "ACL" : "KB"} ID is required`);
4110
4778
  }
4111
4779
  const allowed = /* @__PURE__ */ new Set([...FLAGS[operation], "agent", "json", "raw"]);
4112
- for (const [flag, value] of Object.entries(args.flags)) {
4780
+ for (const [flag, value2] of Object.entries(args.flags)) {
4113
4781
  if (!allowed.has(flag)) throw new Error(`unknown --${flag}`);
4114
4782
  if (flag === "agent" || flag === "json" || flag === "raw") continue;
4115
- if (typeof value !== "string" || !value.trim()) throw new Error(`--${flag} requires a value`);
4116
- values[flag.replaceAll("-", "_")] = value.trim();
4783
+ if (typeof value2 !== "string" || !value2.trim()) throw new Error(`--${flag} requires a value`);
4784
+ values[flag.replaceAll("-", "_")] = value2.trim();
4117
4785
  }
4118
4786
  for (const [field, choices] of Object.entries({
4119
4787
  principal_type: ["member", "group"],
@@ -4164,6 +4832,139 @@ var kbAclSubjects = (args) => kbAcl("subjects", args);
4164
4832
  var kbAclSet = (args) => kbAcl("set", args);
4165
4833
  var kbAclDelete = (args) => kbAcl("delete", args);
4166
4834
 
4835
+ // src/cli/kb-graph-cli.ts
4836
+ var GRAPH_MUTATION_USAGE = {
4837
+ "entity-create": "ctxdb kb entity-create --kb=<kb-id> --name=<name> --type=<type> --description=<text> --aliases-json='<json-array>'",
4838
+ "entity-update": "ctxdb kb entity-update --kb=<kb-id> --entity-id=<id> --name=<name> --type=<type> --description=<text> --aliases-json='<json-array>'",
4839
+ "entity-delete": "ctxdb kb entity-delete --kb=<kb-id> --entity-id=<id> --yes",
4840
+ "relation-create": "ctxdb kb relation-create --kb=<kb-id> --source-entity-id=<id> --target-entity-id=<id> --relation-type=<type> --description=<text> --keywords-json='<json-array>' --weight=<number>",
4841
+ "relation-update": "ctxdb kb relation-update --kb=<kb-id> --relation-id=<id> --relation-type=<type> --description=<text> --keywords-json='<json-array>' --weight=<number>",
4842
+ "relation-delete": "ctxdb kb relation-delete --kb=<kb-id> --relation-id=<id> --yes"
4843
+ };
4844
+ var ENTITY_FIELDS = ["name", "type", "description", "aliases-json"];
4845
+ var RELATION_FIELDS = ["relation-type", "description", "keywords-json", "weight"];
4846
+ function stringFlag2(args, name, allowEmpty = false) {
4847
+ const value2 = args.flags[name];
4848
+ if (typeof value2 !== "string" || !allowEmpty && !value2.trim()) {
4849
+ throw new Error(`--${name} requires ${allowEmpty ? "a string value (use --description='' to clear)" : "a non-empty value"}`);
4850
+ }
4851
+ return value2;
4852
+ }
4853
+ function stringArrayFlag(args, name) {
4854
+ const text = stringFlag2(args, name);
4855
+ let value2;
4856
+ try {
4857
+ value2 = JSON.parse(text);
4858
+ } catch {
4859
+ throw new Error(`--${name} must be a JSON array of strings (use '[]' to clear)`);
4860
+ }
4861
+ if (!Array.isArray(value2) || !value2.every((item) => typeof item === "string")) {
4862
+ throw new Error(`--${name} must be a JSON array of strings (use '[]' to clear)`);
4863
+ }
4864
+ return value2;
4865
+ }
4866
+ function parseMutation(args, resource, action) {
4867
+ const idFlag = `${resource}-id`;
4868
+ const allowed = /* @__PURE__ */ new Set([
4869
+ "kb",
4870
+ "agent",
4871
+ "json",
4872
+ "raw",
4873
+ ...action === "delete" ? [idFlag, "yes"] : [
4874
+ ...resource === "entity" ? ENTITY_FIELDS : RELATION_FIELDS,
4875
+ ...action === "update" ? [idFlag] : resource === "relation" ? ["source-entity-id", "target-entity-id"] : []
4876
+ ]
4877
+ ]);
4878
+ if (args.positional.length) throw new Error("use explicit ID flags; positional locators are not supported");
4879
+ for (const [flag, value2] of Object.entries(args.flags)) {
4880
+ if (!allowed.has(flag)) throw new Error(`unknown --${flag}`);
4881
+ if (["yes", "json", "raw"].includes(flag) && value2 !== true) {
4882
+ throw new Error(`--${flag} is a boolean switch; do not supply a value`);
4883
+ }
4884
+ }
4885
+ if (args.flags.agent !== void 0) stringFlag2(args, "agent");
4886
+ const knowledge_base_id = stringFlag2(args, "kb").trim();
4887
+ const locator = action === "create" ? {} : { [`${resource}_id`]: stringFlag2(args, idFlag).trim() };
4888
+ if (action === "delete") {
4889
+ if (args.flags.yes !== true) {
4890
+ throw new Error(`deletion is irreversible${resource === "entity" ? " and deletes all incoming/outgoing relations" : ""}; pass --yes to confirm`);
4891
+ }
4892
+ return { knowledge_base_id, ...locator };
4893
+ }
4894
+ if (resource === "entity") {
4895
+ return {
4896
+ knowledge_base_id,
4897
+ ...locator,
4898
+ entity_name: stringFlag2(args, "name"),
4899
+ entity_type: stringFlag2(args, "type"),
4900
+ description: stringFlag2(args, "description", true),
4901
+ aliases: stringArrayFlag(args, "aliases-json")
4902
+ };
4903
+ }
4904
+ const weight = Number(stringFlag2(args, "weight"));
4905
+ if (!Number.isFinite(weight)) throw new Error("--weight must be a finite number");
4906
+ return {
4907
+ knowledge_base_id,
4908
+ ...locator,
4909
+ ...action === "create" ? {
4910
+ source_entity_id: stringFlag2(args, "source-entity-id").trim(),
4911
+ target_entity_id: stringFlag2(args, "target-entity-id").trim()
4912
+ } : {},
4913
+ relation_type: stringFlag2(args, "relation-type"),
4914
+ description: stringFlag2(args, "description", true),
4915
+ keywords: stringArrayFlag(args, "keywords-json"),
4916
+ weight
4917
+ };
4918
+ }
4919
+ async function mutateGraph(args, resource, action) {
4920
+ const command = `${resource}-${action}`;
4921
+ const usage2 = `usage: ${GRAPH_MUTATION_USAGE[command]} [--agent=<name>] [--raw] [--json]
4922
+ `;
4923
+ if (args.flags.help || args.flags.h) {
4924
+ process.stdout.write(usage2);
4925
+ return 0;
4926
+ }
4927
+ let payload;
4928
+ try {
4929
+ payload = parseMutation(args, resource, action);
4930
+ } catch (error) {
4931
+ process.stderr.write(`${error.message}
4932
+ ${usage2}`);
4933
+ return 2;
4934
+ }
4935
+ const { client } = buildContext(args);
4936
+ const path = resource === "entity" ? "/v1/knowledge/entities" : "/v1/knowledge/entity-relations";
4937
+ const response = action === "create" ? await client.postJson(path, payload) : action === "update" ? await client.putJson(path, payload) : await client.delete(path, payload);
4938
+ let data;
4939
+ try {
4940
+ data = unwrapReviewData(response, `kb ${command}`);
4941
+ if (!data || typeof data !== "object" || Array.isArray(data)) {
4942
+ throw new CtxdbError(`kb ${command} returned no mutation result`);
4943
+ }
4944
+ const result = data;
4945
+ if (typeof result[`${resource}_id`] !== "string" || !result[`${resource}_id`] || result.knowledge_base_id !== payload.knowledge_base_id || action !== "create" && result[`${resource}_id`] !== payload[`${resource}_id`]) {
4946
+ throw new CtxdbError(`kb ${command} returned an invalid mutation locator`);
4947
+ }
4948
+ if (action === "delete") {
4949
+ if (typeof result.deleted !== "boolean") throw new CtxdbError(`kb ${command} returned no deletion status`);
4950
+ if (resource === "entity" && ["deleted_entity_count", "deleted_relation_count"].some(
4951
+ (key) => !Number.isSafeInteger(result[key]) || result[key] < 0
4952
+ )) throw new CtxdbError(`kb ${command} returned invalid deletion counts`);
4953
+ }
4954
+ } catch (error) {
4955
+ const requestId = response?.requestId;
4956
+ throw new CtxdbError(`${error.message}${typeof requestId === "string" ? ` (RequestId: ${requestId})` : ""}`);
4957
+ }
4958
+ printResult(args.flags.raw ? response : data, !!args.flags.json);
4959
+ return action === "delete" && data.deleted !== true ? 1 : 0;
4960
+ }
4961
+ var kbEntityCreate = (args) => mutateGraph(args, "entity", "create");
4962
+ var kbEntityUpdate = (args) => mutateGraph(args, "entity", "update");
4963
+ var kbEntityDelete = (args) => mutateGraph(args, "entity", "delete");
4964
+ var kbRelationCreate = (args) => mutateGraph(args, "relation", "create");
4965
+ var kbRelationUpdate = (args) => mutateGraph(args, "relation", "update");
4966
+ var kbRelationDelete = (args) => mutateGraph(args, "relation", "delete");
4967
+
4167
4968
  // src/cli/promotion-cli.ts
4168
4969
  var GENERATE_USAGE = "usage: ctxdb memory promotion generate <topic> [--knowledge-base-id=<kb-id>] [--wait|--no-wait] [--agent=<name>] [--json]";
4169
4970
  var LIST_USAGE = "usage: ctxdb memory promotion list [--status=<generating|pending_review|approving|approved|rejected|generation_failed>] [--page=N] [--page-size=N] [--sort-by=quality_score] [--sort-order=asc|desc] [--raw] [--agent=<name>] [--json]";
@@ -4244,7 +5045,7 @@ ${GENERATE_USAGE}`, 2);
4244
5045
  if (!topic || topic.trim() === "") fail(GENERATE_USAGE, 2);
4245
5046
  const ctx = buildContext(args);
4246
5047
  const body = { topic };
4247
- const kbId = stringFlag2(args, "knowledge-base-id");
5048
+ const kbId = stringFlag3(args, "knowledge-base-id");
4248
5049
  if (kbId !== void 0) body.knowledge_base_id = kbId;
4249
5050
  const resp = await ctx.client.postJson("/v1/memory_promotions/generate", body);
4250
5051
  let data = unwrapBox(resp, "memory promotion generate");
@@ -4260,7 +5061,7 @@ ${GENERATE_USAGE}`, 2);
4260
5061
  }) + "\n");
4261
5062
  const deadline = Date.now() + 3e5;
4262
5063
  while (data.status === "generating" && Date.now() < deadline) {
4263
- await new Promise((resolve4) => setTimeout(resolve4, 1500));
5064
+ await new Promise((resolve5) => setTimeout(resolve5, 1500));
4264
5065
  data = unwrapBox(
4265
5066
  await ctx.client.get("/v1/memory_promotions/detail", { promotion_id: promotionId }),
4266
5067
  "memory promotion get"
@@ -4277,7 +5078,7 @@ ${GENERATE_USAGE}`, 2);
4277
5078
  async function promotionList(args) {
4278
5079
  if (showHelp(args, LIST_USAGE)) return 0;
4279
5080
  const params = {};
4280
- const status2 = stringFlag2(args, "status");
5081
+ const status2 = stringFlag3(args, "status");
4281
5082
  if (status2 !== void 0) {
4282
5083
  if (!PROMOTION_STATUSES.includes(status2)) {
4283
5084
  fail(`invalid --status: ${status2} (expected one of ${PROMOTION_STATUSES.join("|")})
@@ -4289,7 +5090,7 @@ ${LIST_USAGE}`, 2);
4289
5090
  if (page !== void 0) params.page = page;
4290
5091
  const pageSize = intFlag(args, "page-size", LIST_USAGE);
4291
5092
  if (pageSize !== void 0) params.page_size = pageSize;
4292
- const sortBy = stringFlag2(args, "sort-by");
5093
+ const sortBy = stringFlag3(args, "sort-by");
4293
5094
  if (sortBy !== void 0) {
4294
5095
  if (sortBy !== "quality_score") {
4295
5096
  fail(`invalid --sort-by: ${sortBy} (expected quality_score)
@@ -4297,7 +5098,7 @@ ${LIST_USAGE}`, 2);
4297
5098
  }
4298
5099
  params.sort_by = sortBy;
4299
5100
  }
4300
- const sortOrder = stringFlag2(args, "sort-order");
5101
+ const sortOrder = stringFlag3(args, "sort-order");
4301
5102
  if (sortOrder !== void 0) {
4302
5103
  if (sortOrder !== "asc" && sortOrder !== "desc") {
4303
5104
  fail(`invalid --sort-order: ${sortOrder} (expected asc|desc)
@@ -4339,7 +5140,7 @@ async function promotionReject(args) {
4339
5140
  const promotionId = requireIdFlag(args, "promotion-id", REJECT_USAGE);
4340
5141
  const ctx = buildContext(args);
4341
5142
  const body = { promotion_id: promotionId };
4342
- const reason = stringFlag2(args, "reason");
5143
+ const reason = stringFlag3(args, "reason");
4343
5144
  if (reason !== void 0) body.reason = reason;
4344
5145
  const resp = await ctx.client.postJson("/v1/memory_promotions/reject", body);
4345
5146
  emit(args, resp, "memory promotion reject", (data) => data);
@@ -4347,17 +5148,17 @@ async function promotionReject(args) {
4347
5148
  }
4348
5149
  async function promotionBatchAction(args) {
4349
5150
  if (showHelp(args, BATCH_ACTION_USAGE)) return 0;
4350
- const action = stringFlag2(args, "action");
5151
+ const action = stringFlag3(args, "action");
4351
5152
  if (action === void 0 || !BATCH_ACTIONS.includes(action)) {
4352
5153
  fail(`--action is required and must be one of ${BATCH_ACTIONS.join("|")}
4353
5154
  ${BATCH_ACTION_USAGE}`, 2);
4354
5155
  }
4355
5156
  const ids = parseIdsFlag(args, BATCH_ACTION_USAGE);
4356
- const kbId = action === "approve" ? requireIdFlag(args, "knowledge-base-id", BATCH_ACTION_USAGE) : stringFlag2(args, "knowledge-base-id");
5157
+ const kbId = action === "approve" ? requireIdFlag(args, "knowledge-base-id", BATCH_ACTION_USAGE) : stringFlag3(args, "knowledge-base-id");
4357
5158
  const ctx = buildContext(args);
4358
5159
  const body = { action, ids };
4359
5160
  if (kbId !== void 0) body.knowledge_base_id = kbId;
4360
- const reason = stringFlag2(args, "reason");
5161
+ const reason = stringFlag3(args, "reason");
4361
5162
  if (reason !== void 0) body.reason = reason;
4362
5163
  const resp = await ctx.client.postJson("/v1/memory_promotions/batch", body);
4363
5164
  const data = emit(args, resp, "memory promotion batch-action", (data2) => data2);
@@ -4383,8 +5184,8 @@ async function promotionResolveDoubts(args) {
4383
5184
  ${RESOLVE_DOUBTS_USAGE}`, 2);
4384
5185
  }
4385
5186
  const resolutions = parseResolutions(resolutionsRaw, RESOLVE_DOUBTS_USAGE);
4386
- const title = stringFlag2(args, "title");
4387
- const content = stringFlag2(args, "content");
5187
+ const title = stringFlag3(args, "title");
5188
+ const content = stringFlag3(args, "content");
4388
5189
  if (resolutions.length === 0 && title === void 0 && content === void 0) {
4389
5190
  fail(`provide a resolution or a non-empty --title/--content edit
4390
5191
  ${RESOLVE_DOUBTS_USAGE}`, 2);
@@ -4478,7 +5279,7 @@ async function promotionSettingsGet(args) {
4478
5279
  async function promotionSettingsUpdate(args) {
4479
5280
  if (showHelp(args, SETTINGS_UPDATE_USAGE)) return 0;
4480
5281
  const body = {};
4481
- const enabled = stringFlag2(args, "auto-ingest-enabled");
5282
+ const enabled = stringFlag3(args, "auto-ingest-enabled");
4482
5283
  if (enabled !== void 0) {
4483
5284
  if (enabled !== "true" && enabled !== "false") {
4484
5285
  fail(`invalid --auto-ingest-enabled: ${enabled} (expected true|false)
@@ -4486,16 +5287,16 @@ ${SETTINGS_UPDATE_USAGE}`, 2);
4486
5287
  }
4487
5288
  body.auto_ingest_enabled = enabled === "true";
4488
5289
  }
4489
- const threshold = stringFlag2(args, "auto-ingest-threshold");
4490
- if (threshold !== void 0) {
4491
- const n = Number(threshold);
5290
+ const threshold2 = stringFlag3(args, "auto-ingest-threshold");
5291
+ if (threshold2 !== void 0) {
5292
+ const n = Number(threshold2);
4492
5293
  if (!Number.isFinite(n) || n < 0 || n > 1) {
4493
- fail(`invalid --auto-ingest-threshold: ${threshold} (expected a number in [0,1])
5294
+ fail(`invalid --auto-ingest-threshold: ${threshold2} (expected a number in [0,1])
4494
5295
  ${SETTINGS_UPDATE_USAGE}`, 2);
4495
5296
  }
4496
5297
  body.auto_ingest_threshold = n;
4497
5298
  }
4498
- const defaultKbId = stringFlag2(args, "default-kb-id");
5299
+ const defaultKbId = stringFlag3(args, "default-kb-id");
4499
5300
  const clearDefaultKb = args.flags["clear-default-kb"] === true;
4500
5301
  if (defaultKbId !== void 0 && clearDefaultKb) {
4501
5302
  fail(`cannot use --default-kb-id and --clear-default-kb together
@@ -4512,15 +5313,15 @@ ${SETTINGS_UPDATE_USAGE}`, 2);
4512
5313
  emit(args, resp, "memory promotion settings update", (data) => data);
4513
5314
  return 0;
4514
5315
  }
4515
- function stringFlag2(args, name) {
4516
- const value = args.flags[name];
4517
- return typeof value === "string" && value.trim() !== "" ? value : void 0;
5316
+ function stringFlag3(args, name) {
5317
+ const value2 = args.flags[name];
5318
+ return typeof value2 === "string" && value2.trim() !== "" ? value2 : void 0;
4518
5319
  }
4519
5320
  function requireIdFlag(args, name, usage2) {
4520
- const value = stringFlag2(args, name);
4521
- if (value === void 0) fail(`--${name} is required
5321
+ const value2 = stringFlag3(args, name);
5322
+ if (value2 === void 0) fail(`--${name} is required
4522
5323
  ${usage2}`, 2);
4523
- return value;
5324
+ return value2;
4524
5325
  }
4525
5326
  function requireTemplateIdPositional(args, usage2) {
4526
5327
  const templateId = args.positional[0];
@@ -4528,7 +5329,7 @@ function requireTemplateIdPositional(args, usage2) {
4528
5329
  return templateId;
4529
5330
  }
4530
5331
  function intFlag(args, name, usage2) {
4531
- const raw = stringFlag2(args, name);
5332
+ const raw = stringFlag3(args, name);
4532
5333
  if (raw === void 0) return void 0;
4533
5334
  const n = Number(raw);
4534
5335
  if (!Number.isInteger(n) || n < 1) {
@@ -4538,7 +5339,7 @@ ${usage2}`, 2);
4538
5339
  return n;
4539
5340
  }
4540
5341
  function parseIdsFlag(args, usage2) {
4541
- const raw = stringFlag2(args, "ids");
5342
+ const raw = stringFlag3(args, "ids");
4542
5343
  if (raw === void 0) fail(`--ids is required (comma-separated)
4543
5344
  ${usage2}`, 2);
4544
5345
  const ids = raw.split(",").map((s) => s.trim()).filter(Boolean);
@@ -4588,7 +5389,7 @@ ${usage2}`, 2);
4588
5389
  });
4589
5390
  }
4590
5391
  function buildTemplateUpsertBody(args, usage2) {
4591
- const name = stringFlag2(args, "name");
5392
+ const name = stringFlag3(args, "name");
4592
5393
  if (name === void 0) fail(`--name is required
4593
5394
  ${usage2}`, 2);
4594
5395
  const sectionsRaw = args.flags["sections-json"];
@@ -4673,8 +5474,8 @@ function batchExitCode(data) {
4673
5474
  function pickFields(source, fields) {
4674
5475
  const out = {};
4675
5476
  for (const key of fields) {
4676
- const value = source[key];
4677
- if (value !== void 0 && value !== null) out[key] = value;
5477
+ const value2 = source[key];
5478
+ if (value2 !== void 0 && value2 !== null) out[key] = value2;
4678
5479
  }
4679
5480
  return out;
4680
5481
  }
@@ -4714,7 +5515,7 @@ function compactTemplateList(data) {
4714
5515
  }
4715
5516
 
4716
5517
  // src/cli/review-cli.ts
4717
- import { readFileSync as readFileSync2 } from "fs";
5518
+ import { readFileSync as readFileSync2 } from "node:fs";
4718
5519
  var REVIEW_LIST_USAGE = "usage: ctxdb kb review list [--knowledge-base-id=<id>] [--decision=<d>]\n [--review-status=<pending|reviewed|rejected>]\n [--page=N] [--page-size=N] [--sort-by=confidence_score]\n [--sort-order=asc|desc] [--verbose] [--raw] [--json]";
4719
5520
  var REVIEW_GET_USAGE = "usage: ctxdb kb review get <review-id> [--content-page=N] [--verbose] [--raw] [--json]";
4720
5521
  var REVIEW_ACTION_USAGE = `usage: ctxdb kb review action <review-id> --action=<${REVIEW_ACTIONS.join("|")}>
@@ -4734,29 +5535,29 @@ var TEMPLATE_CLEAR_DEFAULT_USAGE2 = "usage: ctxdb kb review template clear-defau
4734
5535
  var SETTINGS_GET_USAGE2 = "usage: ctxdb kb review settings get <knowledge-base-id> [--json]";
4735
5536
  var SETTINGS_UPDATE_USAGE2 = "usage: ctxdb kb review settings update <knowledge-base-id>\n [--review-enabled | --no-review-enabled]\n [--high-threshold=F] [--logic-rewrite-threshold=F]\n [--noise-rewrite-threshold=F] [--max-rewrite-iterations=N]\n [--rewrite-enabled | --no-rewrite-enabled]\n [--conflict-detection-enabled | --no-conflict-detection-enabled]\n [--rewrite-auto-ingest-enabled | --no-rewrite-auto-ingest-enabled] [--json]";
4736
5537
  function stringValue(args, name) {
4737
- const value = args.flags[name];
4738
- return typeof value === "string" && value.trim() !== "" ? value : void 0;
5538
+ const value2 = args.flags[name];
5539
+ return typeof value2 === "string" && value2.trim() !== "" ? value2 : void 0;
4739
5540
  }
4740
5541
  function requireId(args, flagName, positionalIndex, usage2) {
4741
5542
  const positional2 = args.positional[positionalIndex]?.trim();
4742
- const value = positional2 !== void 0 && positional2 !== "" ? positional2 : stringValue(args, flagName);
4743
- if (value === void 0) fail(usage2, 2);
4744
- return value.trim();
5543
+ const value2 = positional2 !== void 0 && positional2 !== "" ? positional2 : stringValue(args, flagName);
5544
+ if (value2 === void 0) fail(usage2, 2);
5545
+ return value2.trim();
4745
5546
  }
4746
5547
  function requireEnumFlag(args, flagName, allowed, usage2) {
4747
- const value = stringValue(args, flagName);
4748
- if (value === void 0) fail(usage2, 2);
4749
- if (!allowed.includes(value)) {
5548
+ const value2 = stringValue(args, flagName);
5549
+ if (value2 === void 0) fail(usage2, 2);
5550
+ if (!allowed.includes(value2)) {
4750
5551
  fail(`${flagName} must be one of ${allowed.join("|")}
4751
5552
  ${usage2}`, 2);
4752
5553
  }
4753
- return value;
5554
+ return value2;
4754
5555
  }
4755
5556
  function positiveIntFlag2(args, flagName, usage2) {
4756
- const value = args.flags[flagName];
4757
- if (value === void 0) return void 0;
4758
- if (typeof value !== "string") fail(usage2, 2);
4759
- const parsed = Number(value);
5557
+ const value2 = args.flags[flagName];
5558
+ if (value2 === void 0) return void 0;
5559
+ if (typeof value2 !== "string") fail(usage2, 2);
5560
+ const parsed = Number(value2);
4760
5561
  if (!Number.isInteger(parsed) || parsed < 1) {
4761
5562
  fail(`${flagName} must be a positive integer
4762
5563
  ${usage2}`, 2);
@@ -5111,10 +5912,10 @@ ${SETTINGS_UPDATE_USAGE2}`, 2);
5111
5912
  else if (off) body[field] = false;
5112
5913
  }
5113
5914
  for (const [flag, field, integer] of SETTINGS_NUMBER_FLAG_FIELDS) {
5114
- const value = args.flags[flag];
5115
- if (value === void 0) continue;
5116
- if (typeof value !== "string") fail(SETTINGS_UPDATE_USAGE2, 2);
5117
- const parsed = Number(value);
5915
+ const value2 = args.flags[flag];
5916
+ if (value2 === void 0) continue;
5917
+ if (typeof value2 !== "string") fail(SETTINGS_UPDATE_USAGE2, 2);
5918
+ const parsed = Number(value2);
5118
5919
  if (!Number.isFinite(parsed) || integer && !Number.isInteger(parsed)) {
5119
5920
  fail(`--${flag} must be ${integer ? "an integer" : "a number"}
5120
5921
  ${SETTINGS_UPDATE_USAGE2}`, 2);
@@ -5169,20 +5970,20 @@ function outputs(...extra) {
5169
5970
  ...extra
5170
5971
  ];
5171
5972
  }
5172
- function details(value) {
5973
+ function details(value2) {
5173
5974
  return {
5174
- usage: value.usage,
5175
- positionals: value.positionals ?? [],
5176
- options: value.options ?? [],
5177
- constraints: value.constraints ?? [],
5178
- outputModes: value.outputModes ?? outputs(),
5179
- warnings: value.warnings ?? [],
5180
- examples: value.examples,
5181
- notes: value.notes ?? []
5975
+ usage: value2.usage,
5976
+ positionals: value2.positionals ?? [],
5977
+ options: value2.options ?? [],
5978
+ constraints: value2.constraints ?? [],
5979
+ outputModes: value2.outputModes ?? outputs(),
5980
+ warnings: value2.warnings ?? [],
5981
+ examples: value2.examples,
5982
+ notes: value2.notes ?? []
5182
5983
  };
5183
5984
  }
5184
- function leaf(name, summary, handler, value, aliases = []) {
5185
- return { kind: "command", name, aliases, summary, handler, ...value };
5985
+ function leaf(name, summary, handler, value2, aliases = []) {
5986
+ return { kind: "command", name, aliases, summary, handler, ...value2 };
5186
5987
  }
5187
5988
  function group(name, summary, children, aliases = []) {
5188
5989
  return { kind: "group", name, aliases, summary, children };
@@ -5239,12 +6040,14 @@ function publicSetupSpec(managedCredentials) {
5239
6040
  notes: managedCredentials ? [
5240
6041
  "Without --agent, setup writes the CLI-only default profile and installs no Hooks or Skills.",
5241
6042
  "Run ctxdb login before setup to create managed credentials.",
6043
+ "Codex setup does not grant hook trust: review only ContextDB hooks with /hooks or App Settings > Hooks. JSON includes the pending review action.",
5242
6044
  "Hermes requires approval of pre_llm_call and post_llm_call with: hermes --accept-hooks chat"
5243
6045
  ] : [
5244
6046
  "Without --agent, setup writes the CLI-only default profile and installs no Hooks or Skills.",
5245
6047
  "Ordinary setup preserves existing authentication; a profile with no authentication may bind the default login.",
5246
6048
  "Explicitly unbound, invalid, or conflicting authentication is not automatically replaced; use --login or --api-key to switch.",
5247
6049
  "A shared login keeps its Core origin; --base-url cannot redirect its tokens to another origin.",
6050
+ "Codex setup does not grant hook trust: review only ContextDB hooks with /hooks or App Settings > Hooks. JSON includes the pending review action.",
5248
6051
  "Hermes requires approval of pre_llm_call and post_llm_call with: hermes --accept-hooks chat"
5249
6052
  ],
5250
6053
  examples: managedCredentials ? [
@@ -5403,7 +6206,116 @@ var debugGroup = group("debug", "Control and inspect local recall traces.", [
5403
6206
  })
5404
6207
  )
5405
6208
  ]);
5406
- var skillGroup = group("skill", "Install ContextDB Skill files without configuring Hooks.", [
6209
+ var SKILL_SCOPE_OPTIONS = [
6210
+ option("--session", "Use an existing Skill session binding.", { valueName: "binding-id" }),
6211
+ AGENT_OPTION,
6212
+ JSON_OPTION
6213
+ ];
6214
+ var SKILL_ID_OPTION = option("--skill-id", "Skill UUID.", { valueName: "id", required: true });
6215
+ var skillGroup = group("skill", "Install, discover, publish, and manage ContextDB Skills.", [
6216
+ leaf("bind", "Bind a session to the current member and refresh its Skill manifest.", skillBind, details({
6217
+ usage: ["ctxdb skill bind --session-id=<id> [--agent=<name>] [--json]"],
6218
+ options: [option("--session-id", "Agent session identifier.", { valueName: "id", required: true }), AGENT_OPTION, JSON_OPTION],
6219
+ examples: [{ command: "ctxdb skill bind --session-id=<session-id> --agent=codex --json" }]
6220
+ })),
6221
+ leaf("manifest", "Refresh the visible Skill manifest for this subject.", skillManifest, details({
6222
+ usage: ["ctxdb skill manifest [--session=<binding-id>] [--agent=<name>] [--json]"],
6223
+ options: SKILL_SCOPE_OPTIONS,
6224
+ examples: [{ command: "ctxdb skill manifest --session=<binding-id> --json" }]
6225
+ }), ["sync"]),
6226
+ leaf("list", "List active Skills visible to the current subject.", skillList, details({
6227
+ usage: ["ctxdb skill list [--session=<binding-id>] [--agent=<name>] [--json]"],
6228
+ options: SKILL_SCOPE_OPTIONS,
6229
+ notes: ["Refreshes the authoritative remote manifest; it does not fall back to stale cached permissions."],
6230
+ examples: [{ command: "ctxdb skill list --agent=qwenwork --json" }]
6231
+ })),
6232
+ leaf("config", "Read effective Skill configuration or change an Agent-local setting.", skillConfig, details({
6233
+ usage: [
6234
+ "ctxdb skill config [get [auto-skill-evolution]] [--session=<binding-id>] [--agent=<name>] [--json]",
6235
+ "ctxdb skill config set auto-skill-evolution <on|off> --agent=<name> [--json]",
6236
+ "ctxdb skill config unset auto-skill-evolution --agent=<name> [--json]"
6237
+ ],
6238
+ positionals: [
6239
+ positional("action", "Optional operation: get, set, or unset.", false),
6240
+ positional("key", "Agent-local configuration key.", false),
6241
+ positional("value", "Configuration value; auto-skill-evolution accepts on or off.", false)
6242
+ ],
6243
+ options: SKILL_SCOPE_OPTIONS,
6244
+ constraints: ["set and unset require --agent and cannot be combined with --session."],
6245
+ notes: ["Only auto-skill-evolution is locally writable. Service policy and thresholds remain read-only."],
6246
+ examples: [
6247
+ { command: "ctxdb skill config --session=<binding-id> --json" },
6248
+ { command: "ctxdb skill config set auto-skill-evolution off --agent=qwenwork --json" }
6249
+ ]
6250
+ })),
6251
+ leaf("search", "Search remote Skills visible to the current subject.", skillSearch, details({
6252
+ usage: ["ctxdb skill search <query> [--top-k=<n>] [--session=<binding-id>] [--agent=<name>] [--json]"],
6253
+ positionals: [positional("query", "Search text, at most 512 Unicode code points.")],
6254
+ options: [option("--top-k", "Number of results, from 1 to 10.", { valueName: "n", type: "integer", default: 5 }), ...SKILL_SCOPE_OPTIONS],
6255
+ examples: [{ command: "ctxdb skill search 'validate JSONL' --top-k=5 --json" }]
6256
+ })),
6257
+ leaf("load", "Download and verify a Skill version, optionally creating an editable copy.", skillLoad, details({
6258
+ usage: ["ctxdb skill load --skill-id=<id> [--version-id=<id>] [--editable] [--session=<binding-id>] [--agent=<name>] [--json]"],
6259
+ options: [SKILL_ID_OPTION, option("--version-id", "Exact version UUID; omit to select the active version.", { valueName: "id" }), option("--editable", "Create an editable draft from the verified package.", { type: "boolean" }), ...SKILL_SCOPE_OPTIONS],
6260
+ examples: [{ command: "ctxdb skill load --skill-id=<skill-id> --version-id=<version-id> --json" }]
6261
+ })),
6262
+ leaf("disable", "Disable a private Skill while retaining its history.", skillDisable, details({
6263
+ usage: ["ctxdb skill disable --skill-id=<id> [--session=<binding-id>] [--agent=<name>] [--json]"],
6264
+ options: [SKILL_ID_OPTION, ...SKILL_SCOPE_OPTIONS],
6265
+ warnings: ["Requires an explicit user instruction; unavailable during automatic maintenance."],
6266
+ examples: [{ command: "ctxdb skill disable --skill-id=<skill-id> --json" }]
6267
+ })),
6268
+ leaf("delete", "Delete a private Skill and clean up its history and packages.", skillDelete, details({
6269
+ usage: ["ctxdb skill delete --skill-id=<id> [--session=<binding-id>] [--agent=<name>] [--json]"],
6270
+ options: [SKILL_ID_OPTION, ...SKILL_SCOPE_OPTIONS],
6271
+ warnings: ["Deletes historical packages. Requires an explicit user instruction; unavailable during automatic maintenance."],
6272
+ examples: [{ command: "ctxdb skill delete --skill-id=<skill-id> --json" }]
6273
+ })),
6274
+ leaf("rollback", "Activate a retained version using the expected current version.", skillRollback, details({
6275
+ usage: ["ctxdb skill rollback --skill-id=<id> --version-id=<target> --base-version-id=<current> [--revoke-reason=<reason>] [--session=<binding-id>] [--agent=<name>] [--json]"],
6276
+ options: [SKILL_ID_OPTION, option("--version-id", "Target version UUID.", { valueName: "target", required: true }), option("--base-version-id", "Expected active version UUID.", { valueName: "current", required: true }), option("--revoke-reason", "Optional nonblank reason, at most 1000 UTF-16 code units.", { valueName: "reason" }), ...SKILL_SCOPE_OPTIONS],
6277
+ warnings: ["Requires an explicit user instruction; unavailable during automatic maintenance."],
6278
+ examples: [{ command: "ctxdb skill rollback --skill-id=<skill-id> --version-id=<target> --base-version-id=<current> --json" }]
6279
+ })),
6280
+ leaf("validate", "Check local Skill package structure without executing scripts.", skillValidate, details({
6281
+ usage: ["ctxdb skill validate <path> [--json]"],
6282
+ positionals: [positional("path", "Skill package directory or ZIP path.", true, "path")],
6283
+ options: [JSON_OPTION],
6284
+ notes: ["Reports validation_scope=static_package and scripts_executed=false; does not establish semantic correctness."],
6285
+ examples: [{ command: "ctxdb skill validate ./my-skill --json" }]
6286
+ })),
6287
+ leaf("upload", "Publish a new Skill or update an existing Skill with Base validation.", skillUpload, details({
6288
+ usage: ["ctxdb skill upload <path> [--source=<import|evolution>] [--skill-id=<id> --base-version-id=<id>] [--request-id=<id>] [--session=<binding-id>] [--agent=<name>] [--json]"],
6289
+ positionals: [positional("path", "Skill package directory or ZIP path.", true, "path")],
6290
+ options: [option("--source", "Publication source.", { valueName: "source", choices: ["import", "evolution"], default: "import" }), option("--skill-id", "Existing Skill UUID for an update.", { valueName: "id" }), option("--base-version-id", "Expected active version UUID for an update.", { valueName: "id" }), option("--request-id", "Idempotency UUID; retries retain the original identifier.", { valueName: "id" }), ...SKILL_SCOPE_OPTIONS],
6291
+ constraints: ["Supply --skill-id and --base-version-id together, or omit both to create a Skill."],
6292
+ notes: ["Resolve unfinished uploads with skill status --resume before changing the draft."],
6293
+ examples: [{ command: "ctxdb skill upload ./my-skill --source=import --json" }]
6294
+ })),
6295
+ leaf("evaluate", "Submit an evidence JSON file for Skill evaluation.", skillEvaluate, details({
6296
+ usage: ["ctxdb skill evaluate <evidence.json> [--request-id=<id>] [--session=<binding-id>] [--agent=<name>] [--json]"],
6297
+ positionals: [positional("evidence.json", "Evidence JSON object file, at most 65536 bytes.", true, "path")],
6298
+ options: [option("--request-id", "Idempotency UUID for this evaluation.", { valueName: "id" }), ...SKILL_SCOPE_OPTIONS],
6299
+ examples: [{ command: "ctxdb skill evaluate ./evidence.json --session=<binding-id> --json" }]
6300
+ })),
6301
+ leaf("status", "Inspect Skill evaluation and publication state or resume unfinished work.", skillStatus, details({
6302
+ usage: ["ctxdb skill status [--remote | --resume] [--event-id=<id>] [--session=<binding-id> | --session-id=<agent-session-id>] [--agent=<name>] [--json]"],
6303
+ options: [option("--session-id", "Agent session ID; QoderWork on macOS/Windows also accepts its UI chat ID. Requires --agent; mutually exclusive with --session. Multiple matches require an explicit Binding ID for remote/recovery operations.", { valueName: "id" }), option("--remote", "Query the selected session current server event without resuming work.", { type: "boolean" }), option("--resume", "Resume unfinished evaluations and publications; --session restricts recovery to that binding.", { type: "boolean" }), option("--event-id", "Query one server evaluation event.", { valueName: "id" }), ...SKILL_SCOPE_OPTIONS],
6304
+ notes: ["Default is local read-only status; without a session selector lists bindings, optionally filtered by --agent. --session filters precisely.", "--remote and --resume require --session or a uniquely resolved --session-id and cannot be combined. With --event-id, queries that event only; --resume has no effect.", "--session-id resolves existing bindings only. QoderWork UI lookup requires Node 22.13+, 23.4+, or 24+ and reads its local SQLite database read-only; missing bindings are listed separately. It never creates a binding.", "Drafts are scope-wide. Local Load records do not prove Agent business reuse."],
6305
+ examples: [{ command: "ctxdb skill status --resume --session=<binding-id> --json" }]
6306
+ })),
6307
+ leaf("retry-feedback", "Explicitly re-deliver a retained evaluation on the next user prompt.", skillRetryFeedback, details({
6308
+ usage: ["ctxdb skill retry-feedback --session=<binding-id> --event-id=<id> [--json]"],
6309
+ options: [option("--event-id", "Retained evaluation ID.", { valueName: "id", required: true }), ...SKILL_SCOPE_OPTIONS],
6310
+ notes: ["Use only after explicit user authorization; stop related Agent activity first. Does not author, upload, or re-submit evidence.", "Refuses pending evaluations and existing or uncertain publications. Check status and permissions first; resume frozen uploads separately."],
6311
+ examples: [{ command: "ctxdb skill retry-feedback --session=<binding-id> --event-id=<id> --json" }]
6312
+ })),
6313
+ leaf("purge", "Preview or remove verified publication payloads or the Skill cache.", skillPurge, details({
6314
+ usage: ["ctxdb skill purge [--cache] [--apply] [--session=<binding-id>] [--agent=<name>] [--json]"],
6315
+ options: [option("--cache", "Select the downloaded Skill cache instead of verified upload payloads.", { type: "boolean" }), option("--apply", "Perform deletion; otherwise only preview eligible data.", { type: "boolean" }), ...SKILL_SCOPE_OPTIONS],
6316
+ warnings: ["Stop related sessions and transfers before applying cache deletion. Drafts and unfinished upload payloads are preserved."],
6317
+ examples: [{ command: "ctxdb skill purge --json" }, { command: "ctxdb skill purge --cache --apply --session=<binding-id> --json" }]
6318
+ })),
5407
6319
  leaf(
5408
6320
  "install",
5409
6321
  "Install ContextDB Skills to a built-in Agent or custom directory.",
@@ -6013,6 +6925,70 @@ var kbAclGroup = group("acl", "Manage direct knowledge-base grants and inspect e
6013
6925
  examples: [{ command: "ctxdb kb acl delete <acl-id> --json" }]
6014
6926
  }))
6015
6927
  ]);
6928
+ var graphKbOption = option("--kb", "Knowledge-base ID (not a name).", { valueName: "kb-id", required: true });
6929
+ var graphEntityIdOption = option("--entity-id", "Exact entity ID within the knowledge base.", { valueName: "id", required: true });
6930
+ var graphRelationIdOption = option("--relation-id", "Exact relation ID within the knowledge base.", { valueName: "id", required: true });
6931
+ var graphEntityFields = [
6932
+ option("--name", "Complete entity name.", { valueName: "name", required: true }),
6933
+ option("--type", "Complete entity type.", { valueName: "type", required: true }),
6934
+ option("--description", "Complete description; use --description='' for empty text.", { valueName: "text", required: true }),
6935
+ option("--aliases-json", "Complete string array; use '[]' for no aliases.", { valueName: "json-array", type: "json", required: true })
6936
+ ];
6937
+ var graphRelationFields = [
6938
+ option("--relation-type", "Complete relation type.", { valueName: "type", required: true }),
6939
+ option("--description", "Complete description; use --description='' for empty text.", { valueName: "text", required: true }),
6940
+ option("--keywords-json", "Complete string array; use '[]' for no keywords.", { valueName: "json-array", type: "json", required: true }),
6941
+ option("--weight", "Relation weight between 0 and 1; range checked by the server.", { valueName: "number", type: "number", required: true })
6942
+ ];
6943
+ var graphMutationOutput = [AGENT_OPTION, RAW_OPTION, JSON_OPTION];
6944
+ var graphDeleteConfirmation = option("--yes", "Explicitly confirm irreversible deletion; required in terminals and scripts.", { type: "boolean", required: true });
6945
+ var graphMutationUsage = (command) => [`${GRAPH_MUTATION_USAGE[command]} [--agent=<name>] [--raw] [--json]`];
6946
+ var graphMutationCommands = [
6947
+ leaf("entity-create", "Create one knowledge-graph entity.", kbEntityCreate, details({
6948
+ usage: graphMutationUsage("entity-create"),
6949
+ options: [graphKbOption, ...graphEntityFields, ...graphMutationOutput],
6950
+ notes: ["Returns the server-assigned entity ID and editable fields. This operates on the KB graph, independently of memory entities."],
6951
+ examples: [{ command: "ctxdb kb entity-create --kb=<kb-id> --name=ContextDB --type=Product --description='Context database' --aliases-json='[]' --json" }]
6952
+ })),
6953
+ leaf("entity-update", "Replace the editable fields of one knowledge-graph entity.", kbEntityUpdate, details({
6954
+ usage: graphMutationUsage("entity-update"),
6955
+ options: [graphKbOption, graphEntityIdOption, ...graphEntityFields, ...graphMutationOutput],
6956
+ warnings: ["Full replacement: read entity-get first and supply all editable fields. Empty description/aliases explicitly clear them.", "Renaming preserves the entity ID and updates incident relation names. The server rejects renames exceeding its incident-relation limit."],
6957
+ examples: [{ command: "ctxdb kb entity-update --kb=<kb-id> --entity-id=<id> --name=ContextDB --type=Product --description='' --aliases-json='[]' --json" }]
6958
+ })),
6959
+ leaf("entity-delete", "Irreversibly delete an entity and all incoming/outgoing relations.", kbEntityDelete, details({
6960
+ usage: graphMutationUsage("entity-delete"),
6961
+ options: [graphKbOption, graphEntityIdOption, graphDeleteConfirmation, ...graphMutationOutput],
6962
+ warnings: ["Irreversible: deletes the selected entity and every incident relation. No delete request is sent without --yes."],
6963
+ notes: ["Only exact IDs are accepted; names and bulk deletion are unsupported.", "Returns deleted, deleted_entity_count and deleted_relation_count from the server. A false deleted result exits 1."],
6964
+ examples: [{ command: "ctxdb kb entity-delete --kb=<kb-id> --entity-id=<id> --yes --json" }]
6965
+ })),
6966
+ leaf("relation-create", "Create one relation between knowledge-graph entity IDs.", kbRelationCreate, details({
6967
+ usage: graphMutationUsage("relation-create"),
6968
+ options: [
6969
+ graphKbOption,
6970
+ option("--source-entity-id", "Existing source entity ID.", { valueName: "id", required: true }),
6971
+ option("--target-entity-id", "Existing target entity ID.", { valueName: "id", required: true }),
6972
+ ...graphRelationFields,
6973
+ ...graphMutationOutput
6974
+ ],
6975
+ examples: [{ command: "ctxdb kb relation-create --kb=<kb-id> --source-entity-id=<a> --target-entity-id=<b> --relation-type=USES --description='' --keywords-json='[]' --weight=1 --json" }]
6976
+ })),
6977
+ leaf("relation-update", "Replace the editable fields of one knowledge-graph relation.", kbRelationUpdate, details({
6978
+ usage: graphMutationUsage("relation-update"),
6979
+ options: [graphKbOption, graphRelationIdOption, ...graphRelationFields, ...graphMutationOutput],
6980
+ warnings: ["Full replacement: read relations --verbose first and supply all editable fields. Empty description/keywords explicitly clear them."],
6981
+ constraints: ["Relation ID and source/target entity IDs cannot be changed."],
6982
+ examples: [{ command: "ctxdb kb relation-update --kb=<kb-id> --relation-id=<id> --relation-type=USES --description='' --keywords-json='[]' --weight=1 --json" }]
6983
+ })),
6984
+ leaf("relation-delete", "Irreversibly delete one knowledge-graph relation.", kbRelationDelete, details({
6985
+ usage: graphMutationUsage("relation-delete"),
6986
+ options: [graphKbOption, graphRelationIdOption, graphDeleteConfirmation, ...graphMutationOutput],
6987
+ warnings: ["Irreversible: deletes only this relation; both endpoint entities remain. No delete request is sent without --yes."],
6988
+ notes: ["Only exact relation IDs are accepted. Returns deleted from the server; a false result exits 1. The relation API does not return deletion counts."],
6989
+ examples: [{ command: "ctxdb kb relation-delete --kb=<kb-id> --relation-id=<id> --yes --json" }]
6990
+ }))
6991
+ ];
6016
6992
  var kbGroup = group("kb", "Manage knowledge bases, documents, retrieval, graph, and review.", [
6017
6993
  leaf("create", "Create a knowledge base.", kbCreate, details({
6018
6994
  usage: ["ctxdb kb create <kb-name> [--description=<text>] [--graph-enabled|--no-graph-enabled] [--review-enabled|--no-review-enabled] [--agent=<name>] [--json]"],
@@ -6277,6 +7253,7 @@ var kbGroup = group("kb", "Manage knowledge bases, documents, retrieval, graph,
6277
7253
  notes: ["--entity-id matches either endpoint; --entity-ids requires both endpoints in the supplied set."],
6278
7254
  examples: [{ command: "ctxdb kb relations --kb=<kb-id> --entity-id=<id> --verbose --json" }]
6279
7255
  })),
7256
+ ...graphMutationCommands,
6280
7257
  reviewGroup
6281
7258
  ]);
6282
7259
  function createCommandTree(options = {}) {
@@ -6339,8 +7316,8 @@ function findChild(parent, name) {
6339
7316
 
6340
7317
  // src/cli/device-auth-cli.ts
6341
7318
  import { AuthorizationService, publicAuthorizationTarget, oauthSessionCandidate, oauthSessionFromCredential } from "@aliyunrds/ctxdb-shared";
6342
- import { dirname as dirname2, join as join2 } from "path";
6343
- import { createInterface } from "readline/promises";
7319
+ import { dirname as dirname2, join as join6 } from "node:path";
7320
+ import { createInterface } from "node:readline/promises";
6344
7321
  import {
6345
7322
  CoreDeviceClient,
6346
7323
  normalizeCoreOrigin as normalizeCoreOrigin2,
@@ -6361,12 +7338,12 @@ import {
6361
7338
  } from "@aliyunrds/ctxdb-shared";
6362
7339
 
6363
7340
  // src/lib/oauth-login-config.ts
6364
- import { existsSync as existsSync3, readFileSync as readFileSync3 } from "fs";
7341
+ import { existsSync as existsSync7, readFileSync as readFileSync3 } from "node:fs";
6365
7342
  import { isOfficialProductionUrl, normalizeCoreOrigin } from "@aliyunrds/ctxdb-shared";
6366
7343
  var PRODUCTION_AUTHORIZATION_URL = "https://rdsnext.console.aliyun.com/contextDbCliAuthorization/cn-hangzhou";
6367
- function authorizationPage(value) {
6368
- if (typeof value !== "string" || !value.trim()) throw new Error();
6369
- const url = new URL(value);
7344
+ function authorizationPage(value2) {
7345
+ if (typeof value2 !== "string" || !value2.trim()) throw new Error();
7346
+ const url = new URL(value2);
6370
7347
  if (url.protocol !== "https:" || url.username || url.password || url.search || url.hash) throw new Error();
6371
7348
  return url.toString();
6372
7349
  }
@@ -6379,7 +7356,7 @@ function consoleAuthorizationBase(path, core, loginUrl) {
6379
7356
  }
6380
7357
  }
6381
7358
  const fallback = isOfficialProductionUrl(core) ? PRODUCTION_AUTHORIZATION_URL : void 0;
6382
- if (!existsSync3(path)) return fallback;
7359
+ if (!existsSync7(path)) return fallback;
6383
7360
  try {
6384
7361
  const raw = JSON.parse(readFileSync3(path, "utf8"));
6385
7362
  if (raw.version !== 1 || !Array.isArray(raw.environments) || raw.environments.length > 32) throw new Error();
@@ -6393,8 +7370,8 @@ function consoleAuthorizationBase(path, core, loginUrl) {
6393
7370
  }
6394
7371
 
6395
7372
  // src/lib/open-browser.ts
6396
- import { spawn } from "child_process";
6397
- import { platform } from "os";
7373
+ import { spawn } from "node:child_process";
7374
+ import { platform } from "node:os";
6398
7375
  function openBrowser(url) {
6399
7376
  try {
6400
7377
  const parsed = new URL(url);
@@ -6404,19 +7381,19 @@ function openBrowser(url) {
6404
7381
  }
6405
7382
  const os = platform();
6406
7383
  const command = os === "darwin" ? { file: "open", args: [url] } : os === "win32" ? { file: "rundll32.exe", args: ["url.dll,FileProtocolHandler", url] } : { file: "xdg-open", args: [url] };
6407
- return new Promise((resolve4) => {
7384
+ return new Promise((resolve5) => {
6408
7385
  try {
6409
7386
  const child = spawn(command.file, command.args, { detached: true, stdio: "ignore", windowsHide: true, shell: false });
6410
- const timer = setTimeout(() => resolve4(true), 2e3);
7387
+ const timer = setTimeout(() => resolve5(true), 2e3);
6411
7388
  const finish = (opened) => {
6412
7389
  clearTimeout(timer);
6413
- resolve4(opened);
7390
+ resolve5(opened);
6414
7391
  };
6415
7392
  child.once("error", () => finish(false));
6416
7393
  child.once("exit", (code) => finish(code === 0));
6417
7394
  child.unref();
6418
7395
  } catch {
6419
- resolve4(false);
7396
+ resolve5(false);
6420
7397
  }
6421
7398
  });
6422
7399
  }
@@ -6433,7 +7410,7 @@ function createPublicAuthCommands(dependencies = {}) {
6433
7410
  });
6434
7411
  const client = dependencies.client ?? ((url) => new CoreDeviceClient(url));
6435
7412
  const launchBrowser = dependencies.openBrowser ?? openBrowser;
6436
- const output = (value, json) => stdout(json ? JSON.stringify(value) + "\n" : String(value) + "\n");
7413
+ const output = (value2, json) => stdout(json ? JSON.stringify(value2) + "\n" : String(value2) + "\n");
6437
7414
  function options(args, command) {
6438
7415
  const allowed = ["agent", "json", "help", "h", ...command === "login" ? ["base-url", "login-url", "server", "device-code", "no-browser", "login", "yes"] : command === "logout" ? ["login", "yes"] : command === "status" ? ["all"] : []];
6439
7416
  if (args.positional.length || Object.keys(args.flags).some((k) => !allowed.includes(k))) throw new Error("Unsupported authentication option.");
@@ -6479,7 +7456,7 @@ function createPublicAuthCommands(dependencies = {}) {
6479
7456
  const pending = beginLogin(path, agent, name);
6480
7457
  await confirmImpact(name ? pending.users[0] ?? agent : agent, pending.users, args, "Reauthorizing this login");
6481
7458
  const baseUrl = normalizeCoreOrigin2(explicitBase ?? DEFAULT_BASE_URL);
6482
- const consoleBase = consoleAuthorizationBase(join2(dirname2(path), "device-environments.json"), baseUrl, args.flags["login-url"]);
7459
+ const consoleBase = consoleAuthorizationBase(join6(dirname2(path), "device-environments.json"), baseUrl, args.flags["login-url"]);
6483
7460
  const connection = client(baseUrl), controller = new AbortController();
6484
7461
  let browserOpening = Promise.resolve();
6485
7462
  const interrupt = () => controller.abort();
@@ -6788,23 +7765,23 @@ function renderRows(rows, indent = " ") {
6788
7765
  return rows.map(([label, description]) => `${indent}${label.padEnd(width)} ${description}
6789
7766
  `).join("");
6790
7767
  }
6791
- function optionLabel(value) {
6792
- const valuePart = value.valueName ? ` <${value.valueName}>` : "";
7768
+ function optionLabel(value2) {
7769
+ const valuePart = value2.valueName ? ` <${value2.valueName}>` : "";
6793
7770
  const annotations = [
6794
- value.required ? "required" : "",
6795
- value.repeatable ? "repeatable" : ""
7771
+ value2.required ? "required" : "",
7772
+ value2.repeatable ? "repeatable" : ""
6796
7773
  ].filter(Boolean);
6797
- return `${value.name}${valuePart}${annotations.length ? ` (${annotations.join("; ")})` : ""}`;
7774
+ return `${value2.name}${valuePart}${annotations.length ? ` (${annotations.join("; ")})` : ""}`;
6798
7775
  }
6799
- function optionDescription(value) {
7776
+ function optionDescription(value2) {
6800
7777
  const details2 = [
6801
- value.default !== void 0 ? `Default: ${String(value.default)}.` : "",
6802
- value.choices?.length ? `Choices: ${value.choices.join("|")}.` : ""
7778
+ value2.default !== void 0 ? `Default: ${String(value2.default)}.` : "",
7779
+ value2.choices?.length ? `Choices: ${value2.choices.join("|")}.` : ""
6803
7780
  ].filter(Boolean);
6804
- return `${value.description}${details2.length ? ` ${details2.join(" ")}` : ""}`;
7781
+ return `${value2.description}${details2.length ? ` ${details2.join(" ")}` : ""}`;
6805
7782
  }
6806
- function positionalLabel(value) {
6807
- return `${value.name} <${value.type}> (${value.required ? "required" : "optional"})`;
7783
+ function positionalLabel(value2) {
7784
+ return `${value2.name} <${value2.type}> (${value2.required ? "required" : "optional"})`;
6808
7785
  }
6809
7786
  function renderExamples(examples) {
6810
7787
  return examples.map(
@@ -6854,7 +7831,7 @@ function renderListSection(title, values) {
6854
7831
  if (values.length === 0) return "";
6855
7832
  return `
6856
7833
  ${title}
6857
- ${values.map((value) => ` ${value}
7834
+ ${values.map((value2) => ` ${value2}
6858
7835
  `).join("")}`;
6859
7836
  }
6860
7837
  function renderLeaf(leaf2, path) {
@@ -6864,11 +7841,11 @@ USAGE
6864
7841
  ${leaf2.usage.map((usage2) => ` ${usage2}
6865
7842
  `).join("")}
6866
7843
  ARGUMENTS
6867
- ${renderRows(leaf2.positionals.map((value) => [positionalLabel(value), value.description]))}
7844
+ ${renderRows(leaf2.positionals.map((value2) => [positionalLabel(value2), value2.description]))}
6868
7845
  OPTIONS
6869
- ${renderRows(leaf2.options.map((value) => [optionLabel(value), optionDescription(value)]))}` + renderListSection("CONSTRAINTS", leaf2.constraints) + `
7846
+ ${renderRows(leaf2.options.map((value2) => [optionLabel(value2), optionDescription(value2)]))}` + renderListSection("CONSTRAINTS", leaf2.constraints) + `
6870
7847
  OUTPUT
6871
- ${renderRows(leaf2.outputModes.map((value) => [value.name, value.description]))}` + renderListSection("NOTES", leaf2.notes) + renderListSection("WARNINGS", leaf2.warnings) + `
7848
+ ${renderRows(leaf2.outputModes.map((value2) => [value2.name, value2.description]))}` + renderListSection("NOTES", leaf2.notes) + renderListSection("WARNINGS", leaf2.warnings) + `
6872
7849
  EXAMPLES
6873
7850
  ${renderExamples(leaf2.examples)}`;
6874
7851
  }
@@ -6900,15 +7877,15 @@ function serializeNode(command, path, recursive) {
6900
7877
  return {
6901
7878
  ...base,
6902
7879
  usage: [...serializable.usage],
6903
- positionals: serializable.positionals.map((value) => ({ ...value })),
6904
- options: serializable.options.map((value) => ({
6905
- ...value,
6906
- ...value.choices ? { choices: [...value.choices] } : {}
7880
+ positionals: serializable.positionals.map((value2) => ({ ...value2 })),
7881
+ options: serializable.options.map((value2) => ({
7882
+ ...value2,
7883
+ ...value2.choices ? { choices: [...value2.choices] } : {}
6907
7884
  })),
6908
7885
  constraints: [...serializable.constraints],
6909
- outputModes: serializable.outputModes.map((value) => ({ ...value })),
7886
+ outputModes: serializable.outputModes.map((value2) => ({ ...value2 })),
6910
7887
  warnings: [...serializable.warnings],
6911
- examples: serializable.examples.map((value) => ({ ...value })),
7888
+ examples: serializable.examples.map((value2) => ({ ...value2 })),
6912
7889
  notes: [...serializable.notes]
6913
7890
  };
6914
7891
  }
@@ -6967,8 +7944,8 @@ function resolvePath(root, tokens) {
6967
7944
  }
6968
7945
  return { node, path, consumed, parent };
6969
7946
  }
6970
- function isUnknown(value) {
6971
- return "group" in value;
7947
+ function isUnknown(value2) {
7948
+ return "group" in value2;
6972
7949
  }
6973
7950
  function writeUnknown(unknown, root, version) {
6974
7951
  const scope = unknown.path.length === 1 ? "command" : "subcommand";
@@ -7097,8 +8074,8 @@ function runMain(root = COMMAND_TREE) {
7097
8074
  }
7098
8075
  var directEntry = (() => {
7099
8076
  try {
7100
- const modulePath = resolve3(fileURLToPath2(import.meta.url));
7101
- const argvPath = resolve3(process.argv[1] ?? "");
8077
+ const modulePath = resolve4(fileURLToPath2(import.meta.url));
8078
+ const argvPath = resolve4(process.argv[1] ?? "");
7102
8079
  return modulePath === argvPath || realpathSync(modulePath) === realpathSync(argvPath);
7103
8080
  } catch {
7104
8081
  return false;