@aliyunrds/ctxdb 0.0.7 → 0.0.8-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  configDir
4
- } from "./chunk-S45GOYUU.js";
4
+ } from "./chunk-BWPFGTF7.js";
5
5
 
6
6
  // src/lib/circuit.ts
7
7
  import { statSync, writeFileSync, unlinkSync, mkdirSync, readdirSync, readFileSync } from "fs";
@@ -1,12 +1,15 @@
1
1
  #!/usr/bin/env node
2
+ import {
3
+ listKnowledgeBases
4
+ } from "./chunk-6S5RJYBC.js";
2
5
  import {
3
6
  isConnectionError,
4
7
  resetCircuit,
5
8
  tripCircuit
6
- } from "./chunk-Q2EEP4CE.js";
9
+ } from "./chunk-52IAVJRK.js";
7
10
  import {
8
11
  CtxdbError
9
- } from "./chunk-S45GOYUU.js";
12
+ } from "./chunk-BWPFGTF7.js";
10
13
 
11
14
  // src/lib/recall-orchestrator.ts
12
15
  import {
@@ -138,6 +141,34 @@ async function recallTurn(prompt, cfg, client, agent = "default") {
138
141
  };
139
142
  }
140
143
 
144
+ // src/lib/kb-catalog.ts
145
+ function sanitizeKeyEntities(raw) {
146
+ if (!Array.isArray(raw)) return [];
147
+ const out = [];
148
+ for (const e of raw) {
149
+ if (typeof e !== "string") continue;
150
+ const cleaned = e.replace(/\s+/g, " ").trim();
151
+ if (cleaned.length > 0) out.push(cleaned);
152
+ }
153
+ return out;
154
+ }
155
+ async function fetchKbCatalogBlock(client, agent) {
156
+ const kbs = await listKnowledgeBases(client);
157
+ const active = kbs.filter((kb) => kb.status === "active");
158
+ if (active.length === 0) return "";
159
+ const lines = active.map((kb) => {
160
+ const ents = sanitizeKeyEntities(kb.key_entities);
161
+ return ents.length > 0 ? `\xB7 ${kb.name}: ${ents.join(", ")}` : `\xB7 ${kb.name}`;
162
+ });
163
+ return [
164
+ "<available-knowledge-bases>",
165
+ `When you identify that relevant information may exist in the knowledge bases below, you MUST run \`ctxdb kb search "<query>" --kb=<name> --agent=${agent}\` with targeted keywords after initial analysis to supplement and correct your approach. Knowledge bases:`,
166
+ ...lines,
167
+ "</available-knowledge-bases>"
168
+ ].join("\n");
169
+ }
170
+
141
171
  export {
142
- recallTurn
172
+ recallTurn,
173
+ fetchKbCatalogBlock
143
174
  };
