@aliyunrds/ctxdb 0.0.9 → 1.0.0-beta.1

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/README.md CHANGED
@@ -5,10 +5,12 @@ Unified access layer for RDS ContextDatabase. One `ctxdb` CLI (memory + KB ops),
5
5
  | Agent | Hooks | Skill install path | Skill style |
6
6
  |---|---|---|---|
7
7
  | **qoder** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.qoder/skills/ctxdb/` | hooks-driven (auto capture/recall) |
8
+ | **qoderwork** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.qoderwork/skills/ctxdb/` | hooks-driven (auto capture/recall) |
8
9
  | **codex** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.codex/skills/ctxdb/` | hooks-driven (auto capture/recall) |
9
10
  | **claude** | ✅ UserPromptSubmit + Stop + SessionStart | `~/.claude/skills/ctxdb/` | hooks-driven (auto capture/recall) |
11
+ | **opencode** | Plugin shim (`~/.config/opencode/plugins/ctxdb.ts`) | `~/.config/opencode/skills/` | in-process plugin (auto capture/recall + KB catalog injection) |
10
12
 
11
- Config and logs live under `~/.ctxdb/`. `~/.ctxdb/ctxdb.json` is one file, but runtime config is isolated per agent under `agents.qoder`, `agents.codex`, and `agents.claude`. (Pre-2026-05-23 installs used `~/.ctxdb.json` at the home root; running `ctxdb setup --agent <name>` migrates the file into the selected agent section.)
13
+ Config and logs live under `~/.ctxdb/`. `~/.ctxdb/ctxdb.json` is one file, but runtime config is isolated per agent under `agents.qoder`, `agents.qoderwork`, `agents.codex`, `agents.claude`, and `agents.opencode`. (Pre-2026-05-23 installs used `~/.ctxdb.json` at the home root; running `ctxdb setup --agent <name>` migrates the file into the selected agent section.)
12
14
 
13
15
  ## Install
14
16
 
@@ -17,10 +19,14 @@ npm install -g @aliyunrds/ctxdb
17
19
 
18
20
  # Qoder (hooks + skill)
19
21
  ctxdb setup --agent qoder --api-key ctxdb-...
22
+ # QoderWork (hooks + skill)
23
+ ctxdb setup --agent qoderwork --api-key ctxdb-...
20
24
  # Codex (hooks + skill)
21
25
  ctxdb setup --agent codex --api-key ctxdb-...
22
26
  # Claude Code (hooks + skill)
23
27
  ctxdb setup --agent claude --api-key ctxdb-...
28
+ # OpenCode (plugin shim + skill)
29
+ ctxdb setup --agent opencode --api-key ctxdb-...
24
30
 
25
31
  # --base-url defaults to https://context-database.aliyuncs.com (public prod).
26
32
  # Pass --base-url <host> only if you target a different deployment
@@ -33,13 +39,13 @@ ctxdb setup --agent claude --api-key ctxdb-...
33
39
  ctxdb status --json
34
40
  ```
35
41
 
36
- Setup first checks that the target agent home exists (`~/.qoder`, `~/.codex`, or `~/.claude`). If the directory is missing, install or start that agent once before running `ctxdb setup --agent <name>`.
42
+ Setup first checks that the target agent home exists (`~/.qoder`, `~/.qoderwork`, `~/.codex`, or `~/.claude`). If the directory is missing, install or start that agent once before running `ctxdb setup --agent <name>`. OpenCode is XDG-style: setup creates `~/.config/opencode` and `~/.config/opencode/plugins` when they are missing.
37
43
 
38
- For **qoder**, **codex**, and **claude**, restart the harness (CLI: just exit + restart; app: Cmd+R or quit/relaunch) so it picks up the new hooks/skill. For **codex**, setup also writes `[features].hooks = true` into `~/.codex/config.toml` (creating the file if missing) — Codex won't fire any hook entries without it. On first use Codex may prompt you to trust the new hook commands.
44
+ For **qoder**, **qoderwork**, **codex**, and **claude**, restart the harness (CLI: just exit + restart; app: Cmd+R or quit/relaunch) so it picks up the new hooks/skill. For **codex**, setup also writes `[features].hooks = true` into `~/.codex/config.toml` (creating the file if missing) — Codex won't fire any hook entries without it. On first use Codex may prompt you to trust the new hook commands. For **opencode**, restart OpenCode so it loads `~/.config/opencode/plugins/ctxdb.ts`.
39
45
 
40
46
  ## What it does
41
47
 
42
- For **qoder**, **codex**, and **claude**, hooks fire automatically:
48
+ For **qoder**, **qoderwork**, **codex**, and **claude**, hooks fire automatically:
43
49
 
44
50
  | Trigger | Hook | What runs |
45
51
  |---|---|---|
@@ -49,6 +55,8 @@ For **qoder**, **codex**, and **claude**, hooks fire automatically:
49
55
 
50
56
  Implementation note: qoder/Claude consume the JSON `hookSpecificOutput.additionalContext` shape; Codex has been verified on this machine through `~/.codex/hooks.json`, `[features].hooks = true`, and hook audit logs, where non-empty stdout from `UserPromptSubmit`/`SessionStart` is treated as injected context. The same hook core is shared; setup passes `--agent=<name>` so each hook reads its own config section.
