context-mode 1.0.99 → 1.0.101
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 +22 -8
- package/build/adapters/claude-code/hooks.d.ts +4 -2
- package/build/adapters/claude-code/hooks.js +11 -4
- package/build/adapters/codex/index.d.ts +1 -1
- package/build/adapters/codex/index.js +6 -5
- package/build/adapters/gemini-cli/hooks.js +2 -1
- package/build/adapters/jetbrains-copilot/hooks.js +2 -1
- package/build/adapters/kiro/hooks.js +2 -1
- package/build/adapters/qwen-code/index.d.ts +1 -1
- package/build/adapters/qwen-code/index.js +13 -10
- package/build/adapters/types.d.ts +13 -0
- package/build/adapters/types.js +23 -1
- package/build/adapters/vscode-copilot/hooks.js +2 -1
- package/build/openclaw-plugin.js +3 -2
- package/build/pi-extension.js +1 -1
- package/build/search/auto-memory.d.ts +29 -0
- package/build/search/auto-memory.js +121 -0
- package/build/search/unified.d.ts +41 -0
- package/build/search/unified.js +89 -0
- package/build/server.js +73 -24
- package/build/session/analytics.js +1 -1
- package/build/session/db.d.ts +17 -0
- package/build/session/db.js +28 -0
- package/build/session/extract.d.ts +4 -0
- package/build/session/extract.js +232 -1
- package/build/session/snapshot.js +31 -0
- package/build/store.js +67 -4
- package/build/types.d.ts +1 -0
- package/cli.bundle.mjs +254 -119
- package/configs/claude-code/CLAUDE.md +21 -1
- package/configs/codex/AGENTS.md +22 -1
- package/configs/cursor/context-mode.mdc +18 -1
- package/configs/gemini-cli/GEMINI.md +22 -1
- package/configs/jetbrains-copilot/copilot-instructions.md +22 -1
- package/configs/kilo/AGENTS.md +19 -2
- package/configs/kiro/KIRO.md +18 -1
- package/configs/openclaw/AGENTS.md +22 -2
- package/configs/opencode/AGENTS.md +18 -1
- package/configs/pi/AGENTS.md +18 -1
- package/configs/qwen-code/QWEN.md +38 -18
- package/configs/vscode-copilot/copilot-instructions.md +22 -1
- package/hooks/auto-injection.mjs +76 -0
- package/hooks/codex/userpromptsubmit.mjs +1 -1
- package/hooks/core/mcp-ready.mjs +7 -1
- package/hooks/ensure-deps.mjs +35 -7
- package/hooks/posttooluse.mjs +50 -1
- package/hooks/precompact.mjs +9 -0
- package/hooks/pretooluse.mjs +27 -0
- package/hooks/routing-block.mjs +7 -1
- package/hooks/session-db.bundle.mjs +19 -13
- package/hooks/session-extract.bundle.mjs +2 -2
- package/hooks/session-snapshot.bundle.mjs +18 -17
- package/hooks/sessionstart.mjs +17 -0
- package/hooks/userpromptsubmit.mjs +1 -1
- package/insight/server.mjs +379 -1
- package/insight/src/lib/api.ts +88 -16
- package/insight/src/routes/index.tsx +566 -5
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/server.bundle.mjs +222 -87
- package/start.mjs +3 -1
|
@@ -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.101"
|
|
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.101",
|
|
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.101",
|
|
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.101",
|
|
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.101",
|
|
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
|
@@ -91,7 +91,7 @@ All checks should show `[x]`. The doctor validates runtimes, hooks, FTS5, and pl
|
|
|
91
91
|
| `/context-mode:ctx-doctor` | Diagnostics — runtimes, hooks, FTS5, plugin registration, versions. |
|
|
92
92
|
| `/context-mode:ctx-upgrade` | Pull latest, rebuild, migrate cache, fix hooks. |
|
|
93
93
|
| `/context-mode:ctx-purge` | Permanently delete all indexed content from the knowledge base. |
|
|
94
|
-
| `/context-mode:ctx-insight` | Personal analytics dashboard —
|
|
94
|
+
| `/context-mode:ctx-insight` | Personal analytics dashboard — 90 metrics, 37 insight patterns, 4 composite scores (productivity, quality, delegation, context health) across 23 event categories. Opens a local web UI. |
|
|
95
95
|
|
|
96
96
|
> **Note:** Slash commands are a Claude Code plugin feature. On other platforms, type `ctx stats`, `ctx doctor`, `ctx upgrade`, or `ctx insight` in the chat — the model calls the MCP tool automatically. See [Utility Commands](#utility-commands).
|
|
97
97
|
|
|
@@ -930,18 +930,27 @@ Every tool call passes through hooks that extract structured events:
|
|
|
930
930
|
|---|---|---|---|
|
|
931
931
|
| **Files** | read, edit, write, glob, grep | Critical (P1) | PostToolUse |
|
|
932
932
|
| **Tasks** | create, update, complete | Critical (P1) | PostToolUse |
|
|
933
|
+
| **Plans** | enter, exit, approved, rejected, file write | Critical (P1) | PostToolUse |
|
|
933
934
|
| **Rules** | CLAUDE.md / GEMINI.md / AGENTS.md paths + content | Critical (P1) | SessionStart |
|
|
935
|
+
| **User Prompts** | Every user message (for last-prompt restore) | Critical (P1) | UserPromptSubmit |
|
|
934
936
|
| **Decisions** | User corrections, preferences ("use X instead", "don't do Y") | High (P2) | UserPromptSubmit |
|
|
935
937
|
| **Git** | checkout, commit, merge, rebase, stash, push, pull, diff, status | High (P2) | PostToolUse |
|
|
936
938
|
| **Errors** | Tool failures, non-zero exit codes | High (P2) | PostToolUse |
|
|
937
|
-
| **
|
|
939
|
+
| **Error Resolution** | Error → fix pairs detected across sequential tool calls | High (P2) | PostToolUse |
|
|
940
|
+
| **Constraints** | Discovered limitations ("not supported", "permission denied") | High (P2) | PostToolUse |
|
|
941
|
+
| **Blockers** | "blocked on", "waiting for", "depends on" — tracked until resolved | High (P2) | UserPromptSubmit |
|
|
942
|
+
| **Rejected Approaches** | Tool calls denied by user (PreToolUse → PostToolUse marker) | High (P2) | PreToolUse |
|
|
943
|
+
| **Environment** | cwd changes, venv, nvm, conda, worktree, package installs | High (P2) | PostToolUse |
|
|
944
|
+
| **Agent Findings** | Completed subagent results (first 500 chars) | High (P2) | PostToolUse |
|
|
945
|
+
| **Iteration Loops** | Same tool called 3+ times with similar input (retry detection) | High (P2) | PostToolUse |
|
|
946
|
+
| **Latency** | Tool calls exceeding 5s (tool name + duration in ms) | Normal (P3) | PreToolUse |
|
|
938
947
|
| **MCP Tools** | All `mcp__*` tool calls with usage counts | Normal (P3) | PostToolUse |
|
|
939
|
-
| **Subagents** | Agent tool
|
|
948
|
+
| **Subagents** | Agent tool launches and completions | Normal (P3) | PostToolUse |
|
|
940
949
|
| **Skills** | Slash command invocations | Normal (P3) | PostToolUse |
|
|
950
|
+
| **External Refs** | URLs, GitHub issue references (#123), deduped | Normal (P3) | PostToolUse |
|
|
941
951
|
| **Role** | Persona / behavioral directives ("act as senior engineer") | Normal (P3) | UserPromptSubmit |
|
|
942
|
-
| **Intent** | Session mode classification (investigate, implement,
|
|
952
|
+
| **Intent** | Session mode classification (investigate, implement, review) | Low (P4) | UserPromptSubmit |
|
|
943
953
|
| **Data** | Large user-pasted data references (>1 KB) | Low (P4) | UserPromptSubmit |
|
|
944
|
-
| **User Prompts** | Every user message (for last-prompt restore) | Critical (P1) | UserPromptSubmit |
|
|
945
954
|
|
|
946
955
|
</details>
|
|
947
956
|
|
|
@@ -968,15 +977,20 @@ After compaction, the model receives a **Session Guide** — a structured narrat
|
|
|
968
977
|
|
|
969
978
|
- **Last Request** — user's last prompt, so the model continues without asking "what were we doing?"
|
|
970
979
|
- **Tasks** — checkbox format with completion status (`[x]` completed, `[ ]` pending)
|
|
980
|
+
- **Plans** — plan mode entries, exits, approvals, and rejections
|
|
971
981
|
- **Key Decisions** — user corrections and preferences ("use X instead", "don't do Y")
|
|
972
982
|
- **Files Modified** — all files touched during the session
|
|
973
|
-
- **Unresolved Errors** — errors that haven't been fixed
|
|
983
|
+
- **Unresolved Errors** — errors that haven't been fixed, plus error→fix resolution pairs
|
|
984
|
+
- **Constraints** — discovered limitations and boundaries
|
|
985
|
+
- **Blockers** — open and resolved blockers ("blocked on X", "waiting for Y")
|
|
974
986
|
- **Git** — operations performed (checkout, commit, push, status)
|
|
975
987
|
- **Project Rules** — CLAUDE.md / GEMINI.md / AGENTS.md paths
|
|
976
988
|
- **MCP Tools Used** — tool names with call counts
|
|
977
|
-
- **Subagent Tasks** — delegated work summaries
|
|
989
|
+
- **Subagent Tasks** — delegated work summaries + agent findings
|
|
978
990
|
- **Skills Used** — slash commands invoked
|
|
979
|
-
- **
|
|
991
|
+
- **Rejected Approaches** — tool calls the user denied
|
|
992
|
+
- **External References** — URLs and GitHub issue references
|
|
993
|
+
- **Environment** — working directory, env variables, worktrees
|
|
980
994
|
- **Data References** — large data pasted during the session
|
|
981
995
|
- **Session Intent** — mode classification (implement, investigate, review, discuss)
|
|
982
996
|
- **User Role** — behavioral directives set during the session
|
|
@@ -59,13 +59,15 @@ export declare function isContextModeHook(entry: {
|
|
|
59
59
|
}, hookType: HookType): boolean;
|
|
60
60
|
/**
|
|
61
61
|
* Build the hook command string for a given hook type.
|
|
62
|
-
* Uses
|
|
62
|
+
* Uses process.execPath + forward slashes to avoid PATH issues and MSYS
|
|
63
|
+
* path mangling on Windows (#369, #372).
|
|
63
64
|
* Falls back to CLI dispatcher if pluginRoot is not provided.
|
|
64
65
|
*/
|
|
65
66
|
export declare function buildHookCommand(hookType: HookType, pluginRoot?: string): string;
|
|
66
67
|
/**
|
|
67
68
|
* Extract the hook script file path from a command string.
|
|
68
|
-
* Returns the path if the command uses the `node "/path/to/hook.mjs"` format
|
|
69
|
+
* Returns the path if the command uses the `node "/path/to/hook.mjs"` format
|
|
70
|
+
* or the new `"/path/to/node" "/path/to/hook.mjs"` format (#369, #372),
|
|
69
71
|
* or null if it uses the CLI dispatcher format (which is path-independent).
|
|
70
72
|
*
|
|
71
73
|
* Handles both quoted and unquoted paths, and both forward/back slashes.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildNodeCommand } from "../types.js";
|
|
1
2
|
/**
|
|
2
3
|
* adapters/claude-code/hooks — Claude Code hook definitions and matchers.
|
|
3
4
|
*
|
|
@@ -113,25 +114,31 @@ export function isContextModeHook(entry, hookType) {
|
|
|
113
114
|
}
|
|
114
115
|
/**
|
|
115
116
|
* Build the hook command string for a given hook type.
|
|
116
|
-
* Uses
|
|
117
|
+
* Uses process.execPath + forward slashes to avoid PATH issues and MSYS
|
|
118
|
+
* path mangling on Windows (#369, #372).
|
|
117
119
|
* Falls back to CLI dispatcher if pluginRoot is not provided.
|
|
118
120
|
*/
|
|
119
121
|
export function buildHookCommand(hookType, pluginRoot) {
|
|
120
122
|
if (pluginRoot) {
|
|
121
123
|
const scriptName = HOOK_SCRIPTS[hookType];
|
|
122
|
-
return
|
|
124
|
+
return buildNodeCommand(`${pluginRoot}/hooks/${scriptName}`);
|
|
123
125
|
}
|
|
124
126
|
return `context-mode hook claude-code ${hookType.toLowerCase()}`;
|
|
125
127
|
}
|
|
126
128
|
/**
|
|
127
129
|
* Extract the hook script file path from a command string.
|
|
128
|
-
* Returns the path if the command uses the `node "/path/to/hook.mjs"` format
|
|
130
|
+
* Returns the path if the command uses the `node "/path/to/hook.mjs"` format
|
|
131
|
+
* or the new `"/path/to/node" "/path/to/hook.mjs"` format (#369, #372),
|
|
129
132
|
* or null if it uses the CLI dispatcher format (which is path-independent).
|
|
130
133
|
*
|
|
131
134
|
* Handles both quoted and unquoted paths, and both forward/back slashes.
|
|
132
135
|
*/
|
|
133
136
|
export function extractHookScriptPath(command) {
|
|
134
|
-
//
|
|
137
|
+
// New format: "nodePath" "scriptPath.mjs" (from buildNodeCommand)
|
|
138
|
+
const newFmt = command.match(/"[^"]+"\s+"([^"]+\.mjs)"/);
|
|
139
|
+
if (newFmt)
|
|
140
|
+
return newFmt[1];
|
|
141
|
+
// Legacy format: node "/path/to/hooks/scriptname.mjs" or node /path/to/hooks/scriptname.mjs
|
|
135
142
|
const match = command.match(/node\s+"?([^"]+\.mjs)"?/);
|
|
136
143
|
return match?.[1] ?? null;
|
|
137
144
|
}
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
* Track: https://github.com/openai/codex/issues/18491
|
|
15
15
|
*/
|
|
16
16
|
import { BaseAdapter } from "../base.js";
|
|
17
|
-
import type
|
|
17
|
+
import { type HookAdapter, type HookParadigm, type PlatformCapabilities, type DiagnosticResult, type PreToolUseEvent, type PostToolUseEvent, type PreCompactEvent, type SessionStartEvent, type PreToolUseResponse, type PostToolUseResponse, type PreCompactResponse, type SessionStartResponse, type HookRegistration } from "../types.js";
|
|
18
18
|
export declare class CodexAdapter extends BaseAdapter implements HookAdapter {
|
|
19
19
|
constructor();
|
|
20
20
|
readonly name = "Codex CLI";
|
|
@@ -18,6 +18,7 @@ import { resolve, dirname } from "node:path";
|
|
|
18
18
|
import { fileURLToPath } from "node:url";
|
|
19
19
|
import { homedir } from "node:os";
|
|
20
20
|
import { BaseAdapter } from "../base.js";
|
|
21
|
+
import { buildNodeCommand, } from "../types.js";
|
|
21
22
|
// ─────────────────────────────────────────────────────────
|
|
22
23
|
// Adapter implementation
|
|
23
24
|
// ─────────────────────────────────────────────────────────
|
|
@@ -155,7 +156,7 @@ export class CodexAdapter extends BaseAdapter {
|
|
|
155
156
|
hooks: [
|
|
156
157
|
{
|
|
157
158
|
type: "command",
|
|
158
|
-
command:
|
|
159
|
+
command: buildNodeCommand(`${pluginRoot}/hooks/pretooluse.mjs`),
|
|
159
160
|
},
|
|
160
161
|
],
|
|
161
162
|
},
|
|
@@ -166,7 +167,7 @@ export class CodexAdapter extends BaseAdapter {
|
|
|
166
167
|
hooks: [
|
|
167
168
|
{
|
|
168
169
|
type: "command",
|
|
169
|
-
command:
|
|
170
|
+
command: buildNodeCommand(`${pluginRoot}/hooks/posttooluse.mjs`),
|
|
170
171
|
},
|
|
171
172
|
],
|
|
172
173
|
},
|
|
@@ -177,7 +178,7 @@ export class CodexAdapter extends BaseAdapter {
|
|
|
177
178
|
hooks: [
|
|
178
179
|
{
|
|
179
180
|
type: "command",
|
|
180
|
-
command:
|
|
181
|
+
command: buildNodeCommand(`${pluginRoot}/hooks/sessionstart.mjs`),
|
|
181
182
|
},
|
|
182
183
|
],
|
|
183
184
|
},
|
|
@@ -188,7 +189,7 @@ export class CodexAdapter extends BaseAdapter {
|
|
|
188
189
|
hooks: [
|
|
189
190
|
{
|
|
190
191
|
type: "command",
|
|
191
|
-
command:
|
|
192
|
+
command: buildNodeCommand(`${pluginRoot}/hooks/codex/userpromptsubmit.mjs`),
|
|
192
193
|
},
|
|
193
194
|
],
|
|
194
195
|
},
|
|
@@ -199,7 +200,7 @@ export class CodexAdapter extends BaseAdapter {
|
|
|
199
200
|
hooks: [
|
|
200
201
|
{
|
|
201
202
|
type: "command",
|
|
202
|
-
command:
|
|
203
|
+
command: buildNodeCommand(`${pluginRoot}/hooks/codex/stop.mjs`),
|
|
203
204
|
},
|
|
204
205
|
],
|
|
205
206
|
},
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildNodeCommand } from "../types.js";
|
|
1
2
|
/**
|
|
2
3
|
* adapters/gemini-cli/hooks — Gemini CLI hook definitions and matchers.
|
|
3
4
|
*
|
|
@@ -65,7 +66,7 @@ export function isContextModeHook(entry, hookType) {
|
|
|
65
66
|
export function buildHookCommand(hookType, pluginRoot) {
|
|
66
67
|
const scriptName = HOOK_SCRIPTS[hookType];
|
|
67
68
|
if (pluginRoot && scriptName) {
|
|
68
|
-
return
|
|
69
|
+
return buildNodeCommand(`${pluginRoot}/hooks/${scriptName}`);
|
|
69
70
|
}
|
|
70
71
|
return `context-mode hook gemini-cli ${hookType.toLowerCase()}`;
|
|
71
72
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildNodeCommand } from "../types.js";
|
|
1
2
|
/**
|
|
2
3
|
* adapters/jetbrains-copilot/hooks — JetBrains Copilot hook definitions and matchers.
|
|
3
4
|
*
|
|
@@ -76,7 +77,7 @@ export function buildHookCommand(hookType, pluginRoot) {
|
|
|
76
77
|
throw new Error(`No script defined for hook type: ${hookType}`);
|
|
77
78
|
}
|
|
78
79
|
if (pluginRoot) {
|
|
79
|
-
return
|
|
80
|
+
return buildNodeCommand(`${pluginRoot}/hooks/jetbrains-copilot/${scriptName}`);
|
|
80
81
|
}
|
|
81
82
|
return `context-mode hook jetbrains-copilot ${hookType.toLowerCase()}`;
|
|
82
83
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildNodeCommand } from "../types.js";
|
|
1
2
|
/**
|
|
2
3
|
* adapters/kiro/hooks — Kiro CLI hook definitions and matchers.
|
|
3
4
|
*
|
|
@@ -64,7 +65,7 @@ export function isContextModeHook(entry, hookType) {
|
|
|
64
65
|
export function buildHookCommand(hookType, pluginRoot) {
|
|
65
66
|
const scriptName = HOOK_SCRIPTS[hookType];
|
|
66
67
|
if (pluginRoot && scriptName) {
|
|
67
|
-
return
|
|
68
|
+
return buildNodeCommand(`${pluginRoot}/hooks/kiro/${scriptName}`);
|
|
68
69
|
}
|
|
69
70
|
return `context-mode hook kiro ${hookType.toLowerCase()}`;
|
|
70
71
|
}
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
* - 12 hook events (superset of Claude's 5, but context-mode uses the shared 5)
|
|
14
14
|
*/
|
|
15
15
|
import { ClaudeCodeBaseAdapter, type ClaudeCodeWireInput } from "../claude-code-base.js";
|
|
16
|
-
import type
|
|
16
|
+
import { type HookAdapter, type HookParadigm, type PlatformCapabilities, type DiagnosticResult, type HookRegistration } from "../types.js";
|
|
17
17
|
export declare class QwenCodeAdapter extends ClaudeCodeBaseAdapter implements HookAdapter {
|
|
18
18
|
constructor();
|
|
19
19
|
readonly name = "Qwen Code";
|
|
@@ -16,6 +16,7 @@ import { readFileSync, existsSync, } from "node:fs";
|
|
|
16
16
|
import { resolve, join } from "node:path";
|
|
17
17
|
import { homedir } from "node:os";
|
|
18
18
|
import { ClaudeCodeBaseAdapter } from "../claude-code-base.js";
|
|
19
|
+
import { buildNodeCommand, } from "../types.js";
|
|
19
20
|
// ─────────────────────────────────────────────────────────
|
|
20
21
|
// Adapter implementation
|
|
21
22
|
// ─────────────────────────────────────────────────────────
|
|
@@ -57,7 +58,7 @@ export class QwenCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
|
57
58
|
{
|
|
58
59
|
matcher: preToolUseMatcher,
|
|
59
60
|
hooks: [
|
|
60
|
-
{ type: "command", command:
|
|
61
|
+
{ type: "command", command: buildNodeCommand(`${pluginRoot}/hooks/pretooluse.mjs`) },
|
|
61
62
|
],
|
|
62
63
|
},
|
|
63
64
|
],
|
|
@@ -65,7 +66,7 @@ export class QwenCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
|
65
66
|
{
|
|
66
67
|
matcher: "run_shell_command|read_file|write_file|edit|glob|grep_search|todo_write|agent|ask_user_question|mcp__",
|
|
67
68
|
hooks: [
|
|
68
|
-
{ type: "command", command:
|
|
69
|
+
{ type: "command", command: buildNodeCommand(`${pluginRoot}/hooks/posttooluse.mjs`) },
|
|
69
70
|
],
|
|
70
71
|
},
|
|
71
72
|
],
|
|
@@ -73,7 +74,7 @@ export class QwenCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
|
73
74
|
{
|
|
74
75
|
matcher: "",
|
|
75
76
|
hooks: [
|
|
76
|
-
{ type: "command", command:
|
|
77
|
+
{ type: "command", command: buildNodeCommand(`${pluginRoot}/hooks/sessionstart.mjs`) },
|
|
77
78
|
],
|
|
78
79
|
},
|
|
79
80
|
],
|
|
@@ -81,7 +82,7 @@ export class QwenCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
|
81
82
|
{
|
|
82
83
|
matcher: "",
|
|
83
84
|
hooks: [
|
|
84
|
-
{ type: "command", command:
|
|
85
|
+
{ type: "command", command: buildNodeCommand(`${pluginRoot}/hooks/precompact.mjs`) },
|
|
85
86
|
],
|
|
86
87
|
},
|
|
87
88
|
],
|
|
@@ -89,7 +90,7 @@ export class QwenCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
|
89
90
|
{
|
|
90
91
|
matcher: "",
|
|
91
92
|
hooks: [
|
|
92
|
-
{ type: "command", command:
|
|
93
|
+
{ type: "command", command: buildNodeCommand(`${pluginRoot}/hooks/userpromptsubmit.mjs`) },
|
|
93
94
|
],
|
|
94
95
|
},
|
|
95
96
|
],
|
|
@@ -210,11 +211,13 @@ export class QwenCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
|
210
211
|
return commands.every((h) => {
|
|
211
212
|
if (!h.command)
|
|
212
213
|
return true;
|
|
213
|
-
// Extract path from "node
|
|
214
|
-
const
|
|
215
|
-
|
|
214
|
+
// Extract path from both new ("nodePath" "scriptPath.mjs") and legacy (node .../script.mjs) formats
|
|
215
|
+
const newFmt = h.command.match(/"[^"]+"\s+"([^"]+\.mjs)"/);
|
|
216
|
+
const legacyFmt = h.command.match(/node\s+"?([^"]+\.mjs)"?/);
|
|
217
|
+
const scriptMatch = newFmt || legacyFmt;
|
|
218
|
+
if (!scriptMatch)
|
|
216
219
|
return true; // CLI dispatcher format, always valid
|
|
217
|
-
return existsSync(
|
|
220
|
+
return existsSync(scriptMatch[1]);
|
|
218
221
|
});
|
|
219
222
|
});
|
|
220
223
|
const removed = entries.length - filtered.length;
|
|
@@ -245,7 +248,7 @@ export class QwenCodeAdapter extends ClaudeCodeBaseAdapter {
|
|
|
245
248
|
for (const { name, script, matcher } of hookTypes) {
|
|
246
249
|
const entry = {
|
|
247
250
|
matcher,
|
|
248
|
-
hooks: [{ type: "command", command:
|
|
251
|
+
hooks: [{ type: "command", command: buildNodeCommand(`${pluginRoot}/hooks/${script}`) }],
|
|
249
252
|
};
|
|
250
253
|
const existing = hooks[name];
|
|
251
254
|
if (existing && Array.isArray(existing)) {
|
|
@@ -192,6 +192,19 @@ export interface DiagnosticResult {
|
|
|
192
192
|
/** Suggested fix command (if applicable). */
|
|
193
193
|
fix?: string;
|
|
194
194
|
}
|
|
195
|
+
/**
|
|
196
|
+
* Build a cross-platform `node <script>` command string.
|
|
197
|
+
*
|
|
198
|
+
* Fixes two Windows bugs:
|
|
199
|
+
* #369 — Bare `node` fails on Windows Git Bash (MSYS) because PATH
|
|
200
|
+
* resolution is unreliable. Uses `process.execPath` instead.
|
|
201
|
+
* #372 — MSYS rewrites absolute paths on non-C: drives (e.g.
|
|
202
|
+
* `C:\Users\...` → `D:\c\Users\...`). Forward slashes +
|
|
203
|
+
* double-quoting prevents the translation.
|
|
204
|
+
*
|
|
205
|
+
* Safe on macOS/Linux — quoting and forward slashes are no-ops there.
|
|
206
|
+
*/
|
|
207
|
+
export declare function buildNodeCommand(scriptPath: string): string;
|
|
195
208
|
/** Supported platform identifiers. */
|
|
196
209
|
export type PlatformId = "claude-code" | "gemini-cli" | "opencode" | "kilo" | "openclaw" | "codex" | "vscode-copilot" | "jetbrains-copilot" | "cursor" | "antigravity" | "kiro" | "pi" | "zed" | "qwen-code" | "unknown";
|
|
197
210
|
/** Detection signal used to identify which platform is running. */
|
package/build/adapters/types.js
CHANGED
|
@@ -10,4 +10,26 @@
|
|
|
10
10
|
* The MCP server layer is 100% portable and needs no adapter.
|
|
11
11
|
* Only the hook layer requires platform-specific adapters.
|
|
12
12
|
*/
|
|
13
|
-
|
|
13
|
+
// ─────────────────────────────────────────────────────────
|
|
14
|
+
// Platform detection
|
|
15
|
+
// ─────────────────────────────────────────────────────────
|
|
16
|
+
// ─────────────────────────────────────────────────────────
|
|
17
|
+
// Cross-platform command helpers (#369, #372)
|
|
18
|
+
// ─────────────────────────────────────────────────────────
|
|
19
|
+
/**
|
|
20
|
+
* Build a cross-platform `node <script>` command string.
|
|
21
|
+
*
|
|
22
|
+
* Fixes two Windows bugs:
|
|
23
|
+
* #369 — Bare `node` fails on Windows Git Bash (MSYS) because PATH
|
|
24
|
+
* resolution is unreliable. Uses `process.execPath` instead.
|
|
25
|
+
* #372 — MSYS rewrites absolute paths on non-C: drives (e.g.
|
|
26
|
+
* `C:\Users\...` → `D:\c\Users\...`). Forward slashes +
|
|
27
|
+
* double-quoting prevents the translation.
|
|
28
|
+
*
|
|
29
|
+
* Safe on macOS/Linux — quoting and forward slashes are no-ops there.
|
|
30
|
+
*/
|
|
31
|
+
export function buildNodeCommand(scriptPath) {
|
|
32
|
+
const nodePath = process.execPath.replace(/\\/g, "/");
|
|
33
|
+
const safePath = scriptPath.replace(/\\/g, "/");
|
|
34
|
+
return `"${nodePath}" "${safePath}"`;
|
|
35
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { buildNodeCommand } from "../types.js";
|
|
1
2
|
/**
|
|
2
3
|
* adapters/vscode-copilot/hooks — VS Code Copilot hook definitions and matchers.
|
|
3
4
|
*
|
|
@@ -76,7 +77,7 @@ export function buildHookCommand(hookType, pluginRoot) {
|
|
|
76
77
|
throw new Error(`No script defined for hook type: ${hookType}`);
|
|
77
78
|
}
|
|
78
79
|
if (pluginRoot) {
|
|
79
|
-
return
|
|
80
|
+
return buildNodeCommand(`${pluginRoot}/hooks/${scriptName}`);
|
|
80
81
|
}
|
|
81
82
|
return `context-mode hook vscode-copilot ${hookType.toLowerCase()}`;
|
|
82
83
|
}
|
package/build/openclaw-plugin.js
CHANGED
|
@@ -37,6 +37,7 @@ import { OpenClawSessionDB } from "./adapters/openclaw/session-db.js";
|
|
|
37
37
|
import { extractEvents, extractUserEvents } from "./session/extract.js";
|
|
38
38
|
import { buildResumeSnapshot } from "./session/snapshot.js";
|
|
39
39
|
import { WorkspaceRouter } from "./openclaw/workspace-router.js";
|
|
40
|
+
import { buildNodeCommand } from "./adapters/types.js";
|
|
40
41
|
/** Plugin config schema for OpenClaw validation. */
|
|
41
42
|
const configSchema = {
|
|
42
43
|
type: "object",
|
|
@@ -437,7 +438,7 @@ export default {
|
|
|
437
438
|
const bundlePath = resolve(_latestPluginRoot, "cli.bundle.mjs");
|
|
438
439
|
const fallbackPath = resolve(_latestPluginRoot, "build", "cli.js");
|
|
439
440
|
const cliPath = existsSync(bundlePath) ? bundlePath : fallbackPath;
|
|
440
|
-
const cmd =
|
|
441
|
+
const cmd = `${buildNodeCommand(cliPath)} doctor`;
|
|
441
442
|
return {
|
|
442
443
|
text: [
|
|
443
444
|
"## ctx-doctor",
|
|
@@ -458,7 +459,7 @@ export default {
|
|
|
458
459
|
const bundlePath = resolve(_latestPluginRoot, "cli.bundle.mjs");
|
|
459
460
|
const fallbackPath = resolve(_latestPluginRoot, "build", "cli.js");
|
|
460
461
|
const cliPath = existsSync(bundlePath) ? bundlePath : fallbackPath;
|
|
461
|
-
const cmd =
|
|
462
|
+
const cmd = `${buildNodeCommand(cliPath)} upgrade`;
|
|
462
463
|
return {
|
|
463
464
|
text: [
|
|
464
465
|
"## ctx-upgrade",
|
package/build/pi-extension.js
CHANGED
|
@@ -242,7 +242,7 @@ export default function piExtension(pi) {
|
|
|
242
242
|
// Mark resume as consumed so it is not re-injected
|
|
243
243
|
db.markResumeConsumed(_sessionId);
|
|
244
244
|
// Build memory context from recent high-priority events
|
|
245
|
-
const allEvents = db.getEvents(_sessionId, { minPriority:
|
|
245
|
+
const allEvents = db.getEvents(_sessionId, { minPriority: 3, limit: 50 });
|
|
246
246
|
let memoryContext = "";
|
|
247
247
|
if (allEvents.length > 0) {
|
|
248
248
|
const memoryLines = ["<active_memory>"];
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-memory search — searches CLAUDE.md and MEMORY.md files for
|
|
3
|
+
* persisted decisions, preferences, and context from prior sessions.
|
|
4
|
+
*
|
|
5
|
+
* Returns results in a format compatible with the unified search pipeline.
|
|
6
|
+
*/
|
|
7
|
+
export interface AutoMemoryResult {
|
|
8
|
+
title: string;
|
|
9
|
+
content: string;
|
|
10
|
+
source: string;
|
|
11
|
+
origin: "auto-memory";
|
|
12
|
+
timestamp?: string;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Search auto-memory files (CLAUDE.md, MEMORY.md, user identity files)
|
|
16
|
+
* for content matching any of the given queries.
|
|
17
|
+
*
|
|
18
|
+
* Scans:
|
|
19
|
+
* 1. Project-level: <projectDir>/CLAUDE.md
|
|
20
|
+
* 2. User-level: <configDir>/CLAUDE.md
|
|
21
|
+
* 3. User memory: <configDir>/memory/*.md
|
|
22
|
+
*
|
|
23
|
+
* @param queries Array of search terms
|
|
24
|
+
* @param limit Max results to return
|
|
25
|
+
* @param projectDir Project directory path
|
|
26
|
+
* @param configDir Config directory (e.g. ~/.claude)
|
|
27
|
+
* @returns Matching auto-memory results
|
|
28
|
+
*/
|
|
29
|
+
export declare function searchAutoMemory(queries: string[], limit?: number, projectDir?: string, configDir?: string): AutoMemoryResult[];
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Auto-memory search — searches CLAUDE.md and MEMORY.md files for
|
|
3
|
+
* persisted decisions, preferences, and context from prior sessions.
|
|
4
|
+
*
|
|
5
|
+
* Returns results in a format compatible with the unified search pipeline.
|
|
6
|
+
*/
|
|
7
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import { homedir } from "node:os";
|
|
10
|
+
const DEBUG = process.env.DEBUG?.includes("context-mode");
|
|
11
|
+
/**
|
|
12
|
+
* Search auto-memory files (CLAUDE.md, MEMORY.md, user identity files)
|
|
13
|
+
* for content matching any of the given queries.
|
|
14
|
+
*
|
|
15
|
+
* Scans:
|
|
16
|
+
* 1. Project-level: <projectDir>/CLAUDE.md
|
|
17
|
+
* 2. User-level: <configDir>/CLAUDE.md
|
|
18
|
+
* 3. User memory: <configDir>/memory/*.md
|
|
19
|
+
*
|
|
20
|
+
* @param queries Array of search terms
|
|
21
|
+
* @param limit Max results to return
|
|
22
|
+
* @param projectDir Project directory path
|
|
23
|
+
* @param configDir Config directory (e.g. ~/.claude)
|
|
24
|
+
* @returns Matching auto-memory results
|
|
25
|
+
*/
|
|
26
|
+
export function searchAutoMemory(queries, limit = 5, projectDir, configDir) {
|
|
27
|
+
const results = [];
|
|
28
|
+
const effectiveConfigDir = configDir || join(homedir(), ".claude");
|
|
29
|
+
// Collect candidate files
|
|
30
|
+
const candidates = [];
|
|
31
|
+
// 1. Project-level CLAUDE.md
|
|
32
|
+
if (projectDir) {
|
|
33
|
+
const projectClaude = join(projectDir, "CLAUDE.md");
|
|
34
|
+
if (existsSync(projectClaude)) {
|
|
35
|
+
candidates.push({ path: projectClaude, label: "project/CLAUDE.md" });
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
// 2. User-level CLAUDE.md
|
|
39
|
+
const userClaude = join(effectiveConfigDir, "CLAUDE.md");
|
|
40
|
+
if (existsSync(userClaude)) {
|
|
41
|
+
candidates.push({ path: userClaude, label: "user/CLAUDE.md" });
|
|
42
|
+
}
|
|
43
|
+
// 3. User memory directory
|
|
44
|
+
const memoryDir = join(effectiveConfigDir, "memory");
|
|
45
|
+
if (existsSync(memoryDir)) {
|
|
46
|
+
try {
|
|
47
|
+
const files = readdirSync(memoryDir).filter(f => f.endsWith(".md"));
|
|
48
|
+
for (const file of files) {
|
|
49
|
+
candidates.push({
|
|
50
|
+
path: join(memoryDir, file),
|
|
51
|
+
label: `memory/${file}`,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
catch (e) {
|
|
56
|
+
if (DEBUG)
|
|
57
|
+
process.stderr.write(`[ctx] auto-memory dir scan failed: ${e}\n`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
// Search each candidate file for matching queries
|
|
61
|
+
for (const candidate of candidates) {
|
|
62
|
+
if (results.length >= limit)
|
|
63
|
+
break;
|
|
64
|
+
try {
|
|
65
|
+
// Skip files larger than 1MB to avoid memory issues
|
|
66
|
+
try {
|
|
67
|
+
if (statSync(candidate.path).size > 1_000_000)
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
catch {
|
|
71
|
+
continue;
|
|
72
|
+
}
|
|
73
|
+
const content = readFileSync(candidate.path, "utf-8");
|
|
74
|
+
const contentLower = content.toLowerCase();
|
|
75
|
+
for (const query of queries) {
|
|
76
|
+
if (results.length >= limit)
|
|
77
|
+
break;
|
|
78
|
+
const queryLower = query.toLowerCase();
|
|
79
|
+
// Split query into terms, match if any term is found
|
|
80
|
+
const terms = queryLower.split(/\s+/).filter(t => t.length >= 3);
|
|
81
|
+
const matched = terms.some(term => {
|
|
82
|
+
try {
|
|
83
|
+
return new RegExp(`\\b${term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')}\\b`, "i").test(content);
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
return contentLower.includes(term); // fallback for invalid regex
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
if (matched) {
|
|
90
|
+
// Extract a relevant section around the first match
|
|
91
|
+
const firstTermIdx = terms.reduce((best, term) => {
|
|
92
|
+
const idx = contentLower.indexOf(term);
|
|
93
|
+
return idx >= 0 && (best < 0 || idx < best) ? idx : best;
|
|
94
|
+
}, -1);
|
|
95
|
+
let start = Math.max(0, firstTermIdx - 200);
|
|
96
|
+
let end = Math.min(content.length, firstTermIdx + 500);
|
|
97
|
+
const prevBlank = content.lastIndexOf("\n\n", start);
|
|
98
|
+
const nextBlank = content.indexOf("\n\n", end);
|
|
99
|
+
if (prevBlank >= 0)
|
|
100
|
+
start = prevBlank + 2;
|
|
101
|
+
if (nextBlank >= 0)
|
|
102
|
+
end = nextBlank;
|
|
103
|
+
const snippet = content.slice(start, end).trim();
|
|
104
|
+
results.push({
|
|
105
|
+
title: `[auto-memory] ${candidate.label}`,
|
|
106
|
+
content: snippet,
|
|
107
|
+
source: candidate.label,
|
|
108
|
+
origin: "auto-memory",
|
|
109
|
+
timestamp: statSync(candidate.path).mtime.toISOString(),
|
|
110
|
+
});
|
|
111
|
+
break; // one result per file per query batch
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
catch (e) {
|
|
116
|
+
if (DEBUG)
|
|
117
|
+
process.stderr.write(`[ctx] auto-memory file read failed: ${e}\n`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return results.slice(0, limit);
|
|
121
|
+
}
|