context-mode 1.0.39 → 1.0.41
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/build/adapters/detect.d.ts +1 -1
- package/build/adapters/detect.js +3 -3
- package/build/adapters/openclaw/index.d.ts +1 -1
- package/build/adapters/openclaw/index.js +5 -5
- package/build/adapters/opencode/index.js +6 -6
- package/build/cli.js +2 -2
- package/build/openclaw-plugin.js +1 -1
- package/build/opencode-plugin.d.ts +34 -10
- package/build/opencode-plugin.js +13 -11
- package/build/server.js +111 -36
- package/cli.bundle.mjs +89 -88
- package/hooks/session-loaders.mjs +25 -9
- package/openclaw.plugin.json +1 -1
- package/package.json +1 -1
- package/scripts/postinstall.mjs +19 -6
- package/server.bundle.mjs +71 -70
- package/skills/ctx-doctor/SKILL.md +4 -14
- package/skills/ctx-upgrade/SKILL.md +10 -11
- package/start.mjs +63 -38
|
@@ -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.41"
|
|
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.41",
|
|
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.41",
|
|
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.41",
|
|
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.41",
|
|
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",
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Claude Code: CLAUDE_PROJECT_DIR, CLAUDE_SESSION_ID | ~/.claude/
|
|
11
11
|
* - Gemini CLI: GEMINI_PROJECT_DIR (hooks), GEMINI_CLI (MCP) | ~/.gemini/
|
|
12
12
|
* - OpenCode: OPENCODE, OPENCODE_PID | ~/.config/opencode/
|
|
13
|
-
* - OpenClaw: OPENCLAW_HOME,
|
|
13
|
+
* - OpenClaw: OPENCLAW_HOME, OPENCLAW_CLI | ~/.openclaw/
|
|
14
14
|
* - Codex CLI: CODEX_CI, CODEX_THREAD_ID | ~/.codex/
|
|
15
15
|
* - Cursor: CURSOR_TRACE_ID (MCP), CURSOR_CLI (terminal) | ~/.cursor/
|
|
16
16
|
* - VS Code Copilot: VSCODE_PID, VSCODE_CWD | ~/.vscode/
|
package/build/adapters/detect.js
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
* - Claude Code: CLAUDE_PROJECT_DIR, CLAUDE_SESSION_ID | ~/.claude/
|
|
11
11
|
* - Gemini CLI: GEMINI_PROJECT_DIR (hooks), GEMINI_CLI (MCP) | ~/.gemini/
|
|
12
12
|
* - OpenCode: OPENCODE, OPENCODE_PID | ~/.config/opencode/
|
|
13
|
-
* - OpenClaw: OPENCLAW_HOME,
|
|
13
|
+
* - OpenClaw: OPENCLAW_HOME, OPENCLAW_CLI | ~/.openclaw/
|
|
14
14
|
* - Codex CLI: CODEX_CI, CODEX_THREAD_ID | ~/.codex/
|
|
15
15
|
* - Cursor: CURSOR_TRACE_ID (MCP), CURSOR_CLI (terminal) | ~/.cursor/
|
|
16
16
|
* - VS Code Copilot: VSCODE_PID, VSCODE_CWD | ~/.vscode/
|
|
@@ -67,11 +67,11 @@ export function detectPlatform(clientInfo) {
|
|
|
67
67
|
reason: "GEMINI_PROJECT_DIR or GEMINI_CLI env var set",
|
|
68
68
|
};
|
|
69
69
|
}
|
|
70
|
-
if (process.env.OPENCLAW_HOME || process.env.
|
|
70
|
+
if (process.env.OPENCLAW_HOME || process.env.OPENCLAW_CLI) {
|
|
71
71
|
return {
|
|
72
72
|
platform: "openclaw",
|
|
73
73
|
confidence: "high",
|
|
74
|
-
reason: "OPENCLAW_HOME or
|
|
74
|
+
reason: "OPENCLAW_HOME or OPENCLAW_CLI env var set",
|
|
75
75
|
};
|
|
76
76
|
}
|
|
77
77
|
if (process.env.OPENCODE || process.env.OPENCODE_PID) {
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - Arg modification: mutate event.params in tool_call:before
|
|
12
12
|
* - Blocking: return { block: true, blockReason } from tool_call:before
|
|
13
13
|
* - Session ID: event context (no specific env var)
|
|
14
|
-
* - Project dir: process.
|
|
14
|
+
* - Project dir: process.cwd()
|
|
15
15
|
* - Config: openclaw.json plugins.entries, ~/.openclaw/extensions/
|
|
16
16
|
* - Session dir: ~/.openclaw/context-mode/sessions/
|
|
17
17
|
*/
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
* - Arg modification: mutate event.params in tool_call:before
|
|
12
12
|
* - Blocking: return { block: true, blockReason } from tool_call:before
|
|
13
13
|
* - Session ID: event context (no specific env var)
|
|
14
|
-
* - Project dir: process.
|
|
14
|
+
* - Project dir: process.cwd()
|
|
15
15
|
* - Config: openclaw.json plugins.entries, ~/.openclaw/extensions/
|
|
16
16
|
* - Session dir: ~/.openclaw/context-mode/sessions/
|
|
17
17
|
*/
|
|
@@ -45,7 +45,7 @@ export class OpenClawAdapter {
|
|
|
45
45
|
toolName: input.toolName ?? input.tool_name ?? "",
|
|
46
46
|
toolInput: input.params ?? input.tool_input ?? {},
|
|
47
47
|
sessionId: this.extractSessionId(input),
|
|
48
|
-
projectDir: process.
|
|
48
|
+
projectDir: process.cwd(),
|
|
49
49
|
raw,
|
|
50
50
|
};
|
|
51
51
|
}
|
|
@@ -57,7 +57,7 @@ export class OpenClawAdapter {
|
|
|
57
57
|
toolOutput: input.output ?? input.tool_output,
|
|
58
58
|
isError: input.isError ?? input.is_error,
|
|
59
59
|
sessionId: this.extractSessionId(input),
|
|
60
|
-
projectDir: process.
|
|
60
|
+
projectDir: process.cwd(),
|
|
61
61
|
raw,
|
|
62
62
|
};
|
|
63
63
|
}
|
|
@@ -65,7 +65,7 @@ export class OpenClawAdapter {
|
|
|
65
65
|
const input = raw;
|
|
66
66
|
return {
|
|
67
67
|
sessionId: this.extractSessionId(input),
|
|
68
|
-
projectDir: process.
|
|
68
|
+
projectDir: process.cwd(),
|
|
69
69
|
raw,
|
|
70
70
|
};
|
|
71
71
|
}
|
|
@@ -89,7 +89,7 @@ export class OpenClawAdapter {
|
|
|
89
89
|
return {
|
|
90
90
|
sessionId: this.extractSessionId(input),
|
|
91
91
|
source,
|
|
92
|
-
projectDir: process.
|
|
92
|
+
projectDir: process.cwd(),
|
|
93
93
|
raw,
|
|
94
94
|
};
|
|
95
95
|
}
|
|
@@ -42,8 +42,8 @@ export class OpenCodeAdapter {
|
|
|
42
42
|
parsePreToolUseInput(raw) {
|
|
43
43
|
const input = raw;
|
|
44
44
|
return {
|
|
45
|
-
toolName: input.
|
|
46
|
-
toolInput: input.
|
|
45
|
+
toolName: input.tool ?? "",
|
|
46
|
+
toolInput: input.args ?? {},
|
|
47
47
|
sessionId: this.extractSessionId(input),
|
|
48
48
|
projectDir: process.env.OPENCODE_PROJECT_DIR || process.cwd(),
|
|
49
49
|
raw,
|
|
@@ -52,10 +52,10 @@ export class OpenCodeAdapter {
|
|
|
52
52
|
parsePostToolUseInput(raw) {
|
|
53
53
|
const input = raw;
|
|
54
54
|
return {
|
|
55
|
-
toolName: input.
|
|
56
|
-
toolInput: input.
|
|
57
|
-
toolOutput: input.
|
|
58
|
-
isError:
|
|
55
|
+
toolName: input.tool ?? "",
|
|
56
|
+
toolInput: input.args ?? {},
|
|
57
|
+
toolOutput: input.output,
|
|
58
|
+
isError: undefined, // OpenCode doesn't provide isError
|
|
59
59
|
sessionId: this.extractSessionId(input),
|
|
60
60
|
projectDir: process.env.OPENCODE_PROJECT_DIR || process.cwd(),
|
|
61
61
|
raw,
|
package/build/cli.js
CHANGED
|
@@ -367,12 +367,12 @@ async function upgrade() {
|
|
|
367
367
|
execSync("npm install --no-audit --no-fund", {
|
|
368
368
|
cwd: srcDir,
|
|
369
369
|
stdio: "pipe",
|
|
370
|
-
timeout:
|
|
370
|
+
timeout: 120000,
|
|
371
371
|
});
|
|
372
372
|
execSync("npm run build", {
|
|
373
373
|
cwd: srcDir,
|
|
374
374
|
stdio: "pipe",
|
|
375
|
-
timeout:
|
|
375
|
+
timeout: 60000,
|
|
376
376
|
});
|
|
377
377
|
s.stop("Built successfully");
|
|
378
378
|
// Step 3: Update in-place
|
package/build/openclaw-plugin.js
CHANGED
|
@@ -98,7 +98,7 @@ export default {
|
|
|
98
98
|
register(api) {
|
|
99
99
|
// Resolve build dir from compiled JS location
|
|
100
100
|
const buildDir = dirname(fileURLToPath(import.meta.url));
|
|
101
|
-
const projectDir = process.
|
|
101
|
+
const projectDir = process.cwd();
|
|
102
102
|
const pluginRoot = resolve(buildDir, "..");
|
|
103
103
|
// Structured logger — wraps api.logger, falls back to no-op.
|
|
104
104
|
// info/error always emit; debug only when api.logger.debug is present
|
|
@@ -17,21 +17,45 @@
|
|
|
17
17
|
interface PluginContext {
|
|
18
18
|
directory: string;
|
|
19
19
|
}
|
|
20
|
-
/**
|
|
21
|
-
interface
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
20
|
+
/** OpenCode tool.execute.before — first parameter */
|
|
21
|
+
interface BeforeHookInput {
|
|
22
|
+
tool: string;
|
|
23
|
+
sessionID: string;
|
|
24
|
+
callID: string;
|
|
25
|
+
}
|
|
26
|
+
/** OpenCode tool.execute.before — second parameter */
|
|
27
|
+
interface BeforeHookOutput {
|
|
28
|
+
args: any;
|
|
29
|
+
}
|
|
30
|
+
/** OpenCode tool.execute.after — first parameter */
|
|
31
|
+
interface AfterHookInput {
|
|
32
|
+
tool: string;
|
|
33
|
+
sessionID: string;
|
|
34
|
+
callID: string;
|
|
35
|
+
args: any;
|
|
36
|
+
}
|
|
37
|
+
/** OpenCode tool.execute.after — second parameter */
|
|
38
|
+
interface AfterHookOutput {
|
|
39
|
+
title: string;
|
|
40
|
+
output: string;
|
|
41
|
+
metadata: any;
|
|
42
|
+
}
|
|
43
|
+
/** OpenCode experimental.session.compacting — first parameter */
|
|
44
|
+
interface CompactingHookInput {
|
|
45
|
+
sessionID: string;
|
|
46
|
+
}
|
|
47
|
+
/** OpenCode experimental.session.compacting — second parameter */
|
|
48
|
+
interface CompactingHookOutput {
|
|
49
|
+
context: string[];
|
|
50
|
+
prompt?: string;
|
|
27
51
|
}
|
|
28
52
|
/**
|
|
29
53
|
* OpenCode plugin factory. Called once when OpenCode loads the plugin.
|
|
30
54
|
* Returns an object mapping hook event names to async handler functions.
|
|
31
55
|
*/
|
|
32
56
|
export declare const ContextModePlugin: (ctx: PluginContext) => Promise<{
|
|
33
|
-
"tool.execute.before": (input:
|
|
34
|
-
"tool.execute.after": (input:
|
|
35
|
-
"experimental.session.compacting": () => Promise<string>;
|
|
57
|
+
"tool.execute.before": (input: BeforeHookInput, output: BeforeHookOutput) => Promise<void>;
|
|
58
|
+
"tool.execute.after": (input: AfterHookInput, output: AfterHookOutput) => Promise<void>;
|
|
59
|
+
"experimental.session.compacting": (input: CompactingHookInput, output: CompactingHookOutput) => Promise<string>;
|
|
36
60
|
}>;
|
|
37
61
|
export {};
|
package/build/opencode-plugin.js
CHANGED
|
@@ -63,9 +63,9 @@ export const ContextModePlugin = async (ctx) => {
|
|
|
63
63
|
db.cleanupOldSessions(0);
|
|
64
64
|
return {
|
|
65
65
|
// ── PreToolUse: Routing enforcement ─────────────────
|
|
66
|
-
"tool.execute.before": async (input) => {
|
|
67
|
-
const toolName = input.
|
|
68
|
-
const toolInput =
|
|
66
|
+
"tool.execute.before": async (input, output) => {
|
|
67
|
+
const toolName = input.tool ?? "";
|
|
68
|
+
const toolInput = output.args ?? {};
|
|
69
69
|
let decision;
|
|
70
70
|
try {
|
|
71
71
|
decision = routing.routePreToolUse(toolName, toolInput, projectDir);
|
|
@@ -80,19 +80,19 @@ export const ContextModePlugin = async (ctx) => {
|
|
|
80
80
|
throw new Error(decision.reason ?? "Blocked by context-mode");
|
|
81
81
|
}
|
|
82
82
|
if (decision.action === "modify" && decision.updatedInput) {
|
|
83
|
-
// Mutate args
|
|
84
|
-
Object.assign(
|
|
83
|
+
// Mutate output.args — OpenCode reads the mutated output object
|
|
84
|
+
Object.assign(output.args, decision.updatedInput);
|
|
85
85
|
}
|
|
86
86
|
// "context" action → no-op (OpenCode doesn't support context injection)
|
|
87
87
|
},
|
|
88
88
|
// ── PostToolUse: Session event capture ──────────────
|
|
89
|
-
"tool.execute.after": async (input) => {
|
|
89
|
+
"tool.execute.after": async (input, output) => {
|
|
90
90
|
try {
|
|
91
91
|
const hookInput = {
|
|
92
|
-
tool_name: input.
|
|
93
|
-
tool_input: input.
|
|
94
|
-
tool_response:
|
|
95
|
-
tool_output:
|
|
92
|
+
tool_name: input.tool ?? "",
|
|
93
|
+
tool_input: input.args ?? {},
|
|
94
|
+
tool_response: output.output,
|
|
95
|
+
tool_output: undefined, // OpenCode doesn't provide isError
|
|
96
96
|
};
|
|
97
97
|
const events = extractEvents(hookInput);
|
|
98
98
|
for (const event of events) {
|
|
@@ -105,7 +105,7 @@ export const ContextModePlugin = async (ctx) => {
|
|
|
105
105
|
}
|
|
106
106
|
},
|
|
107
107
|
// ── PreCompact: Snapshot generation ─────────────────
|
|
108
|
-
"experimental.session.compacting": async () => {
|
|
108
|
+
"experimental.session.compacting": async (input, output) => {
|
|
109
109
|
try {
|
|
110
110
|
const events = db.getEvents(sessionId);
|
|
111
111
|
if (events.length === 0)
|
|
@@ -116,6 +116,8 @@ export const ContextModePlugin = async (ctx) => {
|
|
|
116
116
|
});
|
|
117
117
|
db.upsertResume(sessionId, snapshot, events.length);
|
|
118
118
|
db.incrementCompactCount(sessionId);
|
|
119
|
+
// Mutate output.context to inject the snapshot
|
|
120
|
+
output.context.push(snapshot);
|
|
119
121
|
return snapshot;
|
|
120
122
|
}
|
|
121
123
|
catch {
|
package/build/server.js
CHANGED
|
@@ -80,7 +80,7 @@ function maybeIndexSessionEvents(store) {
|
|
|
80
80
|
function getStorePath() {
|
|
81
81
|
const projectDir = process.env.CLAUDE_PROJECT_DIR
|
|
82
82
|
|| process.env.GEMINI_PROJECT_DIR
|
|
83
|
-
|| process.env.
|
|
83
|
+
|| process.env.OPENCLAW_HOME
|
|
84
84
|
|| process.cwd();
|
|
85
85
|
const normalized = projectDir.replace(/\\/g, "/");
|
|
86
86
|
const hash = createHash("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
@@ -1504,43 +1504,71 @@ server.registerTool("ctx_stats", {
|
|
|
1504
1504
|
content: [{ type: "text", text }],
|
|
1505
1505
|
});
|
|
1506
1506
|
});
|
|
1507
|
-
// ── ctx-doctor: diagnostics
|
|
1507
|
+
// ── ctx-doctor: diagnostics (server-side) ─────────────────────────────────
|
|
1508
1508
|
server.registerTool("ctx_doctor", {
|
|
1509
1509
|
title: "Run Diagnostics",
|
|
1510
|
-
description: "Diagnose context-mode installation.
|
|
1511
|
-
"
|
|
1512
|
-
"run_in_terminal, etc.) and display the output as a markdown checklist.",
|
|
1510
|
+
description: "Diagnose context-mode installation. Runs all checks server-side and " +
|
|
1511
|
+
"returns results as a markdown checklist. No CLI execution needed.",
|
|
1513
1512
|
inputSchema: z.object({}),
|
|
1514
1513
|
}, async () => {
|
|
1515
|
-
const
|
|
1516
|
-
|
|
1517
|
-
const
|
|
1518
|
-
|
|
1519
|
-
const
|
|
1520
|
-
const
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
""
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
"
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
"
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1514
|
+
const lines = ["## context-mode doctor", ""];
|
|
1515
|
+
// __pkg_dir is build/ for tsc, plugin root for bundle — resolve to plugin root
|
|
1516
|
+
const pluginRoot = existsSync(resolve(__pkg_dir, "package.json")) ? __pkg_dir : dirname(__pkg_dir);
|
|
1517
|
+
// Runtimes
|
|
1518
|
+
const total = 11;
|
|
1519
|
+
const pct = ((available.length / total) * 100).toFixed(0);
|
|
1520
|
+
lines.push(`- [x] Runtimes: ${available.length}/${total} (${pct}%) — ${available.join(", ")}`);
|
|
1521
|
+
// Performance
|
|
1522
|
+
if (hasBunRuntime()) {
|
|
1523
|
+
lines.push("- [x] Performance: FAST (Bun)");
|
|
1524
|
+
}
|
|
1525
|
+
else {
|
|
1526
|
+
lines.push("- [-] Performance: NORMAL — install Bun for 3-5x speed boost");
|
|
1527
|
+
}
|
|
1528
|
+
// Server test
|
|
1529
|
+
try {
|
|
1530
|
+
const testExecutor = new PolyglotExecutor({ runtimes });
|
|
1531
|
+
const result = await testExecutor.execute({ language: "javascript", code: 'console.log("ok");', timeout: 5000 });
|
|
1532
|
+
if (result.exitCode === 0 && result.stdout.trim() === "ok") {
|
|
1533
|
+
lines.push("- [x] Server test: PASS");
|
|
1534
|
+
}
|
|
1535
|
+
else {
|
|
1536
|
+
lines.push(`- [ ] Server test: FAIL — exit ${result.exitCode}`);
|
|
1537
|
+
}
|
|
1538
|
+
}
|
|
1539
|
+
catch (err) {
|
|
1540
|
+
lines.push(`- [ ] Server test: FAIL — ${err instanceof Error ? err.message : err}`);
|
|
1541
|
+
}
|
|
1542
|
+
// FTS5 / SQLite
|
|
1543
|
+
try {
|
|
1544
|
+
const Database = loadDatabase();
|
|
1545
|
+
const db = new Database(":memory:");
|
|
1546
|
+
db.exec("CREATE VIRTUAL TABLE fts_test USING fts5(content)");
|
|
1547
|
+
db.exec("INSERT INTO fts_test(content) VALUES ('hello world')");
|
|
1548
|
+
const row = db.prepare("SELECT * FROM fts_test WHERE fts_test MATCH 'hello'").get();
|
|
1549
|
+
db.close();
|
|
1550
|
+
if (row && row.content === "hello world") {
|
|
1551
|
+
lines.push("- [x] FTS5 / SQLite: PASS — native module works");
|
|
1552
|
+
}
|
|
1553
|
+
else {
|
|
1554
|
+
lines.push("- [ ] FTS5 / SQLite: FAIL — unexpected result");
|
|
1555
|
+
}
|
|
1556
|
+
}
|
|
1557
|
+
catch (err) {
|
|
1558
|
+
lines.push(`- [ ] FTS5 / SQLite: FAIL — ${err instanceof Error ? err.message : err}`);
|
|
1559
|
+
}
|
|
1560
|
+
// Hook script
|
|
1561
|
+
const hookPath = resolve(pluginRoot, "hooks", "pretooluse.mjs");
|
|
1562
|
+
if (existsSync(hookPath)) {
|
|
1563
|
+
lines.push(`- [x] Hook script: PASS — ${hookPath}`);
|
|
1564
|
+
}
|
|
1565
|
+
else {
|
|
1566
|
+
lines.push(`- [ ] Hook script: FAIL — not found at ${hookPath}`);
|
|
1567
|
+
}
|
|
1568
|
+
// Version
|
|
1569
|
+
lines.push(`- [x] Version: v${VERSION}`);
|
|
1542
1570
|
return trackResponse("ctx_doctor", {
|
|
1543
|
-
content: [{ type: "text", text }],
|
|
1571
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
1544
1572
|
});
|
|
1545
1573
|
});
|
|
1546
1574
|
// ── ctx-upgrade: upgrade meta-tool ─────────────────────────────────────────
|
|
@@ -1552,11 +1580,58 @@ server.registerTool("ctx_upgrade", {
|
|
|
1552
1580
|
"Tell the user to restart their session after upgrade.",
|
|
1553
1581
|
inputSchema: z.object({}),
|
|
1554
1582
|
}, async () => {
|
|
1555
|
-
|
|
1583
|
+
// __pkg_dir is build/ for tsc, plugin root for bundle — resolve to plugin root
|
|
1584
|
+
const pluginRoot = existsSync(resolve(__pkg_dir, "package.json")) ? __pkg_dir : dirname(__pkg_dir);
|
|
1556
1585
|
const bundlePath = resolve(pluginRoot, "cli.bundle.mjs");
|
|
1557
1586
|
const fallbackPath = resolve(pluginRoot, "build", "cli.js");
|
|
1558
|
-
|
|
1559
|
-
|
|
1587
|
+
let cmd;
|
|
1588
|
+
if (existsSync(bundlePath)) {
|
|
1589
|
+
cmd = `node "${bundlePath}" upgrade`;
|
|
1590
|
+
}
|
|
1591
|
+
else if (existsSync(fallbackPath)) {
|
|
1592
|
+
cmd = `node "${fallbackPath}" upgrade`;
|
|
1593
|
+
}
|
|
1594
|
+
else {
|
|
1595
|
+
// Inline fallback: neither CLI file exists (e.g. marketplace installs).
|
|
1596
|
+
// Generate a self-contained node -e script that performs the upgrade.
|
|
1597
|
+
const repoUrl = "https://github.com/mksglu/context-mode.git";
|
|
1598
|
+
const copyDirs = ["build", "hooks", "skills", "scripts", ".claude-plugin"];
|
|
1599
|
+
const copyFiles = ["start.mjs", "server.bundle.mjs", "cli.bundle.mjs", "package.json"];
|
|
1600
|
+
// Write inline script to a temp .mjs file — avoids quote-escaping issues
|
|
1601
|
+
// across cmd.exe, PowerShell, and bash (node -e '...' breaks on Windows).
|
|
1602
|
+
const scriptLines = [
|
|
1603
|
+
`import{execSync}from"node:child_process";`,
|
|
1604
|
+
`import{cpSync,rmSync,existsSync,mkdtempSync}from"node:fs";`,
|
|
1605
|
+
`import{join}from"node:path";`,
|
|
1606
|
+
`import{tmpdir}from"node:os";`,
|
|
1607
|
+
`const P=${JSON.stringify(pluginRoot)};`,
|
|
1608
|
+
`const T=mkdtempSync(join(tmpdir(),"ctx-upgrade-"));`,
|
|
1609
|
+
`try{`,
|
|
1610
|
+
`console.log("- [x] Starting inline upgrade (no CLI found)");`,
|
|
1611
|
+
`execSync("git clone --depth 1 ${repoUrl} \\""+T+"\\"",{stdio:"inherit"});`,
|
|
1612
|
+
`console.log("- [x] Cloned latest source");`,
|
|
1613
|
+
`execSync("npm install",{cwd:T,stdio:"inherit"});`,
|
|
1614
|
+
`execSync("npm run build",{cwd:T,stdio:"inherit"});`,
|
|
1615
|
+
`console.log("- [x] Built from source");`,
|
|
1616
|
+
...copyDirs.map((d) => `if(existsSync(join(T,${JSON.stringify(d)})))cpSync(join(T,${JSON.stringify(d)}),join(P,${JSON.stringify(d)}),{recursive:true,force:true});`),
|
|
1617
|
+
...copyFiles.map((f) => `if(existsSync(join(T,${JSON.stringify(f)})))cpSync(join(T,${JSON.stringify(f)}),join(P,${JSON.stringify(f)}),{force:true});`),
|
|
1618
|
+
`console.log("- [x] Copied build artifacts");`,
|
|
1619
|
+
`execSync("npm install --production",{cwd:P,stdio:"inherit"});`,
|
|
1620
|
+
`console.log("- [x] Installed production dependencies");`,
|
|
1621
|
+
`console.log("## context-mode upgrade complete");`,
|
|
1622
|
+
`}catch(e){`,
|
|
1623
|
+
`console.error("- [ ] Upgrade failed:",e.message);`,
|
|
1624
|
+
`process.exit(1);`,
|
|
1625
|
+
`}finally{`,
|
|
1626
|
+
`try{rmSync(T,{recursive:true,force:true})}catch{}`,
|
|
1627
|
+
`}`,
|
|
1628
|
+
].join("\n");
|
|
1629
|
+
// Server writes the temp script file — avoids shell quoting issues entirely
|
|
1630
|
+
const tmpScript = resolve(pluginRoot, ".ctx-upgrade-inline.mjs");
|
|
1631
|
+
const { writeFileSync: writeTmp } = await import("node:fs");
|
|
1632
|
+
writeTmp(tmpScript, scriptLines);
|
|
1633
|
+
cmd = `node "${tmpScript}"`;
|
|
1634
|
+
}
|
|
1560
1635
|
const text = [
|
|
1561
1636
|
"## ctx-upgrade",
|
|
1562
1637
|
"",
|