51
57
 
58
+ For **opencode**, `ctxdb setup --agent opencode` writes a small re-export shim at `~/.config/opencode/plugins/ctxdb.ts`. The shim points into this package's bundled `dist/opencode/index.js`, so `npm update -g @aliyunrds/ctxdb` updates the plugin implementation without copying source into the user config directory. `ctxdb upgrade --agent opencode` refreshes the absolute shim target after package moves.
59
+
52
60
  ## CLI
53
61
 
54
62
  `ctxdb` ships 18 subcommands:
@@ -59,7 +67,7 @@ Implementation note: qoder/Claude consume the JSON `hookSpecificOutput.additiona
59
67
 
60
68
  See `ctxdb --help`.
61
69
 
62
- `setup` without `--agent` writes `agents.default` in `~/.ctxdb/ctxdb.json` (CLI-only, no hooks or skills); with `--agent <qoder|codex|claude>` it writes agent config and installs hooks + skills for that harness. Memory and KB commands also accept `--agent <name>` so agents use their own config section; when omitted, `CTXDB_AGENT` env wins, otherwise `agents.default` is used. `uninstall` without `--agent` loops every supported agent; with `--agent <name>` it targets just that one. `teardown` is an alias for `uninstall`.
70
+ `setup` without `--agent` writes `agents.default` in `~/.ctxdb/ctxdb.json` (CLI-only, no hooks or skills); with `--agent <qoder|qoderwork|codex|claude|opencode>` it writes agent config and installs hooks/plugins + skills for that harness. Memory and KB commands also accept `--agent <name>` so agents use their own config section; when omitted, `CTXDB_AGENT` env wins, otherwise `agents.default` is used. `uninstall` without `--agent` loops every supported agent; with `--agent <name>` it targets just that one. `teardown` is an alias for `uninstall`.
63
71
 
64
72
  `memory add … --no-infer` stores the text verbatim (skips server-side LLM
65
73
  fact-extraction). Use it when the user explicitly asks for a verbatim
@@ -87,20 +95,22 @@ Lives at `~/.ctxdb/ctxdb.json` (co-located with logs at `~/.ctxdb/logs/`). Schem
87
95
  "user_id": "default",
88
96
  "auto_capture": true,
89
97
  "auto_recall": true,
90
- "warmup_recall": true,
98
+ "warmup_recall": false,
91
99
  "recall_knowledge": false,
92
100
  "top_k": 5,
93
101
  "threshold": 0.4,
94
102
  "knowledge_top_k": 6,
95
103
  "debug": false
96
104
  },
105
+ "qoderwork": { "...": "same schema, independent values" },
97
106
  "codex": { "...": "same schema, independent values" },
98
- "claude": { "...": "same schema, independent values" }
107
+ "claude": { "...": "same schema, independent values" },
108
+ "opencode": { "...": "same schema, independent values" }
99
109
  }
100
110
  }
101
111
  ```
102
112
 
103
- Agent selection is driven by `--agent <qoder|codex|claude|default>` on every CLI invocation, falling back to `CTXDB_AGENT` env, then to `default`. There is no `default_agent` field — older installs that have one written get it dropped on the next save.
113
+ Agent selection is driven by `--agent <qoder|qoderwork|codex|claude|opencode|default>` on every CLI invocation, falling back to `CTXDB_AGENT` env, then to `default`. There is no `default_agent` field — older installs that have one written get it dropped on the next save.
104
114
 
105
115
  Field reference:
106
116
 
@@ -109,7 +119,7 @@ Field reference:
109
119
  | `user_id` | str | `"default"` | mem0-layer bucket key on every capture/recall call. The server's tenant isolation runs on `member_id` (injected from `X-API-Key`), so this is an optional per-user/agent slice. Override with `ctxdb setup --agent <a> --user-id <bucket>` or `CTXDB_USER_ID=<bucket>` when you need to isolate this install from another access layer or machine sharing the same workspace |
110
120
  | `auto_capture` | bool | `true` | Stop hook captures each turn into long-term memory for the selected agent |
111
121
  | `auto_recall` | bool | `true` | UserPromptSubmit hook recalls memory on each user prompt for the selected agent |
112
- | `warmup_recall` | bool | `true` | SessionStart hook recalls cwd/git-related memories for the selected agent |
122
+ | `warmup_recall` | bool | `false` | SessionStart hook recalls cwd/git-related memories for the selected agent. **Default is off** — open it per-agent when you want session-start warmup (adds one bounded, circuit-protected recall call on session start) |
113
123
  | `recall_knowledge` | bool | `false` | When `auto_recall` is on, also pull KB chunks alongside memory. Default is **off** — the recommended KB-recall path is the agent calling `kb search --agent <name>` explicitly when the user asks |
114
124
  | `top_k` / `threshold` | int / float | `5` / `0.4` | Memory recall pagination + similarity floor. `5` keeps the codex TUI `hook context: ...` line short enough to scan and bounds token spend on the modal recall path; raise it per-agent if you want more candidates |
