@mxalbert/context-mode 2.0.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/.claude-plugin/marketplace.json +33 -0
- package/.claude-plugin/plugin.json +31 -0
- package/.codex-plugin/hooks.json +65 -0
- package/.codex-plugin/mcp.json +13 -0
- package/.codex-plugin/plugin.json +31 -0
- package/.openclaw-plugin/index.ts +12 -0
- package/.openclaw-plugin/openclaw.plugin.json +38 -0
- package/.openclaw-plugin/package.json +28 -0
- package/LICENSE +94 -0
- package/README.md +1619 -0
- package/bin/statusline.mjs +378 -0
- package/build/adapters/antigravity/index.d.ts +51 -0
- package/build/adapters/antigravity/index.js +169 -0
- package/build/adapters/antigravity-cli/index.d.ts +51 -0
- package/build/adapters/antigravity-cli/index.js +342 -0
- package/build/adapters/base.d.ts +92 -0
- package/build/adapters/base.js +135 -0
- package/build/adapters/claude-code/config.d.ts +8 -0
- package/build/adapters/claude-code/config.js +8 -0
- package/build/adapters/claude-code/hooks.d.ts +106 -0
- package/build/adapters/claude-code/hooks.js +183 -0
- package/build/adapters/claude-code/index.d.ts +80 -0
- package/build/adapters/claude-code/index.js +569 -0
- package/build/adapters/claude-code-base.d.ts +49 -0
- package/build/adapters/claude-code-base.js +113 -0
- package/build/adapters/client-map.d.ts +10 -0
- package/build/adapters/client-map.js +42 -0
- package/build/adapters/codex/config.d.ts +8 -0
- package/build/adapters/codex/config.js +8 -0
- package/build/adapters/codex/hooks.d.ts +55 -0
- package/build/adapters/codex/hooks.js +64 -0
- package/build/adapters/codex/index.d.ts +87 -0
- package/build/adapters/codex/index.js +995 -0
- package/build/adapters/codex/paths.d.ts +1 -0
- package/build/adapters/codex/paths.js +12 -0
- package/build/adapters/codex/usage.d.ts +107 -0
- package/build/adapters/codex/usage.js +227 -0
- package/build/adapters/copilot-base.d.ts +94 -0
- package/build/adapters/copilot-base.js +297 -0
- package/build/adapters/copilot-cli/hooks.d.ts +33 -0
- package/build/adapters/copilot-cli/hooks.js +64 -0
- package/build/adapters/copilot-cli/index.d.ts +48 -0
- package/build/adapters/copilot-cli/index.js +341 -0
- package/build/adapters/cursor/config.d.ts +4 -0
- package/build/adapters/cursor/config.js +4 -0
- package/build/adapters/cursor/hooks.d.ts +55 -0
- package/build/adapters/cursor/hooks.js +82 -0
- package/build/adapters/cursor/index.d.ts +64 -0
- package/build/adapters/cursor/index.js +517 -0
- package/build/adapters/detect.d.ts +136 -0
- package/build/adapters/detect.js +623 -0
- package/build/adapters/gemini-cli/config.d.ts +8 -0
- package/build/adapters/gemini-cli/config.js +8 -0
- package/build/adapters/gemini-cli/hooks.d.ts +83 -0
- package/build/adapters/gemini-cli/hooks.js +115 -0
- package/build/adapters/gemini-cli/index.d.ts +80 -0
- package/build/adapters/gemini-cli/index.js +483 -0
- package/build/adapters/jetbrains-copilot/config.d.ts +8 -0
- package/build/adapters/jetbrains-copilot/config.js +8 -0
- package/build/adapters/jetbrains-copilot/hooks.d.ts +59 -0
- package/build/adapters/jetbrains-copilot/hooks.js +87 -0
- package/build/adapters/jetbrains-copilot/index.d.ts +31 -0
- package/build/adapters/jetbrains-copilot/index.js +131 -0
- package/build/adapters/kimi/config.d.ts +8 -0
- package/build/adapters/kimi/config.js +8 -0
- package/build/adapters/kimi/hooks.d.ts +28 -0
- package/build/adapters/kimi/hooks.js +34 -0
- package/build/adapters/kimi/index.d.ts +66 -0
- package/build/adapters/kimi/index.js +537 -0
- package/build/adapters/kimi/paths.d.ts +21 -0
- package/build/adapters/kimi/paths.js +52 -0
- package/build/adapters/kimi/usage.d.ts +82 -0
- package/build/adapters/kimi/usage.js +217 -0
- package/build/adapters/kiro/hooks.d.ts +61 -0
- package/build/adapters/kiro/hooks.js +94 -0
- package/build/adapters/kiro/index.d.ts +61 -0
- package/build/adapters/kiro/index.js +301 -0
- package/build/adapters/omp/index.d.ts +65 -0
- package/build/adapters/omp/index.js +182 -0
- package/build/adapters/omp/plugin.d.ts +81 -0
- package/build/adapters/omp/plugin.js +331 -0
- package/build/adapters/omp/usage.d.ts +49 -0
- package/build/adapters/omp/usage.js +110 -0
- package/build/adapters/openclaw/config.d.ts +8 -0
- package/build/adapters/openclaw/config.js +8 -0
- package/build/adapters/openclaw/hooks.d.ts +50 -0
- package/build/adapters/openclaw/hooks.js +61 -0
- package/build/adapters/openclaw/index.d.ts +74 -0
- package/build/adapters/openclaw/index.js +439 -0
- package/build/adapters/openclaw/mcp-tools.d.ts +54 -0
- package/build/adapters/openclaw/mcp-tools.js +207 -0
- package/build/adapters/openclaw/plugin.d.ts +141 -0
- package/build/adapters/openclaw/plugin.js +818 -0
- package/build/adapters/openclaw/session-db.d.ts +55 -0
- package/build/adapters/openclaw/session-db.js +88 -0
- package/build/adapters/openclaw/usage.d.ts +34 -0
- package/build/adapters/openclaw/usage.js +52 -0
- package/build/adapters/openclaw/workspace-router.d.ts +29 -0
- package/build/adapters/openclaw/workspace-router.js +64 -0
- package/build/adapters/opencode/config.d.ts +8 -0
- package/build/adapters/opencode/config.js +8 -0
- package/build/adapters/opencode/hooks.d.ts +38 -0
- package/build/adapters/opencode/hooks.js +50 -0
- package/build/adapters/opencode/index.d.ts +144 -0
- package/build/adapters/opencode/index.js +638 -0
- package/build/adapters/opencode/plugin.d.ts +249 -0
- package/build/adapters/opencode/plugin.js +1536 -0
- package/build/adapters/opencode/v2.d.ts +117 -0
- package/build/adapters/opencode/v2.js +224 -0
- package/build/adapters/opencode/zod3tov4.d.ts +1 -0
- package/build/adapters/opencode/zod3tov4.js +132 -0
- package/build/adapters/pi/extension.d.ts +81 -0
- package/build/adapters/pi/extension.js +861 -0
- package/build/adapters/pi/index.d.ts +57 -0
- package/build/adapters/pi/index.js +173 -0
- package/build/adapters/pi/mcp-bridge.d.ts +268 -0
- package/build/adapters/pi/mcp-bridge.js +894 -0
- package/build/adapters/qwen-code/hooks.d.ts +26 -0
- package/build/adapters/qwen-code/hooks.js +29 -0
- package/build/adapters/qwen-code/index.d.ts +40 -0
- package/build/adapters/qwen-code/index.js +361 -0
- package/build/adapters/qwen-code/usage.d.ts +90 -0
- package/build/adapters/qwen-code/usage.js +222 -0
- package/build/adapters/types.d.ts +360 -0
- package/build/adapters/types.js +118 -0
- package/build/adapters/vscode-copilot/config.d.ts +8 -0
- package/build/adapters/vscode-copilot/config.js +8 -0
- package/build/adapters/vscode-copilot/hooks.d.ts +71 -0
- package/build/adapters/vscode-copilot/hooks.js +98 -0
- package/build/adapters/vscode-copilot/index.d.ts +32 -0
- package/build/adapters/vscode-copilot/index.js +222 -0
- package/build/adapters/zed/index.d.ts +40 -0
- package/build/adapters/zed/index.js +163 -0
- package/build/cli.d.ts +34 -0
- package/build/cli.js +1828 -0
- package/build/db-base.d.ts +250 -0
- package/build/db-base.js +756 -0
- package/build/executor.d.ts +72 -0
- package/build/executor.js +678 -0
- package/build/exit-classify.d.ts +19 -0
- package/build/exit-classify.js +12 -0
- package/build/fetch/blocks.d.ts +145 -0
- package/build/fetch/blocks.js +244 -0
- package/build/fetch/extract.d.ts +68 -0
- package/build/fetch/extract.js +123 -0
- package/build/fetch/page-store.d.ts +66 -0
- package/build/fetch/page-store.js +174 -0
- package/build/fetch-cache.d.ts +13 -0
- package/build/fetch-cache.js +15 -0
- package/build/lifecycle.d.ts +124 -0
- package/build/lifecycle.js +278 -0
- package/build/package-identity.d.ts +26 -0
- package/build/package-identity.js +28 -0
- package/build/runPool.d.ts +36 -0
- package/build/runPool.js +51 -0
- package/build/runtime.d.ts +71 -0
- package/build/runtime.js +659 -0
- package/build/search/auto-memory.d.ts +47 -0
- package/build/search/auto-memory.js +170 -0
- package/build/search/ctx-search-schema.d.ts +90 -0
- package/build/search/ctx-search-schema.js +135 -0
- package/build/search/flood-guard.d.ts +57 -0
- package/build/search/flood-guard.js +80 -0
- package/build/search/unified.d.ts +56 -0
- package/build/search/unified.js +104 -0
- package/build/security.d.ts +206 -0
- package/build/security.js +780 -0
- package/build/server.d.ts +240 -0
- package/build/server.js +5034 -0
- package/build/session/analytics.d.ts +773 -0
- package/build/session/analytics.js +2420 -0
- package/build/session/db.d.ts +468 -0
- package/build/session/db.js +1330 -0
- package/build/session/error-classifier.d.ts +87 -0
- package/build/session/error-classifier.js +303 -0
- package/build/session/event-emit.d.ts +48 -0
- package/build/session/event-emit.js +101 -0
- package/build/session/extract.d.ts +333 -0
- package/build/session/extract.js +2688 -0
- package/build/session/model-prices.json +429 -0
- package/build/session/persist-tool-calls.d.ts +54 -0
- package/build/session/persist-tool-calls.js +105 -0
- package/build/session/pricing.d.ts +64 -0
- package/build/session/pricing.js +151 -0
- package/build/session/project-attribution.d.ts +73 -0
- package/build/session/project-attribution.js +245 -0
- package/build/session/purge.d.ts +138 -0
- package/build/session/purge.js +240 -0
- package/build/session/retrieval-marker.d.ts +39 -0
- package/build/session/retrieval-marker.js +65 -0
- package/build/session/snapshot.d.ts +46 -0
- package/build/session/snapshot.js +532 -0
- package/build/store-directory.d.ts +56 -0
- package/build/store-directory.js +254 -0
- package/build/store.d.ts +147 -0
- package/build/store.js +1714 -0
- package/build/truncate.d.ts +57 -0
- package/build/truncate.js +142 -0
- package/build/types.d.ts +117 -0
- package/build/types.js +20 -0
- package/build/util/claude-config.d.ts +32 -0
- package/build/util/claude-config.js +84 -0
- package/build/util/hook-config.d.ts +27 -0
- package/build/util/hook-config.js +76 -0
- package/build/util/jsonc.d.ts +14 -0
- package/build/util/jsonc.js +104 -0
- package/build/util/plugin-cache-integrity.d.ts +51 -0
- package/build/util/plugin-cache-integrity.js +146 -0
- package/build/util/project-dir.d.ts +149 -0
- package/build/util/project-dir.js +340 -0
- package/build/util/sibling-mcp.d.ts +79 -0
- package/build/util/sibling-mcp.js +181 -0
- package/cli.bundle.mjs +1495 -0
- package/configs/antigravity/GEMINI.md +68 -0
- package/configs/antigravity/mcp_config.json +7 -0
- package/configs/antigravity-cli/hooks/hooks.json +37 -0
- package/configs/antigravity-cli/hooks.json +37 -0
- package/configs/antigravity-cli/mcp_config.json +10 -0
- package/configs/antigravity-cli/plugin.json +14 -0
- package/configs/antigravity-cli/rules/context-mode.md +77 -0
- package/configs/antigravity-cli/skills/context-mode/SKILL.md +77 -0
- package/configs/claude-code/CLAUDE.md +91 -0
- package/configs/codex/AGENTS.md +99 -0
- package/configs/codex/config.toml +8 -0
- package/configs/codex/hooks.json +47 -0
- package/configs/copilot-cli/.github/plugin/plugin.json +23 -0
- package/configs/copilot-cli/.mcp.json +12 -0
- package/configs/copilot-cli/README.md +47 -0
- package/configs/copilot-cli/hooks.json +41 -0
- package/configs/copilot-cli/skills/context-mode/SKILL.md +38 -0
- package/configs/cursor/context-mode.mdc +76 -0
- package/configs/cursor/hooks.json +21 -0
- package/configs/cursor/mcp.json +7 -0
- package/configs/gemini-cli/GEMINI.md +88 -0
- package/configs/gemini-cli/mcp.json +7 -0
- package/configs/gemini-cli/settings.json +60 -0
- package/configs/jetbrains-copilot/copilot-instructions.md +80 -0
- package/configs/jetbrains-copilot/hooks.json +16 -0
- package/configs/jetbrains-copilot/mcp.json +7 -0
- package/configs/kilo/AGENTS.md +84 -0
- package/configs/kilo/kilo.json +6 -0
- package/configs/kimi/hooks.json +54 -0
- package/configs/kiro/KIRO.md +84 -0
- package/configs/kiro/agent.json +18 -0
- package/configs/kiro/mcp.json +7 -0
- package/configs/omp/SYSTEM.md +85 -0
- package/configs/omp/mcp.json +7 -0
- package/configs/openclaw/AGENTS.md +87 -0
- package/configs/openclaw/openclaw.json +13 -0
- package/configs/opencode/AGENTS.md +84 -0
- package/configs/opencode/opencode.json +6 -0
- package/configs/pi/AGENTS.md +3 -0
- package/configs/qwen-code/QWEN.md +91 -0
- package/configs/vscode-copilot/copilot-instructions.md +80 -0
- package/configs/vscode-copilot/hooks.json +16 -0
- package/configs/vscode-copilot/mcp.json +7 -0
- package/configs/zed/AGENTS.md +68 -0
- package/hooks/antigravity-cli/payload.mjs +98 -0
- package/hooks/antigravity-cli/posttooluse.mjs +138 -0
- package/hooks/antigravity-cli/pretooluse.mjs +78 -0
- package/hooks/antigravity-cli/stop.mjs +58 -0
- package/hooks/auto-injection.mjs +102 -0
- package/hooks/cache-heal-utils.mjs +379 -0
- package/hooks/codex/platform.mjs +1 -0
- package/hooks/codex/posttooluse.mjs +66 -0
- package/hooks/codex/precompact.mjs +70 -0
- package/hooks/codex/pretooluse.mjs +42 -0
- package/hooks/codex/sessionstart.mjs +121 -0
- package/hooks/codex/stop.mjs +138 -0
- package/hooks/codex/userpromptsubmit.mjs +75 -0
- package/hooks/copilot-cli/posttooluse.mjs +79 -0
- package/hooks/copilot-cli/precompact.mjs +66 -0
- package/hooks/copilot-cli/pretooluse.mjs +41 -0
- package/hooks/copilot-cli/sessionstart.mjs +121 -0
- package/hooks/copilot-cli/stop.mjs +59 -0
- package/hooks/copilot-cli/userpromptsubmit.mjs +77 -0
- package/hooks/core/codex-caps.mjs +112 -0
- package/hooks/core/formatters.mjs +356 -0
- package/hooks/core/mcp-ready.mjs +109 -0
- package/hooks/core/platform-detect.mjs +49 -0
- package/hooks/core/routing.mjs +1050 -0
- package/hooks/core/stdin.mjs +90 -0
- package/hooks/core/tool-naming.mjs +57 -0
- package/hooks/cursor/afteragentresponse.mjs +74 -0
- package/hooks/cursor/hooks.json +31 -0
- package/hooks/cursor/posttooluse.mjs +72 -0
- package/hooks/cursor/pretooluse.mjs +26 -0
- package/hooks/cursor/sessionstart.mjs +97 -0
- package/hooks/cursor/stop.mjs +48 -0
- package/hooks/ensure-deps.mjs +254 -0
- package/hooks/formatters/claude-code.mjs +113 -0
- package/hooks/formatters/cursor.mjs +37 -0
- package/hooks/formatters/gemini-cli.mjs +55 -0
- package/hooks/formatters/vscode-copilot.mjs +55 -0
- package/hooks/gemini-cli/aftermodel.mjs +70 -0
- package/hooks/gemini-cli/aftertool.mjs +61 -0
- package/hooks/gemini-cli/beforeagent.mjs +99 -0
- package/hooks/gemini-cli/beforetool.mjs +27 -0
- package/hooks/gemini-cli/precompress.mjs +55 -0
- package/hooks/gemini-cli/sessionstart.mjs +137 -0
- package/hooks/heal-partial-install.mjs +712 -0
- package/hooks/hooks.json +143 -0
- package/hooks/jetbrains-copilot/posttooluse.mjs +61 -0
- package/hooks/jetbrains-copilot/precompact.mjs +55 -0
- package/hooks/jetbrains-copilot/pretooluse.mjs +27 -0
- package/hooks/jetbrains-copilot/sessionstart.mjs +124 -0
- package/hooks/kimi/platform.mjs +1 -0
- package/hooks/kimi/posttooluse.mjs +72 -0
- package/hooks/kimi/precompact.mjs +80 -0
- package/hooks/kimi/pretooluse.mjs +42 -0
- package/hooks/kimi/sessionend.mjs +61 -0
- package/hooks/kimi/sessionstart.mjs +113 -0
- package/hooks/kimi/stop.mjs +132 -0
- package/hooks/kimi/userpromptsubmit.mjs +90 -0
- package/hooks/kiro/agentspawn.mjs +97 -0
- package/hooks/kiro/posttooluse.mjs +51 -0
- package/hooks/kiro/pretooluse.mjs +64 -0
- package/hooks/kiro/userpromptsubmit.mjs +88 -0
- package/hooks/normalize-hooks.mjs +323 -0
- package/hooks/platform-bridge.mjs +340 -0
- package/hooks/posttooluse.mjs +225 -0
- package/hooks/precompact.mjs +100 -0
- package/hooks/pretooluse.mjs +227 -0
- package/hooks/qwen-code/platform.mjs +1 -0
- package/hooks/qwen-code/stop.mjs +168 -0
- package/hooks/routing-block.mjs +105 -0
- package/hooks/run-hook.mjs +95 -0
- package/hooks/security.bundle.mjs +2 -0
- package/hooks/session-attribution.bundle.mjs +1 -0
- package/hooks/session-db.bundle.mjs +145 -0
- package/hooks/session-directive.mjs +520 -0
- package/hooks/session-extract.bundle.mjs +3 -0
- package/hooks/session-helpers.mjs +428 -0
- package/hooks/session-loaders.mjs +448 -0
- package/hooks/session-snapshot.bundle.mjs +32 -0
- package/hooks/sessionstart.mjs +464 -0
- package/hooks/stop.mjs +82 -0
- package/hooks/suppress-stderr.mjs +25 -0
- package/hooks/userpromptsubmit.mjs +108 -0
- package/hooks/vscode-copilot/posttooluse.mjs +61 -0
- package/hooks/vscode-copilot/precompact.mjs +55 -0
- package/hooks/vscode-copilot/pretooluse.mjs +27 -0
- package/hooks/vscode-copilot/sessionstart.mjs +129 -0
- package/openclaw.plugin.json +38 -0
- package/package.json +132 -0
- package/scripts/heal-better-sqlite3.mjs +430 -0
- package/scripts/heal-installed-plugins.mjs +635 -0
- package/scripts/plugin-cache-integrity.mjs +248 -0
- package/scripts/postinstall.mjs +397 -0
- package/server.bundle.mjs +1421 -0
- package/server.js +5 -0
- package/skills/.ignore +7 -0
- package/skills/context-mode/SKILL.md +300 -0
- package/skills/context-mode/references/anti-patterns.md +283 -0
- package/skills/context-mode/references/patterns-javascript.md +298 -0
- package/skills/context-mode/references/patterns-python.md +304 -0
- package/skills/context-mode/references/patterns-shell.md +277 -0
- package/skills/ctx-doctor/SKILL.md +22 -0
- package/skills/ctx-index/SKILL.md +46 -0
- package/skills/ctx-insight/SKILL.md +24 -0
- package/skills/ctx-purge/SKILL.md +49 -0
- package/skills/ctx-search/SKILL.md +35 -0
- package/skills/ctx-stats/SKILL.md +26 -0
- package/skills/ctx-upgrade/SKILL.md +31 -0
- package/start.mjs +616 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/qwen-code/hooks — Qwen Code hook definitions.
|
|
3
|
+
*
|
|
4
|
+
* Qwen Code is a Gemini CLI fork (packages/core/src/tools/tool-names.ts —
|
|
5
|
+
* shares native names like `run_shell_command`, `read_file`). The hook wire
|
|
6
|
+
* protocol is JSON stdin / stdout, identical to Claude Code and Gemini CLI.
|
|
7
|
+
*
|
|
8
|
+
* Config: ~/.qwen/settings.json under "hooks" key.
|
|
9
|
+
*/
|
|
10
|
+
/**
|
|
11
|
+
* Negative-lookahead matcher for external MCP tool namespaces on Qwen Code (#529).
|
|
12
|
+
*
|
|
13
|
+
* Qwen Code MCP wire shape: `mcp__<server>__<tool>` (shared with Gemini CLI
|
|
14
|
+
* upstream). Own context-mode MCP surfaces as both
|
|
15
|
+
* `mcp__plugin_context-mode_context-mode__ctx_*` (Claude shim path when users
|
|
16
|
+
* install via the Claude marketplace) and `mcp__context-mode__ctx_*` (Qwen
|
|
17
|
+
* canonical — see hooks/core/tool-naming.mjs). The negative lookahead
|
|
18
|
+
* `(?!.*context-mode)` excludes both variants from the external-MCP routing
|
|
19
|
+
* branch so context-mode's own tools (already wired by the explicit entries
|
|
20
|
+
* above this catch-all) are not double-routed.
|
|
21
|
+
*
|
|
22
|
+
* Without this matcher, large payloads from slack / telegram / gdrive / notion
|
|
23
|
+
* MCPs bypass the routing nudge and flood the model's context window —
|
|
24
|
+
* PostToolUse runs too late to keep the raw data out.
|
|
25
|
+
*/
|
|
26
|
+
export declare const EXTERNAL_MCP_MATCHER_PATTERN = "mcp__(?!.*context-mode)";
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/qwen-code/hooks — Qwen Code hook definitions.
|
|
3
|
+
*
|
|
4
|
+
* Qwen Code is a Gemini CLI fork (packages/core/src/tools/tool-names.ts —
|
|
5
|
+
* shares native names like `run_shell_command`, `read_file`). The hook wire
|
|
6
|
+
* protocol is JSON stdin / stdout, identical to Claude Code and Gemini CLI.
|
|
7
|
+
*
|
|
8
|
+
* Config: ~/.qwen/settings.json under "hooks" key.
|
|
9
|
+
*/
|
|
10
|
+
// ─────────────────────────────────────────────────────────
|
|
11
|
+
// External MCP routing matcher (#529)
|
|
12
|
+
// ─────────────────────────────────────────────────────────
|
|
13
|
+
/**
|
|
14
|
+
* Negative-lookahead matcher for external MCP tool namespaces on Qwen Code (#529).
|
|
15
|
+
*
|
|
16
|
+
* Qwen Code MCP wire shape: `mcp__<server>__<tool>` (shared with Gemini CLI
|
|
17
|
+
* upstream). Own context-mode MCP surfaces as both
|
|
18
|
+
* `mcp__plugin_context-mode_context-mode__ctx_*` (Claude shim path when users
|
|
19
|
+
* install via the Claude marketplace) and `mcp__context-mode__ctx_*` (Qwen
|
|
20
|
+
* canonical — see hooks/core/tool-naming.mjs). The negative lookahead
|
|
21
|
+
* `(?!.*context-mode)` excludes both variants from the external-MCP routing
|
|
22
|
+
* branch so context-mode's own tools (already wired by the explicit entries
|
|
23
|
+
* above this catch-all) are not double-routed.
|
|
24
|
+
*
|
|
25
|
+
* Without this matcher, large payloads from slack / telegram / gdrive / notion
|
|
26
|
+
* MCPs bypass the routing nudge and flood the model's context window —
|
|
27
|
+
* PostToolUse runs too late to keep the raw data out.
|
|
28
|
+
*/
|
|
29
|
+
export const EXTERNAL_MCP_MATCHER_PATTERN = "mcp__(?!.*context-mode)";
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/qwen-code — Qwen Code platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* Extends ClaudeCodeBaseAdapter (shared wire-protocol parse/format methods)
|
|
5
|
+
* with Qwen Code-specific configuration, diagnostics, and session ID logic.
|
|
6
|
+
*
|
|
7
|
+
* Differences from Claude Code:
|
|
8
|
+
* - Config dir: ~/.qwen/ (not ~/.claude/)
|
|
9
|
+
* - Env vars: QWEN_PROJECT_DIR, QWEN_SESSION_ID (not CLAUDE_*)
|
|
10
|
+
* - Session ID priority: session_id field first (Claude: transcript_path first)
|
|
11
|
+
* - No plugin registry (Qwen uses settings.json directly)
|
|
12
|
+
* - MCP clientInfo: qwen-cli-mcp-client-* (pattern)
|
|
13
|
+
* - 12 hook events (superset of Claude's 5, but context-mode uses the shared 5)
|
|
14
|
+
*/
|
|
15
|
+
import { ClaudeCodeBaseAdapter, type ClaudeCodeWireInput } from "../claude-code-base.js";
|
|
16
|
+
import { type HookAdapter, type HookParadigm, type PlatformCapabilities, type DiagnosticResult, type HookRegistration } from "../types.js";
|
|
17
|
+
export declare class QwenCodeAdapter extends ClaudeCodeBaseAdapter implements HookAdapter {
|
|
18
|
+
constructor();
|
|
19
|
+
readonly name = "Qwen Code";
|
|
20
|
+
readonly paradigm: HookParadigm;
|
|
21
|
+
protected readonly projectDirEnvVar = "QWEN_PROJECT_DIR";
|
|
22
|
+
readonly capabilities: PlatformCapabilities;
|
|
23
|
+
getSettingsPath(): string;
|
|
24
|
+
getInstructionFiles(): string[];
|
|
25
|
+
generateHookConfig(pluginRoot: string): HookRegistration;
|
|
26
|
+
readSettings(): Record<string, unknown> | null;
|
|
27
|
+
writeSettings(settings: Record<string, unknown>): void;
|
|
28
|
+
validateHooks(_pluginRoot: string): DiagnosticResult[];
|
|
29
|
+
checkPluginRegistration(): DiagnosticResult;
|
|
30
|
+
getInstalledVersion(): string;
|
|
31
|
+
configureAllHooks(pluginRoot: string): string[];
|
|
32
|
+
setHookPermissions(_pluginRoot: string): string[];
|
|
33
|
+
updatePluginRegistry(_pluginRoot: string, _version: string): void;
|
|
34
|
+
getRoutingInstructionsConfig(): {
|
|
35
|
+
instructionsPath: string;
|
|
36
|
+
targetPath: string;
|
|
37
|
+
platformName: string;
|
|
38
|
+
};
|
|
39
|
+
protected extractSessionId(input: ClaudeCodeWireInput): string;
|
|
40
|
+
}
|
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/qwen-code — Qwen Code platform adapter.
|
|
3
|
+
*
|
|
4
|
+
* Extends ClaudeCodeBaseAdapter (shared wire-protocol parse/format methods)
|
|
5
|
+
* with Qwen Code-specific configuration, diagnostics, and session ID logic.
|
|
6
|
+
*
|
|
7
|
+
* Differences from Claude Code:
|
|
8
|
+
* - Config dir: ~/.qwen/ (not ~/.claude/)
|
|
9
|
+
* - Env vars: QWEN_PROJECT_DIR, QWEN_SESSION_ID (not CLAUDE_*)
|
|
10
|
+
* - Session ID priority: session_id field first (Claude: transcript_path first)
|
|
11
|
+
* - No plugin registry (Qwen uses settings.json directly)
|
|
12
|
+
* - MCP clientInfo: qwen-cli-mcp-client-* (pattern)
|
|
13
|
+
* - 12 hook events (superset of Claude's 5, but context-mode uses the shared 5)
|
|
14
|
+
*/
|
|
15
|
+
import { readFileSync, writeFileSync, existsSync, } from "node:fs";
|
|
16
|
+
import { resolve, join } from "node:path";
|
|
17
|
+
import { homedir } from "node:os";
|
|
18
|
+
import { ClaudeCodeBaseAdapter } from "../claude-code-base.js";
|
|
19
|
+
import { EXTERNAL_MCP_MATCHER_PATTERN } from "./hooks.js";
|
|
20
|
+
import { buildHookRuntimeCommand, } from "../types.js";
|
|
21
|
+
// ─────────────────────────────────────────────────────────
|
|
22
|
+
// Adapter implementation
|
|
23
|
+
// ─────────────────────────────────────────────────────────
|
|
24
|
+
export class QwenCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
25
|
+
constructor() {
|
|
26
|
+
super([".qwen"]);
|
|
27
|
+
}
|
|
28
|
+
name = "Qwen Code";
|
|
29
|
+
paradigm = "json-stdio";
|
|
30
|
+
projectDirEnvVar = "QWEN_PROJECT_DIR";
|
|
31
|
+
capabilities = {
|
|
32
|
+
preToolUse: true,
|
|
33
|
+
postToolUse: true,
|
|
34
|
+
preCompact: true,
|
|
35
|
+
sessionStart: true,
|
|
36
|
+
canModifyArgs: true,
|
|
37
|
+
canModifyOutput: true,
|
|
38
|
+
canInjectSessionContext: true,
|
|
39
|
+
};
|
|
40
|
+
// ── Configuration (differs from Claude Code) ───────────
|
|
41
|
+
getSettingsPath() {
|
|
42
|
+
return resolve(homedir(), ".qwen", "settings.json");
|
|
43
|
+
}
|
|
44
|
+
getInstructionFiles() {
|
|
45
|
+
return ["QWEN.md"];
|
|
46
|
+
}
|
|
47
|
+
generateHookConfig(pluginRoot) {
|
|
48
|
+
// Qwen Code passes native tool names in hook stdin (verified from
|
|
49
|
+
// packages/core/src/tools/tool-names.ts). Claude-style names (Bash, Read)
|
|
50
|
+
// are only accepted in permission configs, NOT in hook tool_name payloads.
|
|
51
|
+
const preToolUseMatcher = [
|
|
52
|
+
// Qwen-native names (canonical tool_name in hook stdin)
|
|
53
|
+
"run_shell_command", "read_file", "read_many_files", "grep_search",
|
|
54
|
+
"web_fetch", "agent",
|
|
55
|
+
// MCP tools (same naming convention as Claude Code)
|
|
56
|
+
"mcp__plugin_context-mode_context-mode__ctx_execute",
|
|
57
|
+
"mcp__plugin_context-mode_context-mode__ctx_execute_file",
|
|
58
|
+
"mcp__plugin_context-mode_context-mode__ctx_batch_execute",
|
|
59
|
+
// External MCP catch-all (#529). Negative-lookahead excludes context-mode's
|
|
60
|
+
// own server segments so the explicit entries above are not double-routed.
|
|
61
|
+
EXTERNAL_MCP_MATCHER_PATTERN,
|
|
62
|
+
].join("|");
|
|
63
|
+
return {
|
|
64
|
+
PreToolUse: [
|
|
65
|
+
{
|
|
66
|
+
matcher: preToolUseMatcher,
|
|
67
|
+
hooks: [
|
|
68
|
+
{ type: "command", command: buildHookRuntimeCommand(`${pluginRoot}/hooks/pretooluse.mjs`) },
|
|
69
|
+
],
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
PostToolUse: [
|
|
73
|
+
{
|
|
74
|
+
matcher: "run_shell_command|read_file|write_file|edit|glob|grep_search|todo_write|agent|ask_user_question|mcp__",
|
|
75
|
+
hooks: [
|
|
76
|
+
{ type: "command", command: buildHookRuntimeCommand(`${pluginRoot}/hooks/posttooluse.mjs`) },
|
|
77
|
+
],
|
|
78
|
+
},
|
|
79
|
+
],
|
|
80
|
+
SessionStart: [
|
|
81
|
+
{
|
|
82
|
+
matcher: "",
|
|
83
|
+
hooks: [
|
|
84
|
+
{ type: "command", command: buildHookRuntimeCommand(`${pluginRoot}/hooks/sessionstart.mjs`) },
|
|
85
|
+
],
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
PreCompact: [
|
|
89
|
+
{
|
|
90
|
+
matcher: "",
|
|
91
|
+
hooks: [
|
|
92
|
+
{ type: "command", command: buildHookRuntimeCommand(`${pluginRoot}/hooks/precompact.mjs`) },
|
|
93
|
+
],
|
|
94
|
+
},
|
|
95
|
+
],
|
|
96
|
+
UserPromptSubmit: [
|
|
97
|
+
{
|
|
98
|
+
matcher: "",
|
|
99
|
+
hooks: [
|
|
100
|
+
{ type: "command", command: buildHookRuntimeCommand(`${pluginRoot}/hooks/userpromptsubmit.mjs`) },
|
|
101
|
+
],
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
// Stop fires at end-of-turn. The qwen-specific stop hook records a
|
|
105
|
+
// turn_end marker AND captures per-turn token cost by tailing the session
|
|
106
|
+
// chats JSONL (~/.qwen/tmp/<hash>/chats/<sessionId>.jsonl) — usage is not
|
|
107
|
+
// reachable through hook stdin (usage.ts matrix §4). Points at the
|
|
108
|
+
// qwen-code/ hook dir (not the shared root) so it sets the qwen platform.
|
|
109
|
+
Stop: [
|
|
110
|
+
{
|
|
111
|
+
matcher: "",
|
|
112
|
+
hooks: [
|
|
113
|
+
{ type: "command", command: buildHookRuntimeCommand(`${pluginRoot}/hooks/qwen-code/stop.mjs`) },
|
|
114
|
+
],
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
// ── Settings read/write ────────────────────────────────
|
|
120
|
+
readSettings() {
|
|
121
|
+
try {
|
|
122
|
+
const raw = readFileSync(this.getSettingsPath(), "utf-8");
|
|
123
|
+
return JSON.parse(raw);
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return null;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
writeSettings(settings) {
|
|
130
|
+
// Issue #511: use top-level static import (line 18) — never inline
|
|
131
|
+
// `require("node:fs")` in ESM-bundled sources. esbuild rewrites them to
|
|
132
|
+
// a `__require` shim that throws `Dynamic require of "node:fs" is not
|
|
133
|
+
// supported` under Node ESM/Bun (this adapter is pulled into both
|
|
134
|
+
// server.bundle.mjs and cli.bundle.mjs via adapter detect).
|
|
135
|
+
writeFileSync(this.getSettingsPath(), JSON.stringify(settings, null, 2));
|
|
136
|
+
}
|
|
137
|
+
// ── Diagnostics (doctor) ───────────────────────────────
|
|
138
|
+
validateHooks(_pluginRoot) {
|
|
139
|
+
const results = [];
|
|
140
|
+
const settings = this.readSettings();
|
|
141
|
+
const hooks = (settings?.hooks ?? {});
|
|
142
|
+
for (const hookName of ["PreToolUse", "PostToolUse", "SessionStart", "PreCompact", "UserPromptSubmit", "Stop"]) {
|
|
143
|
+
const configured = Array.isArray(hooks[hookName]) && hooks[hookName].length > 0;
|
|
144
|
+
results.push({
|
|
145
|
+
check: `${hookName} hook`,
|
|
146
|
+
status: configured ? "pass" : "fail",
|
|
147
|
+
message: configured
|
|
148
|
+
? `${hookName} hook configured in ~/.qwen/settings.json`
|
|
149
|
+
: `${hookName} hook not found in ~/.qwen/settings.json`,
|
|
150
|
+
...(configured ? {} : { fix: `Add ${hookName} hook to ~/.qwen/settings.json` }),
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
return results;
|
|
154
|
+
}
|
|
155
|
+
checkPluginRegistration() {
|
|
156
|
+
// Qwen Code has no plugin registry — check for MCP config instead
|
|
157
|
+
try {
|
|
158
|
+
const settings = this.readSettings();
|
|
159
|
+
if (settings?.mcpServers && typeof settings.mcpServers === "object") {
|
|
160
|
+
const servers = settings.mcpServers;
|
|
161
|
+
if (Object.keys(servers).some(k => k.includes("context-mode"))) {
|
|
162
|
+
return {
|
|
163
|
+
check: "Plugin registration",
|
|
164
|
+
status: "pass",
|
|
165
|
+
message: "context-mode found in mcpServers",
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
check: "Plugin registration",
|
|
170
|
+
status: "fail",
|
|
171
|
+
message: "mcpServers exists but context-mode not found",
|
|
172
|
+
fix: "Add context-mode to mcpServers in ~/.qwen/settings.json",
|
|
173
|
+
};
|
|
174
|
+
}
|
|
175
|
+
return {
|
|
176
|
+
check: "Plugin registration",
|
|
177
|
+
status: "warn",
|
|
178
|
+
message: "No mcpServers in ~/.qwen/settings.json",
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
catch {
|
|
182
|
+
return {
|
|
183
|
+
check: "Plugin registration",
|
|
184
|
+
status: "warn",
|
|
185
|
+
message: "Could not read ~/.qwen/settings.json",
|
|
186
|
+
};
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
getInstalledVersion() {
|
|
190
|
+
const settings = this.readSettings();
|
|
191
|
+
if (!settings)
|
|
192
|
+
return "not installed";
|
|
193
|
+
const hooks = settings.hooks;
|
|
194
|
+
if (!hooks)
|
|
195
|
+
return "not installed";
|
|
196
|
+
// Check if any hook type has context-mode scripts configured
|
|
197
|
+
const contextModeScripts = [
|
|
198
|
+
"pretooluse.mjs",
|
|
199
|
+
"posttooluse.mjs",
|
|
200
|
+
"precompact.mjs",
|
|
201
|
+
"sessionstart.mjs",
|
|
202
|
+
"userpromptsubmit.mjs",
|
|
203
|
+
];
|
|
204
|
+
for (const [, entries] of Object.entries(hooks)) {
|
|
205
|
+
if (!Array.isArray(entries))
|
|
206
|
+
continue;
|
|
207
|
+
for (const entry of entries) {
|
|
208
|
+
const e = entry;
|
|
209
|
+
if (e.hooks?.some((h) => h.command && contextModeScripts.some((s) => h.command.includes(s)))) {
|
|
210
|
+
return "installed (hooks configured)";
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
return "not installed";
|
|
215
|
+
}
|
|
216
|
+
configureAllHooks(pluginRoot) {
|
|
217
|
+
const settings = this.readSettings() ?? {};
|
|
218
|
+
const hooks = (settings.hooks ?? {});
|
|
219
|
+
const changes = [];
|
|
220
|
+
// ── Phase 1: Clean stale context-mode hooks ──────────
|
|
221
|
+
// After an upgrade, settings.json may contain hardcoded paths
|
|
222
|
+
// pointing to deleted version directories. Remove those.
|
|
223
|
+
for (const hookType of Object.keys(hooks)) {
|
|
224
|
+
const entries = hooks[hookType];
|
|
225
|
+
if (!Array.isArray(entries))
|
|
226
|
+
continue;
|
|
227
|
+
const filtered = entries.filter((entry) => {
|
|
228
|
+
const e = entry;
|
|
229
|
+
const commands = e.hooks ?? [];
|
|
230
|
+
// Preserve entries that are not context-mode hooks
|
|
231
|
+
const isContextMode = commands.some((h) => h.command && /context-mode|pretooluse|posttooluse|precompact|sessionstart|userpromptsubmit/i.test(h.command));
|
|
232
|
+
if (!isContextMode)
|
|
233
|
+
return true;
|
|
234
|
+
// For context-mode hooks, check if referenced script files exist
|
|
235
|
+
return commands.every((h) => {
|
|
236
|
+
if (!h.command)
|
|
237
|
+
return true;
|
|
238
|
+
// Extract path from both new ("nodePath" "scriptPath.mjs") and legacy (node .../script.mjs) formats
|
|
239
|
+
const newFmt = h.command.match(/"[^"]+"\s+"([^"]+\.mjs)"/);
|
|
240
|
+
const legacyFmt = h.command.match(/node\s+"?([^"]+\.mjs)"?/);
|
|
241
|
+
const scriptMatch = newFmt || legacyFmt;
|
|
242
|
+
if (!scriptMatch)
|
|
243
|
+
return true; // CLI dispatcher format, always valid
|
|
244
|
+
return existsSync(scriptMatch[1]);
|
|
245
|
+
});
|
|
246
|
+
});
|
|
247
|
+
const removed = entries.length - filtered.length;
|
|
248
|
+
if (removed > 0) {
|
|
249
|
+
hooks[hookType] = filtered;
|
|
250
|
+
changes.push(`Removed ${removed} stale ${hookType} hook(s)`);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
// ── Phase 2: Register fresh hooks ────────────────────
|
|
254
|
+
// All 5 hooks must be wired (z6 — capabilities declare 5 events but
|
|
255
|
+
// configureAllHooks previously only wrote 2). Qwen Code's hook stdin shape
|
|
256
|
+
// is wire-identical to Claude Code, so we reuse top-level hook scripts.
|
|
257
|
+
const hookTypes = [
|
|
258
|
+
{
|
|
259
|
+
name: "PreToolUse",
|
|
260
|
+
script: "pretooluse.mjs",
|
|
261
|
+
matcher: [
|
|
262
|
+
"run_shell_command", "read_file", "read_many_files", "grep_search",
|
|
263
|
+
"web_fetch", "agent",
|
|
264
|
+
"mcp__plugin_context-mode_context-mode__ctx_execute",
|
|
265
|
+
"mcp__plugin_context-mode_context-mode__ctx_execute_file",
|
|
266
|
+
"mcp__plugin_context-mode_context-mode__ctx_batch_execute",
|
|
267
|
+
// External MCP catch-all (#529) — keep in sync with generateHookConfig above.
|
|
268
|
+
EXTERNAL_MCP_MATCHER_PATTERN,
|
|
269
|
+
].join("|"),
|
|
270
|
+
},
|
|
271
|
+
{
|
|
272
|
+
name: "PostToolUse",
|
|
273
|
+
script: "posttooluse.mjs",
|
|
274
|
+
matcher: "run_shell_command|read_file|write_file|edit|glob|grep_search|todo_write|agent|ask_user_question|mcp__",
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
name: "SessionStart",
|
|
278
|
+
script: "sessionstart.mjs",
|
|
279
|
+
matcher: "",
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
name: "PreCompact",
|
|
283
|
+
script: "precompact.mjs",
|
|
284
|
+
matcher: "",
|
|
285
|
+
},
|
|
286
|
+
{
|
|
287
|
+
name: "UserPromptSubmit",
|
|
288
|
+
script: "userpromptsubmit.mjs",
|
|
289
|
+
matcher: "",
|
|
290
|
+
},
|
|
291
|
+
{
|
|
292
|
+
// Stop captures per-turn token cost by tailing the session chats JSONL
|
|
293
|
+
// (usage is unreachable through hook stdin). Routes to the qwen-code/
|
|
294
|
+
// hook dir so it sets the qwen platform — keep in sync with
|
|
295
|
+
// generateHookConfig above.
|
|
296
|
+
name: "Stop",
|
|
297
|
+
script: "qwen-code/stop.mjs",
|
|
298
|
+
matcher: "",
|
|
299
|
+
},
|
|
300
|
+
];
|
|
301
|
+
for (const { name, script, matcher } of hookTypes) {
|
|
302
|
+
const entry = {
|
|
303
|
+
matcher,
|
|
304
|
+
hooks: [{ type: "command", command: buildHookRuntimeCommand(`${pluginRoot}/hooks/${script}`) }],
|
|
305
|
+
};
|
|
306
|
+
const existing = hooks[name];
|
|
307
|
+
if (existing && Array.isArray(existing)) {
|
|
308
|
+
// Replace existing context-mode entry or append
|
|
309
|
+
const idx = existing.findIndex((e) => {
|
|
310
|
+
const typed = e;
|
|
311
|
+
return typed.hooks?.some((h) => h.command?.includes(script)) ?? false;
|
|
312
|
+
});
|
|
313
|
+
if (idx >= 0) {
|
|
314
|
+
existing[idx] = entry;
|
|
315
|
+
changes.push(`Updated ${name} hook`);
|
|
316
|
+
}
|
|
317
|
+
else {
|
|
318
|
+
existing.push(entry);
|
|
319
|
+
changes.push(`Added ${name} hook`);
|
|
320
|
+
}
|
|
321
|
+
hooks[name] = existing;
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
hooks[name] = [entry];
|
|
325
|
+
changes.push(`Created ${name} hooks`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
settings.hooks = hooks;
|
|
329
|
+
this.writeSettings(settings);
|
|
330
|
+
return changes;
|
|
331
|
+
}
|
|
332
|
+
setHookPermissions(_pluginRoot) {
|
|
333
|
+
return [];
|
|
334
|
+
}
|
|
335
|
+
updatePluginRegistry(_pluginRoot, _version) {
|
|
336
|
+
// No plugin registry in Qwen Code
|
|
337
|
+
}
|
|
338
|
+
getRoutingInstructionsConfig() {
|
|
339
|
+
const instructionsPath = resolve(join(homedir(), ".qwen", "QWEN.md"));
|
|
340
|
+
return {
|
|
341
|
+
instructionsPath,
|
|
342
|
+
targetPath: "QWEN.md",
|
|
343
|
+
platformName: "Qwen Code",
|
|
344
|
+
};
|
|
345
|
+
}
|
|
346
|
+
// ── Session ID extraction (differs from Claude Code) ───
|
|
347
|
+
// Qwen Code prioritizes session_id field, then QWEN_SESSION_ID env var.
|
|
348
|
+
// Claude Code prioritizes transcript_path UUID first.
|
|
349
|
+
extractSessionId(input) {
|
|
350
|
+
if (input.session_id)
|
|
351
|
+
return input.session_id;
|
|
352
|
+
if (input.transcript_path) {
|
|
353
|
+
const match = input.transcript_path.match(/([a-f0-9-]{36})\.jsonl$/);
|
|
354
|
+
if (match)
|
|
355
|
+
return match[1];
|
|
356
|
+
}
|
|
357
|
+
if (process.env.QWEN_SESSION_ID)
|
|
358
|
+
return process.env.QWEN_SESSION_ID;
|
|
359
|
+
return `pid-${process.ppid}`;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* adapters/qwen-code/usage — per-turn token capture from the session JSONL.
|
|
3
|
+
*
|
|
4
|
+
* Qwen Code is a Gemini-CLI fork and normalizes EVERY backend (Gemini-native,
|
|
5
|
+
* OpenAI-compat/DashScope, Anthropic) to the same canonical token shape:
|
|
6
|
+
* `GenerateContentResponseUsageMetadata` { promptTokenCount, candidatesTokenCount,
|
|
7
|
+
* cachedContentTokenCount, thoughtsTokenCount, totalTokenCount }
|
|
8
|
+
* (matrix §1: turn.ts:96,417 + converter.ts:1145-1148). That metadata is
|
|
9
|
+
* persisted, per API call, into the session record file as a `ChatRecord`
|
|
10
|
+
* carrying `.usageMetadata` + `.model`
|
|
11
|
+
* (refs: packages/core/src/services/chatRecordingService.ts:259,261,919 file at
|
|
12
|
+
* ~/.qwen/tmp/<project_id>/chats/<sessionId>.jsonl — :451 location comment,
|
|
13
|
+
* :600,628-629 path build).
|
|
14
|
+
*
|
|
15
|
+
* CRITICAL (matrix §4): qwen-code's hook payloads carry tool I/O ONLY — token
|
|
16
|
+
* usage is unreachable through the hook stream (grep of hookEventHandler.ts /
|
|
17
|
+
* hookSystem.ts / toolHookTriggers.ts for token|usageMetadata|usage → zero
|
|
18
|
+
* matches). The ONLY live capture path is a tail of the session JSONL. This
|
|
19
|
+
* module is therefore the JSONL-tail counterpart to claude-code's
|
|
20
|
+
* `extractTranscriptUsageSince` (src/session/extract.ts) — same cursor-gated,
|
|
21
|
+
* char-algorithmic, NO-regex parse, same `buildAgentUsageEvent` emission path.
|
|
22
|
+
*
|
|
23
|
+
* Per matrix §3 each ChatRecord.usageMetadata is INCREMENTAL per API call
|
|
24
|
+
* (cumulative session totals are derived downstream via += in
|
|
25
|
+
* uiTelemetry.ts:237-241), so summing the NEW records since the cursor yields
|
|
26
|
+
* the exact billed delta with no double-count.
|
|
27
|
+
*
|
|
28
|
+
* No native USD — cost_usd is derived from the pricing catalog inside
|
|
29
|
+
* buildAgentUsageEvent (native_cost_usd omitted). Pure, null-safe, NO regex.
|
|
30
|
+
*/
|
|
31
|
+
import { type AgentUsageCounts, type SessionEvent } from "../../session/extract.js";
|
|
32
|
+
/**
|
|
33
|
+
* Parse ONE qwen `ChatRecord` into the `buildAgentUsageEvent` input shape, or
|
|
34
|
+
* null when the record carries no usage / sums to zero.
|
|
35
|
+
*
|
|
36
|
+
* Mapping → builder shape (AgentUsageCounts):
|
|
37
|
+
* promptTokenCount → input_tokens
|
|
38
|
+
* candidatesTokenCount → output_tokens
|
|
39
|
+
* thoughtsTokenCount → ADDED into output_tokens (Gemini-lineage bills
|
|
40
|
+
* reasoning/thoughts as output — same fold as
|
|
41
|
+
* parseGeminiUsage in src/session/extract.ts)
|
|
42
|
+
* cachedContentTokenCount → cache_read_tokens (when present)
|
|
43
|
+
* model_id → ChatRecord.model
|
|
44
|
+
*
|
|
45
|
+
* No native cost — native_cost_usd omitted (catalog-derived). NO regex.
|
|
46
|
+
*/
|
|
47
|
+
export declare function parseQwenUsage(record: unknown): AgentUsageCounts | null;
|
|
48
|
+
/**
|
|
49
|
+
* Cursor-aware tail of the qwen session JSONL. Emits one priced `agent_usage`
|
|
50
|
+
* event PER distinct model across the records NEW since `cursor`, so re-reading
|
|
51
|
+
* the (append-only, ever-growing) JSONL each Stop never double-counts.
|
|
52
|
+
*
|
|
53
|
+
* - cursor null/empty → process ALL records.
|
|
54
|
+
* - cursor found → process records STRICTLY AFTER it.
|
|
55
|
+
* - cursor set but NOT found → compaction/rotation dropped it: bounded
|
|
56
|
+
* fallback processes ONLY THE LAST record (never re-emit full history).
|
|
57
|
+
*
|
|
58
|
+
* `cursor` returns the id of the LAST id-bearing record seen (whether or not it
|
|
59
|
+
* carried usage), so the next call resumes exactly past it. When no record
|
|
60
|
+
* carries an id, the input cursor is returned unchanged.
|
|
61
|
+
*
|
|
62
|
+
* One linear walk, JSON.parse per line, NO regex — mirrors
|
|
63
|
+
* extractTranscriptUsageSince's structure exactly.
|
|
64
|
+
*/
|
|
65
|
+
export declare function extractQwenUsageSince(jsonlText: string, cursor: string | null): {
|
|
66
|
+
events: SessionEvent[];
|
|
67
|
+
cursor: string | null;
|
|
68
|
+
};
|
|
69
|
+
/**
|
|
70
|
+
* Hash a project root into qwen-code's `<project_id>` directory segment.
|
|
71
|
+
*
|
|
72
|
+
* EXACT port of qwen's `getProjectHash`
|
|
73
|
+
* (refs/platforms/qwen-code/packages/core/src/utils/paths.ts:262 —
|
|
74
|
+
* `crypto.createHash('sha256').update(normalizedPath).digest('hex')`). On
|
|
75
|
+
* Windows qwen lowercases the path first (case-insensitive FS); we mirror that
|
|
76
|
+
* so a hook running on win32 resolves the same tmp dir qwen itself wrote.
|
|
77
|
+
* Pure, deterministic, NO regex.
|
|
78
|
+
*/
|
|
79
|
+
export declare function qwenProjectHash(projectRoot: string): string;
|
|
80
|
+
/**
|
|
81
|
+
* Build the canonical session JSONL path qwen-code writes its ChatRecords to:
|
|
82
|
+
* <qwenHome>/tmp/<sha256(projectRoot)>/chats/<sessionId>.jsonl
|
|
83
|
+
* (refs chatRecordingService.ts:451 location + storage.ts:316-320
|
|
84
|
+
* getProjectTempDir → getGlobalTempDir(<qwenHome>/tmp) + getProjectHash).
|
|
85
|
+
*
|
|
86
|
+
* `qwenHome` is normally `<homedir>/.qwen`. Pure path join — does NOT touch the
|
|
87
|
+
* FS, so it is fully unit-testable; existence probing + the glob fallback live
|
|
88
|
+
* in the Stop hook (which cannot import this TS at runtime). NO regex.
|
|
89
|
+
*/
|
|
90
|
+
export declare function qwenChatJsonlPath(qwenHome: string, projectRoot: string, sessionId: string): string;
|