context-mode 1.0.59 → 1.0.61
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 +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/.openclaw-plugin/openclaw.plugin.json +1 -1
- package/.openclaw-plugin/package.json +1 -1
- package/README.md +34 -18
- package/build/adapters/codex/index.d.ts +11 -12
- package/build/adapters/codex/index.js +19 -19
- package/build/cli.js +5 -0
- package/build/server.js +2 -1
- package/cli.bundle.mjs +3 -3
- package/hooks/codex/posttooluse.mjs +57 -0
- package/hooks/codex/pretooluse.mjs +31 -0
- package/hooks/codex/sessionstart.mjs +98 -0
- package/hooks/core/formatters.mjs +13 -0
- package/hooks/session-helpers.mjs +7 -0
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +2 -2
|
@@ -6,14 +6,14 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code plugins by Mert Koseoğlu",
|
|
9
|
-
"version": "1.0.
|
|
9
|
+
"version": "1.0.61"
|
|
10
10
|
},
|
|
11
11
|
"plugins": [
|
|
12
12
|
{
|
|
13
13
|
"name": "context-mode",
|
|
14
14
|
"source": "./",
|
|
15
15
|
"description": "Claude Code MCP plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
16
|
-
"version": "1.0.
|
|
16
|
+
"version": "1.0.61",
|
|
17
17
|
"author": {
|
|
18
18
|
"name": "Mert Koseoğlu"
|
|
19
19
|
},
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.61",
|
|
4
4
|
"description": "MCP server that saves 98% of your context window with session continuity. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and automatic state restore across compactions.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Context Mode",
|
|
4
4
|
"kind": "tool",
|
|
5
5
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.61",
|
|
7
7
|
"sandbox": {
|
|
8
8
|
"mode": "permissive",
|
|
9
9
|
"filesystem_access": "full",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.61",
|
|
4
4
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Mert Koseoğlu",
|
package/README.md
CHANGED
|
@@ -393,7 +393,7 @@ Full documentation: [`docs/adapters/openclaw.md`](docs/adapters/openclaw.md)
|
|
|
393
393
|
</details>
|
|
394
394
|
|
|
395
395
|
<details>
|
|
396
|
-
<summary><strong>Codex CLI</strong> —
|
|
396
|
+
<summary><strong>Codex CLI</strong> — MCP + hooks (waiting for upstream dispatch)</summary>
|
|
397
397
|
|
|
398
398
|
**Prerequisites:** Node.js 18+, Codex CLI installed.
|
|
399
399
|
|
|
@@ -412,7 +412,16 @@ Full documentation: [`docs/adapters/openclaw.md`](docs/adapters/openclaw.md)
|
|
|
412
412
|
command = "context-mode"
|
|
413
413
|
```
|
|
414
414
|
|
|
415
|
-
3.
|
|
415
|
+
3. *(Waiting for upstream)* Enable the hooks feature flag. Add to `~/.codex/config.toml`:
|
|
416
|
+
|
|
417
|
+
```toml
|
|
418
|
+
[features]
|
|
419
|
+
codex_hooks = true
|
|
420
|
+
```
|
|
421
|
+
|
|
422
|
+
> **Status:** Codex CLI's hook system is implemented in source (`codex-rs/hooks/`) but hook dispatch is not yet wired into the tool execution pipeline (`Stage::UnderDevelopment`). The feature flag is accepted but hooks don't fire during sessions as of v0.118.0. Our hook scripts are ready — they'll work immediately once Codex enables dispatch. Track progress: [openai/codex#16685](https://github.com/openai/codex/issues/16685).
|
|
423
|
+
|
|
424
|
+
4. *(Prepare for when dispatch is enabled)* Add hooks for routing enforcement and session tracking. Create `~/.codex/hooks.json`:
|
|
416
425
|
|
|
417
426
|
```json
|
|
418
427
|
{
|
|
@@ -426,7 +435,9 @@ Full documentation: [`docs/adapters/openclaw.md`](docs/adapters/openclaw.md)
|
|
|
426
435
|
|
|
427
436
|
`PreToolUse` enforces sandbox routing (blocks dangerous commands, redirects to MCP tools). `PostToolUse` captures session events. `SessionStart` restores state after compaction.
|
|
428
437
|
|
|
429
|
-
|
|
438
|
+
> **Note:** PreToolUse routing supports deny rules only (blocks dangerous commands). Context injection (`additionalContext`) is not supported in Codex PreToolUse — it works via PostToolUse and SessionStart instead. This is handled automatically.
|
|
439
|
+
|
|
440
|
+
5. Copy routing instructions (recommended even with hooks for full routing awareness):
|
|
430
441
|
|
|
431
442
|
```bash
|
|
432
443
|
cp node_modules/context-mode/configs/codex/AGENTS.md ./AGENTS.md
|
|
@@ -434,11 +445,13 @@ Full documentation: [`docs/adapters/openclaw.md`](docs/adapters/openclaw.md)
|
|
|
434
445
|
|
|
435
446
|
For global use: `cp node_modules/context-mode/configs/codex/AGENTS.md ~/.codex/AGENTS.md`. Global applies to all projects. If both exist, Codex CLI merges them.
|
|
436
447
|
|
|
437
|
-
|
|
448
|
+
6. Restart Codex CLI.
|
|
438
449
|
|
|
439
450
|
**Verify:** Start a session and type `ctx stats`. Context-mode tools should appear and respond.
|
|
440
451
|
|
|
441
|
-
**Routing:**
|
|
452
|
+
**Routing:** MCP tools work. Hook-based routing is ready but waiting for Codex to enable hook dispatch. The `AGENTS.md` file provides routing instructions for model awareness in the meantime.
|
|
453
|
+
|
|
454
|
+
> **Exec mode regression (v0.118.0):** `codex exec` cancels all MCP tool calls with "user cancelled MCP tool call". The `tool_call_mcp_elicitation` flag went stable in 0.118.0, adding an approval prompt that exec-mode can't handle. **Pin to Codex ≤0.116.0 for exec-mode MCP.** Confirmed by upstream: [openai/codex#16685](https://github.com/openai/codex/issues/16685). Interactive mode (`codex` / `codex --full-auto`) is not affected.
|
|
442
455
|
|
|
443
456
|
</details>
|
|
444
457
|
|
|
@@ -746,7 +759,7 @@ Session continuity requires 4 hooks working together:
|
|
|
746
759
|
| **SessionStart** | Restores state after compaction or resume | Yes | Yes | Yes | -- | -- | -- | Plugin | Yes | -- | -- | -- | ✓ (via session_start event) |
|
|
747
760
|
| | **Session completeness** | **Full** | **High** | **High** | **Partial** | **High** | **High** | **High** | **Partial** | **--** | **Partial** | **--** | **High** |
|
|
748
761
|
|
|
749
|
-
> **Note:** Full session continuity (capture + snapshot + restore) works on **Claude Code**, **Gemini CLI**, and **VS Code Copilot**. **OpenCode** provides **high** session continuity: it captures tool events and injects compaction snapshots via the plugin, but SessionStart is not yet available ([#14808](https://github.com/sst/opencode/issues/14808)), so startup/resume restore is not supported. **KiloCode** shares the same plugin architecture as OpenCode via the OpenCodeAdapter, so its continuity level depends on KiloCode's SessionStart support. **Cursor** captures tool events via `preToolUse`/`postToolUse`, but `sessionStart` is currently rejected by Cursor's validator ([forum report](https://forum.cursor.com/t/unknown-hook-type-sessionstart/149566)), so session restore after compaction is not available yet. **OpenClaw** uses native gateway plugin hooks (`api.on()`) for full session continuity. **Pi Coding Agent** provides high session continuity via extension hooks (`tool_call`, `tool_result`, `session_start`, `session_before_compact`). **Codex CLI**
|
|
762
|
+
> **Note:** Full session continuity (capture + snapshot + restore) works on **Claude Code**, **Gemini CLI**, and **VS Code Copilot**. **OpenCode** provides **high** session continuity: it captures tool events and injects compaction snapshots via the plugin, but SessionStart is not yet available ([#14808](https://github.com/sst/opencode/issues/14808)), so startup/resume restore is not supported. **KiloCode** shares the same plugin architecture as OpenCode via the OpenCodeAdapter, so its continuity level depends on KiloCode's SessionStart support. **Cursor** captures tool events via `preToolUse`/`postToolUse`, but `sessionStart` is currently rejected by Cursor's validator ([forum report](https://forum.cursor.com/t/unknown-hook-type-sessionstart/149566)), so session restore after compaction is not available yet. **OpenClaw** uses native gateway plugin hooks (`api.on()`) for full session continuity. **Pi Coding Agent** provides high session continuity via extension hooks (`tool_call`, `tool_result`, `session_start`, `session_before_compact`). **Codex CLI** hook-based session tracking is ready but waiting for upstream hook dispatch (codex_hooks Stage::UnderDevelopment, [openai/codex#16685](https://github.com/openai/codex/issues/16685)). MCP tools work. Once dispatch is enabled, session tracking will activate automatically. **Antigravity**, **Kiro**, and **Zed** have no hook support in the current release, so session tracking is not available.
|
|
750
763
|
|
|
751
764
|
<details>
|
|
752
765
|
<summary><strong>What gets captured</strong></summary>
|
|
@@ -829,11 +842,11 @@ Detailed event data is also indexed into FTS5 for on-demand retrieval via `searc
|
|
|
829
842
|
|
|
830
843
|
**OpenClaw / Pi Agent** — High coverage. All tool lifecycle hooks (`after_tool_call`, `before_compaction`, `session_start`) fire via the native gateway plugin. User decisions aren't captured but file edits, git ops, errors, and tasks are fully tracked. Falls back to DB snapshot reconstruction if compaction hooks fail on older gateway versions. See [`docs/adapters/openclaw.md`](docs/adapters/openclaw.md).
|
|
831
844
|
|
|
832
|
-
**Codex CLI** —
|
|
845
|
+
**Codex CLI** — MCP active, hooks ready. Hook scripts (PreToolUse, PostToolUse, SessionStart) are implemented and tested but Codex CLI doesn't dispatch them yet (Stage::UnderDevelopment). MCP tools work. Track: [openai/codex#16685](https://github.com/openai/codex/issues/16685).
|
|
833
846
|
|
|
834
|
-
**Antigravity** — No session support.
|
|
847
|
+
**Antigravity** — No session support. No hooks, no event capture. Requires manually copying `GEMINI.md` to your project root. Auto-detected via MCP protocol handshake (`clientInfo.name`).
|
|
835
848
|
|
|
836
|
-
**Zed** — No session support.
|
|
849
|
+
**Zed** — No session support. No hooks, no event capture. Requires manually copying `AGENTS.md` to your project root. Auto-detected via MCP protocol handshake (`clientInfo.name`).
|
|
837
850
|
|
|
838
851
|
**Kiro** — Partial coverage. Native `preToolUse` and `postToolUse` hooks capture tool events and enforce sandbox routing. `agentSpawn` (the Kiro equivalent of SessionStart) is not yet implemented, so session restore after compaction is not available. Requires manually copying `KIRO.md` to your project root. Auto-detected via MCP protocol handshake (`clientInfo.name`).
|
|
839
852
|
|
|
@@ -846,12 +859,12 @@ Detailed event data is also indexed into FTS5 for on-demand retrieval via `searc
|
|
|
846
859
|
| Feature | Claude Code | Gemini CLI | VS Code Copilot | Cursor | OpenCode | KiloCode | OpenClaw | Codex CLI | Antigravity | Kiro | Zed | Pi |
|
|
847
860
|
|---|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
848
861
|
| MCP Server | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes |
|
|
849
|
-
| PreToolUse Hook | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin |
|
|
850
|
-
| PostToolUse Hook | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin |
|
|
851
|
-
| SessionStart Hook | Yes | Yes | Yes | -- | -- | -- | Plugin |
|
|
862
|
+
| PreToolUse Hook | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin | Yes | -- | Yes | -- | Yes (extension) |
|
|
863
|
+
| PostToolUse Hook | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin | Yes | -- | Yes | -- | Yes (extension) |
|
|
864
|
+
| SessionStart Hook | Yes | Yes | Yes | -- | -- | -- | Plugin | Yes | -- | -- | -- | Yes (extension) |
|
|
852
865
|
| PreCompact Hook | Yes | Yes | Yes | -- | Plugin | Plugin | Plugin | -- | -- | -- | -- | Yes (extension) |
|
|
853
|
-
| Can Modify Args | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin |
|
|
854
|
-
| Can Block Tools | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin |
|
|
866
|
+
| Can Modify Args | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin | Yes | -- | -- | -- | Yes (extension) |
|
|
867
|
+
| Can Block Tools | Yes | Yes | Yes | Yes | Plugin | Plugin | Plugin | Yes | -- | Yes | -- | Yes (extension) |
|
|
855
868
|
| Utility Commands (ctx) | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes | Yes (/ctx-stats, /ctx-doctor) |
|
|
856
869
|
| Slash Commands | Yes | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |
|
|
857
870
|
| Plugin Marketplace | Yes | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- | -- |
|
|
@@ -862,7 +875,7 @@ Detailed event data is also indexed into FTS5 for on-demand retrieval via `searc
|
|
|
862
875
|
>
|
|
863
876
|
> **OpenClaw** runs context-mode as a native gateway plugin targeting Pi Agent sessions. Hooks register via `api.on()` (tool/lifecycle) and `api.registerHook()` (commands). All tool interception and compaction hooks are supported. See [`docs/adapters/openclaw.md`](docs/adapters/openclaw.md).
|
|
864
877
|
>
|
|
865
|
-
> **Codex CLI
|
|
878
|
+
> **Codex CLI** hooks are implemented but dispatch is not yet active (`codex_hooks` is `Stage::UnderDevelopment`). MCP tools work. Hook scripts are ready and will activate once Codex enables dispatch ([openai/codex#16685](https://github.com/openai/codex/issues/16685)). PreToolUse supports `permissionDecision: "deny"` only — `additionalContext` is not supported in PreToolUse (context injection works via PostToolUse and SessionStart instead; the codex formatter handles this automatically). See the Codex install section for setup. **Antigravity** and **Zed** do not support hooks. They rely solely on manually-copied routing instruction files (`AGENTS.md` / `GEMINI.md`) for enforcement (~60% compliance). See each platform's install section for copy instructions. Antigravity and Zed are auto-detected via MCP protocol handshake — no manual platform configuration needed.
|
|
866
879
|
>
|
|
867
880
|
> **Kiro** supports native `preToolUse` and `postToolUse` hooks for routing enforcement and tool event capture. `agentSpawn` (SessionStart equivalent) and `stop` are not yet wired. Requires manually copying `KIRO.md` to your project root. Kiro is auto-detected via MCP protocol handshake (`clientInfo.name`).
|
|
868
881
|
>
|
|
@@ -872,7 +885,7 @@ Detailed event data is also indexed into FTS5 for on-demand retrieval via `searc
|
|
|
872
885
|
|
|
873
886
|
Hooks intercept tool calls programmatically — they can block dangerous commands and redirect them to the sandbox before execution. Instruction files guide the model via prompt instructions but cannot block anything. **Always enable hooks where supported.**
|
|
874
887
|
|
|
875
|
-
> **Note:** Routing instruction files were previously auto-written to project directories on first session start. This was disabled to prevent git tree pollution ([#158](https://github.com/mksglu/context-mode/issues/158), [#164](https://github.com/mksglu/context-mode/issues/164)). Hook-capable platforms (Claude Code, Gemini CLI, VS Code Copilot, OpenCode, OpenClaw) inject routing via hooks and need no file. Non-hook platforms (
|
|
888
|
+
> **Note:** Routing instruction files were previously auto-written to project directories on first session start. This was disabled to prevent git tree pollution ([#158](https://github.com/mksglu/context-mode/issues/158), [#164](https://github.com/mksglu/context-mode/issues/164)). Hook-capable platforms (Claude Code, Gemini CLI, VS Code Copilot, Cursor, OpenCode, OpenClaw, Codex CLI) inject routing via hooks and need no file. Non-hook platforms (Zed, Kiro, Antigravity) require a one-time manual copy — see each platform's install section.
|
|
876
889
|
|
|
877
890
|
| Platform | Hooks | Instruction File | With Hooks | Without Hooks |
|
|
878
891
|
|---|:---:|---|:---:|:---:|
|
|
@@ -882,7 +895,7 @@ Hooks intercept tool calls programmatically — they can block dangerous command
|
|
|
882
895
|
| Cursor | Yes | [`context-mode.mdc`](configs/cursor/context-mode.mdc) | **~98% saved** | ~60% saved |
|
|
883
896
|
| OpenCode | Plugin | [`AGENTS.md`](configs/opencode/AGENTS.md) | **~98% saved** | ~60% saved |
|
|
884
897
|
| OpenClaw | Plugin | [`AGENTS.md`](configs/openclaw/AGENTS.md) | **~98% saved** | ~60% saved |
|
|
885
|
-
| Codex CLI |
|
|
898
|
+
| Codex CLI | Yes | [`AGENTS.md`](configs/codex/AGENTS.md) | **~98% saved** | ~60% saved |
|
|
886
899
|
| Antigravity | -- | [`GEMINI.md`](configs/antigravity/GEMINI.md) | -- | ~60% saved |
|
|
887
900
|
| Kiro | Yes | [`KIRO.md`](configs/kiro/KIRO.md) | **~98% saved** | ~60% saved |
|
|
888
901
|
| Zed | -- | [`AGENTS.md`](configs/zed/AGENTS.md) | -- | ~60% saved |
|
|
@@ -907,8 +920,11 @@ ctx upgrade → update from GitHub, rebuild, reconfigure hooks
|
|
|
907
920
|
```bash
|
|
908
921
|
context-mode doctor
|
|
909
922
|
context-mode upgrade
|
|
923
|
+
bash scripts/ctx-debug.sh # full diagnostic report for bug reports
|
|
910
924
|
```
|
|
911
925
|
|
|
926
|
+
The debug script collects OS info, runtime versions, better-sqlite3 status, adapter detection, config files (redacted), hook validation, FTS5/SQLite test, executor test, process check, session databases, and environment variables into a single pasteable markdown report.
|
|
927
|
+
|
|
912
928
|
Works on **all platforms**. On Claude Code, slash commands (`/ctx-stats`, `/ctx-doctor`, `/ctx-upgrade`) are also available.
|
|
913
929
|
|
|
914
930
|
## Benchmarks
|
|
@@ -1003,7 +1019,7 @@ Context Mode enforces the same permission rules you already use — but extends
|
|
|
1003
1019
|
}
|
|
1004
1020
|
```
|
|
1005
1021
|
|
|
1006
|
-
Add this to your project's `.claude/settings.json` (or `~/.claude/settings.json` for global rules). All platforms read security policies from Claude Code's settings format — even on Gemini CLI, VS Code Copilot, and OpenCode. Codex CLI
|
|
1022
|
+
Add this to your project's `.claude/settings.json` (or `~/.claude/settings.json` for global rules). All platforms read security policies from Claude Code's settings format — even on Gemini CLI, VS Code Copilot, and OpenCode. Codex CLI security enforcement requires the `codex_hooks` feature flag to be enabled.
|
|
1007
1023
|
|
|
1008
1024
|
The pattern is `Tool(what to match)` where `*` means "anything".
|
|
1009
1025
|
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* adapters/codex — Codex CLI
|
|
2
|
+
* adapters/codex — Codex CLI platform adapter.
|
|
3
3
|
*
|
|
4
|
-
* Implements HookAdapter for Codex CLI's JSON stdin/stdout
|
|
4
|
+
* Implements HookAdapter for Codex CLI's JSON stdin/stdout paradigm.
|
|
5
5
|
*
|
|
6
6
|
* Codex CLI hook specifics:
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
* - Output modification: NOT supported (no updatedMCPToolOutput field)
|
|
11
|
-
* - Blocking: `hookSpecificOutput.permissionDecision: "deny"` in response
|
|
12
|
-
* - Context injection: `hookSpecificOutput.additionalContext` in response
|
|
13
|
-
* - Session ID: session_id field from hook input
|
|
14
|
-
* - Project dir: cwd field from hook input
|
|
15
|
-
* - Config: ~/.codex/config.toml (TOML format)
|
|
16
|
-
* - MCP: full support via [mcp_servers] in config.toml
|
|
7
|
+
* - 5 hook events: PreToolUse, PostToolUse, SessionStart, UserPromptSubmit, Stop
|
|
8
|
+
* - Same wire protocol as Claude Code (JSON stdin → stdout)
|
|
9
|
+
* - Config: ~/.codex/hooks.json + ~/.codex/config.toml (TOML for MCP/features)
|
|
17
10
|
* - Session dir: ~/.codex/context-mode/sessions/
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT: Hook dispatch is NOT yet active in Codex CLI (v0.118.0).
|
|
13
|
+
* codex_hooks feature flag is Stage::UnderDevelopment — hooks are implemented
|
|
14
|
+
* in codex-rs/hooks/ but not wired into the tool execution pipeline.
|
|
15
|
+
* Our adapter is ready; it will work once Codex enables dispatch.
|
|
16
|
+
* Track: https://github.com/openai/codex/issues/16685
|
|
18
17
|
*/
|
|
19
18
|
import type { HookAdapter, HookParadigm, PlatformCapabilities, DiagnosticResult, PreToolUseEvent, PostToolUseEvent, PreCompactEvent, SessionStartEvent, PreToolUseResponse, PostToolUseResponse, PreCompactResponse, SessionStartResponse, HookRegistration } from "../types.js";
|
|
20
19
|
export declare class CodexAdapter implements HookAdapter {
|
|
@@ -1,20 +1,19 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* adapters/codex — Codex CLI
|
|
2
|
+
* adapters/codex — Codex CLI platform adapter.
|
|
3
3
|
*
|
|
4
|
-
* Implements HookAdapter for Codex CLI's JSON stdin/stdout
|
|
4
|
+
* Implements HookAdapter for Codex CLI's JSON stdin/stdout paradigm.
|
|
5
5
|
*
|
|
6
6
|
* Codex CLI hook specifics:
|
|
7
|
-
* -
|
|
8
|
-
* -
|
|
9
|
-
* -
|
|
10
|
-
* - Output modification: NOT supported (no updatedMCPToolOutput field)
|
|
11
|
-
* - Blocking: `hookSpecificOutput.permissionDecision: "deny"` in response
|
|
12
|
-
* - Context injection: `hookSpecificOutput.additionalContext` in response
|
|
13
|
-
* - Session ID: session_id field from hook input
|
|
14
|
-
* - Project dir: cwd field from hook input
|
|
15
|
-
* - Config: ~/.codex/config.toml (TOML format)
|
|
16
|
-
* - MCP: full support via [mcp_servers] in config.toml
|
|
7
|
+
* - 5 hook events: PreToolUse, PostToolUse, SessionStart, UserPromptSubmit, Stop
|
|
8
|
+
* - Same wire protocol as Claude Code (JSON stdin → stdout)
|
|
9
|
+
* - Config: ~/.codex/hooks.json + ~/.codex/config.toml (TOML for MCP/features)
|
|
17
10
|
* - Session dir: ~/.codex/context-mode/sessions/
|
|
11
|
+
*
|
|
12
|
+
* IMPORTANT: Hook dispatch is NOT yet active in Codex CLI (v0.118.0).
|
|
13
|
+
* codex_hooks feature flag is Stage::UnderDevelopment — hooks are implemented
|
|
14
|
+
* in codex-rs/hooks/ but not wired into the tool execution pipeline.
|
|
15
|
+
* Our adapter is ready; it will work once Codex enables dispatch.
|
|
16
|
+
* Track: https://github.com/openai/codex/issues/16685
|
|
18
17
|
*/
|
|
19
18
|
import { createHash } from "node:crypto";
|
|
20
19
|
import { readFileSync, mkdirSync, copyFileSync, accessSync, constants, } from "node:fs";
|
|
@@ -97,17 +96,16 @@ export class CodexAdapter {
|
|
|
97
96
|
if (response.decision === "deny") {
|
|
98
97
|
return {
|
|
99
98
|
hookSpecificOutput: {
|
|
99
|
+
hookEventName: "PreToolUse",
|
|
100
100
|
permissionDecision: "deny",
|
|
101
101
|
permissionDecisionReason: response.reason ?? "Blocked by context-mode hook",
|
|
102
102
|
},
|
|
103
103
|
};
|
|
104
104
|
}
|
|
105
105
|
if (response.decision === "context" && response.additionalContext) {
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
},
|
|
110
|
-
};
|
|
106
|
+
// Codex does not support additionalContext in PreToolUse (fails open).
|
|
107
|
+
// Context injection works via PostToolUse and SessionStart instead.
|
|
108
|
+
return {};
|
|
111
109
|
}
|
|
112
110
|
// "allow" — return empty object for passthrough
|
|
113
111
|
return {};
|
|
@@ -116,6 +114,7 @@ export class CodexAdapter {
|
|
|
116
114
|
if (response.additionalContext) {
|
|
117
115
|
return {
|
|
118
116
|
hookSpecificOutput: {
|
|
117
|
+
hookEventName: "PostToolUse",
|
|
119
118
|
additionalContext: response.additionalContext,
|
|
120
119
|
},
|
|
121
120
|
};
|
|
@@ -136,6 +135,7 @@ export class CodexAdapter {
|
|
|
136
135
|
if (response.context) {
|
|
137
136
|
return {
|
|
138
137
|
hookSpecificOutput: {
|
|
138
|
+
hookEventName: "SessionStart",
|
|
139
139
|
additionalContext: response.context,
|
|
140
140
|
},
|
|
141
141
|
};
|
|
@@ -225,8 +225,8 @@ export class CodexAdapter {
|
|
|
225
225
|
return [
|
|
226
226
|
{
|
|
227
227
|
check: "Hook support",
|
|
228
|
-
status: "
|
|
229
|
-
message: "Codex CLI
|
|
228
|
+
status: "warn",
|
|
229
|
+
message: "Codex CLI hooks are implemented but dispatch is not yet active (Stage::UnderDevelopment, v0.118.0). Enable flag: [features] codex_hooks = true in ~/.codex/config.toml. Track: openai/codex#16685",
|
|
230
230
|
},
|
|
231
231
|
];
|
|
232
232
|
}
|
package/build/cli.js
CHANGED
|
@@ -50,6 +50,11 @@ const HOOK_MAP = {
|
|
|
50
50
|
posttooluse: "hooks/cursor/posttooluse.mjs",
|
|
51
51
|
sessionstart: "hooks/cursor/sessionstart.mjs",
|
|
52
52
|
},
|
|
53
|
+
"codex": {
|
|
54
|
+
pretooluse: "hooks/codex/pretooluse.mjs",
|
|
55
|
+
posttooluse: "hooks/codex/posttooluse.mjs",
|
|
56
|
+
sessionstart: "hooks/codex/sessionstart.mjs",
|
|
57
|
+
},
|
|
53
58
|
"kiro": {
|
|
54
59
|
pretooluse: "hooks/kiro/pretooluse.mjs",
|
|
55
60
|
posttooluse: "hooks/kiro/posttooluse.mjs",
|
package/build/server.js
CHANGED
|
@@ -1606,7 +1606,8 @@ server.registerTool("ctx_doctor", {
|
|
|
1606
1606
|
lines.push("- [x] Server test: PASS");
|
|
1607
1607
|
}
|
|
1608
1608
|
else {
|
|
1609
|
-
|
|
1609
|
+
const detail = result.stderr?.trim() ? ` (${result.stderr.trim().slice(0, 200)})` : "";
|
|
1610
|
+
lines.push(`- [ ] Server test: FAIL — exit ${result.exitCode}${detail}`);
|
|
1610
1611
|
}
|
|
1611
1612
|
}
|
|
1612
1613
|
catch (err) {
|
package/cli.bundle.mjs
CHANGED
|
@@ -6,7 +6,7 @@ var Pk=Object.create;var jc=Object.defineProperty;var Rk=Object.getOwnPropertyDe
|
|
|
6
6
|
`,"utf-8")}validateHooks(e){let r=[],n=this.readSettings();if(!n)return r.push({check:"BeforeTool hook",status:"fail",message:"Could not read ~/.gemini/settings.json",fix:"context-mode upgrade"}),r;let o=n.hooks,s=o?.[Oe.BEFORE_TOOL];if(s&&s.length>0){let a=s.some(c=>c.hooks?.some(u=>u.command?.includes("context-mode")));r.push({check:"BeforeTool hook",status:a?"pass":"fail",message:a?"BeforeTool hook configured":"BeforeTool exists but does not point to context-mode",fix:a?void 0:"context-mode upgrade"})}else r.push({check:"BeforeTool hook",status:"fail",message:"No BeforeTool hooks found",fix:"context-mode upgrade"});let i=o?.[Oe.SESSION_START];if(i&&i.length>0){let a=i.some(c=>c.hooks?.some(u=>u.command?.includes("context-mode")));r.push({check:"SessionStart hook",status:a?"pass":"fail",message:a?"SessionStart hook configured":"SessionStart exists but does not point to context-mode",fix:a?void 0:"context-mode upgrade"})}else r.push({check:"SessionStart hook",status:"fail",message:"No SessionStart hooks found",fix:"context-mode upgrade"});return r}checkPluginRegistration(){let e=this.readSettings();if(!e)return{check:"Plugin registration",status:"warn",message:"Could not read ~/.gemini/settings.json"};let r=e.extensions;return r&&(Array.isArray(r)?r.some(o=>typeof o=="string"&&o.includes("context-mode")):Object.keys(r).some(o=>o.includes("context-mode")))?{check:"Plugin registration",status:"pass",message:"context-mode found in extensions"}:{check:"Plugin registration",status:"warn",message:"context-mode not found in extensions (might be using standalone MCP mode)"}}getInstalledVersion(){try{let e=Zo(Uo(),".gemini","extensions","context-mode","package.json"),r=JSON.parse(su(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}return"not installed"}configureAllHooks(e){let r=this.readSettings()??{},n=r.hooks??{},o=[],s=[{name:Oe.BEFORE_TOOL},{name:Oe.SESSION_START}];for(let i of s){let c={matcher:"",hooks:[{type:"command",command:Zn(i.name,e)}]},u=n[i.name];if(u&&Array.isArray(u)){let l=u.findIndex(d=>d.hooks?.some(h=>h.command?.includes("context-mode")));l>=0?(u[l]=c,o.push(`Updated existing ${i.name} hook entry`)):(u.push(c),o.push(`Added ${i.name} hook entry`)),n[i.name]=u}else n[i.name]=[c],o.push(`Created ${i.name} hooks section`)}return r.hooks=n,this.writeSettings(r),o}backupSettings(){let e=this.getSettingsPath();try{rg(e,ng.R_OK);let r=e+".bak";return E$(e,r),r}catch{return null}}setHookPermissions(e){let r=[],n=xi(e,"hooks","gemini-cli");for(let o of Object.values(ou)){let s=Zo(n,o);try{rg(s,ng.R_OK),T$(s,493),r.push(s)}catch{}}return r}updatePluginRegistry(e,r){try{let n=Zo(Uo(),".gemini","extensions","context-mode","package.json"),o=JSON.parse(su(n,"utf-8"));o.version=r,o.installPath=e,o.lastUpdated=new Date().toISOString(),eg(n,JSON.stringify(o,null,2)+`
|
|
7
7
|
`,"utf-8")}catch{}}getProjectDir(){return process.env.GEMINI_PROJECT_DIR??process.env.CLAUDE_PROJECT_DIR}extractSessionId(e){return e.session_id?e.session_id:`pid-${process.ppid}`}}});var Vr,CM,IM,ig=S(()=>{"use strict";Vr={BEFORE:"tool.execute.before",AFTER:"tool.execute.after",COMPACTING:"experimental.session.compacting"},CM=[Vr.BEFORE,Vr.AFTER],IM=[Vr.COMPACTING]});var ug={};Me(ug,{OpenCodeAdapter:()=>au});import{createHash as ag}from"node:crypto";import{readFileSync as cg,writeFileSync as R$,mkdirSync as O$,copyFileSync as C$,accessSync as I$,constants as z$}from"node:fs";import{resolve as qt,join as cr}from"node:path";import{homedir as br}from"node:os";function P$(t){return t.replace(/\/\/.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").replace(/,(\s*[}\]])/g,"$1")}var au,lg=S(()=>{"use strict";ig();au=class{get name(){return this.platform==="kilo"?"KiloCode":"OpenCode"}paradigm="ts-plugin";settingsPath;capabilities={preToolUse:!0,postToolUse:!0,preCompact:!0,sessionStart:!1,canModifyArgs:!0,canModifyOutput:!0,canInjectSessionContext:!1};platform;constructor(e="opencode"){this.platform=e}parsePreToolUseInput(e){let r=e;return{toolName:r.tool??"",toolInput:r.args??{},sessionId:this.extractSessionId(r),projectDir:process.env.OPENCODE_PROJECT_DIR||process.cwd(),raw:e}}parsePostToolUseInput(e){let r=e;return{toolName:r.tool??"",toolInput:r.args??{},toolOutput:r.output,isError:void 0,sessionId:this.extractSessionId(r),projectDir:process.env.OPENCODE_PROJECT_DIR||process.cwd(),raw:e}}parsePreCompactInput(e){let r=e;return{sessionId:this.extractSessionId(r),projectDir:process.env.OPENCODE_PROJECT_DIR||process.cwd(),raw:e}}parseSessionStartInput(e){let r=e,n=r.source??"startup",o;switch(n){case"compact":o="compact";break;case"resume":o="resume";break;case"clear":o="clear";break;default:o="startup"}return{sessionId:this.extractSessionId(r),source:o,projectDir:process.env.OPENCODE_PROJECT_DIR||process.cwd(),raw:e}}formatPreToolUseResponse(e){if(e.decision==="deny")throw new Error(e.reason??"Blocked by context-mode hook");if(e.decision==="modify"&&e.updatedInput)return{args:e.updatedInput};if(e.decision==="ask")throw new Error(e.reason??"Action requires user confirmation (security policy)")}formatPostToolUseResponse(e){let r={};return e.updatedOutput&&(r.output=e.updatedOutput),e.additionalContext&&(r.additionalContext=e.additionalContext),Object.keys(r).length>0?r:void 0}formatPreCompactResponse(e){return e.context??""}formatSessionStartResponse(e){return e.context??""}getSettingsPath(){return this.settingsPath??qt(`${this.platform}.json`)}paths(){return this.platform==="kilo"?[qt("kilo.json"),qt("kilo.jsonc"),qt(".kilo","kilo.json"),qt(".kilo","kilo.jsonc"),cr(br(),".config","kilo","kilo.json"),cr(br(),".config","kilo","kilo.jsonc")]:[qt("opencode.json"),qt("opencode.jsonc"),qt(".opencode","opencode.json"),qt(".opencode","opencode.jsonc"),cr(br(),".config","opencode","opencode.json"),cr(br(),".config","opencode","opencode.jsonc")]}getSessionDir(){let e;process.platform==="win32"?e=process.env.APPDATA||cr(br(),"AppData","Roaming"):e=process.env.XDG_CONFIG_HOME||cr(br(),".config");let r=cr(e,this.platform,"context-mode","sessions");return O$(r,{recursive:!0}),r}getSessionDBPath(e){let r=ag("sha256").update(e).digest("hex").slice(0,16);return cr(this.getSessionDir(),`${r}.db`)}getSessionEventsPath(e){let r=ag("sha256").update(e).digest("hex").slice(0,16);return cr(this.getSessionDir(),`${r}-events.md`)}generateHookConfig(e){return{[Vr.BEFORE]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}],[Vr.AFTER]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}],[Vr.COMPACTING]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}]}}readSettings(){this.settingsPath=void 0;let e=this.paths(),r=new Set(e.filter(s=>s.includes(br()))),n=null,o;for(let s of e)try{let i=cg(s,"utf-8"),a=s.endsWith(".jsonc")?P$(i):i,c=JSON.parse(a);n||(n=c,o=s);let u=r.has(s);if(this.hasContextModePlugin(c)||u)return this.settingsPath=s,c}catch{continue}return n?(this.settingsPath=o,n):null}writeSettings(e){R$(this.getSettingsPath(),JSON.stringify(e,null,2)+`
|
|
8
8
|
`,"utf-8")}validateHooks(e){let r=[],n=this.readSettings();if(!n)return r.push({check:"Plugin configuration",status:"fail",message:`Could not read ${this.platform}.json or ${this.platform}.jsonc`,fix:"context-mode upgrade"}),r;let o=this.hasContextModePlugin(n);return Array.isArray(n.plugin)?r.push({check:"Plugin registration",status:o?"pass":"fail",message:o?"context-mode found in plugin array":"context-mode not found in plugin array",fix:o?void 0:"context-mode upgrade"}):r.push({check:"Plugin registration",status:"fail",message:`No plugin array found in ${this.platform}.json or ${this.platform}.jsonc`,fix:"context-mode upgrade"}),r.push({check:"SessionStart hook",status:"warn",message:`SessionStart not supported in ${this.name} (see issues #14808, #5409)`}),r}checkPluginRegistration(){let e=this.readSettings();return e?this.hasContextModePlugin(e)?{check:"Plugin registration",status:"pass",message:"context-mode found in plugin array"}:{check:"Plugin registration",status:"fail",message:`context-mode not found in ${this.platform}.json plugin array`,fix:"context-mode upgrade"}:{check:"Plugin registration",status:"warn",message:`Could not read ${this.platform}.json or ${this.platform}.jsonc`}}getInstalledVersion(){try{let e=qt(br(),".cache",this.platform,"node_modules","context-mode","package.json"),r=JSON.parse(cg(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}return"not installed"}configureAllHooks(e){let r=this.readSettings()??{},n=[],o=r.plugin??[];return o.some(s=>s.includes("context-mode"))?n.push("context-mode already in plugin array"):(o.push("context-mode"),n.push("Added context-mode to plugin array")),r.plugin=o,this.writeSettings(r),n}backupSettings(){let e=this.checkPluginRegistration();if(!this.settingsPath)return null;if(e.status==="pass")return this.settingsPath;try{I$(this.settingsPath,z$.R_OK);let r=this.settingsPath+".bak";return C$(this.settingsPath,r),r}catch{return null}}setHookPermissions(e){return[]}updatePluginRegistry(e,r){}hasContextModePlugin(e){let r=e.plugin;return Array.isArray(r)&&r.some(n=>typeof n=="string"&&n.includes("context-mode"))}extractSessionId(e){return e.sessionID?e.sessionID:`pid-${process.ppid}`}}});var Wr,LM,FM,dg=S(()=>{"use strict";Wr={TOOL_CALL_BEFORE:"tool_call:before",TOOL_CALL_AFTER:"tool_call:after",COMMAND_NEW:"command:new",COMMAND_RESET:"command:reset",COMMAND_STOP:"command:stop"},LM=[Wr.TOOL_CALL_BEFORE,Wr.TOOL_CALL_AFTER],FM=[Wr.COMMAND_NEW]});var mg={};Me(mg,{OpenClawAdapter:()=>uu});import{createHash as pg}from"node:crypto";import{readFileSync as cu,writeFileSync as A$,mkdirSync as N$,copyFileSync as j$,accessSync as M$,constants as D$}from"node:fs";import{resolve as kr,join as Ho}from"node:path";import{homedir as vi}from"node:os";var uu,fg=S(()=>{"use strict";dg();uu=class{name="OpenClaw";paradigm="ts-plugin";capabilities={preToolUse:!0,postToolUse:!0,preCompact:!0,sessionStart:!0,canModifyArgs:!0,canModifyOutput:!1,canInjectSessionContext:!0};parsePreToolUseInput(e){let r=e;return{toolName:r.toolName??r.tool_name??"",toolInput:r.params??r.tool_input??{},sessionId:this.extractSessionId(r),projectDir:process.cwd(),raw:e}}parsePostToolUseInput(e){let r=e;return{toolName:r.toolName??r.tool_name??"",toolInput:r.params??r.tool_input??{},toolOutput:r.output??r.tool_output,isError:r.isError??r.is_error,sessionId:this.extractSessionId(r),projectDir:process.cwd(),raw:e}}parsePreCompactInput(e){let r=e;return{sessionId:this.extractSessionId(r),projectDir:process.cwd(),raw:e}}parseSessionStartInput(e){let r=e,n=r.source??"startup",o;switch(n){case"compact":o="compact";break;case"resume":o="resume";break;case"clear":o="clear";break;default:o="startup"}return{sessionId:this.extractSessionId(r),source:o,projectDir:process.cwd(),raw:e}}formatPreToolUseResponse(e){if(e.decision==="deny")return{block:!0,blockReason:e.reason??"Blocked by context-mode hook"};if(e.decision==="modify"&&e.updatedInput)return{params:e.updatedInput};if(e.decision==="ask")return{block:!0,blockReason:e.reason??"Action requires user confirmation (security policy)"};e.decision==="context"&&e.additionalContext}formatPostToolUseResponse(e){let r={};return e.additionalContext&&(r.additionalContext=e.additionalContext),Object.keys(r).length>0?r:void 0}formatPreCompactResponse(e){return e.context??""}formatSessionStartResponse(e){return e.context??""}getSettingsPath(){return kr("openclaw.json")}getSessionDir(){let e=Ho(vi(),".openclaw","context-mode","sessions");return N$(e,{recursive:!0}),e}getSessionDBPath(e){let r=pg("sha256").update(e).digest("hex").slice(0,16);return Ho(this.getSessionDir(),`${r}.db`)}getSessionEventsPath(e){let r=pg("sha256").update(e).digest("hex").slice(0,16);return Ho(this.getSessionDir(),`${r}-events.md`)}generateHookConfig(e){return{[Wr.TOOL_CALL_BEFORE]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}],[Wr.TOOL_CALL_AFTER]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}],[Wr.COMMAND_NEW]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}]}}readSettings(){let e=[kr("openclaw.json"),kr(".openclaw","openclaw.json"),Ho(vi(),".openclaw","openclaw.json")];for(let r of e)try{let n=cu(r,"utf-8");return JSON.parse(n)}catch{continue}return null}writeSettings(e){let r=kr("openclaw.json");A$(r,JSON.stringify(e,null,2)+`
|
|
9
|
-
`,"utf-8")}validateHooks(e){let r=[],n=this.readSettings();if(!n)return r.push({check:"Plugin configuration",status:"fail",message:"Could not read openclaw.json",fix:"context-mode upgrade"}),r;let o=n.plugins,s=o?.entries;if(s){let a=Object.keys(s).some(c=>c.includes("context-mode"));if(r.push({check:"Plugin registration",status:a?"pass":"fail",message:a?"context-mode found in plugins.entries":"context-mode not found in plugins.entries",fix:a?void 0:"context-mode upgrade"}),a){let u=s["context-mode"]?.enabled!==!1;r.push({check:"Plugin enabled",status:u?"pass":"warn",message:u?"context-mode plugin is enabled":"context-mode plugin is disabled"})}}else r.push({check:"Plugin registration",status:"fail",message:"No plugins.entries found in openclaw.json",fix:"context-mode upgrade"});return o?.slots?.contextEngine==="context-mode"?r.push({check:"Context engine",status:"pass",message:"context-mode registered as context engine (owns compaction)"}):r.push({check:"Context engine",status:"warn",message:"context-mode not set as context engine \u2014 compaction will use default engine"}),r}checkPluginRegistration(){let e=this.readSettings();if(!e)return{check:"Plugin registration",status:"warn",message:"Could not read openclaw.json"};let n=e.plugins?.entries;return n&&Object.keys(n).some(s=>s.includes("context-mode"))?{check:"Plugin registration",status:"pass",message:"context-mode found in plugins.entries"}:{check:"Plugin registration",status:"fail",message:"context-mode not found in openclaw.json plugins.entries",fix:"context-mode upgrade"}}getInstalledVersion(){try{let e=kr(vi(),".openclaw","extensions","context-mode","package.json"),r=JSON.parse(cu(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}try{let e=kr("node_modules","context-mode","package.json"),r=JSON.parse(cu(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}return"not installed"}configureAllHooks(e){let r=this.readSettings()??{},n=[];r.plugins||(r.plugins={});let o=r.plugins;o.entries||(o.entries={});let s=o.entries;if(!s["context-mode"])s["context-mode"]={enabled:!0},n.push("Added context-mode to plugins.entries");else{let a=s["context-mode"];a.enabled===!1?(a.enabled=!0,n.push("Enabled context-mode plugin")):n.push("context-mode already configured in plugins.entries")}o.slots||(o.slots={});let i=o.slots;return i.contextEngine?i.contextEngine!=="context-mode"&&n.push(`Context engine already set to "${i.contextEngine}" \u2014 not overwriting`):(i.contextEngine="context-mode",n.push("Set context-mode as context engine (owns compaction)")),this.writeSettings(r),n}backupSettings(){let e=[kr("openclaw.json"),kr(".openclaw","openclaw.json"),Ho(vi(),".openclaw","openclaw.json")];for(let r of e)try{M$(r,D$.R_OK);let n=r+".bak";return j$(r,n),n}catch{continue}return null}setHookPermissions(e){return[]}updatePluginRegistry(e,r){}extractSessionId(e){return e.sessionId?e.sessionId:`pid-${process.ppid}`}}});var yg={};Me(yg,{CodexAdapter:()=>pu});import{createHash as hg}from"node:crypto";import{readFileSync as lu,mkdirSync as L$,copyFileSync as F$,accessSync as Z$,constants as U$}from"node:fs";import{resolve as gg,join as du,dirname as H$}from"node:path";import{fileURLToPath as q$}from"node:url";import{homedir as _g}from"node:os";var pu,xg=S(()=>{"use strict";pu=class{name="Codex CLI";paradigm="json-stdio";capabilities={preToolUse:!0,postToolUse:!0,preCompact:!1,sessionStart:!0,canModifyArgs:!1,canModifyOutput:!1,canInjectSessionContext:!0};parsePreToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parsePostToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},toolOutput:r.tool_response,sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parsePreCompactInput(e){let r=e;return{sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parseSessionStartInput(e){let r=e,n=r.source??"startup",o;switch(n){case"compact":o="compact";break;case"resume":o="resume";break;case"clear":o="clear";break;default:o="startup"}return{sessionId:this.extractSessionId(r),source:o,projectDir:r.cwd,raw:e}}formatPreToolUseResponse(e){return e.decision==="deny"?{hookSpecificOutput:{permissionDecision:"deny",permissionDecisionReason:e.reason??"Blocked by context-mode hook"}}:e.decision==="context"&&e.additionalContext?{
|
|
9
|
+
`,"utf-8")}validateHooks(e){let r=[],n=this.readSettings();if(!n)return r.push({check:"Plugin configuration",status:"fail",message:"Could not read openclaw.json",fix:"context-mode upgrade"}),r;let o=n.plugins,s=o?.entries;if(s){let a=Object.keys(s).some(c=>c.includes("context-mode"));if(r.push({check:"Plugin registration",status:a?"pass":"fail",message:a?"context-mode found in plugins.entries":"context-mode not found in plugins.entries",fix:a?void 0:"context-mode upgrade"}),a){let u=s["context-mode"]?.enabled!==!1;r.push({check:"Plugin enabled",status:u?"pass":"warn",message:u?"context-mode plugin is enabled":"context-mode plugin is disabled"})}}else r.push({check:"Plugin registration",status:"fail",message:"No plugins.entries found in openclaw.json",fix:"context-mode upgrade"});return o?.slots?.contextEngine==="context-mode"?r.push({check:"Context engine",status:"pass",message:"context-mode registered as context engine (owns compaction)"}):r.push({check:"Context engine",status:"warn",message:"context-mode not set as context engine \u2014 compaction will use default engine"}),r}checkPluginRegistration(){let e=this.readSettings();if(!e)return{check:"Plugin registration",status:"warn",message:"Could not read openclaw.json"};let n=e.plugins?.entries;return n&&Object.keys(n).some(s=>s.includes("context-mode"))?{check:"Plugin registration",status:"pass",message:"context-mode found in plugins.entries"}:{check:"Plugin registration",status:"fail",message:"context-mode not found in openclaw.json plugins.entries",fix:"context-mode upgrade"}}getInstalledVersion(){try{let e=kr(vi(),".openclaw","extensions","context-mode","package.json"),r=JSON.parse(cu(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}try{let e=kr("node_modules","context-mode","package.json"),r=JSON.parse(cu(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}return"not installed"}configureAllHooks(e){let r=this.readSettings()??{},n=[];r.plugins||(r.plugins={});let o=r.plugins;o.entries||(o.entries={});let s=o.entries;if(!s["context-mode"])s["context-mode"]={enabled:!0},n.push("Added context-mode to plugins.entries");else{let a=s["context-mode"];a.enabled===!1?(a.enabled=!0,n.push("Enabled context-mode plugin")):n.push("context-mode already configured in plugins.entries")}o.slots||(o.slots={});let i=o.slots;return i.contextEngine?i.contextEngine!=="context-mode"&&n.push(`Context engine already set to "${i.contextEngine}" \u2014 not overwriting`):(i.contextEngine="context-mode",n.push("Set context-mode as context engine (owns compaction)")),this.writeSettings(r),n}backupSettings(){let e=[kr("openclaw.json"),kr(".openclaw","openclaw.json"),Ho(vi(),".openclaw","openclaw.json")];for(let r of e)try{M$(r,D$.R_OK);let n=r+".bak";return j$(r,n),n}catch{continue}return null}setHookPermissions(e){return[]}updatePluginRegistry(e,r){}extractSessionId(e){return e.sessionId?e.sessionId:`pid-${process.ppid}`}}});var yg={};Me(yg,{CodexAdapter:()=>pu});import{createHash as hg}from"node:crypto";import{readFileSync as lu,mkdirSync as L$,copyFileSync as F$,accessSync as Z$,constants as U$}from"node:fs";import{resolve as gg,join as du,dirname as H$}from"node:path";import{fileURLToPath as q$}from"node:url";import{homedir as _g}from"node:os";var pu,xg=S(()=>{"use strict";pu=class{name="Codex CLI";paradigm="json-stdio";capabilities={preToolUse:!0,postToolUse:!0,preCompact:!1,sessionStart:!0,canModifyArgs:!1,canModifyOutput:!1,canInjectSessionContext:!0};parsePreToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parsePostToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},toolOutput:r.tool_response,sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parsePreCompactInput(e){let r=e;return{sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parseSessionStartInput(e){let r=e,n=r.source??"startup",o;switch(n){case"compact":o="compact";break;case"resume":o="resume";break;case"clear":o="clear";break;default:o="startup"}return{sessionId:this.extractSessionId(r),source:o,projectDir:r.cwd,raw:e}}formatPreToolUseResponse(e){return e.decision==="deny"?{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:e.reason??"Blocked by context-mode hook"}}:e.decision==="context"&&e.additionalContext?{}:{}}formatPostToolUseResponse(e){return e.additionalContext?{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:e.additionalContext}}:{}}formatPreCompactResponse(e){return e.context?{hookSpecificOutput:{additionalContext:e.context}}:{}}formatSessionStartResponse(e){return e.context?{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.context}}:{}}getSettingsPath(){return gg(_g(),".codex","config.toml")}getSessionDir(){let e=du(_g(),".codex","context-mode","sessions");return L$(e,{recursive:!0}),e}getSessionDBPath(e){let r=hg("sha256").update(e).digest("hex").slice(0,16);return du(this.getSessionDir(),`${r}.db`)}getSessionEventsPath(e){let r=hg("sha256").update(e).digest("hex").slice(0,16);return du(this.getSessionDir(),`${r}-events.md`)}generateHookConfig(e){return{PreToolUse:[{matcher:"",hooks:[{type:"command",command:`node ${e}/hooks/pretooluse.mjs`}]}],PostToolUse:[{matcher:"",hooks:[{type:"command",command:`node ${e}/hooks/posttooluse.mjs`}]}],SessionStart:[{matcher:"",hooks:[{type:"command",command:`node ${e}/hooks/sessionstart.mjs`}]}]}}readSettings(){try{return{_raw_toml:lu(this.getSettingsPath(),"utf-8")}}catch{return null}}writeSettings(e){}validateHooks(e){return[{check:"Hook support",status:"warn",message:"Codex CLI hooks are implemented but dispatch is not yet active (Stage::UnderDevelopment, v0.118.0). Enable flag: [features] codex_hooks = true in ~/.codex/config.toml. Track: openai/codex#16685"}]}checkPluginRegistration(){try{let e=lu(this.getSettingsPath(),"utf-8"),r=e.includes("context-mode"),n=e.includes("[mcp_servers]")||e.includes("[mcp_servers.");return r&&n?{check:"MCP registration",status:"pass",message:"context-mode found in [mcp_servers] config"}:n?{check:"MCP registration",status:"fail",message:"[mcp_servers] section exists but context-mode not found",fix:"Add context-mode to [mcp_servers] in ~/.codex/config.toml"}:{check:"MCP registration",status:"fail",message:"No [mcp_servers] section in config.toml",fix:"Add [mcp_servers.context-mode] to ~/.codex/config.toml"}}catch{return{check:"MCP registration",status:"warn",message:"Could not read ~/.codex/config.toml"}}}getInstalledVersion(){return"not installed"}configureAllHooks(e){return[]}backupSettings(){let e=this.getSettingsPath();try{Z$(e,U$.R_OK);let r=e+".bak";return F$(e,r),r}catch{return null}}setHookPermissions(e){return[]}updatePluginRegistry(e,r){}getRoutingInstructions(){let e=gg(H$(q$(import.meta.url)),"..","..","..","configs","codex","AGENTS.md");try{return lu(e,"utf-8")}catch{return`# context-mode
|
|
10
10
|
|
|
11
11
|
Use context-mode MCP tools (execute, execute_file, batch_execute, fetch_and_index, search) instead of bash/cat/curl for data-heavy operations.`}}extractSessionId(e){return e.session_id?e.session_id:`pid-${process.ppid}`}}});function Un(t,e){let r=Si[t];if(!r)throw new Error(`No script defined for hook type: ${t}`);return e?`node "${e}/hooks/${r}"`:`context-mode hook vscode-copilot ${t.toLowerCase()}`}var me,Si,XM,QM,vg=S(()=>{"use strict";me={PRE_TOOL_USE:"PreToolUse",POST_TOOL_USE:"PostToolUse",PRE_COMPACT:"PreCompact",SESSION_START:"SessionStart",STOP:"Stop",SUBAGENT_START:"SubagentStart",SUBAGENT_STOP:"SubagentStop"},Si={[me.PRE_TOOL_USE]:"pretooluse.mjs",[me.POST_TOOL_USE]:"posttooluse.mjs",[me.PRE_COMPACT]:"precompact.mjs",[me.SESSION_START]:"sessionstart.mjs"},XM=[me.PRE_TOOL_USE,me.SESSION_START],QM=[me.POST_TOOL_USE,me.PRE_COMPACT]});var kg={};Me(kg,{VSCodeCopilotAdapter:()=>_u});import{createHash as Sg}from"node:crypto";import{readFileSync as bi,writeFileSync as bg,mkdirSync as mu,copyFileSync as B$,accessSync as fu,existsSync as V$,chmodSync as W$,constants as hu}from"node:fs";import{resolve as Ct,join as Gr}from"node:path";import{homedir as gu}from"node:os";var _u,$g=S(()=>{"use strict";vg();_u=class{name="VS Code Copilot";paradigm="json-stdio";capabilities={preToolUse:!0,postToolUse:!0,preCompact:!0,sessionStart:!0,canModifyArgs:!0,canModifyOutput:!0,canInjectSessionContext:!0};parsePreToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},sessionId:this.extractSessionId(r),projectDir:process.env.CLAUDE_PROJECT_DIR||process.cwd(),raw:e}}parsePostToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},toolOutput:r.tool_output,isError:r.is_error,sessionId:this.extractSessionId(r),projectDir:process.env.CLAUDE_PROJECT_DIR||process.cwd(),raw:e}}parsePreCompactInput(e){let r=e;return{sessionId:this.extractSessionId(r),projectDir:process.env.CLAUDE_PROJECT_DIR||process.cwd(),raw:e}}parseSessionStartInput(e){let r=e,n=r.source??"startup",o;switch(n){case"compact":o="compact";break;case"resume":o="resume";break;case"clear":o="clear";break;default:o="startup"}return{sessionId:this.extractSessionId(r),source:o,projectDir:process.env.CLAUDE_PROJECT_DIR||process.cwd(),raw:e}}formatPreToolUseResponse(e){if(e.decision==="deny")return{permissionDecision:"deny",reason:e.reason??"Blocked by context-mode hook"};if(e.decision==="modify"&&e.updatedInput)return{hookSpecificOutput:{hookEventName:me.PRE_TOOL_USE,updatedInput:e.updatedInput}};if(e.decision==="context"&&e.additionalContext)return{hookSpecificOutput:{hookEventName:me.PRE_TOOL_USE,additionalContext:e.additionalContext}};if(e.decision==="ask")return{permissionDecision:"deny",reason:e.reason??"Action requires user confirmation (security policy)"}}formatPostToolUseResponse(e){if(e.updatedOutput)return{hookSpecificOutput:{hookEventName:me.POST_TOOL_USE,decision:"block",reason:e.updatedOutput}};if(e.additionalContext)return{hookSpecificOutput:{hookEventName:me.POST_TOOL_USE,additionalContext:e.additionalContext}}}formatPreCompactResponse(e){return e.context??""}formatSessionStartResponse(e){return e.context??""}getSettingsPath(){return Ct(".github","hooks","context-mode.json")}getSessionDir(){let e=Ct(".github","context-mode","sessions"),r=Gr(gu(),".vscode","context-mode","sessions"),n=V$(Ct(".github"))?e:r;return mu(n,{recursive:!0}),n}getSessionDBPath(e){let r=Sg("sha256").update(e).digest("hex").slice(0,16);return Gr(this.getSessionDir(),`${r}.db`)}getSessionEventsPath(e){let r=Sg("sha256").update(e).digest("hex").slice(0,16);return Gr(this.getSessionDir(),`${r}-events.md`)}generateHookConfig(e){return{[me.PRE_TOOL_USE]:[{matcher:"",hooks:[{type:"command",command:Un(me.PRE_TOOL_USE,e)}]}],[me.POST_TOOL_USE]:[{matcher:"",hooks:[{type:"command",command:Un(me.POST_TOOL_USE,e)}]}],[me.PRE_COMPACT]:[{matcher:"",hooks:[{type:"command",command:Un(me.PRE_COMPACT,e)}]}],[me.SESSION_START]:[{matcher:"",hooks:[{type:"command",command:Un(me.SESSION_START,e)}]}]}}readSettings(){let e=[this.getSettingsPath(),Ct(".claude","settings.json")];for(let r of e)try{let n=bi(r,"utf-8");return JSON.parse(n)}catch{continue}return null}writeSettings(e){let r=this.getSettingsPath(),n=Ct(".github","hooks");mu(n,{recursive:!0}),bg(r,JSON.stringify(e,null,2)+`
|
|
12
12
|
`,"utf-8")}validateHooks(e){let r=[],n=Ct(".github","hooks");try{fu(n,hu.R_OK)}catch{return r.push({check:"Hooks directory",status:"fail",message:".github/hooks/ directory not found",fix:"context-mode upgrade"}),r}let o=Ct(n,"context-mode.json");try{let s=bi(o,"utf-8"),a=JSON.parse(s).hooks;a?.[me.PRE_TOOL_USE]?r.push({check:"PreToolUse hook",status:"pass",message:"PreToolUse hook configured in context-mode.json"}):r.push({check:"PreToolUse hook",status:"fail",message:"PreToolUse not found in context-mode.json",fix:"context-mode upgrade"}),a?.[me.SESSION_START]?r.push({check:"SessionStart hook",status:"pass",message:"SessionStart hook configured in context-mode.json"}):r.push({check:"SessionStart hook",status:"fail",message:"SessionStart not found in context-mode.json",fix:"context-mode upgrade"})}catch{r.push({check:"Hook configuration",status:"fail",message:"Could not read .github/hooks/context-mode.json",fix:"context-mode upgrade"})}return r.push({check:"API stability",status:"warn",message:"VS Code Copilot hooks are in preview \u2014 API may change without notice"}),r.push({check:"Matcher support",status:"warn",message:"Matchers are parsed but IGNORED \u2014 all hooks fire on all tools"}),r}checkPluginRegistration(){try{let e=Ct(".vscode","mcp.json"),r=bi(e,"utf-8"),o=JSON.parse(r).servers;return o&&Object.keys(o).some(i=>i.includes("context-mode"))?{check:"MCP registration",status:"pass",message:"context-mode found in .vscode/mcp.json"}:{check:"MCP registration",status:"fail",message:"context-mode not found in .vscode/mcp.json",fix:"Add context-mode server to .vscode/mcp.json"}}catch{return{check:"MCP registration",status:"warn",message:"Could not read .vscode/mcp.json"}}}getInstalledVersion(){let e=[Gr(gu(),".vscode","extensions"),Gr(gu(),".vscode-insiders","extensions")];for(let r of e)try{let n=bi(Gr(r,"extensions.json"),"utf-8"),s=JSON.parse(n).find(i=>typeof i.identifier=="object"&&i.identifier!==null&&i.identifier.id?.toString().includes("context-mode"));if(s&&typeof s.version=="string")return s.version}catch{continue}return"not installed"}configureAllHooks(e){let r=[],n={hooks:{}},o=n.hooks,s=[me.PRE_TOOL_USE,me.POST_TOOL_USE,me.PRE_COMPACT,me.SESSION_START];for(let c of s)Si[c]&&(o[c]=[{matcher:"",hooks:[{type:"command",command:Un(c,e)}]}],r.push(`Configured ${c} hook`));let i=Ct(".github","hooks");mu(i,{recursive:!0});let a=Ct(i,"context-mode.json");return bg(a,JSON.stringify(n,null,2)+`
|
|
@@ -522,7 +522,7 @@ ${ae}
|
|
|
522
522
|
`).length;if(s&&n.length===0)return H("ctx_batch_execute",{content:[{type:"text",text:`Batch timed out after ${r}ms. No output captured.`}],isError:!0});Zt(a);let u=Nn(),l=`batch:${t.map(y=>y.label).join(",").slice(0,80)}`,d=u.index({content:i,source:l}),m=u.getChunksBySource(d.sourceId),h=["## Indexed Sections",""],p=[];for(let y of m){let b=Buffer.byteLength(y.content);h.push(`- ${y.title} (${(b/1024).toFixed(1)}KB)`),p.push(y.title)}let f=gk(u,e,l),g=u.getDistinctiveTerms?u.getDistinctiveTerms(d.sourceId):[],_=[`Executed ${t.length} commands (${c} lines, ${(a/1024).toFixed(1)}KB). Indexed ${d.totalChunks} sections. Searched ${e.length} queries.`,"",...h,"",...f,g.length>0?`
|
|
523
523
|
Searchable terms for follow-up: ${g.join(", ")}`:""].join(`
|
|
524
524
|
`);return H("ctx_batch_execute",{content:[{type:"text",text:_}]})}catch(n){let o=n instanceof Error?n.message:String(n);return H("ctx_batch_execute",{content:[{type:"text",text:`Batch execution error: ${o}`}],isError:!0})}});rt.registerTool("ctx_stats",{title:"Session Statistics",description:"Returns context consumption statistics for the current session. Shows total bytes returned to context, breakdown by tool, call counts, estimated token usage, and context savings ratio.",inputSchema:U.object({reset:U.boolean().optional().describe("Reset all stats and FTS5 store to zero. Use after /clear.")})},async({reset:t})=>{if(mk(),t)return pk(),H("ctx_stats",{content:[{type:"text",text:"Session stats and search index reset."}]});let e=Object.values(se.bytesReturned).reduce((m,h)=>m+h,0),r=Object.values(se.calls).reduce((m,h)=>m+h,0),o=((Date.now()-se.sessionStart)/6e4).toFixed(1),s=se.bytesIndexed+se.bytesSandboxed,i=s+e,a=i/Math.max(e,1),c=i>0?((1-e/i)*100).toFixed(0):"0",u=m=>m>=1024*1024?`${(m/1024/1024).toFixed(1)}MB`:`${(m/1024).toFixed(1)}KB`,l=[`## context-mode \u2014 Session Report (${o} min)`];if(l.push("","### Context Window Protection",""),r===0)l.push("No context-mode tool calls yet. Use `batch_execute`, `execute`, or `fetch_and_index` to keep raw output out of your context window.");else{l.push("| Metric | Value |","|--------|------:|",`| Total data processed | **${u(i)}** |`,`| Kept in sandbox (never entered context) | **${u(s)}** |`,`| Entered context | ${u(e)} |`,`| Estimated tokens saved | ~${Math.round(s/4).toLocaleString()} |`,`| **Context savings** | **${a.toFixed(1)}x (${c}% reduction)** |`);let m=new Set([...Object.keys(se.calls),...Object.keys(se.bytesReturned)]);if(m.size>0){l.push("","| Tool | Calls | Context | Tokens |","|------|------:|--------:|-------:|");for(let h of Array.from(m).sort()){let p=se.calls[h]||0,f=se.bytesReturned[h]||0,g=Math.round(f/4);l.push(`| ${h} | ${p} | ${u(f)} | ~${g.toLocaleString()} |`)}l.push(`| **Total** | **${r}** | **${u(e)}** | **~${Math.round(e/4).toLocaleString()}** |`)}if(s>0&&l.push("",`Without context-mode, **${u(i)}** of raw output would flood your context window. Instead, **${c}%** stayed in sandbox.`),se.cacheHits>0||se.cacheBytesSaved>0){let h=i+se.cacheBytesSaved,p=h/Math.max(e,1),f=Math.max(0,24-Math.floor((Date.now()-se.sessionStart)/(3600*1e3)));l.push("","### TTL Cache","","| Metric | Value |","|--------|------:|",`| Cache hits | **${se.cacheHits}** |`,`| Data avoided by cache | **${u(se.cacheBytesSaved)}** |`,`| Network requests saved | **${se.cacheHits}** |`,`| TTL remaining | **~${f}h** |`,"",`Content was already indexed in the knowledge base \u2014 ${se.cacheHits} fetch${se.cacheHits>1?"es":""} skipped entirely. **${u(se.cacheBytesSaved)}** of network I/O avoided. Search results served directly from local FTS5 index.`),p>a&&l.push("",`**Total context savings (sandbox + cache): ${p.toFixed(1)}x** \u2014 ${u(h)} processed, only ${u(e)} entered context.`)}}try{let m=process.env.CLAUDE_PROJECT_DIR||process.cwd(),h=ck("sha256").update(m).digest("hex").slice(0,16),p=nk(),f=xr(oi(),".claude","context-mode","sessions",`${h}${p}.db`);if(qr(f)){let g=Cn(),_=new g(f,{readonly:!0}),y=_.prepare("SELECT COUNT(*) as cnt FROM session_events").get(),b=_.prepare("SELECT category, COUNT(*) as cnt FROM session_events GROUP BY category ORDER BY cnt DESC").all(),w=_.prepare("SELECT compact_count FROM session_meta ORDER BY started_at DESC LIMIT 1").get(),T=_.prepare("SELECT event_count, consumed FROM session_resume ORDER BY created_at DESC LIMIT 1").get();if(y.cnt>0){let ae=w?.compact_count??0,B=_.prepare("SELECT category, type, data FROM session_events ORDER BY id DESC").all(),J=new Map;for(let be of B){J.has(be.category)||J.set(be.category,new Set);let sr=J.get(be.category);if(sr.size<5){let Re=be.data;be.category==="file"?Re=be.data.split("/").pop()||be.data:be.category==="prompt"&&(Re=Re.length>50?Re.slice(0,47)+"...":Re),Re.length>40&&(Re=Re.slice(0,37)+"..."),sr.add(Re)}}let Ut={file:"Files tracked",rule:"Project rules (CLAUDE.md)",prompt:"Your requests saved",mcp:"Plugin tools used",git:"Git operations",env:"Environment setup",error:"Errors caught",task:"Tasks in progress",decision:"Your decisions",cwd:"Working directory",skill:"Skills used",subagent:"Delegated work",intent:"Session mode",data:"Data references",role:"Behavioral directives"},pt={file:"Restored after compact \u2014 no need to re-read",rule:"Your project instructions survive context resets",prompt:"Continues exactly where you left off",decision:"Applied automatically \u2014 won't ask again",task:"Picks up from where it stopped",error:"Tracked and monitored across compacts",git:"Branch, commit, and repo state preserved",env:"Runtime config carried forward",mcp:"Tool usage patterns remembered",subagent:"Delegation history preserved",skill:"Skill invocations tracked"};l.push("","### Session Continuity","","| What's preserved | Count | I remember... | Why it matters |","|------------------|------:|---------------|----------------|");for(let be of b){let sr=Ut[be.category]||be.category,Re=J.get(be.category),he=Re?Array.from(Re).join(", "):"",nt=pt[be.category]||"Survives context resets";l.push(`| ${sr} | ${be.cnt} | ${he} | ${nt} |`)}l.push(`| **Total** | **${y.cnt}** | | **Zero knowledge lost on compact** |`),l.push(""),ae>0?l.push(`Context has been compacted **${ae} time(s)** \u2014 session knowledge was preserved each time.`):l.push("When your context compacts, all of this will restore Claude's awareness \u2014 no starting from scratch."),T&&!T.consumed&&l.push(`Resume snapshot ready (${T.event_count} events) for the next compaction.`),l.push(""),l.push("> **Note:** Previous session data is loaded when you start a new session. Without `--continue`, old session history is cleaned up to keep the database lean.")}_.close()}}catch{}l.push("","---","_Display this entire report as-is in your response. Do NOT summarize, collapse, or paraphrase any section._");let d=l.join(`
|
|
525
|
-
`);return H("ctx_stats",{content:[{type:"text",text:d}]})});rt.registerTool("ctx_doctor",{title:"Run Diagnostics",description:"Diagnose context-mode installation. Runs all checks server-side and returns results as a markdown checklist. No CLI execution needed.",inputSchema:U.object({})},async()=>{let t=["## context-mode doctor",""],e=qr(zn(An,"package.json"))?An:fh(An),r=11,n=(Oc.length/r*100).toFixed(0);t.push(`- [x] Runtimes: ${Oc.length}/${r} (${n}%) \u2014 ${Oc.join(", ")}`),Ln()?t.push("- [x] Performance: FAST (Bun)"):t.push("- [-] Performance: NORMAL \u2014 install Bun for 3-5x speed boost");try{let i=await new Io({runtimes:zc}).execute({language:"javascript",code:'console.log("ok");',timeout:5e3});i.exitCode===0&&i.stdout.trim()==="ok"
|
|
525
|
+
`);return H("ctx_stats",{content:[{type:"text",text:d}]})});rt.registerTool("ctx_doctor",{title:"Run Diagnostics",description:"Diagnose context-mode installation. Runs all checks server-side and returns results as a markdown checklist. No CLI execution needed.",inputSchema:U.object({})},async()=>{let t=["## context-mode doctor",""],e=qr(zn(An,"package.json"))?An:fh(An),r=11,n=(Oc.length/r*100).toFixed(0);t.push(`- [x] Runtimes: ${Oc.length}/${r} (${n}%) \u2014 ${Oc.join(", ")}`),Ln()?t.push("- [x] Performance: FAST (Bun)"):t.push("- [-] Performance: NORMAL \u2014 install Bun for 3-5x speed boost");try{let i=await new Io({runtimes:zc}).execute({language:"javascript",code:'console.log("ok");',timeout:5e3});if(i.exitCode===0&&i.stdout.trim()==="ok")t.push("- [x] Server test: PASS");else{let a=i.stderr?.trim()?` (${i.stderr.trim().slice(0,200)})`:"";t.push(`- [ ] Server test: FAIL \u2014 exit ${i.exitCode}${a}`)}}catch(s){t.push(`- [ ] Server test: FAIL \u2014 ${s instanceof Error?s.message:s}`)}try{let s=Cn(),i=new s(":memory:");i.exec("CREATE VIRTUAL TABLE fts_test USING fts5(content)"),i.exec("INSERT INTO fts_test(content) VALUES ('hello world')");let a=i.prepare("SELECT * FROM fts_test WHERE fts_test MATCH 'hello'").get();i.close(),a&&a.content==="hello world"?t.push("- [x] FTS5 / SQLite: PASS \u2014 native module works"):t.push("- [ ] FTS5 / SQLite: FAIL \u2014 unexpected result")}catch(s){t.push(`- [ ] FTS5 / SQLite: FAIL \u2014 ${s instanceof Error?s.message:s}`)}let o=zn(e,"hooks","pretooluse.mjs");return qr(o)?t.push(`- [x] Hook script: PASS \u2014 ${o}`):t.push(`- [ ] Hook script: FAIL \u2014 not found at ${o}`),t.push(`- [x] Version: v${hh}`),H("ctx_doctor",{content:[{type:"text",text:t.join(`
|
|
526
526
|
`)}]})});rt.registerTool("ctx_upgrade",{title:"Upgrade Plugin",description:"Upgrade context-mode to the latest version. Returns a shell command to execute. You MUST run the returned command using your shell tool (Bash, shell_execute, run_in_terminal, etc.) and display the output as a checklist. Tell the user to restart their session after upgrade.",inputSchema:U.object({})},async()=>{let t=qr(zn(An,"package.json"))?An:fh(An),e=zn(t,"cli.bundle.mjs"),r=zn(t,"build","cli.js"),n;if(qr(e))n=`node "${e}" upgrade`;else if(qr(r))n=`node "${r}" upgrade`;else{let s="https://github.com/mksglu/context-mode.git",i=["build","hooks","skills","scripts",".claude-plugin"],a=["start.mjs","server.bundle.mjs","cli.bundle.mjs","package.json"],c=['import{execFileSync}from"node:child_process";','import{cpSync,rmSync,existsSync,mkdtempSync}from"node:fs";','import{join}from"node:path";','import{tmpdir}from"node:os";',`const P=${JSON.stringify(t)};`,'const T=mkdtempSync(join(tmpdir(),"ctx-upgrade-"));',"try{",'console.log("- [x] Starting inline upgrade (no CLI found)");',`execFileSync("git",["clone","--depth","1","${s}",T],{stdio:"inherit"});`,'console.log("- [x] Cloned latest source");','execFileSync("npm",["install"],{cwd:T,stdio:"inherit"});','execFileSync("npm",["run","build"],{cwd:T,stdio:"inherit"});','console.log("- [x] Built from source");',...i.map(d=>`if(existsSync(join(T,${JSON.stringify(d)})))cpSync(join(T,${JSON.stringify(d)}),join(P,${JSON.stringify(d)}),{recursive:true,force:true});`),...a.map(d=>`if(existsSync(join(T,${JSON.stringify(d)})))cpSync(join(T,${JSON.stringify(d)}),join(P,${JSON.stringify(d)}),{force:true});`),'console.log("- [x] Copied build artifacts");','execFileSync("npm",["install","--production"],{cwd:P,stdio:"inherit"});','console.log("- [x] Installed production dependencies");','console.log("## context-mode upgrade complete");',"}catch(e){",'console.error("- [ ] Upgrade failed:",e.message);',"process.exit(1);","}finally{","try{rmSync(T,{recursive:true,force:true})}catch{}","}"].join(`
|
|
527
527
|
`),u=zn(t,".ctx-upgrade-inline.mjs"),{writeFileSync:l}=await import("node:fs");l(u,c),n=`node "${u}"`}let o=["## ctx-upgrade","","Run this command using your shell execution tool:","","```",n,"```","","After the command completes, display results as a markdown checklist:","- `[x]` for success, `[ ]` for failure","- Example format:"," ```"," ## context-mode upgrade"," - [x] Pulled latest from GitHub"," - [x] Built and installed v0.9.24"," - [x] npm global updated"," - [x] Hooks configured"," - [x] Doctor: all checks PASS"," ```","- Tell the user to restart their session to pick up the new version."].join(`
|
|
528
528
|
`);return H("ctx_upgrade",{content:[{type:"text",text:o}]})});tj().catch(t=>{console.error("Fatal:",t),process.exit(1)})});import{stdout as Bk,stdin as Vk}from"node:process";import*as vr from"node:readline";var xh=t=>t===161||t===164||t===167||t===168||t===170||t===173||t===174||t>=176&&t<=180||t>=182&&t<=186||t>=188&&t<=191||t===198||t===208||t===215||t===216||t>=222&&t<=225||t===230||t>=232&&t<=234||t===236||t===237||t===240||t===242||t===243||t>=247&&t<=250||t===252||t===254||t===257||t===273||t===275||t===283||t===294||t===295||t===299||t>=305&&t<=307||t===312||t>=319&&t<=322||t===324||t>=328&&t<=331||t===333||t===338||t===339||t===358||t===359||t===363||t===462||t===464||t===466||t===468||t===470||t===472||t===474||t===476||t===593||t===609||t===708||t===711||t>=713&&t<=715||t===717||t===720||t>=728&&t<=731||t===733||t===735||t>=768&&t<=879||t>=913&&t<=929||t>=931&&t<=937||t>=945&&t<=961||t>=963&&t<=969||t===1025||t>=1040&&t<=1103||t===1105||t===8208||t>=8211&&t<=8214||t===8216||t===8217||t===8220||t===8221||t>=8224&&t<=8226||t>=8228&&t<=8231||t===8240||t===8242||t===8243||t===8245||t===8251||t===8254||t===8308||t===8319||t>=8321&&t<=8324||t===8364||t===8451||t===8453||t===8457||t===8467||t===8470||t===8481||t===8482||t===8486||t===8491||t===8531||t===8532||t>=8539&&t<=8542||t>=8544&&t<=8555||t>=8560&&t<=8569||t===8585||t>=8592&&t<=8601||t===8632||t===8633||t===8658||t===8660||t===8679||t===8704||t===8706||t===8707||t===8711||t===8712||t===8715||t===8719||t===8721||t===8725||t===8730||t>=8733&&t<=8736||t===8739||t===8741||t>=8743&&t<=8748||t===8750||t>=8756&&t<=8759||t===8764||t===8765||t===8776||t===8780||t===8786||t===8800||t===8801||t>=8804&&t<=8807||t===8810||t===8811||t===8814||t===8815||t===8834||t===8835||t===8838||t===8839||t===8853||t===8857||t===8869||t===8895||t===8978||t>=9312&&t<=9449||t>=9451&&t<=9547||t>=9552&&t<=9587||t>=9600&&t<=9615||t>=9618&&t<=9621||t===9632||t===9633||t>=9635&&t<=9641||t===9650||t===9651||t===9654||t===9655||t===9660||t===9661||t===9664||t===9665||t>=9670&&t<=9672||t===9675||t>=9678&&t<=9681||t>=9698&&t<=9701||t===9711||t===9733||t===9734||t===9737||t===9742||t===9743||t===9756||t===9758||t===9792||t===9794||t===9824||t===9825||t>=9827&&t<=9829||t>=9831&&t<=9834||t===9836||t===9837||t===9839||t===9886||t===9887||t===9919||t>=9926&&t<=9933||t>=9935&&t<=9939||t>=9941&&t<=9953||t===9955||t===9960||t===9961||t>=9963&&t<=9969||t===9972||t>=9974&&t<=9977||t===9979||t===9980||t===9982||t===9983||t===10045||t>=10102&&t<=10111||t>=11094&&t<=11097||t>=12872&&t<=12879||t>=57344&&t<=63743||t>=65024&&t<=65039||t===65533||t>=127232&&t<=127242||t>=127248&&t<=127277||t>=127280&&t<=127337||t>=127344&&t<=127373||t===127375||t===127376||t>=127387&&t<=127404||t>=917760&&t<=917999||t>=983040&&t<=1048573||t>=1048576&&t<=1114109,vh=t=>t===12288||t>=65281&&t<=65376||t>=65504&&t<=65510,Sh=t=>t>=4352&&t<=4447||t===8986||t===8987||t===9001||t===9002||t>=9193&&t<=9196||t===9200||t===9203||t===9725||t===9726||t===9748||t===9749||t>=9800&&t<=9811||t===9855||t===9875||t===9889||t===9898||t===9899||t===9917||t===9918||t===9924||t===9925||t===9934||t===9940||t===9962||t===9970||t===9971||t===9973||t===9978||t===9981||t===9989||t===9994||t===9995||t===10024||t===10060||t===10062||t>=10067&&t<=10069||t===10071||t>=10133&&t<=10135||t===10160||t===10175||t===11035||t===11036||t===11088||t===11093||t>=11904&&t<=11929||t>=11931&&t<=12019||t>=12032&&t<=12245||t>=12272&&t<=12287||t>=12289&&t<=12350||t>=12353&&t<=12438||t>=12441&&t<=12543||t>=12549&&t<=12591||t>=12593&&t<=12686||t>=12688&&t<=12771||t>=12783&&t<=12830||t>=12832&&t<=12871||t>=12880&&t<=19903||t>=19968&&t<=42124||t>=42128&&t<=42182||t>=43360&&t<=43388||t>=44032&&t<=55203||t>=63744&&t<=64255||t>=65040&&t<=65049||t>=65072&&t<=65106||t>=65108&&t<=65126||t>=65128&&t<=65131||t>=94176&&t<=94180||t===94192||t===94193||t>=94208&&t<=100343||t>=100352&&t<=101589||t>=101632&&t<=101640||t>=110576&&t<=110579||t>=110581&&t<=110587||t===110589||t===110590||t>=110592&&t<=110882||t===110898||t>=110928&&t<=110930||t===110933||t>=110948&&t<=110951||t>=110960&&t<=111355||t===126980||t===127183||t===127374||t>=127377&&t<=127386||t>=127488&&t<=127490||t>=127504&&t<=127547||t>=127552&&t<=127560||t===127568||t===127569||t>=127584&&t<=127589||t>=127744&&t<=127776||t>=127789&&t<=127797||t>=127799&&t<=127868||t>=127870&&t<=127891||t>=127904&&t<=127946||t>=127951&&t<=127955||t>=127968&&t<=127984||t===127988||t>=127992&&t<=128062||t===128064||t>=128066&&t<=128252||t>=128255&&t<=128317||t>=128331&&t<=128334||t>=128336&&t<=128359||t===128378||t===128405||t===128406||t===128420||t>=128507&&t<=128591||t>=128640&&t<=128709||t===128716||t>=128720&&t<=128722||t>=128725&&t<=128727||t>=128732&&t<=128735||t===128747||t===128748||t>=128756&&t<=128764||t>=128992&&t<=129003||t===129008||t>=129292&&t<=129338||t>=129340&&t<=129349||t>=129351&&t<=129535||t>=129648&&t<=129660||t>=129664&&t<=129672||t>=129680&&t<=129725||t>=129727&&t<=129733||t>=129742&&t<=129755||t>=129760&&t<=129768||t>=129776&&t<=129784||t>=131072&&t<=196605||t>=196608&&t<=262141;var Mc=/[\u001b\u009b][[()#;?]*(?:[0-9]{1,4}(?:;[0-9]{0,4})*)?[0-9A-ORZcf-nqry=><]/y,ci=/[\x00-\x08\x0A-\x1F\x7F-\x9F]{1,1000}/y,ui=/\t{1,1000}/y,Dc=new RegExp("[\\u{1F1E6}-\\u{1F1FF}]{2}|\\u{1F3F4}[\\u{E0061}-\\u{E007A}]{2}[\\u{E0030}-\\u{E0039}\\u{E0061}-\\u{E007A}]{1,3}\\u{E007F}|(?:\\p{Emoji}\\uFE0F\\u20E3?|\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation})(?:\\u200D(?:\\p{Emoji_Modifier_Base}\\p{Emoji_Modifier}?|\\p{Emoji_Presentation}|\\p{Emoji}\\uFE0F\\u20E3?))*","yu"),li=/(?:[\x20-\x7E\xA0-\xFF](?!\uFE0F)){1,1000}/y,Ak=new RegExp("\\p{M}+","gu"),Nk={limit:1/0,ellipsis:""},bh=(t,e={},r={})=>{let n=e.limit??1/0,o=e.ellipsis??"",s=e?.ellipsisWidth??(o?bh(o,Nk,r).width:0),i=r.ansiWidth??0,a=r.controlWidth??0,c=r.tabWidth??8,u=r.ambiguousWidth??1,l=r.emojiWidth??2,d=r.fullWidthWidth??2,m=r.regularWidth??1,h=r.wideWidth??2,p=0,f=0,g=t.length,_=0,y=!1,b=g,w=Math.max(0,n-s),T=0,ae=0,B=0,J=0;e:for(;;){if(ae>T||f>=g&&f>p){let Ut=t.slice(T,ae)||t.slice(p,f);_=0;for(let pt of Ut.replaceAll(Ak,"")){let be=pt.codePointAt(0)||0;if(vh(be)?J=d:Sh(be)?J=h:u!==m&&xh(be)?J=u:J=m,B+J>w&&(b=Math.min(b,Math.max(T,p)+_)),B+J>n){y=!0;break e}_+=pt.length,B+=J}T=ae=0}if(f>=g)break;if(li.lastIndex=f,li.test(t)){if(_=li.lastIndex-f,J=_*m,B+J>w&&(b=Math.min(b,f+Math.floor((w-B)/m))),B+J>n){y=!0;break}B+=J,T=p,ae=f,f=p=li.lastIndex;continue}if(Mc.lastIndex=f,Mc.test(t)){if(B+i>w&&(b=Math.min(b,f)),B+i>n){y=!0;break}B+=i,T=p,ae=f,f=p=Mc.lastIndex;continue}if(ci.lastIndex=f,ci.test(t)){if(_=ci.lastIndex-f,J=_*a,B+J>w&&(b=Math.min(b,f+Math.floor((w-B)/a))),B+J>n){y=!0;break}B+=J,T=p,ae=f,f=p=ci.lastIndex;continue}if(ui.lastIndex=f,ui.test(t)){if(_=ui.lastIndex-f,J=_*c,B+J>w&&(b=Math.min(b,f+Math.floor((w-B)/c))),B+J>n){y=!0;break}B+=J,T=p,ae=f,f=p=ui.lastIndex;continue}if(Dc.lastIndex=f,Dc.test(t)){if(B+l>w&&(b=Math.min(b,f)),B+l>n){y=!0;break}B+=l,T=p,ae=f,f=p=Dc.lastIndex;continue}f+=1}return{width:y?w:B,index:y?b:g,truncated:y,ellipsed:y&&n>=s}},kh=bh;var jk={limit:1/0,ellipsis:"",ellipsisWidth:0},Mk=(t,e={})=>kh(t,jk,e).width,Je=Mk;var di="\x1B",Ph="\x9B",Dk=39,Fc="\x07",Rh="[",Lk="]",Oh="m",Zc=`${Lk}8;;`,$h=new RegExp(`(?:\\${Rh}(?<code>\\d+)m|\\${Zc}(?<uri>.*)${Fc})`,"y"),wh=t=>{if(t>=30&&t<=37||t>=90&&t<=97)return 39;if(t>=40&&t<=47||t>=100&&t<=107)return 49;if(t===1||t===2)return 22;if(t===3)return 23;if(t===4)return 24;if(t===7)return 27;if(t===8)return 28;if(t===9)return 29;if(t===0)return 0},Eh=t=>`${di}${Rh}${t}${Oh}`,Th=t=>`${di}${Zc}${t}${Fc}`,Lc=(t,e,r)=>{let n=e[Symbol.iterator](),o=!1,s=!1,i=t.at(-1),a=i===void 0?0:Je(i),c=n.next(),u=n.next(),l=0;for(;!c.done;){let d=c.value,m=Je(d);a+m<=r?t[t.length-1]+=d:(t.push(d),a=0),(d===di||d===Ph)&&(o=!0,s=e.startsWith(Zc,l+1)),o?s?d===Fc&&(o=!1,s=!1):d===Oh&&(o=!1):(a+=m,a===r&&!u.done&&(t.push(""),a=0)),c=u,u=n.next(),l+=d.length}i=t.at(-1),!a&&i!==void 0&&i.length&&t.length>1&&(t[t.length-2]+=t.pop())},Fk=t=>{let e=t.split(" "),r=e.length;for(;r&&!Je(e[r-1]);)r--;return r===e.length?t:e.slice(0,r).join(" ")+e.slice(r).join("")},Zk=(t,e,r={})=>{if(r.trim!==!1&&t.trim()==="")return"";let n="",o,s,i=t.split(" "),a=[""],c=0;for(let d=0;d<i.length;d++){let m=i[d];if(r.trim!==!1){let p=a.at(-1)??"",f=p.trimStart();p.length!==f.length&&(a[a.length-1]=f,c=Je(f))}d!==0&&(c>=e&&(r.wordWrap===!1||r.trim===!1)&&(a.push(""),c=0),(c||r.trim===!1)&&(a[a.length-1]+=" ",c++));let h=Je(m);if(r.hard&&h>e){let p=e-c,f=1+Math.floor((h-p-1)/e);Math.floor((h-1)/e)<f&&a.push(""),Lc(a,m,e),c=Je(a.at(-1)??"");continue}if(c+h>e&&c&&h){if(r.wordWrap===!1&&c<e){Lc(a,m,e),c=Je(a.at(-1)??"");continue}a.push(""),c=0}if(c+h>e&&r.wordWrap===!1){Lc(a,m,e),c=Je(a.at(-1)??"");continue}a[a.length-1]+=m,c+=h}r.trim!==!1&&(a=a.map(d=>Fk(d)));let u=a.join(`
|
|
@@ -548,7 +548,7 @@ ${ke("gray",d+Nh.repeat(c+2)+s$)}
|
|
|
548
548
|
`);he.length>1&&r.write(Mo.cursor.up(he.length-1)),r.write(Mo.cursor.to(0)),r.write(Mo.erase.down())},Ut=he=>he.replace(/\.+$/,""),pt=he=>{let nt=(performance.now()-he)/1e3,ir=Math.floor(nt/60),ar=Math.floor(nt%60);return ir>0?`[${ir}m ${ar}s]`:`[${ar}s]`},be=c.withGuide??Ht.withGuide,sr=(he="")=>{m=!0,l=Ah({output:r}),p=Ut(he),g=performance.now(),be&&r.write(`${ke("gray",Sr)}
|
|
549
549
|
`);let nt=0,ir=0;ae(),d=setInterval(()=>{if(u&&p===f)return;J(),f=p;let ar=y(s[nt]),ai;if(u)ai=`${ar} ${p}...`;else if(t==="timer")ai=`${ar} ${p} ${pt(g)}`;else{let Tk=".".repeat(Math.floor(ir)).slice(0,3);ai=`${ar} ${p}${Tk}`}let Ek=Mn(ai,_,{hard:!0,trim:!1});r.write(Ek),nt=nt+1<s.length?nt+1:0,ir=ir<4?ir+.125:0},i)},Re=(he="",nt=0,ir=!1)=>{if(!m)return;m=!1,clearInterval(d),J();let ar=nt===0?ke("green",Vc):nt===1?ke("red",Qk):ke("red",e$);p=he??p,ir||(t==="timer"?r.write(`${ar} ${p} ${pt(g)}
|
|
550
550
|
`):r.write(`${ar} ${p}
|
|
551
|
-
`)),B(),l()};return{start:sr,stop:(he="")=>Re(he,0),message:(he="")=>{p=Ut(he??p)},cancel:(he="")=>Re(he,1),error:(he="")=>Re(he,2),clear:()=>Re("",0,!0),get isCancelled(){return h}}},cM={light:ue("\u2500","-"),heavy:ue("\u2501","="),block:ue("\u2588","#")};var uM=`${ke("gray",Sr)} `;var E=No(Dh(),1);_i();Iu();import{execFileSync as jn}from"node:child_process";import{readFileSync as Sk,writeFileSync as rj,cpSync as nj,accessSync as bk,existsSync as oj,rmSync as Ac,closeSync as sj,openSync as ij,chmodSync as aj,constants as kk}from"node:fs";import{request as cj}from"node:https";import{resolve as Ke,dirname as uj,join as $k}from"node:path";import{tmpdir as lj,devNull as dj,homedir as vk}from"node:os";import{fileURLToPath as pj,pathToFileURL as mj}from"node:url";var fj={"claude-code":{pretooluse:"hooks/pretooluse.mjs",posttooluse:"hooks/posttooluse.mjs",precompact:"hooks/precompact.mjs",sessionstart:"hooks/sessionstart.mjs",userpromptsubmit:"hooks/userpromptsubmit.mjs"},"gemini-cli":{beforetool:"hooks/gemini-cli/beforetool.mjs",aftertool:"hooks/gemini-cli/aftertool.mjs",precompress:"hooks/gemini-cli/precompress.mjs",sessionstart:"hooks/gemini-cli/sessionstart.mjs"},"vscode-copilot":{pretooluse:"hooks/vscode-copilot/pretooluse.mjs",posttooluse:"hooks/vscode-copilot/posttooluse.mjs",precompact:"hooks/vscode-copilot/precompact.mjs",sessionstart:"hooks/vscode-copilot/sessionstart.mjs"},cursor:{pretooluse:"hooks/cursor/pretooluse.mjs",posttooluse:"hooks/cursor/posttooluse.mjs",sessionstart:"hooks/cursor/sessionstart.mjs"},kiro:{pretooluse:"hooks/kiro/pretooluse.mjs",posttooluse:"hooks/kiro/posttooluse.mjs"}};async function hj(t,e){try{sj(2),ij(dj,"w")}catch{process.stderr.write=(()=>!0)}let r=fj[t]?.[e];r||process.exit(1);let n=Nc();await import(mj($k(n,r)).href)}var ii=process.argv.slice(2);ii[0]==="doctor"?xj().then(t=>process.exit(t)):ii[0]==="upgrade"?vj():ii[0]==="hook"?hj(ii[1],ii[2]):Promise.resolve().then(()=>(xk(),yk));function N9(t){return t.replace(/\\/g,"/")}function gj(){let t=pj(import.meta.url),e=uj(t);return e.endsWith("/build")||e.endsWith("\\build")||e.endsWith("/src")||e.endsWith("\\src")?Ke(e,".."):e}function _j(t){if(process.platform==="win32"){let e=process.env.LOCALAPPDATA;return e?Ke(e,t,"node_modules","context-mode"):Ke(vk(),"AppData","Local",t,"node_modules","context-mode")}return Ke(vk(),".cache",t,"node_modules","context-mode")}function Nc(){let t=Vn().platform;return t==="opencode"||t==="kilo"?_j(t):gj()}function wk(){try{return JSON.parse(Sk(Ke(Nc(),"package.json"),"utf-8")).version??"unknown"}catch{return"unknown"}}async function yj(){return new Promise(t=>{let e=cj("https://registry.npmjs.org/context-mode/latest",{headers:{Connection:"close"}},r=>{let n="";r.on("data",o=>{n+=o}),r.on("end",()=>{try{let o=JSON.parse(n);t(o.version??"unknown")}catch{t("unknown")}})});e.on("error",()=>t("unknown")),e.setTimeout(5e3,()=>{e.destroy(),t("unknown")}),e.end()})}async function xj(){process.stdout.isTTY&&console.clear();let t=Vn(),e=await Ei(t.platform);Wc(E.default.bgMagenta(E.default.white(" context-mode doctor "))),I.info(`Platform: ${E.default.cyan(e.name)}`+E.default.dim(` (${t.confidence} confidence \u2014 ${t.reason})`));let r=0,n=Kc();n.start("Running diagnostics");let o,s;try{o=Dn(),s=gi(o)}catch{return n.stop("Diagnostics partial"),I.warn(E.default.yellow("Could not detect runtimes")+E.default.dim(" \u2014 module may be missing, restart session after upgrade")),pi(E.default.yellow("Doctor could not fully run \u2014 try again after restarting")),1}n.stop("Diagnostics complete"),Gc(hi(o),"Runtimes"),Ln()?I.success(E.default.green("Performance: FAST")+" \u2014 Bun detected for JS/TS execution"):I.warn(E.default.yellow("Performance: NORMAL")+" \u2014 Using Node.js (install Bun for 3-5x speed boost)");let i=11,a=(s.length/i*100).toFixed(0);s.length<2?(r++,I.error(E.default.red(`Language coverage: ${s.length}/${i} (${a}%)`)+" \u2014 too few runtimes detected"+E.default.dim(` \u2014 ${s.join(", ")||"none"}`))):I.info(`Language coverage: ${s.length}/${i} (${a}%)`+E.default.dim(` \u2014 ${s.join(", ")}`)),I.step("Testing server initialization...");try{let{PolyglotExecutor:f}=await Promise.resolve().then(()=>(th(),Nb)),_=await new f({runtimes:o}).execute({language:"javascript",code:'console.log("ok");',timeout:5e3});if(_.exitCode===0&&_.stdout.trim()==="ok")I.success(E.default.green("Server test: PASS"));else{r++;let y=_.stderr?.trim()?` (${_.stderr.trim().slice(0,200)})`:"";I.error(E.default.red("Server test: FAIL")+` \u2014 exit ${_.exitCode}${y}`)}}catch(f){let g=f instanceof Error?f.message:String(f);g.includes("Cannot find module")||g.includes("MODULE_NOT_FOUND")?I.warn(E.default.yellow("Server test: SKIP")+E.default.dim(" \u2014 module not available (restart session after upgrade)")):(r++,I.error(E.default.red("Server test: FAIL")+` \u2014 ${g}`))}I.step(`Checking ${e.name} hooks configuration...`);let c=Nc(),u=e.validateHooks(c);for(let f of u)f.status==="pass"?I.success(E.default.green(`${f.check}: PASS`)+` \u2014 ${f.message}`):I.error(E.default.red(`${f.check}: FAIL`)+` \u2014 ${f.message}`+(f.fix?E.default.dim(`
|
|
551
|
+
`)),B(),l()};return{start:sr,stop:(he="")=>Re(he,0),message:(he="")=>{p=Ut(he??p)},cancel:(he="")=>Re(he,1),error:(he="")=>Re(he,2),clear:()=>Re("",0,!0),get isCancelled(){return h}}},cM={light:ue("\u2500","-"),heavy:ue("\u2501","="),block:ue("\u2588","#")};var uM=`${ke("gray",Sr)} `;var E=No(Dh(),1);_i();Iu();import{execFileSync as jn}from"node:child_process";import{readFileSync as Sk,writeFileSync as rj,cpSync as nj,accessSync as bk,existsSync as oj,rmSync as Ac,closeSync as sj,openSync as ij,chmodSync as aj,constants as kk}from"node:fs";import{request as cj}from"node:https";import{resolve as Ke,dirname as uj,join as $k}from"node:path";import{tmpdir as lj,devNull as dj,homedir as vk}from"node:os";import{fileURLToPath as pj,pathToFileURL as mj}from"node:url";var fj={"claude-code":{pretooluse:"hooks/pretooluse.mjs",posttooluse:"hooks/posttooluse.mjs",precompact:"hooks/precompact.mjs",sessionstart:"hooks/sessionstart.mjs",userpromptsubmit:"hooks/userpromptsubmit.mjs"},"gemini-cli":{beforetool:"hooks/gemini-cli/beforetool.mjs",aftertool:"hooks/gemini-cli/aftertool.mjs",precompress:"hooks/gemini-cli/precompress.mjs",sessionstart:"hooks/gemini-cli/sessionstart.mjs"},"vscode-copilot":{pretooluse:"hooks/vscode-copilot/pretooluse.mjs",posttooluse:"hooks/vscode-copilot/posttooluse.mjs",precompact:"hooks/vscode-copilot/precompact.mjs",sessionstart:"hooks/vscode-copilot/sessionstart.mjs"},cursor:{pretooluse:"hooks/cursor/pretooluse.mjs",posttooluse:"hooks/cursor/posttooluse.mjs",sessionstart:"hooks/cursor/sessionstart.mjs"},codex:{pretooluse:"hooks/codex/pretooluse.mjs",posttooluse:"hooks/codex/posttooluse.mjs",sessionstart:"hooks/codex/sessionstart.mjs"},kiro:{pretooluse:"hooks/kiro/pretooluse.mjs",posttooluse:"hooks/kiro/posttooluse.mjs"}};async function hj(t,e){try{sj(2),ij(dj,"w")}catch{process.stderr.write=(()=>!0)}let r=fj[t]?.[e];r||process.exit(1);let n=Nc();await import(mj($k(n,r)).href)}var ii=process.argv.slice(2);ii[0]==="doctor"?xj().then(t=>process.exit(t)):ii[0]==="upgrade"?vj():ii[0]==="hook"?hj(ii[1],ii[2]):Promise.resolve().then(()=>(xk(),yk));function N9(t){return t.replace(/\\/g,"/")}function gj(){let t=pj(import.meta.url),e=uj(t);return e.endsWith("/build")||e.endsWith("\\build")||e.endsWith("/src")||e.endsWith("\\src")?Ke(e,".."):e}function _j(t){if(process.platform==="win32"){let e=process.env.LOCALAPPDATA;return e?Ke(e,t,"node_modules","context-mode"):Ke(vk(),"AppData","Local",t,"node_modules","context-mode")}return Ke(vk(),".cache",t,"node_modules","context-mode")}function Nc(){let t=Vn().platform;return t==="opencode"||t==="kilo"?_j(t):gj()}function wk(){try{return JSON.parse(Sk(Ke(Nc(),"package.json"),"utf-8")).version??"unknown"}catch{return"unknown"}}async function yj(){return new Promise(t=>{let e=cj("https://registry.npmjs.org/context-mode/latest",{headers:{Connection:"close"}},r=>{let n="";r.on("data",o=>{n+=o}),r.on("end",()=>{try{let o=JSON.parse(n);t(o.version??"unknown")}catch{t("unknown")}})});e.on("error",()=>t("unknown")),e.setTimeout(5e3,()=>{e.destroy(),t("unknown")}),e.end()})}async function xj(){process.stdout.isTTY&&console.clear();let t=Vn(),e=await Ei(t.platform);Wc(E.default.bgMagenta(E.default.white(" context-mode doctor "))),I.info(`Platform: ${E.default.cyan(e.name)}`+E.default.dim(` (${t.confidence} confidence \u2014 ${t.reason})`));let r=0,n=Kc();n.start("Running diagnostics");let o,s;try{o=Dn(),s=gi(o)}catch{return n.stop("Diagnostics partial"),I.warn(E.default.yellow("Could not detect runtimes")+E.default.dim(" \u2014 module may be missing, restart session after upgrade")),pi(E.default.yellow("Doctor could not fully run \u2014 try again after restarting")),1}n.stop("Diagnostics complete"),Gc(hi(o),"Runtimes"),Ln()?I.success(E.default.green("Performance: FAST")+" \u2014 Bun detected for JS/TS execution"):I.warn(E.default.yellow("Performance: NORMAL")+" \u2014 Using Node.js (install Bun for 3-5x speed boost)");let i=11,a=(s.length/i*100).toFixed(0);s.length<2?(r++,I.error(E.default.red(`Language coverage: ${s.length}/${i} (${a}%)`)+" \u2014 too few runtimes detected"+E.default.dim(` \u2014 ${s.join(", ")||"none"}`))):I.info(`Language coverage: ${s.length}/${i} (${a}%)`+E.default.dim(` \u2014 ${s.join(", ")}`)),I.step("Testing server initialization...");try{let{PolyglotExecutor:f}=await Promise.resolve().then(()=>(th(),Nb)),_=await new f({runtimes:o}).execute({language:"javascript",code:'console.log("ok");',timeout:5e3});if(_.exitCode===0&&_.stdout.trim()==="ok")I.success(E.default.green("Server test: PASS"));else{r++;let y=_.stderr?.trim()?` (${_.stderr.trim().slice(0,200)})`:"";I.error(E.default.red("Server test: FAIL")+` \u2014 exit ${_.exitCode}${y}`)}}catch(f){let g=f instanceof Error?f.message:String(f);g.includes("Cannot find module")||g.includes("MODULE_NOT_FOUND")?I.warn(E.default.yellow("Server test: SKIP")+E.default.dim(" \u2014 module not available (restart session after upgrade)")):(r++,I.error(E.default.red("Server test: FAIL")+` \u2014 ${g}`))}I.step(`Checking ${e.name} hooks configuration...`);let c=Nc(),u=e.validateHooks(c);for(let f of u)f.status==="pass"?I.success(E.default.green(`${f.check}: PASS`)+` \u2014 ${f.message}`):I.error(E.default.red(`${f.check}: FAIL`)+` \u2014 ${f.message}`+(f.fix?E.default.dim(`
|
|
552
552
|
Run: ${f.fix}`):""));I.step("Checking hook script...");let l=Ke(c,"hooks","pretooluse.mjs");try{bk(l,kk.R_OK),I.success(E.default.green("Hook script exists: PASS")+E.default.dim(` \u2014 ${l}`))}catch{I.error(E.default.red("Hook script exists: FAIL")+E.default.dim(` \u2014 not found at ${l}`))}I.step(`Checking ${e.name} plugin registration...`);let d=e.checkPluginRegistration();d.status==="pass"?I.success(E.default.green("Plugin enabled: PASS")+E.default.dim(` \u2014 ${d.message}`)):I.warn(E.default.yellow("Plugin enabled: WARN")+` \u2014 ${d.message}`),I.step("Checking FTS5 / SQLite...");try{let f=(await Promise.resolve().then(()=>(ni(),Db))).loadDatabase(),g=new f(":memory:");g.exec("CREATE VIRTUAL TABLE fts_test USING fts5(content)"),g.exec("INSERT INTO fts_test(content) VALUES ('hello world')");let _=g.prepare("SELECT * FROM fts_test WHERE fts_test MATCH 'hello'").get();g.close(),_&&_.content==="hello world"?I.success(E.default.green("FTS5 / SQLite: PASS")+" \u2014 native module works"):(r++,I.error(E.default.red("FTS5 / SQLite: FAIL")+" \u2014 query returned unexpected result"))}catch(f){let g=f instanceof Error?f.message:String(f);g.includes("Cannot find module")||g.includes("MODULE_NOT_FOUND")?I.warn(E.default.yellow("FTS5 / better-sqlite3: SKIP")+E.default.dim(" \u2014 module not available (restart session after upgrade)")):(r++,I.error(E.default.red("FTS5 / better-sqlite3: FAIL")+` \u2014 ${g}`+E.default.dim(`
|
|
553
553
|
Try: npm rebuild better-sqlite3`)))}I.step("Checking versions...");let m=wk(),h=await yj(),p=e.getInstalledVersion();return h==="unknown"?I.warn(E.default.yellow("npm (MCP): WARN")+` \u2014 local v${m}, could not reach npm registry`):m===h?I.success(E.default.green("npm (MCP): PASS")+` \u2014 v${m}`):I.warn(E.default.yellow("npm (MCP): WARN")+` \u2014 local v${m}, latest v${h}`+E.default.dim(`
|
|
554
554
|
Run: /context-mode:ctx-upgrade`)),p==="not installed"?I.info(E.default.dim(`${e.name}: not installed`)+" \u2014 using standalone MCP mode"):h!=="unknown"&&p===h?I.success(E.default.green(`${e.name}: PASS`)+` \u2014 v${p}`):h!=="unknown"?I.warn(E.default.yellow(`${e.name}: WARN`)+` \u2014 v${p}, latest v${h}`+E.default.dim(`
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "../suppress-stderr.mjs";
|
|
3
|
+
import "../ensure-deps.mjs";
|
|
4
|
+
/**
|
|
5
|
+
* Codex CLI postToolUse hook — session event capture.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { readStdin, getSessionId, getSessionDBPath, getInputProjectDir, CODEX_OPTS } from "../session-helpers.mjs";
|
|
9
|
+
import { join, dirname } from "node:path";
|
|
10
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
11
|
+
|
|
12
|
+
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
13
|
+
const PKG_SESSION = join(HOOK_DIR, "..", "..", "build", "session");
|
|
14
|
+
const OPTS = CODEX_OPTS;
|
|
15
|
+
|
|
16
|
+
function normalizeToolName(toolName) {
|
|
17
|
+
// Codex CLI tool_name is always "Bash" (single tool type)
|
|
18
|
+
if (toolName === "Shell") return "Bash";
|
|
19
|
+
return toolName;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const raw = await readStdin();
|
|
24
|
+
const input = JSON.parse(raw);
|
|
25
|
+
const projectDir = getInputProjectDir(input, OPTS);
|
|
26
|
+
|
|
27
|
+
const { extractEvents } = await import(pathToFileURL(join(PKG_SESSION, "extract.js")).href);
|
|
28
|
+
const { SessionDB } = await import(pathToFileURL(join(PKG_SESSION, "db.js")).href);
|
|
29
|
+
|
|
30
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
31
|
+
const db = new SessionDB({ dbPath });
|
|
32
|
+
const sessionId = getSessionId(input, OPTS);
|
|
33
|
+
|
|
34
|
+
db.ensureSession(sessionId, projectDir);
|
|
35
|
+
|
|
36
|
+
const normalizedInput = {
|
|
37
|
+
tool_name: normalizeToolName(input.tool_name ?? ""),
|
|
38
|
+
tool_input: input.tool_input ?? {},
|
|
39
|
+
tool_response: typeof input.tool_response === "string"
|
|
40
|
+
? input.tool_response
|
|
41
|
+
: JSON.stringify(input.tool_response ?? ""),
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const events = extractEvents(normalizedInput);
|
|
45
|
+
for (const event of events) {
|
|
46
|
+
db.insertEvent(sessionId, event, "PostToolUse");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
db.close();
|
|
50
|
+
} catch {
|
|
51
|
+
// Swallow errors — hook must not fail
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Codex PostToolUse requires hookEventName in hookSpecificOutput
|
|
55
|
+
process.stdout.write(JSON.stringify({
|
|
56
|
+
hookSpecificOutput: { hookEventName: "PostToolUse", additionalContext: "" },
|
|
57
|
+
}) + "\n");
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "../suppress-stderr.mjs";
|
|
3
|
+
/**
|
|
4
|
+
* Codex CLI preToolUse hook for context-mode.
|
|
5
|
+
*
|
|
6
|
+
* Codex PreToolUse supports deny only — additionalContext, updatedInput,
|
|
7
|
+
* ask, and allow are rejected by codex-rs output_parser.rs.
|
|
8
|
+
* Source: codex-rs/hooks/src/engine/output_parser.rs
|
|
9
|
+
*/
|
|
10
|
+
|
|
11
|
+
import { dirname, resolve } from "node:path";
|
|
12
|
+
import { fileURLToPath } from "node:url";
|
|
13
|
+
import { readStdin, getInputProjectDir, CODEX_OPTS } from "../session-helpers.mjs";
|
|
14
|
+
import { routePreToolUse, initSecurity } from "../core/routing.mjs";
|
|
15
|
+
import { formatDecision } from "../core/formatters.mjs";
|
|
16
|
+
|
|
17
|
+
const __hookDir = dirname(fileURLToPath(import.meta.url));
|
|
18
|
+
await initSecurity(resolve(__hookDir, "..", "..", "build"));
|
|
19
|
+
|
|
20
|
+
const raw = await readStdin();
|
|
21
|
+
const input = JSON.parse(raw);
|
|
22
|
+
const tool = input.tool_name ?? "";
|
|
23
|
+
const toolInput = input.tool_input ?? {};
|
|
24
|
+
const projectDir = getInputProjectDir(input, CODEX_OPTS);
|
|
25
|
+
|
|
26
|
+
const decision = routePreToolUse(tool, toolInput, projectDir, "codex");
|
|
27
|
+
const response = formatDecision("codex", decision);
|
|
28
|
+
const output = response ?? {
|
|
29
|
+
hookSpecificOutput: { hookEventName: "PreToolUse" },
|
|
30
|
+
};
|
|
31
|
+
process.stdout.write(JSON.stringify(output) + "\n");
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import "../suppress-stderr.mjs";
|
|
3
|
+
import "../ensure-deps.mjs";
|
|
4
|
+
/**
|
|
5
|
+
* Codex CLI sessionStart hook for context-mode.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { createRoutingBlock } from "../routing-block.mjs";
|
|
9
|
+
import { createToolNamer } from "../core/tool-naming.mjs";
|
|
10
|
+
|
|
11
|
+
const ROUTING_BLOCK = createRoutingBlock(createToolNamer("codex"));
|
|
12
|
+
import {
|
|
13
|
+
writeSessionEventsFile,
|
|
14
|
+
buildSessionDirective,
|
|
15
|
+
getSessionEvents,
|
|
16
|
+
getLatestSessionEvents,
|
|
17
|
+
} from "../session-directive.mjs";
|
|
18
|
+
import {
|
|
19
|
+
readStdin,
|
|
20
|
+
getSessionId,
|
|
21
|
+
getSessionDBPath,
|
|
22
|
+
getSessionEventsPath,
|
|
23
|
+
getCleanupFlagPath,
|
|
24
|
+
getInputProjectDir,
|
|
25
|
+
CODEX_OPTS,
|
|
26
|
+
} from "../session-helpers.mjs";
|
|
27
|
+
import { join } from "node:path";
|
|
28
|
+
import { readFileSync, writeFileSync, unlinkSync } from "node:fs";
|
|
29
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
30
|
+
|
|
31
|
+
const HOOK_DIR = fileURLToPath(new URL(".", import.meta.url));
|
|
32
|
+
const PKG_SESSION = join(HOOK_DIR, "..", "..", "build", "session");
|
|
33
|
+
const OPTS = CODEX_OPTS;
|
|
34
|
+
|
|
35
|
+
let additionalContext = ROUTING_BLOCK;
|
|
36
|
+
|
|
37
|
+
try {
|
|
38
|
+
const raw = await readStdin();
|
|
39
|
+
const input = JSON.parse(raw);
|
|
40
|
+
const source = input.source ?? "startup";
|
|
41
|
+
const projectDir = getInputProjectDir(input, CODEX_OPTS);
|
|
42
|
+
|
|
43
|
+
if (source === "compact" || source === "resume") {
|
|
44
|
+
const { SessionDB } = await import(pathToFileURL(join(PKG_SESSION, "db.js")).href);
|
|
45
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
46
|
+
const db = new SessionDB({ dbPath });
|
|
47
|
+
|
|
48
|
+
if (source === "compact") {
|
|
49
|
+
const sessionId = getSessionId(input, OPTS);
|
|
50
|
+
const resume = db.getResume(sessionId);
|
|
51
|
+
if (resume && !resume.consumed) {
|
|
52
|
+
db.markResumeConsumed(sessionId);
|
|
53
|
+
}
|
|
54
|
+
} else {
|
|
55
|
+
try { unlinkSync(getCleanupFlagPath(OPTS)); } catch { /* no flag */ }
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const events = source === "compact"
|
|
59
|
+
? getSessionEvents(db, getSessionId(input, OPTS))
|
|
60
|
+
: getLatestSessionEvents(db);
|
|
61
|
+
if (events.length > 0) {
|
|
62
|
+
const eventMeta = writeSessionEventsFile(events, getSessionEventsPath(OPTS));
|
|
63
|
+
additionalContext += buildSessionDirective(source, eventMeta);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
db.close();
|
|
67
|
+
} else if (source === "startup") {
|
|
68
|
+
const { SessionDB } = await import(pathToFileURL(join(PKG_SESSION, "db.js")).href);
|
|
69
|
+
const dbPath = getSessionDBPath(OPTS);
|
|
70
|
+
const db = new SessionDB({ dbPath });
|
|
71
|
+
try { unlinkSync(getSessionEventsPath(OPTS)); } catch { /* no stale file */ }
|
|
72
|
+
|
|
73
|
+
const cleanupFlag = getCleanupFlagPath(OPTS);
|
|
74
|
+
let previousWasFresh = false;
|
|
75
|
+
try { readFileSync(cleanupFlag); previousWasFresh = true; } catch { /* no flag */ }
|
|
76
|
+
|
|
77
|
+
if (previousWasFresh) {
|
|
78
|
+
db.cleanupOldSessions(0);
|
|
79
|
+
} else {
|
|
80
|
+
db.cleanupOldSessions(7);
|
|
81
|
+
}
|
|
82
|
+
db.db.exec(`DELETE FROM session_events WHERE session_id NOT IN (SELECT session_id FROM session_meta)`);
|
|
83
|
+
writeFileSync(cleanupFlag, new Date().toISOString(), "utf-8");
|
|
84
|
+
|
|
85
|
+
const sessionId = getSessionId(input, OPTS);
|
|
86
|
+
db.ensureSession(sessionId, projectDir);
|
|
87
|
+
|
|
88
|
+
db.close();
|
|
89
|
+
}
|
|
90
|
+
// clear => routing block only
|
|
91
|
+
} catch {
|
|
92
|
+
// Swallow errors — hook must not fail
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// Codex SessionStart requires hookEventName in hookSpecificOutput
|
|
96
|
+
process.stdout.write(JSON.stringify({
|
|
97
|
+
hookSpecificOutput: { hookEventName: "SessionStart", additionalContext },
|
|
98
|
+
}) + "\n");
|
|
@@ -69,6 +69,19 @@ export const formatters = {
|
|
|
69
69
|
}),
|
|
70
70
|
},
|
|
71
71
|
|
|
72
|
+
"codex": {
|
|
73
|
+
deny: (reason) => ({
|
|
74
|
+
hookSpecificOutput: {
|
|
75
|
+
hookEventName: "PreToolUse",
|
|
76
|
+
permissionDecision: "deny",
|
|
77
|
+
permissionDecisionReason: reason,
|
|
78
|
+
},
|
|
79
|
+
}),
|
|
80
|
+
ask: () => null, // Codex rejects permissionDecision: "ask" in PreToolUse
|
|
81
|
+
modify: () => null, // Codex rejects updatedInput in PreToolUse
|
|
82
|
+
context: () => null, // Codex rejects additionalContext in PreToolUse (fails open)
|
|
83
|
+
},
|
|
84
|
+
|
|
72
85
|
"cursor": {
|
|
73
86
|
deny: (reason) => ({
|
|
74
87
|
permission: "deny",
|
|
@@ -71,6 +71,13 @@ export const CURSOR_OPTS = {
|
|
|
71
71
|
sessionIdEnv: "CURSOR_SESSION_ID",
|
|
72
72
|
};
|
|
73
73
|
|
|
74
|
+
/** Codex CLI platform options. */
|
|
75
|
+
export const CODEX_OPTS = {
|
|
76
|
+
configDir: ".codex",
|
|
77
|
+
projectDirEnv: undefined, // Codex passes cwd in hook stdin, no env var
|
|
78
|
+
sessionIdEnv: undefined, // Uses session_id from hook stdin or ppid fallback
|
|
79
|
+
};
|
|
80
|
+
|
|
74
81
|
/** Kiro CLI platform options. */
|
|
75
82
|
export const KIRO_OPTS = {
|
|
76
83
|
configDir: ".kiro",
|
package/openclaw.plugin.json
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
"name": "Context Mode",
|
|
4
4
|
"kind": "tool",
|
|
5
5
|
"description": "OpenClaw plugin that saves 98% of your context window. Sandboxed code execution in 11 languages, FTS5 knowledge base with BM25 ranking, and intent-driven search.",
|
|
6
|
-
"version": "1.0.
|
|
6
|
+
"version": "1.0.61",
|
|
7
7
|
"sandbox": {
|
|
8
8
|
"mode": "permissive",
|
|
9
9
|
"filesystem_access": "full",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "context-mode",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.61",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "MCP plugin that saves 98% of your context window. Works with Claude Code, Gemini CLI, VS Code Copilot, OpenCode, and Codex CLI. Sandboxed code execution, FTS5 knowledge base, and intent-driven search.",
|
|
6
6
|
"author": "Mert Koseoğlu",
|
package/server.bundle.mjs
CHANGED
|
@@ -12,7 +12,7 @@ var eS=Object.create;var Pa=Object.defineProperty;var tS=Object.getOwnPropertyDe
|
|
|
12
12
|
`,"utf-8")}validateHooks(e){let r=[],n=this.readSettings();if(!n)return r.push({check:"BeforeTool hook",status:"fail",message:"Could not read ~/.gemini/settings.json",fix:"context-mode upgrade"}),r;let o=n.hooks,s=o?.[Se.BEFORE_TOOL];if(s&&s.length>0){let a=s.some(c=>c.hooks?.some(u=>u.command?.includes("context-mode")));r.push({check:"BeforeTool hook",status:a?"pass":"fail",message:a?"BeforeTool hook configured":"BeforeTool exists but does not point to context-mode",fix:a?void 0:"context-mode upgrade"})}else r.push({check:"BeforeTool hook",status:"fail",message:"No BeforeTool hooks found",fix:"context-mode upgrade"});let i=o?.[Se.SESSION_START];if(i&&i.length>0){let a=i.some(c=>c.hooks?.some(u=>u.command?.includes("context-mode")));r.push({check:"SessionStart hook",status:a?"pass":"fail",message:a?"SessionStart hook configured":"SessionStart exists but does not point to context-mode",fix:a?void 0:"context-mode upgrade"})}else r.push({check:"SessionStart hook",status:"fail",message:"No SessionStart hooks found",fix:"context-mode upgrade"});return r}checkPluginRegistration(){let e=this.readSettings();if(!e)return{check:"Plugin registration",status:"warn",message:"Could not read ~/.gemini/settings.json"};let r=e.extensions;return r&&(Array.isArray(r)?r.some(o=>typeof o=="string"&&o.includes("context-mode")):Object.keys(r).some(o=>o.includes("context-mode")))?{check:"Plugin registration",status:"pass",message:"context-mode found in extensions"}:{check:"Plugin registration",status:"warn",message:"context-mode not found in extensions (might be using standalone MCP mode)"}}getInstalledVersion(){try{let e=_s(ys(),".gemini","extensions","context-mode","package.json"),r=JSON.parse(bp(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}return"not installed"}configureAllHooks(e){let r=this.readSettings()??{},n=r.hooks??{},o=[],s=[{name:Se.BEFORE_TOOL},{name:Se.SESSION_START}];for(let i of s){let c={matcher:"",hooks:[{type:"command",command:Qn(i.name,e)}]},u=n[i.name];if(u&&Array.isArray(u)){let l=u.findIndex(d=>d.hooks?.some(m=>m.command?.includes("context-mode")));l>=0?(u[l]=c,o.push(`Updated existing ${i.name} hook entry`)):(u.push(c),o.push(`Added ${i.name} hook entry`)),n[i.name]=u}else n[i.name]=[c],o.push(`Created ${i.name} hooks section`)}return r.hooks=n,this.writeSettings(r),o}backupSettings(){let e=this.getSettingsPath();try{Lx(e,Zx.R_OK);let r=e+".bak";return IC(e,r),r}catch{return null}}setHookPermissions(e){let r=[],n=ga(e,"hooks","gemini-cli");for(let o of Object.values(kp)){let s=_s(n,o);try{Lx(s,Zx.R_OK),zC(s,493),r.push(s)}catch{}}return r}updatePluginRegistry(e,r){try{let n=_s(ys(),".gemini","extensions","context-mode","package.json"),o=JSON.parse(bp(n,"utf-8"));o.version=r,o.installPath=e,o.lastUpdated=new Date().toISOString(),Mx(n,JSON.stringify(o,null,2)+`
|
|
13
13
|
`,"utf-8")}catch{}}getProjectDir(){return process.env.GEMINI_PROJECT_DIR??process.env.CLAUDE_PROJECT_DIR}extractSessionId(e){return e.session_id?e.session_id:`pid-${process.ppid}`}}});var sn,RL,OL,qx=be(()=>{"use strict";sn={BEFORE:"tool.execute.before",AFTER:"tool.execute.after",COMPACTING:"experimental.session.compacting"},RL=[sn.BEFORE,sn.AFTER],OL=[sn.COMPACTING]});var Vx={};Ke(Vx,{OpenCodeAdapter:()=>Ep});import{createHash as Fx}from"node:crypto";import{readFileSync as Bx,writeFileSync as AC,mkdirSync as jC,copyFileSync as MC,accessSync as DC,constants as LC}from"node:fs";import{resolve as Lt,join as Qt}from"node:path";import{homedir as vr}from"node:os";function NC(t){return t.replace(/\/\/.*$/gm,"").replace(/\/\*[\s\S]*?\*\//g,"").replace(/,(\s*[}\]])/g,"$1")}var Ep,Kx=be(()=>{"use strict";qx();Ep=class{get name(){return this.platform==="kilo"?"KiloCode":"OpenCode"}paradigm="ts-plugin";settingsPath;capabilities={preToolUse:!0,postToolUse:!0,preCompact:!0,sessionStart:!1,canModifyArgs:!0,canModifyOutput:!0,canInjectSessionContext:!1};platform;constructor(e="opencode"){this.platform=e}parsePreToolUseInput(e){let r=e;return{toolName:r.tool??"",toolInput:r.args??{},sessionId:this.extractSessionId(r),projectDir:process.env.OPENCODE_PROJECT_DIR||process.cwd(),raw:e}}parsePostToolUseInput(e){let r=e;return{toolName:r.tool??"",toolInput:r.args??{},toolOutput:r.output,isError:void 0,sessionId:this.extractSessionId(r),projectDir:process.env.OPENCODE_PROJECT_DIR||process.cwd(),raw:e}}parsePreCompactInput(e){let r=e;return{sessionId:this.extractSessionId(r),projectDir:process.env.OPENCODE_PROJECT_DIR||process.cwd(),raw:e}}parseSessionStartInput(e){let r=e,n=r.source??"startup",o;switch(n){case"compact":o="compact";break;case"resume":o="resume";break;case"clear":o="clear";break;default:o="startup"}return{sessionId:this.extractSessionId(r),source:o,projectDir:process.env.OPENCODE_PROJECT_DIR||process.cwd(),raw:e}}formatPreToolUseResponse(e){if(e.decision==="deny")throw new Error(e.reason??"Blocked by context-mode hook");if(e.decision==="modify"&&e.updatedInput)return{args:e.updatedInput};if(e.decision==="ask")throw new Error(e.reason??"Action requires user confirmation (security policy)")}formatPostToolUseResponse(e){let r={};return e.updatedOutput&&(r.output=e.updatedOutput),e.additionalContext&&(r.additionalContext=e.additionalContext),Object.keys(r).length>0?r:void 0}formatPreCompactResponse(e){return e.context??""}formatSessionStartResponse(e){return e.context??""}getSettingsPath(){return this.settingsPath??Lt(`${this.platform}.json`)}paths(){return this.platform==="kilo"?[Lt("kilo.json"),Lt("kilo.jsonc"),Lt(".kilo","kilo.json"),Lt(".kilo","kilo.jsonc"),Qt(vr(),".config","kilo","kilo.json"),Qt(vr(),".config","kilo","kilo.jsonc")]:[Lt("opencode.json"),Lt("opencode.jsonc"),Lt(".opencode","opencode.json"),Lt(".opencode","opencode.jsonc"),Qt(vr(),".config","opencode","opencode.json"),Qt(vr(),".config","opencode","opencode.jsonc")]}getSessionDir(){let e;process.platform==="win32"?e=process.env.APPDATA||Qt(vr(),"AppData","Roaming"):e=process.env.XDG_CONFIG_HOME||Qt(vr(),".config");let r=Qt(e,this.platform,"context-mode","sessions");return jC(r,{recursive:!0}),r}getSessionDBPath(e){let r=Fx("sha256").update(e).digest("hex").slice(0,16);return Qt(this.getSessionDir(),`${r}.db`)}getSessionEventsPath(e){let r=Fx("sha256").update(e).digest("hex").slice(0,16);return Qt(this.getSessionDir(),`${r}-events.md`)}generateHookConfig(e){return{[sn.BEFORE]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}],[sn.AFTER]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}],[sn.COMPACTING]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}]}}readSettings(){this.settingsPath=void 0;let e=this.paths(),r=new Set(e.filter(s=>s.includes(vr()))),n=null,o;for(let s of e)try{let i=Bx(s,"utf-8"),a=s.endsWith(".jsonc")?NC(i):i,c=JSON.parse(a);n||(n=c,o=s);let u=r.has(s);if(this.hasContextModePlugin(c)||u)return this.settingsPath=s,c}catch{continue}return n?(this.settingsPath=o,n):null}writeSettings(e){AC(this.getSettingsPath(),JSON.stringify(e,null,2)+`
|
|
14
14
|
`,"utf-8")}validateHooks(e){let r=[],n=this.readSettings();if(!n)return r.push({check:"Plugin configuration",status:"fail",message:`Could not read ${this.platform}.json or ${this.platform}.jsonc`,fix:"context-mode upgrade"}),r;let o=this.hasContextModePlugin(n);return Array.isArray(n.plugin)?r.push({check:"Plugin registration",status:o?"pass":"fail",message:o?"context-mode found in plugin array":"context-mode not found in plugin array",fix:o?void 0:"context-mode upgrade"}):r.push({check:"Plugin registration",status:"fail",message:`No plugin array found in ${this.platform}.json or ${this.platform}.jsonc`,fix:"context-mode upgrade"}),r.push({check:"SessionStart hook",status:"warn",message:`SessionStart not supported in ${this.name} (see issues #14808, #5409)`}),r}checkPluginRegistration(){let e=this.readSettings();return e?this.hasContextModePlugin(e)?{check:"Plugin registration",status:"pass",message:"context-mode found in plugin array"}:{check:"Plugin registration",status:"fail",message:`context-mode not found in ${this.platform}.json plugin array`,fix:"context-mode upgrade"}:{check:"Plugin registration",status:"warn",message:`Could not read ${this.platform}.json or ${this.platform}.jsonc`}}getInstalledVersion(){try{let e=Lt(vr(),".cache",this.platform,"node_modules","context-mode","package.json"),r=JSON.parse(Bx(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}return"not installed"}configureAllHooks(e){let r=this.readSettings()??{},n=[],o=r.plugin??[];return o.some(s=>s.includes("context-mode"))?n.push("context-mode already in plugin array"):(o.push("context-mode"),n.push("Added context-mode to plugin array")),r.plugin=o,this.writeSettings(r),n}backupSettings(){let e=this.checkPluginRegistration();if(!this.settingsPath)return null;if(e.status==="pass")return this.settingsPath;try{DC(this.settingsPath,LC.R_OK);let r=this.settingsPath+".bak";return MC(this.settingsPath,r),r}catch{return null}}setHookPermissions(e){return[]}updatePluginRegistry(e,r){}hasContextModePlugin(e){let r=e.plugin;return Array.isArray(r)&&r.some(n=>typeof n=="string"&&n.includes("context-mode"))}extractSessionId(e){return e.sessionID?e.sessionID:`pid-${process.ppid}`}}});var an,ML,DL,Jx=be(()=>{"use strict";an={TOOL_CALL_BEFORE:"tool_call:before",TOOL_CALL_AFTER:"tool_call:after",COMMAND_NEW:"command:new",COMMAND_RESET:"command:reset",COMMAND_STOP:"command:stop"},ML=[an.TOOL_CALL_BEFORE,an.TOOL_CALL_AFTER],DL=[an.COMMAND_NEW]});var Gx={};Ke(Gx,{OpenClawAdapter:()=>$p});import{createHash as Wx}from"node:crypto";import{readFileSync as Tp,writeFileSync as ZC,mkdirSync as UC,copyFileSync as HC,accessSync as qC,constants as FC}from"node:fs";import{resolve as Sr,join as xs}from"node:path";import{homedir as _a}from"node:os";var $p,Yx=be(()=>{"use strict";Jx();$p=class{name="OpenClaw";paradigm="ts-plugin";capabilities={preToolUse:!0,postToolUse:!0,preCompact:!0,sessionStart:!0,canModifyArgs:!0,canModifyOutput:!1,canInjectSessionContext:!0};parsePreToolUseInput(e){let r=e;return{toolName:r.toolName??r.tool_name??"",toolInput:r.params??r.tool_input??{},sessionId:this.extractSessionId(r),projectDir:process.cwd(),raw:e}}parsePostToolUseInput(e){let r=e;return{toolName:r.toolName??r.tool_name??"",toolInput:r.params??r.tool_input??{},toolOutput:r.output??r.tool_output,isError:r.isError??r.is_error,sessionId:this.extractSessionId(r),projectDir:process.cwd(),raw:e}}parsePreCompactInput(e){let r=e;return{sessionId:this.extractSessionId(r),projectDir:process.cwd(),raw:e}}parseSessionStartInput(e){let r=e,n=r.source??"startup",o;switch(n){case"compact":o="compact";break;case"resume":o="resume";break;case"clear":o="clear";break;default:o="startup"}return{sessionId:this.extractSessionId(r),source:o,projectDir:process.cwd(),raw:e}}formatPreToolUseResponse(e){if(e.decision==="deny")return{block:!0,blockReason:e.reason??"Blocked by context-mode hook"};if(e.decision==="modify"&&e.updatedInput)return{params:e.updatedInput};if(e.decision==="ask")return{block:!0,blockReason:e.reason??"Action requires user confirmation (security policy)"};e.decision==="context"&&e.additionalContext}formatPostToolUseResponse(e){let r={};return e.additionalContext&&(r.additionalContext=e.additionalContext),Object.keys(r).length>0?r:void 0}formatPreCompactResponse(e){return e.context??""}formatSessionStartResponse(e){return e.context??""}getSettingsPath(){return Sr("openclaw.json")}getSessionDir(){let e=xs(_a(),".openclaw","context-mode","sessions");return UC(e,{recursive:!0}),e}getSessionDBPath(e){let r=Wx("sha256").update(e).digest("hex").slice(0,16);return xs(this.getSessionDir(),`${r}.db`)}getSessionEventsPath(e){let r=Wx("sha256").update(e).digest("hex").slice(0,16);return xs(this.getSessionDir(),`${r}-events.md`)}generateHookConfig(e){return{[an.TOOL_CALL_BEFORE]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}],[an.TOOL_CALL_AFTER]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}],[an.COMMAND_NEW]:[{matcher:"",hooks:[{type:"plugin",command:"context-mode"}]}]}}readSettings(){let e=[Sr("openclaw.json"),Sr(".openclaw","openclaw.json"),xs(_a(),".openclaw","openclaw.json")];for(let r of e)try{let n=Tp(r,"utf-8");return JSON.parse(n)}catch{continue}return null}writeSettings(e){let r=Sr("openclaw.json");ZC(r,JSON.stringify(e,null,2)+`
|
|
15
|
-
`,"utf-8")}validateHooks(e){let r=[],n=this.readSettings();if(!n)return r.push({check:"Plugin configuration",status:"fail",message:"Could not read openclaw.json",fix:"context-mode upgrade"}),r;let o=n.plugins,s=o?.entries;if(s){let a=Object.keys(s).some(c=>c.includes("context-mode"));if(r.push({check:"Plugin registration",status:a?"pass":"fail",message:a?"context-mode found in plugins.entries":"context-mode not found in plugins.entries",fix:a?void 0:"context-mode upgrade"}),a){let u=s["context-mode"]?.enabled!==!1;r.push({check:"Plugin enabled",status:u?"pass":"warn",message:u?"context-mode plugin is enabled":"context-mode plugin is disabled"})}}else r.push({check:"Plugin registration",status:"fail",message:"No plugins.entries found in openclaw.json",fix:"context-mode upgrade"});return o?.slots?.contextEngine==="context-mode"?r.push({check:"Context engine",status:"pass",message:"context-mode registered as context engine (owns compaction)"}):r.push({check:"Context engine",status:"warn",message:"context-mode not set as context engine \u2014 compaction will use default engine"}),r}checkPluginRegistration(){let e=this.readSettings();if(!e)return{check:"Plugin registration",status:"warn",message:"Could not read openclaw.json"};let n=e.plugins?.entries;return n&&Object.keys(n).some(s=>s.includes("context-mode"))?{check:"Plugin registration",status:"pass",message:"context-mode found in plugins.entries"}:{check:"Plugin registration",status:"fail",message:"context-mode not found in openclaw.json plugins.entries",fix:"context-mode upgrade"}}getInstalledVersion(){try{let e=Sr(_a(),".openclaw","extensions","context-mode","package.json"),r=JSON.parse(Tp(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}try{let e=Sr("node_modules","context-mode","package.json"),r=JSON.parse(Tp(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}return"not installed"}configureAllHooks(e){let r=this.readSettings()??{},n=[];r.plugins||(r.plugins={});let o=r.plugins;o.entries||(o.entries={});let s=o.entries;if(!s["context-mode"])s["context-mode"]={enabled:!0},n.push("Added context-mode to plugins.entries");else{let a=s["context-mode"];a.enabled===!1?(a.enabled=!0,n.push("Enabled context-mode plugin")):n.push("context-mode already configured in plugins.entries")}o.slots||(o.slots={});let i=o.slots;return i.contextEngine?i.contextEngine!=="context-mode"&&n.push(`Context engine already set to "${i.contextEngine}" \u2014 not overwriting`):(i.contextEngine="context-mode",n.push("Set context-mode as context engine (owns compaction)")),this.writeSettings(r),n}backupSettings(){let e=[Sr("openclaw.json"),Sr(".openclaw","openclaw.json"),xs(_a(),".openclaw","openclaw.json")];for(let r of e)try{qC(r,FC.R_OK);let n=r+".bak";return HC(r,n),n}catch{continue}return null}setHookPermissions(e){return[]}updatePluginRegistry(e,r){}extractSessionId(e){return e.sessionId?e.sessionId:`pid-${process.ppid}`}}});var tv={};Ke(tv,{CodexAdapter:()=>Op});import{createHash as Xx}from"node:crypto";import{readFileSync as Pp,mkdirSync as BC,copyFileSync as VC,accessSync as KC,constants as JC}from"node:fs";import{resolve as Qx,join as Rp,dirname as WC}from"node:path";import{fileURLToPath as GC}from"node:url";import{homedir as ev}from"node:os";var Op,rv=be(()=>{"use strict";Op=class{name="Codex CLI";paradigm="json-stdio";capabilities={preToolUse:!0,postToolUse:!0,preCompact:!1,sessionStart:!0,canModifyArgs:!1,canModifyOutput:!1,canInjectSessionContext:!0};parsePreToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parsePostToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},toolOutput:r.tool_response,sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parsePreCompactInput(e){let r=e;return{sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parseSessionStartInput(e){let r=e,n=r.source??"startup",o;switch(n){case"compact":o="compact";break;case"resume":o="resume";break;case"clear":o="clear";break;default:o="startup"}return{sessionId:this.extractSessionId(r),source:o,projectDir:r.cwd,raw:e}}formatPreToolUseResponse(e){return e.decision==="deny"?{hookSpecificOutput:{permissionDecision:"deny",permissionDecisionReason:e.reason??"Blocked by context-mode hook"}}:e.decision==="context"&&e.additionalContext?{
|
|
15
|
+
`,"utf-8")}validateHooks(e){let r=[],n=this.readSettings();if(!n)return r.push({check:"Plugin configuration",status:"fail",message:"Could not read openclaw.json",fix:"context-mode upgrade"}),r;let o=n.plugins,s=o?.entries;if(s){let a=Object.keys(s).some(c=>c.includes("context-mode"));if(r.push({check:"Plugin registration",status:a?"pass":"fail",message:a?"context-mode found in plugins.entries":"context-mode not found in plugins.entries",fix:a?void 0:"context-mode upgrade"}),a){let u=s["context-mode"]?.enabled!==!1;r.push({check:"Plugin enabled",status:u?"pass":"warn",message:u?"context-mode plugin is enabled":"context-mode plugin is disabled"})}}else r.push({check:"Plugin registration",status:"fail",message:"No plugins.entries found in openclaw.json",fix:"context-mode upgrade"});return o?.slots?.contextEngine==="context-mode"?r.push({check:"Context engine",status:"pass",message:"context-mode registered as context engine (owns compaction)"}):r.push({check:"Context engine",status:"warn",message:"context-mode not set as context engine \u2014 compaction will use default engine"}),r}checkPluginRegistration(){let e=this.readSettings();if(!e)return{check:"Plugin registration",status:"warn",message:"Could not read openclaw.json"};let n=e.plugins?.entries;return n&&Object.keys(n).some(s=>s.includes("context-mode"))?{check:"Plugin registration",status:"pass",message:"context-mode found in plugins.entries"}:{check:"Plugin registration",status:"fail",message:"context-mode not found in openclaw.json plugins.entries",fix:"context-mode upgrade"}}getInstalledVersion(){try{let e=Sr(_a(),".openclaw","extensions","context-mode","package.json"),r=JSON.parse(Tp(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}try{let e=Sr("node_modules","context-mode","package.json"),r=JSON.parse(Tp(e,"utf-8"));if(typeof r.version=="string")return r.version}catch{}return"not installed"}configureAllHooks(e){let r=this.readSettings()??{},n=[];r.plugins||(r.plugins={});let o=r.plugins;o.entries||(o.entries={});let s=o.entries;if(!s["context-mode"])s["context-mode"]={enabled:!0},n.push("Added context-mode to plugins.entries");else{let a=s["context-mode"];a.enabled===!1?(a.enabled=!0,n.push("Enabled context-mode plugin")):n.push("context-mode already configured in plugins.entries")}o.slots||(o.slots={});let i=o.slots;return i.contextEngine?i.contextEngine!=="context-mode"&&n.push(`Context engine already set to "${i.contextEngine}" \u2014 not overwriting`):(i.contextEngine="context-mode",n.push("Set context-mode as context engine (owns compaction)")),this.writeSettings(r),n}backupSettings(){let e=[Sr("openclaw.json"),Sr(".openclaw","openclaw.json"),xs(_a(),".openclaw","openclaw.json")];for(let r of e)try{qC(r,FC.R_OK);let n=r+".bak";return HC(r,n),n}catch{continue}return null}setHookPermissions(e){return[]}updatePluginRegistry(e,r){}extractSessionId(e){return e.sessionId?e.sessionId:`pid-${process.ppid}`}}});var tv={};Ke(tv,{CodexAdapter:()=>Op});import{createHash as Xx}from"node:crypto";import{readFileSync as Pp,mkdirSync as BC,copyFileSync as VC,accessSync as KC,constants as JC}from"node:fs";import{resolve as Qx,join as Rp,dirname as WC}from"node:path";import{fileURLToPath as GC}from"node:url";import{homedir as ev}from"node:os";var Op,rv=be(()=>{"use strict";Op=class{name="Codex CLI";paradigm="json-stdio";capabilities={preToolUse:!0,postToolUse:!0,preCompact:!1,sessionStart:!0,canModifyArgs:!1,canModifyOutput:!1,canInjectSessionContext:!0};parsePreToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parsePostToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},toolOutput:r.tool_response,sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parsePreCompactInput(e){let r=e;return{sessionId:this.extractSessionId(r),projectDir:r.cwd,raw:e}}parseSessionStartInput(e){let r=e,n=r.source??"startup",o;switch(n){case"compact":o="compact";break;case"resume":o="resume";break;case"clear":o="clear";break;default:o="startup"}return{sessionId:this.extractSessionId(r),source:o,projectDir:r.cwd,raw:e}}formatPreToolUseResponse(e){return e.decision==="deny"?{hookSpecificOutput:{hookEventName:"PreToolUse",permissionDecision:"deny",permissionDecisionReason:e.reason??"Blocked by context-mode hook"}}:e.decision==="context"&&e.additionalContext?{}:{}}formatPostToolUseResponse(e){return e.additionalContext?{hookSpecificOutput:{hookEventName:"PostToolUse",additionalContext:e.additionalContext}}:{}}formatPreCompactResponse(e){return e.context?{hookSpecificOutput:{additionalContext:e.context}}:{}}formatSessionStartResponse(e){return e.context?{hookSpecificOutput:{hookEventName:"SessionStart",additionalContext:e.context}}:{}}getSettingsPath(){return Qx(ev(),".codex","config.toml")}getSessionDir(){let e=Rp(ev(),".codex","context-mode","sessions");return BC(e,{recursive:!0}),e}getSessionDBPath(e){let r=Xx("sha256").update(e).digest("hex").slice(0,16);return Rp(this.getSessionDir(),`${r}.db`)}getSessionEventsPath(e){let r=Xx("sha256").update(e).digest("hex").slice(0,16);return Rp(this.getSessionDir(),`${r}-events.md`)}generateHookConfig(e){return{PreToolUse:[{matcher:"",hooks:[{type:"command",command:`node ${e}/hooks/pretooluse.mjs`}]}],PostToolUse:[{matcher:"",hooks:[{type:"command",command:`node ${e}/hooks/posttooluse.mjs`}]}],SessionStart:[{matcher:"",hooks:[{type:"command",command:`node ${e}/hooks/sessionstart.mjs`}]}]}}readSettings(){try{return{_raw_toml:Pp(this.getSettingsPath(),"utf-8")}}catch{return null}}writeSettings(e){}validateHooks(e){return[{check:"Hook support",status:"warn",message:"Codex CLI hooks are implemented but dispatch is not yet active (Stage::UnderDevelopment, v0.118.0). Enable flag: [features] codex_hooks = true in ~/.codex/config.toml. Track: openai/codex#16685"}]}checkPluginRegistration(){try{let e=Pp(this.getSettingsPath(),"utf-8"),r=e.includes("context-mode"),n=e.includes("[mcp_servers]")||e.includes("[mcp_servers.");return r&&n?{check:"MCP registration",status:"pass",message:"context-mode found in [mcp_servers] config"}:n?{check:"MCP registration",status:"fail",message:"[mcp_servers] section exists but context-mode not found",fix:"Add context-mode to [mcp_servers] in ~/.codex/config.toml"}:{check:"MCP registration",status:"fail",message:"No [mcp_servers] section in config.toml",fix:"Add [mcp_servers.context-mode] to ~/.codex/config.toml"}}catch{return{check:"MCP registration",status:"warn",message:"Could not read ~/.codex/config.toml"}}}getInstalledVersion(){return"not installed"}configureAllHooks(e){return[]}backupSettings(){let e=this.getSettingsPath();try{KC(e,JC.R_OK);let r=e+".bak";return VC(e,r),r}catch{return null}}setHookPermissions(e){return[]}updatePluginRegistry(e,r){}getRoutingInstructions(){let e=Qx(WC(GC(import.meta.url)),"..","..","..","configs","codex","AGENTS.md");try{return Pp(e,"utf-8")}catch{return`# context-mode
|
|
16
16
|
|
|
17
17
|
Use context-mode MCP tools (execute, execute_file, batch_execute, fetch_and_index, search) instead of bash/cat/curl for data-heavy operations.`}}extractSessionId(e){return e.session_id?e.session_id:`pid-${process.ppid}`}}});function eo(t,e){let r=ya[t];if(!r)throw new Error(`No script defined for hook type: ${t}`);return e?`node "${e}/hooks/${r}"`:`context-mode hook vscode-copilot ${t.toLowerCase()}`}var ae,ya,GL,YL,nv=be(()=>{"use strict";ae={PRE_TOOL_USE:"PreToolUse",POST_TOOL_USE:"PostToolUse",PRE_COMPACT:"PreCompact",SESSION_START:"SessionStart",STOP:"Stop",SUBAGENT_START:"SubagentStart",SUBAGENT_STOP:"SubagentStop"},ya={[ae.PRE_TOOL_USE]:"pretooluse.mjs",[ae.POST_TOOL_USE]:"posttooluse.mjs",[ae.PRE_COMPACT]:"precompact.mjs",[ae.SESSION_START]:"sessionstart.mjs"},GL=[ae.PRE_TOOL_USE,ae.SESSION_START],YL=[ae.POST_TOOL_USE,ae.PRE_COMPACT]});var iv={};Ke(iv,{VSCodeCopilotAdapter:()=>Ap});import{createHash as ov}from"node:crypto";import{readFileSync as xa,writeFileSync as sv,mkdirSync as Cp,copyFileSync as YC,accessSync as Ip,existsSync as XC,chmodSync as QC,constants as zp}from"node:fs";import{resolve as $t,join as cn}from"node:path";import{homedir as Np}from"node:os";var Ap,av=be(()=>{"use strict";nv();Ap=class{name="VS Code Copilot";paradigm="json-stdio";capabilities={preToolUse:!0,postToolUse:!0,preCompact:!0,sessionStart:!0,canModifyArgs:!0,canModifyOutput:!0,canInjectSessionContext:!0};parsePreToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},sessionId:this.extractSessionId(r),projectDir:process.env.CLAUDE_PROJECT_DIR||process.cwd(),raw:e}}parsePostToolUseInput(e){let r=e;return{toolName:r.tool_name??"",toolInput:r.tool_input??{},toolOutput:r.tool_output,isError:r.is_error,sessionId:this.extractSessionId(r),projectDir:process.env.CLAUDE_PROJECT_DIR||process.cwd(),raw:e}}parsePreCompactInput(e){let r=e;return{sessionId:this.extractSessionId(r),projectDir:process.env.CLAUDE_PROJECT_DIR||process.cwd(),raw:e}}parseSessionStartInput(e){let r=e,n=r.source??"startup",o;switch(n){case"compact":o="compact";break;case"resume":o="resume";break;case"clear":o="clear";break;default:o="startup"}return{sessionId:this.extractSessionId(r),source:o,projectDir:process.env.CLAUDE_PROJECT_DIR||process.cwd(),raw:e}}formatPreToolUseResponse(e){if(e.decision==="deny")return{permissionDecision:"deny",reason:e.reason??"Blocked by context-mode hook"};if(e.decision==="modify"&&e.updatedInput)return{hookSpecificOutput:{hookEventName:ae.PRE_TOOL_USE,updatedInput:e.updatedInput}};if(e.decision==="context"&&e.additionalContext)return{hookSpecificOutput:{hookEventName:ae.PRE_TOOL_USE,additionalContext:e.additionalContext}};if(e.decision==="ask")return{permissionDecision:"deny",reason:e.reason??"Action requires user confirmation (security policy)"}}formatPostToolUseResponse(e){if(e.updatedOutput)return{hookSpecificOutput:{hookEventName:ae.POST_TOOL_USE,decision:"block",reason:e.updatedOutput}};if(e.additionalContext)return{hookSpecificOutput:{hookEventName:ae.POST_TOOL_USE,additionalContext:e.additionalContext}}}formatPreCompactResponse(e){return e.context??""}formatSessionStartResponse(e){return e.context??""}getSettingsPath(){return $t(".github","hooks","context-mode.json")}getSessionDir(){let e=$t(".github","context-mode","sessions"),r=cn(Np(),".vscode","context-mode","sessions"),n=XC($t(".github"))?e:r;return Cp(n,{recursive:!0}),n}getSessionDBPath(e){let r=ov("sha256").update(e).digest("hex").slice(0,16);return cn(this.getSessionDir(),`${r}.db`)}getSessionEventsPath(e){let r=ov("sha256").update(e).digest("hex").slice(0,16);return cn(this.getSessionDir(),`${r}-events.md`)}generateHookConfig(e){return{[ae.PRE_TOOL_USE]:[{matcher:"",hooks:[{type:"command",command:eo(ae.PRE_TOOL_USE,e)}]}],[ae.POST_TOOL_USE]:[{matcher:"",hooks:[{type:"command",command:eo(ae.POST_TOOL_USE,e)}]}],[ae.PRE_COMPACT]:[{matcher:"",hooks:[{type:"command",command:eo(ae.PRE_COMPACT,e)}]}],[ae.SESSION_START]:[{matcher:"",hooks:[{type:"command",command:eo(ae.SESSION_START,e)}]}]}}readSettings(){let e=[this.getSettingsPath(),$t(".claude","settings.json")];for(let r of e)try{let n=xa(r,"utf-8");return JSON.parse(n)}catch{continue}return null}writeSettings(e){let r=this.getSettingsPath(),n=$t(".github","hooks");Cp(n,{recursive:!0}),sv(r,JSON.stringify(e,null,2)+`
|
|
18
18
|
`,"utf-8")}validateHooks(e){let r=[],n=$t(".github","hooks");try{Ip(n,zp.R_OK)}catch{return r.push({check:"Hooks directory",status:"fail",message:".github/hooks/ directory not found",fix:"context-mode upgrade"}),r}let o=$t(n,"context-mode.json");try{let s=xa(o,"utf-8"),a=JSON.parse(s).hooks;a?.[ae.PRE_TOOL_USE]?r.push({check:"PreToolUse hook",status:"pass",message:"PreToolUse hook configured in context-mode.json"}):r.push({check:"PreToolUse hook",status:"fail",message:"PreToolUse not found in context-mode.json",fix:"context-mode upgrade"}),a?.[ae.SESSION_START]?r.push({check:"SessionStart hook",status:"pass",message:"SessionStart hook configured in context-mode.json"}):r.push({check:"SessionStart hook",status:"fail",message:"SessionStart not found in context-mode.json",fix:"context-mode upgrade"})}catch{r.push({check:"Hook configuration",status:"fail",message:"Could not read .github/hooks/context-mode.json",fix:"context-mode upgrade"})}return r.push({check:"API stability",status:"warn",message:"VS Code Copilot hooks are in preview \u2014 API may change without notice"}),r.push({check:"Matcher support",status:"warn",message:"Matchers are parsed but IGNORED \u2014 all hooks fire on all tools"}),r}checkPluginRegistration(){try{let e=$t(".vscode","mcp.json"),r=xa(e,"utf-8"),o=JSON.parse(r).servers;return o&&Object.keys(o).some(i=>i.includes("context-mode"))?{check:"MCP registration",status:"pass",message:"context-mode found in .vscode/mcp.json"}:{check:"MCP registration",status:"fail",message:"context-mode not found in .vscode/mcp.json",fix:"Add context-mode server to .vscode/mcp.json"}}catch{return{check:"MCP registration",status:"warn",message:"Could not read .vscode/mcp.json"}}}getInstalledVersion(){let e=[cn(Np(),".vscode","extensions"),cn(Np(),".vscode-insiders","extensions")];for(let r of e)try{let n=xa(cn(r,"extensions.json"),"utf-8"),s=JSON.parse(n).find(i=>typeof i.identifier=="object"&&i.identifier!==null&&i.identifier.id?.toString().includes("context-mode"));if(s&&typeof s.version=="string")return s.version}catch{continue}return"not installed"}configureAllHooks(e){let r=[],n={hooks:{}},o=n.hooks,s=[ae.PRE_TOOL_USE,ae.POST_TOOL_USE,ae.PRE_COMPACT,ae.SESSION_START];for(let c of s)ya[c]&&(o[c]=[{matcher:"",hooks:[{type:"command",command:eo(c,e)}]}],r.push(`Configured ${c} hook`));let i=$t(".github","hooks");Cp(i,{recursive:!0});let a=$t(i,"context-mode.json");return sv(a,JSON.stringify(n,null,2)+`
|
|
@@ -520,7 +520,7 @@ ${xe}
|
|
|
520
520
|
`).length;if(s&&n.length===0)return Z("ctx_batch_execute",{content:[{type:"text",text:`Batch timed out after ${r}ms. No output captured.`}],isError:!0});Pt(a);let u=fn(),l=`batch:${t.map(y=>y.label).join(",").slice(0,80)}`,d=u.index({content:i,source:l}),f=u.getChunksBySource(d.sourceId),m=["## Indexed Sections",""],p=[];for(let y of f){let S=Buffer.byteLength(y.content);m.push(`- ${y.title} (${(S/1024).toFixed(1)}KB)`),p.push(y.title)}let h=zI(u,e,l),g=u.getDistinctiveTerms?u.getDistinctiveTerms(d.sourceId):[],v=[`Executed ${t.length} commands (${c} lines, ${(a/1024).toFixed(1)}KB). Indexed ${d.totalChunks} sections. Searched ${e.length} queries.`,"",...m,"",...h,g.length>0?`
|
|
521
521
|
Searchable terms for follow-up: ${g.join(", ")}`:""].join(`
|
|
522
522
|
`);return Z("ctx_batch_execute",{content:[{type:"text",text:v}]})}catch(n){let o=n instanceof Error?n.message:String(n);return Z("ctx_batch_execute",{content:[{type:"text",text:`Batch execution error: ${o}`}],isError:!0})}});Ve.registerTool("ctx_stats",{title:"Session Statistics",description:"Returns context consumption statistics for the current session. Shows total bytes returned to context, breakdown by tool, call counts, estimated token usage, and context savings ratio.",inputSchema:L.object({reset:L.boolean().optional().describe("Reset all stats and FTS5 store to zero. Use after /clear.")})},async({reset:t})=>{if(Wv(),t)return Jv(),Z("ctx_stats",{content:[{type:"text",text:"Session stats and search index reset."}]});let e=Object.values(ee.bytesReturned).reduce((f,m)=>f+m,0),r=Object.values(ee.calls).reduce((f,m)=>f+m,0),o=((Date.now()-ee.sessionStart)/6e4).toFixed(1),s=ee.bytesIndexed+ee.bytesSandboxed,i=s+e,a=i/Math.max(e,1),c=i>0?((1-e/i)*100).toFixed(0):"0",u=f=>f>=1024*1024?`${(f/1024/1024).toFixed(1)}MB`:`${(f/1024).toFixed(1)}KB`,l=[`## context-mode \u2014 Session Report (${o} min)`];if(l.push("","### Context Window Protection",""),r===0)l.push("No context-mode tool calls yet. Use `batch_execute`, `execute`, or `fetch_and_index` to keep raw output out of your context window.");else{l.push("| Metric | Value |","|--------|------:|",`| Total data processed | **${u(i)}** |`,`| Kept in sandbox (never entered context) | **${u(s)}** |`,`| Entered context | ${u(e)} |`,`| Estimated tokens saved | ~${Math.round(s/4).toLocaleString()} |`,`| **Context savings** | **${a.toFixed(1)}x (${c}% reduction)** |`);let f=new Set([...Object.keys(ee.calls),...Object.keys(ee.bytesReturned)]);if(f.size>0){l.push("","| Tool | Calls | Context | Tokens |","|------|------:|--------:|-------:|");for(let m of Array.from(f).sort()){let p=ee.calls[m]||0,h=ee.bytesReturned[m]||0,g=Math.round(h/4);l.push(`| ${m} | ${p} | ${u(h)} | ~${g.toLocaleString()} |`)}l.push(`| **Total** | **${r}** | **${u(e)}** | **~${Math.round(e/4).toLocaleString()}** |`)}if(s>0&&l.push("",`Without context-mode, **${u(i)}** of raw output would flood your context window. Instead, **${c}%** stayed in sandbox.`),ee.cacheHits>0||ee.cacheBytesSaved>0){let m=i+ee.cacheBytesSaved,p=m/Math.max(e,1),h=Math.max(0,24-Math.floor((Date.now()-ee.sessionStart)/(3600*1e3)));l.push("","### TTL Cache","","| Metric | Value |","|--------|------:|",`| Cache hits | **${ee.cacheHits}** |`,`| Data avoided by cache | **${u(ee.cacheBytesSaved)}** |`,`| Network requests saved | **${ee.cacheHits}** |`,`| TTL remaining | **~${h}h** |`,"",`Content was already indexed in the knowledge base \u2014 ${ee.cacheHits} fetch${ee.cacheHits>1?"es":""} skipped entirely. **${u(ee.cacheBytesSaved)}** of network I/O avoided. Search results served directly from local FTS5 index.`),p>a&&l.push("",`**Total context savings (sandbox + cache): ${p.toFixed(1)}x** \u2014 ${u(m)} processed, only ${u(e)} entered context.`)}}try{let f=process.env.CLAUDE_PROJECT_DIR||process.cwd(),m=Fv("sha256").update(f).digest("hex").slice(0,16),p=wx(),h=er(bs(),".claude","context-mode","sessions",`${m}${p}.db`);if(kr(h)){let g=fs(),v=new g(h,{readonly:!0}),y=v.prepare("SELECT COUNT(*) as cnt FROM session_events").get(),S=v.prepare("SELECT category, COUNT(*) as cnt FROM session_events GROUP BY category ORDER BY cnt DESC").all(),E=v.prepare("SELECT compact_count FROM session_meta ORDER BY started_at DESC LIMIT 1").get(),C=v.prepare("SELECT event_count, consumed FROM session_resume ORDER BY created_at DESC LIMIT 1").get();if(y.cnt>0){let xe=E?.compact_count??0,Ae=v.prepare("SELECT category, type, data FROM session_events ORDER BY id DESC").all(),Ze=new Map;for(let Te of Ae){Ze.has(Te.category)||Ze.set(Te.category,new Set);let wr=Ze.get(Te.category);if(wr.size<5){let Ue=Te.data;Te.category==="file"?Ue=Te.data.split("/").pop()||Te.data:Te.category==="prompt"&&(Ue=Ue.length>50?Ue.slice(0,47)+"...":Ue),Ue.length>40&&(Ue=Ue.slice(0,37)+"..."),wr.add(Ue)}}let so={file:"Files tracked",rule:"Project rules (CLAUDE.md)",prompt:"Your requests saved",mcp:"Plugin tools used",git:"Git operations",env:"Environment setup",error:"Errors caught",task:"Tasks in progress",decision:"Your decisions",cwd:"Working directory",skill:"Skills used",subagent:"Delegated work",intent:"Session mode",data:"Data references",role:"Behavioral directives"},br={file:"Restored after compact \u2014 no need to re-read",rule:"Your project instructions survive context resets",prompt:"Continues exactly where you left off",decision:"Applied automatically \u2014 won't ask again",task:"Picks up from where it stopped",error:"Tracked and monitored across compacts",git:"Branch, commit, and repo state preserved",env:"Runtime config carried forward",mcp:"Tool usage patterns remembered",subagent:"Delegation history preserved",skill:"Skill invocations tracked"};l.push("","### Session Continuity","","| What's preserved | Count | I remember... | Why it matters |","|------------------|------:|---------------|----------------|");for(let Te of S){let wr=so[Te.category]||Te.category,Ue=Ze.get(Te.category),$a=Ue?Array.from(Ue).join(", "):"",Qv=br[Te.category]||"Survives context resets";l.push(`| ${wr} | ${Te.cnt} | ${$a} | ${Qv} |`)}l.push(`| **Total** | **${y.cnt}** | | **Zero knowledge lost on compact** |`),l.push(""),xe>0?l.push(`Context has been compacted **${xe} time(s)** \u2014 session knowledge was preserved each time.`):l.push("When your context compacts, all of this will restore Claude's awareness \u2014 no starting from scratch."),C&&!C.consumed&&l.push(`Resume snapshot ready (${C.event_count} events) for the next compaction.`),l.push(""),l.push("> **Note:** Previous session data is loaded when you start a new session. Without `--continue`, old session history is cleaned up to keep the database lean.")}v.close()}}catch{}l.push("","---","_Display this entire report as-is in your response. Do NOT summarize, collapse, or paraphrase any section._");let d=l.join(`
|
|
523
|
-
`);return Z("ctx_stats",{content:[{type:"text",text:d}]})});Ve.registerTool("ctx_doctor",{title:"Run Diagnostics",description:"Diagnose context-mode installation. Runs all checks server-side and returns results as a markdown checklist. No CLI execution needed.",inputSchema:L.object({})},async()=>{let t=["## context-mode doctor",""],e=kr(dn(pn,"package.json"))?pn:Qp(pn),r=11,n=(ba.length/r*100).toFixed(0);t.push(`- [x] Runtimes: ${ba.length}/${r} (${n}%) \u2014 ${ba.join(", ")}`),da()?t.push("- [x] Performance: FAST (Bun)"):t.push("- [-] Performance: NORMAL \u2014 install Bun for 3-5x speed boost");try{let i=await new ds({runtimes:Ta}).execute({language:"javascript",code:'console.log("ok");',timeout:5e3});i.exitCode===0&&i.stdout.trim()==="ok"
|
|
523
|
+
`);return Z("ctx_stats",{content:[{type:"text",text:d}]})});Ve.registerTool("ctx_doctor",{title:"Run Diagnostics",description:"Diagnose context-mode installation. Runs all checks server-side and returns results as a markdown checklist. No CLI execution needed.",inputSchema:L.object({})},async()=>{let t=["## context-mode doctor",""],e=kr(dn(pn,"package.json"))?pn:Qp(pn),r=11,n=(ba.length/r*100).toFixed(0);t.push(`- [x] Runtimes: ${ba.length}/${r} (${n}%) \u2014 ${ba.join(", ")}`),da()?t.push("- [x] Performance: FAST (Bun)"):t.push("- [-] Performance: NORMAL \u2014 install Bun for 3-5x speed boost");try{let i=await new ds({runtimes:Ta}).execute({language:"javascript",code:'console.log("ok");',timeout:5e3});if(i.exitCode===0&&i.stdout.trim()==="ok")t.push("- [x] Server test: PASS");else{let a=i.stderr?.trim()?` (${i.stderr.trim().slice(0,200)})`:"";t.push(`- [ ] Server test: FAIL \u2014 exit ${i.exitCode}${a}`)}}catch(s){t.push(`- [ ] Server test: FAIL \u2014 ${s instanceof Error?s.message:s}`)}try{let s=fs(),i=new s(":memory:");i.exec("CREATE VIRTUAL TABLE fts_test USING fts5(content)"),i.exec("INSERT INTO fts_test(content) VALUES ('hello world')");let a=i.prepare("SELECT * FROM fts_test WHERE fts_test MATCH 'hello'").get();i.close(),a&&a.content==="hello world"?t.push("- [x] FTS5 / SQLite: PASS \u2014 native module works"):t.push("- [ ] FTS5 / SQLite: FAIL \u2014 unexpected result")}catch(s){t.push(`- [ ] FTS5 / SQLite: FAIL \u2014 ${s instanceof Error?s.message:s}`)}let o=dn(e,"hooks","pretooluse.mjs");return kr(o)?t.push(`- [x] Hook script: PASS \u2014 ${o}`):t.push(`- [ ] Hook script: FAIL \u2014 not found at ${o}`),t.push(`- [x] Version: v${ef}`),Z("ctx_doctor",{content:[{type:"text",text:t.join(`
|
|
524
524
|
`)}]})});Ve.registerTool("ctx_upgrade",{title:"Upgrade Plugin",description:"Upgrade context-mode to the latest version. Returns a shell command to execute. You MUST run the returned command using your shell tool (Bash, shell_execute, run_in_terminal, etc.) and display the output as a checklist. Tell the user to restart their session after upgrade.",inputSchema:L.object({})},async()=>{let t=kr(dn(pn,"package.json"))?pn:Qp(pn),e=dn(t,"cli.bundle.mjs"),r=dn(t,"build","cli.js"),n;if(kr(e))n=`node "${e}" upgrade`;else if(kr(r))n=`node "${r}" upgrade`;else{let s="https://github.com/mksglu/context-mode.git",i=["build","hooks","skills","scripts",".claude-plugin"],a=["start.mjs","server.bundle.mjs","cli.bundle.mjs","package.json"],c=['import{execFileSync}from"node:child_process";','import{cpSync,rmSync,existsSync,mkdtempSync}from"node:fs";','import{join}from"node:path";','import{tmpdir}from"node:os";',`const P=${JSON.stringify(t)};`,'const T=mkdtempSync(join(tmpdir(),"ctx-upgrade-"));',"try{",'console.log("- [x] Starting inline upgrade (no CLI found)");',`execFileSync("git",["clone","--depth","1","${s}",T],{stdio:"inherit"});`,'console.log("- [x] Cloned latest source");','execFileSync("npm",["install"],{cwd:T,stdio:"inherit"});','execFileSync("npm",["run","build"],{cwd:T,stdio:"inherit"});','console.log("- [x] Built from source");',...i.map(d=>`if(existsSync(join(T,${JSON.stringify(d)})))cpSync(join(T,${JSON.stringify(d)}),join(P,${JSON.stringify(d)}),{recursive:true,force:true});`),...a.map(d=>`if(existsSync(join(T,${JSON.stringify(d)})))cpSync(join(T,${JSON.stringify(d)}),join(P,${JSON.stringify(d)}),{force:true});`),'console.log("- [x] Copied build artifacts");','execFileSync("npm",["install","--production"],{cwd:P,stdio:"inherit"});','console.log("- [x] Installed production dependencies");','console.log("## context-mode upgrade complete");',"}catch(e){",'console.error("- [ ] Upgrade failed:",e.message);',"process.exit(1);","}finally{","try{rmSync(T,{recursive:true,force:true})}catch{}","}"].join(`
|
|
525
525
|
`),u=dn(t,".ctx-upgrade-inline.mjs"),{writeFileSync:l}=await import("node:fs");l(u,c),n=`node "${u}"`}let o=["## ctx-upgrade","","Run this command using your shell execution tool:","","```",n,"```","","After the command completes, display results as a markdown checklist:","- `[x]` for success, `[ ]` for failure","- Example format:"," ```"," ## context-mode upgrade"," - [x] Pulled latest from GitHub"," - [x] Built and installed v0.9.24"," - [x] npm global updated"," - [x] Hooks configured"," - [x] Doctor: all checks PASS"," ```","- Tell the user to restart their session to pick up the new version."].join(`
|
|
526
526
|
`);return Z("ctx_upgrade",{content:[{type:"text",text:o}]})});async function LI(){let t=pp();t>0&&console.error(`Cleaned up ${t} stale DB file(s) from previous sessions`);let e=()=>{ws.cleanupBackgrounded(),Ht&&Ht.close()},r=async()=>{e(),process.exit(0)};process.on("exit",e),process.on("SIGINT",()=>{r()}),process.on("SIGTERM",()=>{r()}),bx({onShutdown:()=>r()});let n=new ua;await Ve.connect(n);try{let{detectPlatform:o,getAdapter:s}=await Promise.resolve().then(()=>(Zv(),Lv)),i=Ve.server.getClientVersion(),a=o(i??void 0);await s(a.platform),i&&console.error(`MCP client: ${i.name} v${i.version} \u2192 ${a.platform}`)}catch{}console.error(`Context Mode MCP server v${ef} running on stdio`),console.error(`Detected runtimes:
|