@@ -39,23 +39,27 @@ ${s}`;
39
39
  }
40
40
  }
41
41
 
42
- // src/lib/http-client.ts
42
+ // src/lib/package-version.ts
43
43
  import { readFileSync } from "fs";
44
- import { fileURLToPath } from "url";
45
44
  import { dirname as dirname2, join as join2 } from "path";
45
+ import { fileURLToPath } from "url";
46
46
  function findPackageVersion() {
47
47
  let dir = dirname2(fileURLToPath(import.meta.url));
48
48
  for (let i = 0; i < 5; i++) {
49
49
  try {
50
50
  const pkg = JSON.parse(readFileSync(join2(dir, "package.json"), "utf-8"));
51
- if (pkg.name === "@aliyunrds/ctxdb") return pkg.version;
51
+ if (pkg.name === "@aliyunrds/ctxdb" && typeof pkg.version === "string") {
52
+ return pkg.version;
53
+ }
52
54
  } catch {
53
55
  }
54
56
  dir = dirname2(dir);
55
57
  }
56
58
  return "0.0.0";
57
59
  }
58
- var PKG_VERSION = findPackageVersion();
60
+ var PACKAGE_VERSION = findPackageVersion();
61
+
62
+ // src/lib/http-client.ts
59
63
  var DEFAULT_TIMEOUT_MS = 3e4;
60
64
  var CtxdbError = class extends Error {
61
65
  constructor(message) {
@@ -108,7 +112,7 @@ var HttpClient = class {
108
112
  this.baseUrl = opts.baseUrl.replace(/\/+$/, "");
109
113
  this.apiKey = opts.apiKey;
110
114
  this.timeoutMs = opts.timeoutMs ?? DEFAULT_TIMEOUT_MS;
111
- this.userAgent = opts.userAgent ?? `ctxdb-cli/${PKG_VERSION}`;
115
+ this.userAgent = opts.userAgent ?? `ctxdb-cli/${PACKAGE_VERSION}`;
112
116
  this.extraHeaders = { ...opts.extraHeaders ?? {} };
113
117
  const f = opts.fetchImpl ?? globalThis.fetch.bind(globalThis);
114
118
  this.fetchImpl = f;
@@ -257,7 +261,7 @@ function extractErrorDetail(text, fallback) {
257
261
  // src/lib/agents.ts
258
262
  import { homedir as homedir2 } from "os";
259
263
  import { join as join3 } from "path";
260
- var SUPPORTED_AGENTS = ["qoder", "codex", "claude"];
264
+ var SUPPORTED_AGENTS = ["qoder", "qoderwork", "codex", "claude"];
261
265
  function isBuiltinAgent(v) {
262
266
  return typeof v === "string" && SUPPORTED_AGENTS.includes(v);
263
267
  }
@@ -269,6 +273,8 @@ function agentHomeDir(agent) {
269
273
  switch (agent) {
270
274
  case "qoder":
271
275
  return join3(homedir2(), ".qoder");
276
+ case "qoderwork":
277
+ return join3(homedir2(), ".qoderwork");
272
278
  case "codex":
273
279
  return join3(homedir2(), ".codex");
274
280
  case "claude":
@@ -308,6 +314,7 @@ var DEFAULT_USER_ID = "default";
308
314
  var DEFAULT_TOP_K = 5;
309
315
  var DEFAULT_THRESHOLD = 0.4;
310
316
  var DEFAULT_KNOWLEDGE_TOP_K = 6;
317
+ var DEFAULT_KB_CATALOG_INJECTION = "session_start";
311
318
  function isComplete(cfg) {
312
319
  return Boolean(cfg.apiKey && cfg.baseUrl);
313
320
  }
@@ -330,6 +337,12 @@ function coerceBool(v, fallback) {
330
337
  if (v === void 0 || v === null) return fallback;
331
338
  return Boolean(v);
332
339
  }
340
+ function coerceKbCatalogInjection(v) {
341
+ if (v === "session_start" || v === "user_prompt_submit" || v === "off") {
342
+ return v;
343
+ }
344
+ return DEFAULT_KB_CATALOG_INJECTION;
345
+ }
333
346
  function readRaw(path) {
334
347
  if (!existsSync(path)) return {};
335
348
  try {
@@ -367,7 +380,8 @@ function configFromDisk(raw) {
367
380
  topK: coerceInt(raw.top_k, DEFAULT_TOP_K),
368
381
  threshold: coerceFloat(raw.threshold, DEFAULT_THRESHOLD),
369
382
  knowledgeTopK: coerceInt(raw.knowledge_top_k, DEFAULT_KNOWLEDGE_TOP_K),
370
- debug: coerceBool(raw.debug, false)
383
+ debug: coerceBool(raw.debug, false),
384
+ kbCatalogInjection: coerceKbCatalogInjection(raw.kb_catalog_injection)
371
385
  };
372
386
  }
373
387
  function applyEnv(cfg, env) {
@@ -416,7 +430,8 @@ function configToDisk(cfg) {
416
430
  top_k: cfg.topK,
417
431
  threshold: cfg.threshold,
418
432
  knowledge_top_k: cfg.knowledgeTopK,
419
- debug: cfg.debug
433
+ debug: cfg.debug,
434
+ kb_catalog_injection: cfg.kbCatalogInjection
420
435
  };
421
436
  }
422
437
  function removeAgent(agent, path, options = {}) {
@@ -481,7 +496,9 @@ function writeInstalledPkgVersion(version, path) {
481
496
 
482
497
  export {
483
498
  setDebug,
499
+ isDebug,
484
500
  debug,
501
+ PACKAGE_VERSION,
485
502
  CtxdbError,
486
503
  HttpClient,
487
504
  SUPPORTED_AGENTS,
package/dist/cli/main.js CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  DEFAULT_BASE_URL,
19
19
  DEFAULT_USER_ID,
20
20
  HttpClient,
21
+ PACKAGE_VERSION,
21
22
  SUPPORTED_AGENTS,
22
23
  agentFromEnv,
23
24
  agentHomeDir,
@@ -29,7 +30,7 @@ import {
29
30
  removeAgent,
30
31
  save,
31
32
  writeInstalledPkgVersion
32
- } from "../chunk-S45GOYUU.js";
33
+ } from "../chunk-BWPFGTF7.js";
33
34
 
34
35
  // src/cli/util.ts
35
36
  var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
@@ -216,7 +217,6 @@ function fail(message, code = 1) {
216
217
  `);