115
125
  | `knowledge_top_k` | int | `6` | KB chunks pulled per recall (only effective when `recall_knowledge: true`) |
@@ -121,6 +131,8 @@ Env-var overrides apply to the selected agent config (env wins): `CTXDB_AGENT` /
121
131
 
122
132
  ```
123
133
  @aliyunrds/ctxdb ← this package (CLI + setup + hooks + skills)
134
+
135
+ ├─ bundles OpenCode plugin in dist/opencode/index.js
124
136
 
125
137
  └─ depends on @aliyunrds/ctxdb-shared ← input sanitation,
126
138
  prompt-injection defenses,
@@ -155,8 +167,10 @@ After step 1, `ctxdb uninstall` prints the exact `npm uninstall` command to run
155
167
  # itself is unlinked so a future `ctxdb setup --agent <X>` runs through the
156
168
  # first-time install path cleanly.
157
169
  ctxdb uninstall --agent qoder
170
+ ctxdb uninstall --agent qoderwork
158
171
  ctxdb uninstall --agent codex
159
172
  ctxdb uninstall --agent claude
173
+ ctxdb uninstall --agent opencode
160
174
 
161
175
  # Wholesale across all agents, but keep config + logs for re-setup later:
162
176
  ctxdb uninstall # all agents, keeps ~/.ctxdb/{ctxdb.json,logs/}
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import {
3
3
  configDir
4
- } from "./chunk-TOXXGL5L.js";
4
+ } from "./chunk-QHYJ7OXC.js";
5
5
 
6
6
  // src/lib/circuit.ts
7
7
  import { statSync, writeFileSync, unlinkSync, mkdirSync, readdirSync, readFileSync } from "fs";
@@ -262,7 +262,7 @@ function extractErrorDetail(text, fallback) {
262
262
  // src/lib/agents.ts
263
263
  import { homedir as homedir2 } from "os";
264
264
  import { join as join3 } from "path";
265
- var SUPPORTED_AGENTS = ["qoder", "qoderwork", "codex", "claude"];
265
+ var SUPPORTED_AGENTS = ["qoder", "qoderwork", "codex", "claude", "opencode"];
266
266
  function isBuiltinAgent(v) {
267
267
  return typeof v === "string" && SUPPORTED_AGENTS.includes(v);
268
268
  }
@@ -280,9 +280,12 @@ function agentHomeDir(agent) {
280
280
  return join3(homedir2(), ".codex");
281
281
  case "claude":
282
282
  return join3(homedir2(), ".claude");
283
+ case "opencode":
284
+ return join3(homedir2(), ".config", "opencode");
283
285
  }
284
286
  }
285
287
  var AGENT_VARIANT_HOMES = {
288
+ qoder: [".qoder-cn"],
286
289
  qoderwork: [".qoderworkcn"]
287
290
  };
288
291
  function agentHomeDirs(agent) {
@@ -6,10 +6,10 @@ import {
6
6
  isConnectionError,
7
7
  resetCircuit,
8
8
  tripCircuit
9
- } from "./chunk-PZJZ5O62.js";
9
+ } from "./chunk-PLWEIEFH.js";
10
10
  import {
11
11
  CtxdbError
12
- } from "./chunk-TOXXGL5L.js";
12
+ } from "./chunk-QHYJ7OXC.js";
13
13
 
14
14
  // src/lib/recall-orchestrator.ts
15
15
  import {
@@ -165,7 +165,7 @@ async function fetchKbCatalogBlock(client, agent) {
165
165
  });
166
166
  return [
167
167
  "<available-knowledge-bases>",
168
- `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:`,
168
+ `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. For keyword-based KB search, keep each single query focused: use at most 5 keywords or short phrases; run multiple targeted searches if more are needed. Knowledge bases:`,
169
169
  ...lines,
170
170
  "</available-knowledge-bases>"
171
171
  ].join("\n");
package/dist/cli/main.js CHANGED
@@ -31,7 +31,7 @@ import {
31
31
  removeAgent,
32
32
  save,
33
33
  writeInstalledPkgVersion
34
- } from "../chunk-TOXXGL5L.js";
34
+ } from "../chunk-QHYJ7OXC.js";
35
35
 
36
36
  // src/cli/util.ts
37
37
  var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
@@ -115,6 +115,9 @@ var BOLD = "\x1B[1m";
115
115
  var RESET = "\x1B[0m";
116
116
  var CHECK = `${GREEN}\u2714${RESET}`;
117
117
  var CROSS = `${RED}\u2718${RESET}`;
118
+ function dim(text) {
119
+ return `${DIM}${text}${RESET}`;
120
+ }
118
121
  function isSetupResult(v) {
119
122
  if (typeof v !== "object" || v === null) return false;
120
123
  return "steps" in v && Array.isArray(v.steps);
@@ -249,11 +252,14 @@ function skillInstallRoot(agent) {
249
252
  return join(homedir(), ".codex", "skills");
250
253
  case "claude":
251
254
  return join(homedir(), ".claude", "skills");
255
+ case "opencode":
256
+ return join(homedir(), ".config", "opencode", "skills");
252
257
  }
253
258
  }
254
259
  var SKILL_DIRS = ["contextdb-memory", "contextdb-knowledge"];
255
260
  var LEGACY_SKILL_DIRS = ["ctxdb"];
256
261
  function agentSupportsHooks(agent) {
262
+ if (agent === "opencode") return false;
257
263
  return isBuiltinAgent(agent);
258
264
  }
259
265
  function hookConfigPath(agent) {
@@ -266,6 +272,8 @@ function hookConfigPath(agent) {
266
272
  return join(homedir(), ".codex", "hooks.json");
267
273
  case "claude":
268
274
  return join(homedir(), ".claude", "settings.json");
275
+ case "opencode":
276
+ return null;
269
277
  }
270
278
  }
271
279
  var LEGACY_QODER_SKILL_DIRS = [
@@ -274,6 +282,24 @@ var LEGACY_QODER_SKILL_DIRS = [
274
282
  "qoder-ctxdb"
275
283
  ];
276
284
  var HOOK_EVENTS = ["UserPromptSubmit", "Stop", "SessionStart"];
285
+ var MIN_NODE_MAJOR = 20;
286
+ function parseNodeMajor(version) {
287
+ const match = /^v?(\d+)/.exec(version.trim());
288
+ if (!match) return null;
289
+ const major = Number.parseInt(match[1], 10);
290
+ return Number.isFinite(major) ? major : null;
291
+ }
292
+ function checkCurrentNodeVersion() {
293
+ const version = process.versions.node;
294
+ const major = parseNodeMajor(version);
295
+ if (major !== null && major >= MIN_NODE_MAJOR) return null;
296
+ const current = version.startsWith("v") ? version : `v${version}`;
297
+ return {
298
+ step: "check-node-version",
299
+ ok: false,
300
+ detail: `Node.js >=${MIN_NODE_MAJOR} is required by ctxdb; current ${current} at ${process.execPath}. Switch to Node 20+ (for example: \`nvm install 20 && nvm use 20\`), reinstall @aliyunrds/ctxdb, then rerun \`ctxdb setup\`.`
301
+ };
302
+ }
277
303
  async function runSetup(options) {
278
304
  const steps = [];
279
305
  if (!isAgentSlug(options.agent)) {
@@ -287,6 +313,11 @@ async function runSetup(options) {
287
313
  const agent = options.agent;
288
314
  const installSkill = options.installSkill !== false;
289
315
  const validate = options.validate !== false;
316
+ const nodeVersionCheck = checkCurrentNodeVersion();
317
+ if (nodeVersionCheck) {
318
+ steps.push(nodeVersionCheck);
319
+ return { ok: false, steps };
320
+ }
290
321
  if (isBuiltinAgent(agent)) {
291
322
  const homeCheck = checkAgentHome(agent);
292
323
  steps.push(homeCheck);
@@ -407,6 +438,32 @@ async function runSetup(options) {
407
438
  }
408
439
  }
409
440
  }
441
+ if (agent === "opencode") {
442
+ const absEntry = resolveOpencodePluginEntry();
443
+ if (!absEntry) {
444
+ steps.push({
445
+ step: "install-opencode-shim",
446
+ ok: false,
447
+ detail: "could not resolve plugin entry under @aliyunrds/ctxdb/dist/opencode"
448
+ });
449
+ return { ok: false, steps };
450
+ }
451
+ try {
452
+ writeOpencodeShim(absEntry);
453
+ steps.push({
454
+ step: "install-opencode-shim",
455
+ ok: true,
456
+ detail: `${opencodeShimPath()} \u2192 ${absEntry}`
457
+ });
458
+ } catch (err) {
459
+ steps.push({
460
+ step: "install-opencode-shim",
461
+ ok: false,
462
+ detail: err?.message ?? String(err)
463
+ });
464
+ return { ok: false, steps };
465
+ }
466
+ }
410
467
  if (validate) {
411
468
  if (!cfg.apiKey || !cfg.baseUrl) {
412
469
  steps.push({
@@ -429,8 +486,14 @@ async function runSetup(options) {
429
486
  }
430
487
  }
431
488
  const ok = steps.every((s) => s.ok);
432
- const hints = ok ? [UNINSTALL_ORDER_HINT] : void 0;
433
- return { ok, steps, hints };
489
+ const hints = ok ? [UNINSTALL_ORDER_HINT] : [];
490
+ if (ok && agent === "opencode") {
491
+ hints.unshift(
492
+ `OpenCode plugin shim: ${opencodeShimPath()}`,
493
+ "Restart OpenCode to load the plugin. `ctxdb upgrade` auto-refreshes the shim after npm updates; only re-run setup if you move ctxdb's install dir."
494
+ );
495
+ }
496
+ return { ok, steps, hints: hints.length > 0 ? hints : void 0 };
434
497
  }
435
498
  function runRemove(agent, options = {}) {
436
499
  const steps = [];
@@ -537,6 +600,32 @@ function runRemove(agent, options = {}) {
537
600
  }
538
601
  }
539
602
  }
603
+ if (agent === "opencode") {
604
+ const removed = removeOpencodeShim();
605
+ steps.push({
606
+ step: "remove-opencode-shim",
607
+ ok: true,
608
+ detail: removed ? opencodeShimPath() : "nothing to remove"
609
+ });
610
+ const root = skillInstallRoot(agent);
611
+ const dirs = [
612
+ ...SKILL_DIRS.map((d) => join(root, d)),
613
+ ...LEGACY_SKILL_DIRS.map((d) => join(root, d))
614
+ ];
615
+ for (const p of dirs) {
616
+ if (!existsSync(p)) continue;
617
+ try {
618
+ rmSync(p, { recursive: true, force: true });
619
+ steps.push({ step: "remove-skill-dir", ok: true, detail: p });
620
+ } catch (err) {
621
+ steps.push({
622
+ step: "remove-skill-dir",
623
+ ok: false,
624
+ detail: `${p}: ${err?.message ?? String(err)}`
625
+ });
626
+ }
627
+ }
628
+ }
540
629
  try {
541
630
  const r = removeAgent(agent, void 0, {
542
631
  keepEmptyShell: !!options.keepConfigShell
@@ -570,6 +659,19 @@ function runRemove(agent, options = {}) {
570
659
  return { ok: steps.every((s) => s.ok), steps };
571
660
  }
572
661
  function checkAgentHome(agent) {
662
+ if (agent === "opencode") {
663
+ const primary = agentHomeDir(agent);
664
+ try {
665
+ mkdirSync(primary, { recursive: true });
666
+ return { step: "check-agent-home", ok: true, detail: primary };
667
+ } catch (err) {
668
+ return {
669
+ step: "check-agent-home",
670
+ ok: false,
671
+ detail: `${primary}: ${err?.message ?? String(err)}`
672
+ };
673
+ }
674
+ }
573
675
  const dirs = agentHomeDirs(agent).filter((d) => existsSync(d));
574
676
  if (dirs.length === 0) {
575
677
  const primary = agentHomeDir(agent);
@@ -679,6 +781,7 @@ function verifyNodeBinary(nodePath) {
679
781
  }
680
782
  }
681
783
  function checkHookNodePaths(agent) {
784
+ if (agent === "opencode") return checkOpencodeShimHealth();
682
785
  const empty = {
683
786
  installed: false,
684
787
  nodePaths: [],
@@ -712,7 +815,7 @@ function checkHookNodePaths(agent) {
712
815
  if (closeQuote > 1) nodePath = cmd.slice(1, closeQuote);
713
816
  } else {
714
817
  const parts = cmd.split(" ");
715
- if (parts.length >= 2 && !parts[0].endsWith(".js") && !parts[0].endsWith(".ts")) {
818
+ if (parts.length >= 2 && !parts[0].endsWith(".js") && !parts[0].endsWith(".ts") && (parts[0].includes("/") || parts[0].includes("\\"))) {
716
819
  nodePath = parts[0];
717
820
  }
718
821
  }
@@ -742,6 +845,51 @@ function checkHookNodePaths(agent) {
742
845
  }
743
846
  return { installed: true, nodePaths: paths, nodeOk: true, detail: `${paths[0]} (${goodVersion})` };
744
847
  }
848
+ function checkOpencodeShimHealth() {
849
+ const path = opencodeShimPath();
850
+ if (!existsSync(path)) {
851
+ return {
852
+ installed: false,
853
+ nodePaths: [],
854
+ nodeOk: false,
855
+ detail: `opencode shim not installed \u2014 run \`ctxdb setup --agent opencode\``
856
+ };
857
+ }
858
+ let text = "";
859
+ try {
860
+ text = readFileSync(path, "utf-8");
861
+ } catch (err) {
862
+ return {
863
+ installed: true,
864
+ nodePaths: [],
865
+ nodeOk: false,
866
+ detail: `opencode shim unreadable: ${err?.message ?? String(err)}`
867
+ };
868
+ }
869
+ const target = text.match(/export\s+\{\s*default\s*\}\s+from\s+["']([^"']+)["'];/)?.[1];
870
+ if (!target) {
871
+ return {
872
+ installed: true,
873
+ nodePaths: [],
874
+ nodeOk: false,
875
+ detail: `opencode shim malformed: ${path} \u2014 re-run \`ctxdb setup --agent opencode\``
876
+ };
877
+ }
878
+ if (!existsSync(target)) {
879
+ return {
880
+ installed: true,
881
+ nodePaths: [target],
882
+ nodeOk: false,
883
+ detail: `opencode shim target missing: ${target} \u2014 re-run \`ctxdb setup --agent opencode\``
884
+ };
885
+ }
886
+ return {
887
+ installed: true,
888
+ nodePaths: [target],
889
+ nodeOk: true,
890
+ detail: `opencode shim: ${path} \u2192 ${target}`
891
+ };
892
+ }
745
893
  var SETTINGS_BACKUP_KEEP = 5;
746
894
  function backupSettingsFile(path) {
747
895
  if (!existsSync(path)) return;
@@ -899,10 +1047,12 @@ function copySkillDir(src, dest, agent) {
899
1047
  }
900
1048
  var SKILL_INSTALL_TARGETS = {
901
1049
  qoder: join(homedir(), ".qoder", "skills"),
1050
+ qodercn: join(homedir(), ".qoder-cn", "skills"),
902
1051
  qoderwork: join(homedir(), ".qoderwork", "skills"),
903
1052
  qoderworkcn: join(homedir(), ".qoderworkcn", "skills"),
904
1053
  codex: join(homedir(), ".codex", "skills"),
905
1054
  claude: join(homedir(), ".claude", "skills"),
1055
+ opencode: join(homedir(), ".config", "opencode", "skills"),
906
1056
  openclaw: join(homedir(), ".openclaw", "skills"),
907
1057
  hermes: join(homedir(), ".hermes", "skills")
908
1058
  };
@@ -958,6 +1108,39 @@ function appendHooks(agent, hookPaths) {
958
1108
  if (!path) return;
959
1109
  appendHooksToFile(path, agent, hookPaths);
960
1110
  }
1111
+ var OPENCODE_SHIM_FILENAME = "ctxdb.ts";
1112
+ function opencodeShimPath() {
1113
+ return join(homedir(), ".config", "opencode", "plugins", OPENCODE_SHIM_FILENAME);
1114
+ }
1115
+ function resolveOpencodePluginEntry() {
1116
+ const here = dirname(fileURLToPath(import.meta.url));
1117
+ const pkgRoot = walkUpToPackageJson(here, "@aliyunrds/ctxdb");
1118
+ if (!pkgRoot) return null;
1119
+ const candidates = [
1120
+ join(pkgRoot, "dist", "opencode", "index.js"),
1121
+ join(pkgRoot, "..", "opencode", "src", "index.ts")
1122
+ ];
1123
+ return candidates.find((candidate) => existsSync(candidate)) ?? null;
1124
+ }
1125
+ var OPENCODE_SHIM_HEADER = "// Generated by ctxdb setup --agent opencode. Do not edit by hand.\n// To refresh after moving ctxdb's install directory, run ctxdb upgrade --agent opencode or re-run setup.\n// To remove, run: ctxdb setup --remove --agent opencode\n";
1126
+ function writeOpencodeShim(absEntry) {
1127
+ const path = opencodeShimPath();
1128
+ mkdirSync(dirname(path), { recursive: true });
1129
+ const importSpec = absEntry.replace(/\\/g, "/");
1130
+ const body = `${OPENCODE_SHIM_HEADER}export { default } from ${JSON.stringify(importSpec)};
1131
+ `;
1132
+ writeFileSync(path, body, "utf-8");
1133
+ }
1134
+ function removeOpencodeShim() {
1135
+ const path = opencodeShimPath();
1136
+ if (!existsSync(path)) return false;
1137
+ try {
1138
+ unlinkSync(path);
1139
+ return true;
1140
+ } catch {
1141
+ return false;
1142
+ }
1143
+ }
961
1144
  var ENTRY_MARKER_KEY = "_ctxdb";
962
1145
  var ENTRY_MARKER_VALUE = "@aliyunrds/ctxdb";
963
1146
  var ENTRY_AGENT_KEY = "_ctxdbAgent";
@@ -967,7 +1150,8 @@ var TOOL_SCOPED_EVENTS = /* @__PURE__ */ new Set(["PreToolUse", "PostToolUse"]);
967
1150
  function appendOne(hooks, event, command, agent) {
968
1151
  if (!Array.isArray(hooks[event])) hooks[event] = [];
969
1152
  const useExecForm = agent === "claude";
970
- const shellCmd = process.platform === "win32" ? `"${process.execPath.replace(/\\/g, "/")}" "${command.replace(/\\/g, "/")}" --agent=${agent}` : `${process.execPath} ${command} --agent=${agent}`;
1153
+ const cmdRunner = agent === "qoder";
1154
+ const shellCmd = process.platform === "win32" ? cmdRunner ? `node ${command.replace(/\\/g, "/")} --agent=${agent}` : `"${process.execPath.replace(/\\/g, "/")}" "${command.replace(/\\/g, "/")}" --agent=${agent}` : `${process.execPath} ${command} --agent=${agent}`;
971
1155
  const dup = hooks[event].some(
972
1156
  (entry2) => Array.isArray(entry2?.hooks) && entry2.hooks.some(
973
1157
  (h) => h?.type === "command" && (Array.isArray(h.args) && h.args[0] === command || typeof h.command === "string" && h.command.includes(command))
@@ -1300,6 +1484,28 @@ function upgradeOneAgent(agent) {
1300
1484
  }
1301
1485
  }
1302
1486
  }
1487
+ if (agent === "opencode") {
1488
+ const absEntry = resolveOpencodePluginEntry();
1489
+ if (!absEntry) {
1490
+ steps.push({
1491
+ step: "refresh-opencode-shim",
1492
+ ok: false,
1493
+ detail: "could not resolve plugin entry under @aliyunrds/ctxdb/dist/opencode"
1494
+ });
1495
+ return { ok: false, steps };
1496
+ }
1497
+ try {
1498
+ writeOpencodeShim(absEntry);
1499
+ steps.push({
1500
+ step: "refresh-opencode-shim",
1501
+ ok: true,
1502
+ detail: `${opencodeShimPath()} \u2192 ${absEntry}`
1503
+ });
1504
+ } catch (err) {
1505
+ steps.push({ step: "refresh-opencode-shim", ok: false, detail: err?.message ?? String(err) });
1506
+ return { ok: false, steps };
1507
+ }
1508
+ }
1303
1509
  return { ok: steps.every((s) => s.ok), steps };
1304
1510
  }
1305
1511
 
@@ -1360,7 +1566,7 @@ async function ping(args) {
1360
1566
  var HELP = `ctxdb setup \u2014 configure an agent
1361
1567
 
1362
1568
  USAGE
1363
- ctxdb setup [--agent <qoder|qoderwork|codex|claude>]
1569
+ ctxdb setup [--agent <qoder|qoderwork|codex|claude|opencode>]
1364
1570
  [--api-key=K] [--base-url=URL] [--user-id=ID]
1365
1571
  [--no-install-skill] [--no-validate] [--json]
1366
1572
 
@@ -1371,7 +1577,7 @@ BEHAVIOR
1371
1577
  specified agent harness.
1372
1578
 
1373
1579
  FLAGS
1374
- --agent <name> Target agent (qoder|qoderwork|codex|claude)
1580
+ --agent <name> Target agent (qoder|qoderwork|codex|claude|opencode)
1375
1581
  --api-key <key> API key (required on first setup)
1376
1582
  --base-url <url> Server URL (default: https://context-database.aliyuncs.com)
1377
1583
  --user-id <id> User bucket (default: "default")
@@ -1398,7 +1604,7 @@ async function setup(args) {
1398
1604
  const json = !!args.flags.json;
1399
1605
  if (agent === "default" && !json && !configuredAgents().includes("default")) {
1400
1606
  process.stderr.write(
1401
- `hint: creating CLI-only "default" agent (no hooks/skills). Use --agent <claude|qoder|qoderwork|codex> for full setup.
1607
+ `hint: creating CLI-only "default" agent (no hooks/skills). Use --agent <claude|qoder|qoderwork|codex|opencode> for full setup.
1402
1608
  `
1403
1609
  );
1404
1610
  }
@@ -1416,6 +1622,13 @@ async function setup(args) {
1416
1622
  ${h}
1417
1623
  `);
1418
1624
  }
1625
+ if (!json && agent === "codex" && result.ok) {
1626
+ process.stderr.write(
1627
+ `
1628
+ ${dim("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")}
1629
+ `
1630
+ );
1631
+ }
1419
1632
  return result.ok ? 0 : 1;
1420
1633
  }
1421
1634
 
@@ -1423,11 +1636,11 @@ ${h}
1423
1636
  var HELP2 = `ctxdb uninstall \u2014 remove hooks + skills
1424
1637
 
1425
1638
  USAGE
1426
- ctxdb uninstall [--agent <qoder|qoderwork|codex|claude>] [--purge-config] [--purge-logs] [--purge-all] [--json]
1639
+ ctxdb uninstall [--agent <qoder|qoderwork|codex|claude|opencode>] [--purge-config] [--purge-logs] [--purge-all] [--json]
1427
1640
 
1428
1641
  DEFAULT BEHAVIOR (no --agent, no purge flags)
1429
1642
  Strips ctxdb hook entries and skill directories for ALL agents (qoder,
1430
- qoderwork, codex, claude). Keeps ~/.ctxdb/ctxdb.json (credentials) and ~/.ctxdb/logs/
1643
+ qoderwork, codex, claude, opencode). Keeps ~/.ctxdb/ctxdb.json (credentials) and ~/.ctxdb/logs/
1431
1644
  so you can re-run \`ctxdb setup\` later without losing configuration.
1432
1645
 
1433
1646
  WITH --agent <name>
@@ -1435,7 +1648,7 @@ WITH --agent <name>
1435
1648
  Other agents remain untouched.
1436
1649
 
1437
1650
  FLAGS
1438
- --agent <name> Target a single agent (qoder|qoderwork|codex|claude)
1651
+ --agent <name> Target a single agent (qoder|qoderwork|codex|claude|opencode)
1439
1652
  --purge-config Also delete ~/.ctxdb/ctxdb.json
1440
1653
  --purge-logs Also delete ~/.ctxdb/logs/
1441
1654
  --purge-all Delete everything under ~/.ctxdb/
@@ -1593,7 +1806,7 @@ function runSelfUpdate(currentVersion, passthroughArgs = []) {
1593
1806
  var HELP3 = `ctxdb upgrade \u2014 refresh skills + hooks
1594
1807
 
1595
1808
  USAGE
1596
- ctxdb upgrade [--agent <qoder|qoderwork|codex|claude>] [--self-update] [--json]
1809
+ ctxdb upgrade [--agent <qoder|qoderwork|codex|claude|opencode>] [--self-update] [--json]
1597
1810
 
1598
1811
  BEHAVIOR
1599
1812
  Refreshes skill files and hook entries for configured agents without
@@ -1603,7 +1816,7 @@ BEHAVIOR
1603
1816
  With --agent: upgrades only the specified agent.
1604
1817
 
1605
1818
  FLAGS
1606
- --agent <name> Target a single agent (qoder|qoderwork|codex|claude)
1819
+ --agent <name> Target a single agent (qoder|qoderwork|codex|claude|opencode)
1607
1820
  --self-update Also pull latest package from npm before upgrading
1608
1821
  --json Machine-readable JSON output
1609
1822
  `;
@@ -1709,7 +1922,11 @@ function skillInstall(args) {
1709
1922
  }
1710
1923
  targetRoot = resolved;
1711
1924
  }
1712
- const templateAgent = target && isBuiltinAgent(target) ? target : "default";
1925
+ const TARGET_TEMPLATE_AGENT = {
1926
+ qodercn: "qoder",
1927
+ qoderworkcn: "qoderwork"
1928
+ };
1929
+ const templateAgent = TARGET_TEMPLATE_AGENT[target] ?? (target && isBuiltinAgent(target) ? target : "default");
1713
1930
  const result = installSkillsTo(targetRoot, templateAgent);
1714
1931
  const ok = result.ok;
1715
1932
  const output = {
@@ -1966,7 +2183,7 @@ USAGE
1966
2183
  ctxdb <command> [args] [--flags]
1967
2184
 
1968
2185
  COMMANDS
1969
- setup [--agent <qoder|qoderwork|codex|claude>]
2186
+ setup [--agent <qoder|qoderwork|codex|claude|opencode>]
1970
2187
  [--api-key=K] [--base-url=URL] [--user-id=ID]
1971
2188
  [--no-install-skill] [--no-validate] [--json]
1972
2189
  Without --agent \u2192 writes agents.default (CLI-only, no hooks/skills)
@@ -1974,6 +2191,7 @@ COMMANDS
1974
2191
  QoderWork \u2192 writes agents.qoderwork config + ~/.qoderwork/settings.json hooks + skill
1975
2192
  Codex \u2192 writes agents.codex config + ~/.codex/hooks.json hooks + skill
1976
2193
  Claude \u2192 writes agents.claude config + ~/.claude/settings.json hooks + skill
2194
+ OpenCode \u2192 writes agents.opencode config + ~/.config/opencode/plugins/ctxdb.ts shim + skill
1977
2195
  status [--agent <name>] [--json]
1978
2196
  ping [--agent <name>] [--json]
1979
2197
  uninstall [--agent <name>] [--purge-config] [--purge-logs] [--purge-all] [--json]
@@ -2016,7 +2234,7 @@ COMMANDS
2016
2234
  doc_id?/tags? for citation.
2017
2235
  --raw: server response verbatim (curl parity).
2018
2236
 
2019
- skill install --target <qoder|qoderwork|codex|claude|openclaw|hermes> [--json]
2237
+ skill install --target <qoder|qoderwork|codex|claude|opencode|openclaw|hermes> [--json]
2020
2238
  skill install --path <dir> [--json]
2021
2239
  Install skill files (contextdb-memory,
2022
2240
  contextdb-knowledge) to an agent's skill directory.
@@ -2029,7 +2247,7 @@ AGENT RESOLUTION (when --agent is omitted):
2029
2247
  ENV VARS (override selected ~/.ctxdb/ctxdb.json agent config):
2030
2248
  CTXDB_AGENT CTXDB_API_KEY CTXDB_BASE_URL CTXDB_USER_ID
2031
2249
 
2032
- CONFIG: ~/.ctxdb/ctxdb.json (agents.default / agents.qoder / agents.qoderwork / agents.codex / agents.claude)
2250
+ CONFIG: ~/.ctxdb/ctxdb.json (agents.default / agents.qoder / agents.qoderwork / agents.codex / agents.claude / agents.opencode)
2033
2251
  LOGS: ~/.ctxdb/logs/ctxdb.log
2034
2252
  `;
2035
2253
  var ROUTES = {
@@ -2,11 +2,11 @@
2
2
  import {
3
3
  fetchKbCatalogBlock,
4
4
  recallTurn
5
- } from "../chunk-RHWGDY6S.js";
5
+ } from "../chunk-QPFFMW52.js";
6
6
  import "../chunk-6S5RJYBC.js";
7
7
  import {
8
8
  isCircuitOpen
9
- } from "../chunk-PZJZ5O62.js";
9
+ } from "../chunk-PLWEIEFH.js";
10
10
  import {
11
11
  HttpClient,
12
12
  agentFromArgvWithFallback,
@@ -14,7 +14,7 @@ import {
14
14
  isComplete,
15
15
  load,
16
16
  setDebug
17
- } from "../chunk-TOXXGL5L.js";
17
+ } from "../chunk-QHYJ7OXC.js";
18
18
 
19
19
  // src/hooks/session-start.ts
20
20
  import { pathToFileURL } from "url";