217
218
  process.exit(code);
218
219
  }
219
- var PACKAGE_VERSION = "0.0.7";
220
220
 
221
221
  // src/setup/installer.ts
222
222
  import {
@@ -242,6 +242,8 @@ function skillInstallRoot(agent) {
242
242
  switch (agent) {
243
243
  case "qoder":
244
244
  return join(homedir(), ".qoder", "skills");
245
+ case "qoderwork":
246
+ return join(homedir(), ".qoderwork", "skills");
245
247
  case "codex":
246
248
  return join(homedir(), ".codex", "skills");
247
249
  case "claude":
@@ -257,6 +259,8 @@ function hookConfigPath(agent) {
257
259
  switch (agent) {
258
260
  case "qoder":
259
261
  return join(homedir(), ".qoder", "settings.json");
262
+ case "qoderwork":
263
+ return join(homedir(), ".qoderwork", "settings.json");
260
264
  case "codex":
261
265
  return join(homedir(), ".codex", "hooks.json");
262
266
  case "claude":
@@ -306,6 +310,14 @@ async function runSetup(options) {
306
310
  ok: true,
307
311
  detail: `${agent}: ${cfg.baseUrl} (user_id=${cfg.userId})`
308
312
  });
313
+ if (isBuiltinAgent(agent) && !configuredAgents().includes("default")) {
314
+ save(cfg, void 0, { agent: "default" });
315
+ steps.push({
316
+ step: "write-default-config",
317
+ ok: true,
318
+ detail: `default: copied from ${agent}`
319
+ });
320
+ }
309
321
  if (installSkill && isBuiltinAgent(agent)) {
310
322
  const installRoot = skillInstallRoot(agent);
311
323
  const skillResult = installSkillsTo(installRoot, agent);
@@ -624,8 +636,10 @@ function checkHookNodePaths(agent) {
624
636
  if (!entryIsCtxdb(entry)) continue;
625
637
  for (const h of entry.hooks ?? []) {
626
638
  if (h?.type !== "command" || typeof h.command !== "string") continue;
627
- const cmd = h.command;
628
- if (cmd.includes("@aliyunrds/ctxdb")) {
639
+ if (Array.isArray(h.args) && h.args.length > 0) {
640
+ nodePaths.add(h.command);
641
+ } else {
642
+ const cmd = h.command;
629
643
  const parts = cmd.split(" ");
630
644
  if (parts.length >= 2 && !parts[0].endsWith(".js") && !parts[0].endsWith(".ts")) {
631
645
  nodePaths.add(parts[0]);
@@ -804,6 +818,7 @@ function copySkillDir(src, dest, agent) {
804
818
  }
805
819
  var SKILL_INSTALL_TARGETS = {
806
820
  qoder: join(homedir(), ".qoder", "skills"),
821
+ qoderwork: join(homedir(), ".qoderwork", "skills"),
807
822
  codex: join(homedir(), ".codex", "skills"),
808
823
  claude: join(homedir(), ".claude", "skills"),
809
824
  openclaw: join(homedir(), ".openclaw", "skills"),
@@ -865,16 +880,18 @@ var LEGACY_MARKER_KEYS = ["_ctxdbQoder", "_ctxdbPackage"];
865
880
  var LEGACY_MARKER_VALUES = ["@aliyunrds/ctxdb-qoder"];
866
881
  var TOOL_SCOPED_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse"]);
867
882
  function appendOne(hooks, event, command, agent) {
868
- const commandWithAgent = `${process.execPath} ${command} --agent=${agent}`;
869
883
  if (!Array.isArray(hooks[event])) hooks[event] = [];
884
+ const useExecForm = agent === "claude";
885
+ const shellCmd = `${process.execPath} ${command} --agent=${agent}`;
870
886
  const dup = hooks[event].some(
871
887
  (entry2) => Array.isArray(entry2?.hooks) && entry2.hooks.some(
872
- (h) => h?.type === "command" && typeof h.command === "string" && h.command === commandWithAgent
888
+ (h) => h?.type === "command" && (Array.isArray(h.args) && h.args[0] === command || typeof h.command === "string" && h.command.includes(command))
873
889
  )
874
890
  );
875
891
  if (dup) return;
892
+ const inner = useExecForm ? { type: "command", command: process.execPath, args: [command, `--agent=${agent}`], timeout: 60 } : { type: "command", command: shellCmd, timeout: 60 };
876
893
  const entry = {
877
- hooks: [{ type: "command", command: commandWithAgent, timeout: 60 }],
894
+ hooks: [inner],
878
895
  [ENTRY_MARKER_KEY]: ENTRY_MARKER_VALUE,
879
896
  [ENTRY_AGENT_KEY]: agent
880
897
  };
@@ -1237,7 +1254,7 @@ async function ping(args) {
1237
1254
  var HELP = `ctxdb setup \u2014 configure an agent
1238
1255
 
1239
1256
  USAGE
1240
- ctxdb setup [--agent <qoder|codex|claude>]
1257
+ ctxdb setup [--agent <qoder|qoderwork|codex|claude>]
1241
1258
  [--api-key=K] [--base-url=URL] [--user-id=ID]
1242
1259
  [--no-install-skill] [--no-validate] [--json]
1243
1260
 
@@ -1248,7 +1265,7 @@ BEHAVIOR
1248
1265
  specified agent harness.
1249
1266
 
1250
1267
  FLAGS
1251
- --agent <name> Target agent (qoder|codex|claude)
1268
+ --agent <name> Target agent (qoder|qoderwork|codex|claude)
1252
1269
  --api-key <key> API key (required on first setup)
1253
1270
  --base-url <url> Server URL (default: https://context-database.aliyuncs.com)
1254
1271
  --user-id <id> User bucket (default: "default")
@@ -1275,7 +1292,7 @@ async function setup(args) {
1275
1292
  const json = !!args.flags.json;
1276
1293
  if (agent === "default" && !json && !configuredAgents().includes("default")) {
1277
1294
  process.stderr.write(
1278
- `hint: creating CLI-only "default" agent (no hooks/skills). Use --agent <claude|qoder|codex> for full setup.
1295
+ `hint: creating CLI-only "default" agent (no hooks/skills). Use --agent <claude|qoder|qoderwork|codex> for full setup.
1279
1296
  `
1280
1297
  );
1281
1298
  }
@@ -1300,11 +1317,11 @@ ${h}
1300
1317
  var HELP2 = `ctxdb uninstall \u2014 remove hooks + skills
1301
1318
 
1302
1319
  USAGE
1303
- ctxdb uninstall [--agent <qoder|codex|claude>] [--purge-config] [--purge-logs] [--purge-all] [--json]
1320
+ ctxdb uninstall [--agent <qoder|qoderwork|codex|claude>] [--purge-config] [--purge-logs] [--purge-all] [--json]
1304
1321
 
1305
1322
  DEFAULT BEHAVIOR (no --agent, no purge flags)
1306
1323
  Strips ctxdb hook entries and skill directories for ALL agents (qoder,
1307
- codex, claude). Keeps ~/.ctxdb/ctxdb.json (credentials) and ~/.ctxdb/logs/
1324
+ qoderwork, codex, claude). Keeps ~/.ctxdb/ctxdb.json (credentials) and ~/.ctxdb/logs/
1308
1325
  so you can re-run \`ctxdb setup\` later without losing configuration.
1309
1326
 
1310
1327
  WITH --agent <name>
@@ -1312,7 +1329,7 @@ WITH --agent <name>
1312
1329
  Other agents remain untouched.
1313
1330
 
1314
1331
  FLAGS
1315
- --agent <name> Target a single agent (qoder|codex|claude)
1332
+ --agent <name> Target a single agent (qoder|qoderwork|codex|claude)
1316
1333
  --purge-config Also delete ~/.ctxdb/ctxdb.json
1317
1334
  --purge-logs Also delete ~/.ctxdb/logs/
1318
1335
  --purge-all Delete everything under ~/.ctxdb/
@@ -1454,7 +1471,7 @@ function runSelfUpdate(currentVersion, passthroughArgs = []) {
1454
1471
  var HELP3 = `ctxdb upgrade \u2014 refresh skills + hooks
1455
1472
 
1456
1473
  USAGE
1457
- ctxdb upgrade [--agent <qoder|codex|claude>] [--self-update] [--json]
1474
+ ctxdb upgrade [--agent <qoder|qoderwork|codex|claude>] [--self-update] [--json]
1458
1475
 
1459
1476
  BEHAVIOR
1460
1477
  Refreshes skill files and hook entries for configured agents without
@@ -1464,7 +1481,7 @@ BEHAVIOR
1464
1481
  With --agent: upgrades only the specified agent.
1465
1482
 
1466
1483
  FLAGS
1467
- --agent <name> Target a single agent (qoder|codex|claude)
1484
+ --agent <name> Target a single agent (qoder|qoderwork|codex|claude)
1468
1485
  --self-update Also pull latest package from npm before upgrading
1469
1486
  --json Machine-readable JSON output
1470
1487
  `;
@@ -1570,7 +1587,8 @@ function skillInstall(args) {
1570
1587
  }
1571
1588
  targetRoot = resolved;
1572
1589
  }
1573
- const result = installSkillsTo(targetRoot);
1590
+ const templateAgent = target && isBuiltinAgent(target) ? target : "default";
1591
+ const result = installSkillsTo(targetRoot, templateAgent);
1574
1592
  const ok = result.ok;
1575
1593
  const output = {
1576
1594
  ok,
@@ -1826,13 +1844,14 @@ USAGE
1826
1844
  ctxdb <command> [args] [--flags]
1827
1845
 
1828
1846
  COMMANDS
1829
- setup [--agent <qoder|codex|claude>]
1847
+ setup [--agent <qoder|qoderwork|codex|claude>]
1830
1848
  [--api-key=K] [--base-url=URL] [--user-id=ID]
1831
1849
  [--no-install-skill] [--no-validate] [--json]
1832
1850
  Without --agent \u2192 writes agents.default (CLI-only, no hooks/skills)
1833
- Qoder \u2192 writes agents.qoder config + ~/.qoder/settings.json hooks + skill
1834
- Codex \u2192 writes agents.codex config + ~/.codex/hooks.json hooks + skill
1835
- Claude \u2192 writes agents.claude config + ~/.claude/settings.json hooks + skill
1851
+ Qoder \u2192 writes agents.qoder config + ~/.qoder/settings.json hooks + skill
1852
+ QoderWork \u2192 writes agents.qoderwork config + ~/.qoderwork/settings.json hooks + skill
1853
+ Codex \u2192 writes agents.codex config + ~/.codex/hooks.json hooks + skill
1854
+ Claude \u2192 writes agents.claude config + ~/.claude/settings.json hooks + skill
1836
1855
  status [--agent <name>] [--json]
1837
1856
  ping [--agent <name>] [--json]
1838
1857
  uninstall [--agent <name>] [--purge-config] [--purge-logs] [--purge-all] [--json]
@@ -1875,7 +1894,7 @@ COMMANDS
1875
1894
  doc_id?/tags? for citation.
1876
1895
  --raw: server response verbatim (curl parity).
1877
1896
 
1878
- skill install --target <qoder|codex|claude|openclaw|hermes> [--json]
1897
+ skill install --target <qoder|qoderwork|codex|claude|openclaw|hermes> [--json]
1879
1898
  skill install --path <dir> [--json]
1880
1899
  Install skill files (contextdb-memory,
1881
1900
  contextdb-knowledge) to an agent's skill directory.
@@ -1888,7 +1907,7 @@ AGENT RESOLUTION (when --agent is omitted):
1888
1907
  ENV VARS (override selected ~/.ctxdb/ctxdb.json agent config):
1889
1908
  CTXDB_AGENT CTXDB_API_KEY CTXDB_BASE_URL CTXDB_USER_ID
1890
1909
 
1891
- CONFIG: ~/.ctxdb/ctxdb.json (agents.default / agents.qoder / agents.codex / agents.claude)
1910
+ CONFIG: ~/.ctxdb/ctxdb.json (agents.default / agents.qoder / agents.qoderwork / agents.codex / agents.claude)
1892
1911
  LOGS: ~/.ctxdb/logs/ctxdb.log
1893
1912
  `;
1894
1913
  var ROUTES = {
@@ -1,10 +1,12 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
+ fetchKbCatalogBlock,
3
4
  recallTurn
4
- } from "../chunk-LIC44DR6.js";
5
+ } from "../chunk-73OY44GY.js";
6
+ import "../chunk-6S5RJYBC.js";
5
7
  import {
6
8
  isCircuitOpen
7
- } from "../chunk-Q2EEP4CE.js";
9
+ } from "../chunk-52IAVJRK.js";
8
10
  import {
9
11
  HttpClient,
10
12
  agentFromArgvWithFallback,
@@ -12,7 +14,10 @@ import {
12
14
  isComplete,
13
15
  load,
14
16
  setDebug
15
- } from "../chunk-S45GOYUU.js";
17
+ } from "../chunk-BWPFGTF7.js";
18
+
19
+ // src/hooks/session-start.ts
20
+ import { pathToFileURL } from "url";
16
21
 
17
22
  // src/lib/warmup-recall.ts
18
23
  import { execSync } from "child_process";
@@ -67,6 +72,41 @@ async function warmupRecall(cwd, cfg, client) {
67
72
 
68
73
  // src/hooks/session-start.ts
69
74
  var HOOK_TIMEOUT_MS = 5e3;
75
+ function timeout(ms) {
76
+ return new Promise((resolve) => setTimeout(() => resolve(null), ms));
77
+ }
78
+ async function composeSessionStart(cfg, agent, client, cwd, timeoutMs = HOOK_TIMEOUT_MS) {
79
+ const kbInjectHere = cfg.kbCatalogInjection === "session_start";
80
+ const warmupPromise = cfg.warmupRecall ? Promise.race([warmupRecall(cwd, cfg, client), timeout(timeoutMs).then(() => null)]) : Promise.resolve(null);
81
+ const kbPromise = kbInjectHere ? Promise.race([fetchKbCatalogBlock(client, agent).catch(() => ""), timeout(timeoutMs).then(() => "")]) : Promise.resolve("");
82
+ const [result, kbBlock] = await Promise.all([warmupPromise, kbPromise]);
83
+ const warmupTimedOut = cfg.warmupRecall && result === null;
84
+ const warmupCtx = result && result.ok ? result.additionalContext || "" : "";
85
+ if (!warmupCtx && result && !result.ok) {
86
+ debug("warmup", `no result: ${result.reason}`);
87
+ }
88
+ let ctx = warmupCtx;
89
+ if (kbBlock) ctx = ctx ? `${ctx}
90
+
91
+ ${kbBlock}` : kbBlock;
92
+ return {
93
+ ctx,
94
+ memoryCount: result?.ok ? result.memoryCount : 0,
95
+ kbChunkCount: result?.ok ? result.knowledgeChunkCount : 0,
96
+ kbCatalogLines: kbBlock ? kbBlock.split("\n").length : 0,
97
+ warmupTimedOut
98
+ };
99
+ }
100
+ function formatSessionStartStdout(agent, ctx) {
101
+ if (agent === "codex") return ctx + "\n";
102
+ const out = {
103
+ hookSpecificOutput: {
104
+ hookEventName: "SessionStart",
105
+ additionalContext: ctx
106
+ }
107
+ };
108
+ return JSON.stringify(out) + "\n";
109
+ }
70
110
  async function readStdinJson() {
71
111
  let raw = "";
72
112
  for await (const chunk of process.stdin) raw += chunk;
@@ -78,9 +118,6 @@ async function readStdinJson() {
78
118
  return {};
79
119
  }
80
120
  }
81
- function timeout(ms) {
82
- return new Promise((resolve) => setTimeout(() => resolve(null), ms));
83
- }
84
121
  async function main() {
85
122
  try {
86
123
  const event = await readStdinJson();
@@ -96,8 +133,9 @@ async function main() {
96
133
  const cfg = load({ agent });
97
134
  setDebug(cfg.debug);
98
135
  debug("warmup", "start", { cwd, userId: cfg.userId });
99
- if (!isComplete(cfg) || !cfg.warmupRecall) {
100
- debug("warmup", "skip (config incomplete or warmupRecall=false)");
136
+ const kbInjectHere = cfg.kbCatalogInjection === "session_start";
137
+ if (!isComplete(cfg) || !cfg.warmupRecall && !kbInjectHere) {
138
+ debug("warmup", "skip (config incomplete or both warmup+kb off)");
101
139
  return 0;
102
140
  }
103
141
  if (isCircuitOpen(agent, cfg.baseUrl)) {
@@ -109,34 +147,21 @@ async function main() {
109
147
  apiKey: cfg.apiKey,
110
148
  timeoutMs: HOOK_TIMEOUT_MS
111
149
  });
112
- const result = await Promise.race([
113
- warmupRecall(cwd, cfg, client),
114
- timeout(HOOK_TIMEOUT_MS).then(() => null)
115
- ]);
116
- if (!result) {
150
+ const composed = await composeSessionStart(cfg, agent, client, cwd);
151
+ if (composed.warmupTimedOut) {
117
152
  process.stderr.write("ctxdb warmup: timeout\n");
118
- return 0;
119
153
  }
120
- if (!result.ok || !result.additionalContext) {
121
- debug("warmup", `no result: ${result.reason}`);
122
- return 0;
123
- }
124
- debug("warmup", `ok, memories=${result.memoryCount} kb=${result.knowledgeChunkCount}`);
154
+ if (!composed.ctx) return 0;
155
+ const { memoryCount, kbChunkCount, kbCatalogLines, ctx } = composed;
156
+ debug(
157
+ "warmup",
158
+ `ok, memories=${memoryCount} kb=${kbChunkCount} kb_catalog=${kbCatalogLines}`
159
+ );
125
160
  process.stderr.write(
126
- `ctxdb warmup: ok (${result.memoryCount} memories, ${result.knowledgeChunkCount} kb chunks)
161
+ `ctxdb warmup: ok (${memoryCount} memories, ${kbChunkCount} kb chunks, kb_catalog=${kbCatalogLines} lines)
127
162
  `
128
163
  );
129
- if (agent === "codex") {
130
- process.stdout.write(result.additionalContext + "\n");
131
- } else {
132
- const out = {
133
- hookSpecificOutput: {
134
- hookEventName: "SessionStart",
135
- additionalContext: result.additionalContext
136
- }
137
- };
138
- process.stdout.write(JSON.stringify(out) + "\n");
139
- }
164
+ process.stdout.write(formatSessionStartStdout(agent, ctx));
140
165
  return 0;
141
166
  } catch (err) {
142
167
  process.stderr.write(`ctxdb warmup: unexpected error: ${err?.message ?? err}
@@ -144,4 +169,11 @@ async function main() {
144
169
  return 0;
145
170
  }
146
171
  }
147
- main().then((code) => process.exit(code));
172
+ if (import.meta.url === pathToFileURL(process.argv[1] ?? "").href) {
173
+ main().then((code) => process.exit(code));
174
+ }
175
+ export {
176
+ HOOK_TIMEOUT_MS,
177
+ composeSessionStart,
178
+ formatSessionStartStdout
179
+ };