context-mode 1.0.88 → 1.0.90
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 +184 -60
- package/build/adapters/antigravity/index.d.ts +3 -5
- package/build/adapters/antigravity/index.js +7 -35
- package/build/adapters/base.d.ts +27 -0
- package/build/adapters/base.js +59 -0
- package/build/adapters/claude-code/index.d.ts +9 -25
- package/build/adapters/claude-code/index.js +27 -141
- package/build/adapters/claude-code-base.d.ts +49 -0
- package/build/adapters/claude-code-base.js +113 -0
- package/build/adapters/client-map.js +5 -0
- package/build/adapters/codex/hooks.d.ts +21 -14
- package/build/adapters/codex/hooks.js +22 -15
- package/build/adapters/codex/index.d.ts +6 -10
- package/build/adapters/codex/index.js +13 -43
- package/build/adapters/copilot-base.d.ts +78 -0
- package/build/adapters/copilot-base.js +281 -0
- package/build/adapters/cursor/index.d.ts +3 -5
- package/build/adapters/cursor/index.js +6 -34
- package/build/adapters/detect.d.ts +7 -0
- package/build/adapters/detect.js +57 -56
- package/build/adapters/gemini-cli/index.d.ts +3 -5
- package/build/adapters/gemini-cli/index.js +7 -35
- package/build/adapters/jetbrains-copilot/config.d.ts +8 -0
- package/build/adapters/jetbrains-copilot/config.js +8 -0
- package/build/adapters/jetbrains-copilot/hooks.d.ts +51 -0
- package/build/adapters/jetbrains-copilot/hooks.js +82 -0
- package/build/adapters/jetbrains-copilot/index.d.ts +24 -0
- package/build/adapters/jetbrains-copilot/index.js +119 -0
- package/build/adapters/kiro/hooks.d.ts +14 -0
- package/build/adapters/kiro/hooks.js +23 -0
- package/build/adapters/kiro/index.d.ts +3 -5
- package/build/adapters/kiro/index.js +10 -38
- package/build/adapters/openclaw/index.d.ts +3 -4
- package/build/adapters/openclaw/index.js +6 -22
- package/build/adapters/opencode/index.d.ts +2 -3
- package/build/adapters/opencode/index.js +5 -16
- package/build/adapters/qwen-code/index.d.ts +39 -0
- package/build/adapters/qwen-code/index.js +199 -0
- package/build/adapters/types.d.ts +1 -1
- package/build/adapters/vscode-copilot/index.d.ts +16 -46
- package/build/adapters/vscode-copilot/index.js +29 -320
- package/build/adapters/zed/index.d.ts +3 -5
- package/build/adapters/zed/index.js +7 -35
- package/build/cli.js +113 -47
- package/build/lifecycle.d.ts +23 -0
- package/build/lifecycle.js +54 -13
- package/build/opencode-plugin.d.ts +19 -7
- package/build/opencode-plugin.js +19 -7
- package/build/pi-extension.js +24 -7
- package/build/runtime.js +24 -9
- package/build/security.d.ts +17 -1
- package/build/security.js +40 -6
- package/build/server.js +129 -21
- package/build/session/analytics.d.ts +8 -7
- package/build/session/analytics.js +95 -75
- package/build/session/db.d.ts +10 -1
- package/build/session/db.js +67 -8
- package/build/session/extract.js +10 -2
- package/build/session/project-attribution.d.ts +73 -0
- package/build/session/project-attribution.js +231 -0
- package/build/store.d.ts +7 -0
- package/build/store.js +117 -18
- package/build/truncate.d.ts +6 -0
- package/build/truncate.js +51 -29
- package/build/types.d.ts +8 -0
- package/cli.bundle.mjs +157 -136
- package/configs/antigravity/GEMINI.md +31 -36
- package/configs/claude-code/CLAUDE.md +31 -37
- package/configs/codex/AGENTS.md +35 -49
- package/configs/cursor/context-mode.mdc +24 -25
- package/configs/gemini-cli/GEMINI.md +30 -36
- package/configs/jetbrains-copilot/copilot-instructions.md +59 -0
- package/configs/jetbrains-copilot/hooks.json +16 -0
- package/configs/jetbrains-copilot/mcp.json +8 -0
- package/configs/kilo/AGENTS.md +30 -36
- package/configs/kiro/KIRO.md +30 -36
- package/configs/kiro/agent.json +1 -1
- package/configs/openclaw/AGENTS.md +30 -36
- package/configs/opencode/AGENTS.md +30 -36
- package/configs/pi/AGENTS.md +31 -36
- package/configs/qwen-code/QWEN.md +63 -0
- package/configs/vscode-copilot/copilot-instructions.md +30 -36
- package/configs/zed/AGENTS.md +31 -36
- package/hooks/codex/posttooluse.mjs +7 -7
- package/hooks/codex/pretooluse.mjs +3 -3
- package/hooks/codex/sessionstart.mjs +2 -1
- package/hooks/core/formatters.mjs +24 -0
- package/hooks/core/routing.mjs +40 -15
- package/hooks/core/tool-naming.mjs +2 -0
- package/hooks/cursor/posttooluse.mjs +7 -7
- package/hooks/cursor/pretooluse.mjs +3 -3
- package/hooks/cursor/sessionstart.mjs +2 -1
- package/hooks/cursor/stop.mjs +2 -2
- package/hooks/ensure-deps.mjs +22 -10
- package/hooks/gemini-cli/aftertool.mjs +8 -8
- package/hooks/gemini-cli/beforetool.mjs +3 -2
- package/hooks/gemini-cli/precompress.mjs +2 -2
- package/hooks/gemini-cli/sessionstart.mjs +12 -4
- package/hooks/jetbrains-copilot/posttooluse.mjs +61 -0
- package/hooks/jetbrains-copilot/precompact.mjs +54 -0
- package/hooks/jetbrains-copilot/pretooluse.mjs +27 -0
- package/hooks/jetbrains-copilot/sessionstart.mjs +119 -0
- package/hooks/kiro/posttooluse.mjs +6 -7
- package/hooks/kiro/pretooluse.mjs +3 -2
- package/hooks/posttooluse.mjs +8 -8
- package/hooks/precompact.mjs +3 -4
- package/hooks/pretooluse.mjs +43 -20
- package/hooks/routing-block.mjs +35 -33
- package/hooks/session-attribution.bundle.mjs +1 -0
- package/hooks/session-db.bundle.mjs +27 -8
- package/hooks/session-extract.bundle.mjs +2 -1
- package/hooks/session-helpers.mjs +44 -3
- package/hooks/session-loaders.mjs +37 -0
- package/hooks/session-snapshot.bundle.mjs +14 -14
- package/hooks/sessionstart.mjs +5 -5
- package/hooks/userpromptsubmit.mjs +26 -9
- package/hooks/vscode-copilot/posttooluse.mjs +8 -8
- package/hooks/vscode-copilot/precompact.mjs +2 -2
- package/hooks/vscode-copilot/pretooluse.mjs +3 -2
- package/hooks/vscode-copilot/sessionstart.mjs +2 -2
- package/insight/server.mjs +262 -32
- package/insight/src/lib/api.ts +2 -1
- package/insight/src/routes/index.tsx +16 -3
- package/insight/src/routes/search.tsx +1 -1
- package/openclaw.plugin.json +1 -1
- package/package.json +11 -2
- package/server.bundle.mjs +117 -99
- package/skills/ctx-insight/SKILL.md +1 -1
|
@@ -10,32 +10,34 @@ import "../ensure-deps.mjs";
|
|
|
10
10
|
* Must be fast (<20ms). No network, no LLM, just SQLite writes.
|
|
11
11
|
*/
|
|
12
12
|
|
|
13
|
-
import { createSessionLoaders } from "../session-loaders.mjs";
|
|
14
|
-
import { readStdin, getSessionId, getSessionDBPath,
|
|
13
|
+
import { createSessionLoaders, attributeAndInsertEvents } from "../session-loaders.mjs";
|
|
14
|
+
import { readStdin, parseStdin, getSessionId, getSessionDBPath, getInputProjectDir, VSCODE_OPTS } from "../session-helpers.mjs";
|
|
15
15
|
import { appendFileSync } from "node:fs";
|
|
16
16
|
import { join, dirname } from "node:path";
|
|
17
17
|
import { fileURLToPath } from "node:url";
|
|
18
18
|
import { homedir } from "node:os";
|
|
19
19
|
|
|
20
20
|
const HOOK_DIR = dirname(fileURLToPath(import.meta.url));
|
|
21
|
-
const { loadSessionDB, loadExtract } = createSessionLoaders(HOOK_DIR);
|
|
21
|
+
const { loadSessionDB, loadExtract, loadProjectAttribution } = createSessionLoaders(HOOK_DIR);
|
|
22
22
|
const OPTS = VSCODE_OPTS;
|
|
23
23
|
const DEBUG_LOG = join(homedir(), ".vscode", "context-mode", "posttooluse-debug.log");
|
|
24
24
|
|
|
25
25
|
try {
|
|
26
26
|
const raw = await readStdin();
|
|
27
|
-
const input =
|
|
27
|
+
const input = parseStdin(raw);
|
|
28
|
+
const projectDir = getInputProjectDir(input, OPTS);
|
|
28
29
|
|
|
29
30
|
appendFileSync(DEBUG_LOG, `[${new Date().toISOString()}] CALL: ${input.tool_name}\n`);
|
|
30
31
|
|
|
31
32
|
const { extractEvents } = await loadExtract();
|
|
33
|
+
const { resolveProjectAttributions } = await loadProjectAttribution();
|
|
32
34
|
const { SessionDB } = await loadSessionDB();
|
|
33
35
|
|
|
34
36
|
const dbPath = getSessionDBPath(OPTS);
|
|
35
37
|
const db = new SessionDB({ dbPath });
|
|
36
38
|
const sessionId = getSessionId(input, OPTS);
|
|
37
39
|
|
|
38
|
-
db.ensureSession(sessionId,
|
|
40
|
+
db.ensureSession(sessionId, projectDir);
|
|
39
41
|
|
|
40
42
|
const events = extractEvents({
|
|
41
43
|
tool_name: input.tool_name,
|
|
@@ -46,9 +48,7 @@ try {
|
|
|
46
48
|
tool_output: input.tool_output,
|
|
47
49
|
});
|
|
48
50
|
|
|
49
|
-
|
|
50
|
-
db.insertEvent(sessionId, event, "PostToolUse");
|
|
51
|
-
}
|
|
51
|
+
attributeAndInsertEvents(db, sessionId, events, input, projectDir, "PostToolUse", resolveProjectAttributions);
|
|
52
52
|
|
|
53
53
|
appendFileSync(DEBUG_LOG, `[${new Date().toISOString()}] OK: ${input.tool_name} → ${events.length} events\n`);
|
|
54
54
|
db.close();
|
|
@@ -10,7 +10,7 @@ import "../ensure-deps.mjs";
|
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { createSessionLoaders } from "../session-loaders.mjs";
|
|
13
|
-
import { readStdin, getSessionId, getSessionDBPath, VSCODE_OPTS } from "../session-helpers.mjs";
|
|
13
|
+
import { readStdin, parseStdin, getSessionId, getSessionDBPath, VSCODE_OPTS } from "../session-helpers.mjs";
|
|
14
14
|
import { appendFileSync } from "node:fs";
|
|
15
15
|
import { join, dirname } from "node:path";
|
|
16
16
|
import { fileURLToPath } from "node:url";
|
|
@@ -23,7 +23,7 @@ const DEBUG_LOG = join(homedir(), ".vscode", "context-mode", "precompact-debug.l
|
|
|
23
23
|
|
|
24
24
|
try {
|
|
25
25
|
const raw = await readStdin();
|
|
26
|
-
const input =
|
|
26
|
+
const input = parseStdin(raw);
|
|
27
27
|
|
|
28
28
|
const { buildResumeSnapshot } = await loadSnapshot();
|
|
29
29
|
const { SessionDB } = await loadSessionDB();
|
|
@@ -10,16 +10,17 @@ import { fileURLToPath } from "node:url";
|
|
|
10
10
|
import { readStdin } from "../core/stdin.mjs";
|
|
11
11
|
import { routePreToolUse, initSecurity } from "../core/routing.mjs";
|
|
12
12
|
import { formatDecision } from "../core/formatters.mjs";
|
|
13
|
+
import { parseStdin, getSessionId, VSCODE_OPTS } from "../session-helpers.mjs";
|
|
13
14
|
|
|
14
15
|
const __hookDir = dirname(fileURLToPath(import.meta.url));
|
|
15
16
|
await initSecurity(resolve(__hookDir, "..", "..", "build"));
|
|
16
17
|
|
|
17
18
|
const raw = await readStdin();
|
|
18
|
-
const input =
|
|
19
|
+
const input = parseStdin(raw);
|
|
19
20
|
const tool = input.tool_name ?? "";
|
|
20
21
|
const toolInput = input.tool_input ?? {};
|
|
21
22
|
|
|
22
|
-
const decision = routePreToolUse(tool, toolInput, process.env.VSCODE_CWD || process.env.CLAUDE_PROJECT_DIR, "vscode-copilot");
|
|
23
|
+
const decision = routePreToolUse(tool, toolInput, process.env.VSCODE_CWD || process.env.CLAUDE_PROJECT_DIR, "vscode-copilot", getSessionId(input, VSCODE_OPTS));
|
|
23
24
|
const response = formatDecision("vscode-copilot", decision);
|
|
24
25
|
if (response !== null) {
|
|
25
26
|
process.stdout.write(JSON.stringify(response) + "\n");
|
|
@@ -19,7 +19,7 @@ const toolNamer = createToolNamer("vscode-copilot");
|
|
|
19
19
|
const ROUTING_BLOCK = createRoutingBlock(toolNamer);
|
|
20
20
|
import { writeSessionEventsFile, buildSessionDirective, getSessionEvents, getLatestSessionEvents } from "../session-directive.mjs";
|
|
21
21
|
import {
|
|
22
|
-
readStdin, getSessionId, getSessionDBPath, getSessionEventsPath, getCleanupFlagPath,
|
|
22
|
+
readStdin, parseStdin, getSessionId, getSessionDBPath, getSessionEventsPath, getCleanupFlagPath,
|
|
23
23
|
getProjectDir, VSCODE_OPTS,
|
|
24
24
|
} from "../session-helpers.mjs";
|
|
25
25
|
import { join } from "node:path";
|
|
@@ -35,7 +35,7 @@ let additionalContext = ROUTING_BLOCK;
|
|
|
35
35
|
|
|
36
36
|
try {
|
|
37
37
|
const raw = await readStdin();
|
|
38
|
-
const input =
|
|
38
|
+
const input = parseStdin(raw);
|
|
39
39
|
const source = input.source ?? "startup";
|
|
40
40
|
|
|
41
41
|
if (source === "compact") {
|
package/insight/server.mjs
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
*/
|
|
10
10
|
|
|
11
11
|
import { readFileSync, readdirSync, statSync, existsSync, mkdirSync } from "node:fs";
|
|
12
|
-
import { join, dirname, extname } from "node:path";
|
|
12
|
+
import { join, dirname, extname, normalize } from "node:path";
|
|
13
13
|
import { homedir } from "node:os";
|
|
14
14
|
import { fileURLToPath } from "node:url";
|
|
15
15
|
import { createServer as createHttpServer } from "node:http";
|
|
@@ -28,18 +28,28 @@ if (isBun) {
|
|
|
28
28
|
} else {
|
|
29
29
|
try {
|
|
30
30
|
Database = (await import("better-sqlite3")).default;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
31
|
+
// Verify native addon loads correctly (catches arch mismatch: x86_64 vs arm64)
|
|
32
|
+
const testDb = new Database(":memory:");
|
|
33
|
+
testDb.close();
|
|
34
|
+
} catch (err) {
|
|
35
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
36
|
+
console.error("\n Error: better-sqlite3 failed to load.");
|
|
37
|
+
console.error(` ${msg}`);
|
|
38
|
+
if (msg.includes("incompatible architecture") || msg.includes("dlopen")) {
|
|
39
|
+
const cacheHint = process.env.INSIGHT_SESSION_DIR
|
|
40
|
+
? join(dirname(process.env.INSIGHT_SESSION_DIR), "insight-cache", "node_modules")
|
|
41
|
+
: join("~", ".claude", "context-mode", "insight-cache", "node_modules");
|
|
42
|
+
console.error(`\n Fix: rm -rf ${cacheHint} && context-mode insight`);
|
|
43
|
+
} else {
|
|
44
|
+
console.error(" Install it: npm install better-sqlite3");
|
|
45
|
+
}
|
|
35
46
|
process.exit(1);
|
|
36
47
|
}
|
|
37
48
|
}
|
|
38
49
|
|
|
39
50
|
// ── Paths ────────────────────────────────────────────────
|
|
40
|
-
const
|
|
41
|
-
const CONTENT_DIR = join(
|
|
42
|
-
const SESSION_DIR = join(BASE, "sessions");
|
|
51
|
+
const SESSION_DIR = process.env.INSIGHT_SESSION_DIR || join(homedir(), ".claude", "context-mode", "sessions");
|
|
52
|
+
const CONTENT_DIR = process.env.INSIGHT_CONTENT_DIR || join(homedir(), ".claude", "context-mode", "content");
|
|
43
53
|
const DIST_DIR = join(__dirname, "dist");
|
|
44
54
|
|
|
45
55
|
// ── SQLite helpers ───────────────────────────────────────
|
|
@@ -60,6 +70,107 @@ function safeGet(db, sql, params = []) {
|
|
|
60
70
|
try { return db.prepare(sql).get(...params); } catch { return null; }
|
|
61
71
|
}
|
|
62
72
|
|
|
73
|
+
function hasColumn(db, table, column) {
|
|
74
|
+
try {
|
|
75
|
+
const rows = db.prepare(`PRAGMA table_xinfo(${table})`).all();
|
|
76
|
+
return rows.some(r => r.name === column);
|
|
77
|
+
} catch {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const UNKNOWN_PROJECT_KEY = "__unknown__";
|
|
83
|
+
|
|
84
|
+
function normalizeFsPath(path) {
|
|
85
|
+
const norm = normalize(String(path || "")).replace(/\\/g, "/");
|
|
86
|
+
if (norm.length <= 1) return norm;
|
|
87
|
+
return norm.replace(/\/+$/, "");
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function parseFileSearchPath(data) {
|
|
91
|
+
const marker = " in ";
|
|
92
|
+
const idx = String(data || "").lastIndexOf(marker);
|
|
93
|
+
if (idx < 0) return null;
|
|
94
|
+
const p = String(data || "").slice(idx + marker.length).trim();
|
|
95
|
+
return p || null;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function isLikelyPath(value) {
|
|
99
|
+
const v = String(value || "");
|
|
100
|
+
return v.includes("/") || v.includes("\\") || v.startsWith(".") || /^[A-Za-z]:[\\/]/.test(v);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function legacyProjectAttribution(db) {
|
|
104
|
+
const origins = new Map(
|
|
105
|
+
safeAll(db, "SELECT session_id, project_dir FROM session_meta")
|
|
106
|
+
.map((r) => [r.session_id, r.project_dir || UNKNOWN_PROJECT_KEY]),
|
|
107
|
+
);
|
|
108
|
+
|
|
109
|
+
const events = safeAll(db, `SELECT id, session_id, type, data FROM session_events ORDER BY id ASC`);
|
|
110
|
+
const lastProjectBySession = new Map();
|
|
111
|
+
const projectAgg = new Map();
|
|
112
|
+
let unknownEvents = 0;
|
|
113
|
+
|
|
114
|
+
function addProject(projectDir, sessionId) {
|
|
115
|
+
const key = projectDir || UNKNOWN_PROJECT_KEY;
|
|
116
|
+
const existing = projectAgg.get(key) || { project_dir: key, sessionsSet: new Set(), events: 0, compacts: 0, avg_confidence: 0, high_conf_events: 0 };
|
|
117
|
+
existing.events += 1;
|
|
118
|
+
existing.sessionsSet.add(sessionId);
|
|
119
|
+
projectAgg.set(key, existing);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
for (const ev of events) {
|
|
123
|
+
const sessionId = ev.session_id;
|
|
124
|
+
const origin = origins.get(sessionId) || UNKNOWN_PROJECT_KEY;
|
|
125
|
+
const last = lastProjectBySession.get(sessionId) || "";
|
|
126
|
+
let projectDir = "";
|
|
127
|
+
|
|
128
|
+
if (ev.type === "cwd" && isLikelyPath(ev.data)) {
|
|
129
|
+
projectDir = normalizeFsPath(ev.data);
|
|
130
|
+
} else if (ev.type === "file_read" || ev.type === "file_write" || ev.type === "file_edit" || ev.type === "rule") {
|
|
131
|
+
if (isLikelyPath(ev.data)) {
|
|
132
|
+
const p = normalizeFsPath(ev.data);
|
|
133
|
+
if (origin !== UNKNOWN_PROJECT_KEY && (p === origin || p.startsWith(`${origin}/`))) projectDir = origin;
|
|
134
|
+
else projectDir = p.includes("/") ? p.slice(0, p.lastIndexOf("/")) : p;
|
|
135
|
+
}
|
|
136
|
+
} else if (ev.type === "file_search") {
|
|
137
|
+
const p = parseFileSearchPath(ev.data);
|
|
138
|
+
if (p && isLikelyPath(p)) {
|
|
139
|
+
const pp = normalizeFsPath(p);
|
|
140
|
+
if (origin !== UNKNOWN_PROJECT_KEY && (pp === origin || pp.startsWith(`${origin}/`))) projectDir = origin;
|
|
141
|
+
else projectDir = pp;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
if (!projectDir) {
|
|
146
|
+
projectDir = last || origin || UNKNOWN_PROJECT_KEY;
|
|
147
|
+
}
|
|
148
|
+
if (!projectDir || projectDir === UNKNOWN_PROJECT_KEY) unknownEvents += 1;
|
|
149
|
+
|
|
150
|
+
addProject(projectDir, sessionId);
|
|
151
|
+
if (projectDir && projectDir !== UNKNOWN_PROJECT_KEY) {
|
|
152
|
+
lastProjectBySession.set(sessionId, projectDir);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
const rows = [...projectAgg.values()].map((r) => ({
|
|
157
|
+
project_dir: r.project_dir,
|
|
158
|
+
sessions: r.sessionsSet.size,
|
|
159
|
+
events: r.events,
|
|
160
|
+
compacts: 0,
|
|
161
|
+
avg_confidence: 0,
|
|
162
|
+
high_conf_events: 0,
|
|
163
|
+
})).sort((a, b) => b.events - a.events);
|
|
164
|
+
|
|
165
|
+
return {
|
|
166
|
+
projectRows: rows,
|
|
167
|
+
total_events: events.length,
|
|
168
|
+
unknown_events: unknownEvents,
|
|
169
|
+
avg_confidence: 0,
|
|
170
|
+
high_conf_events: 0,
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
|
|
63
174
|
function listDBFiles(dir) {
|
|
64
175
|
if (!existsSync(dir)) return [];
|
|
65
176
|
return readdirSync(dir)
|
|
@@ -103,6 +214,11 @@ function mergeByKey(arr, key, mergeFn) {
|
|
|
103
214
|
return [...map.values()];
|
|
104
215
|
}
|
|
105
216
|
|
|
217
|
+
// ── Input validation ────────────────────────────────────
|
|
218
|
+
function isValidHash(hash) {
|
|
219
|
+
return /^[a-f0-9_]+$/.test(hash);
|
|
220
|
+
}
|
|
221
|
+
|
|
106
222
|
// ── API Handlers ─────────────────────────────────────────
|
|
107
223
|
|
|
108
224
|
function apiOverview() {
|
|
@@ -347,13 +463,21 @@ function apiAnalytics() {
|
|
|
347
463
|
GROUP BY se.session_id, se.data HAVING edit_count > 1
|
|
348
464
|
ORDER BY edit_count DESC LIMIT 20`)
|
|
349
465
|
);
|
|
350
|
-
const gitActivity = queryAllSessionDBs(db =>
|
|
351
|
-
|
|
352
|
-
|
|
466
|
+
const gitActivity = queryAllSessionDBs(db => {
|
|
467
|
+
if (hasColumn(db, "session_events", "project_dir")) {
|
|
468
|
+
return safeAll(db, `SELECT se.data as action, se.created_at, se.session_id,
|
|
469
|
+
COALESCE(NULLIF(se.project_dir, ''), sm.project_dir, '${UNKNOWN_PROJECT_KEY}') as project_dir,
|
|
470
|
+
sm.started_at as session_start
|
|
471
|
+
FROM session_events se
|
|
472
|
+
LEFT JOIN session_meta sm ON se.session_id = sm.session_id
|
|
473
|
+
WHERE se.type = 'git' ORDER BY se.created_at DESC LIMIT 20`);
|
|
474
|
+
}
|
|
475
|
+
return safeAll(db, `SELECT se.data as action, se.created_at, se.session_id,
|
|
476
|
+
COALESCE(sm.project_dir, '${UNKNOWN_PROJECT_KEY}') as project_dir, sm.started_at as session_start
|
|
353
477
|
FROM session_events se
|
|
354
|
-
JOIN session_meta sm ON se.session_id = sm.session_id
|
|
355
|
-
WHERE se.type = 'git' ORDER BY se.created_at DESC LIMIT 20`)
|
|
356
|
-
);
|
|
478
|
+
LEFT JOIN session_meta sm ON se.session_id = sm.session_id
|
|
479
|
+
WHERE se.type = 'git' ORDER BY se.created_at DESC LIMIT 20`);
|
|
480
|
+
});
|
|
357
481
|
const rawSubagents = queryAllSessionDBs(db =>
|
|
358
482
|
safeAll(db, `SELECT data as task, created_at, session_id FROM session_events
|
|
359
483
|
WHERE type = 'subagent' ORDER BY created_at ASC`)
|
|
@@ -378,12 +502,34 @@ function apiAnalytics() {
|
|
|
378
502
|
timeSavedMin: parallelBursts.reduce((a, b) => a + (b.length - 1) * 2, 0),
|
|
379
503
|
burstDetails: parallelBursts.map(b => ({ size: b.length, time: b[0].created_at })),
|
|
380
504
|
};
|
|
381
|
-
const projectActivity = queryAllSessionDBs(db =>
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
505
|
+
const projectActivity = queryAllSessionDBs(db => {
|
|
506
|
+
if (hasColumn(db, "session_events", "project_dir")) {
|
|
507
|
+
return safeAll(db, `SELECT
|
|
508
|
+
COALESCE(NULLIF(se.project_dir, ''), '${UNKNOWN_PROJECT_KEY}') as project_dir,
|
|
509
|
+
COUNT(DISTINCT se.session_id) as sessions,
|
|
510
|
+
COUNT(*) as events,
|
|
511
|
+
0 as compacts,
|
|
512
|
+
AVG(COALESCE(se.attribution_confidence, 0)) as avg_confidence,
|
|
513
|
+
SUM(CASE WHEN COALESCE(se.attribution_confidence, 0) >= 0.8 THEN 1 ELSE 0 END) as high_conf_events
|
|
514
|
+
FROM session_events se
|
|
515
|
+
GROUP BY project_dir
|
|
516
|
+
ORDER BY events DESC
|
|
517
|
+
LIMIT 20`);
|
|
518
|
+
}
|
|
519
|
+
return legacyProjectAttribution(db).projectRows;
|
|
520
|
+
});
|
|
521
|
+
|
|
522
|
+
const attributionSummary = queryAllSessionDBs(db => {
|
|
523
|
+
if (hasColumn(db, "session_events", "project_dir")) {
|
|
524
|
+
return safeAll(db, `SELECT
|
|
525
|
+
COUNT(*) as total_events,
|
|
526
|
+
SUM(CASE WHEN COALESCE(project_dir, '') = '' THEN 1 ELSE 0 END) as unknown_events,
|
|
527
|
+
AVG(COALESCE(attribution_confidence, 0)) as avg_confidence,
|
|
528
|
+
SUM(CASE WHEN COALESCE(attribution_confidence, 0) >= 0.8 THEN 1 ELSE 0 END) as high_conf_events
|
|
529
|
+
FROM session_events`);
|
|
530
|
+
}
|
|
531
|
+
return [legacyProjectAttribution(db)];
|
|
532
|
+
});
|
|
387
533
|
const hourlyPattern = queryAllSessionDBs(db =>
|
|
388
534
|
safeAll(db, `SELECT CAST(strftime('%H', created_at) AS INTEGER) as hour, COUNT(*) as count
|
|
389
535
|
FROM session_events WHERE created_at IS NOT NULL
|
|
@@ -426,13 +572,22 @@ function apiAnalytics() {
|
|
|
426
572
|
);
|
|
427
573
|
|
|
428
574
|
// 2. Personal Commit Rate — commits per session
|
|
429
|
-
const commitRate = queryAllSessionDBs(db =>
|
|
430
|
-
|
|
575
|
+
const commitRate = queryAllSessionDBs(db => {
|
|
576
|
+
if (hasColumn(db, "session_events", "project_dir")) {
|
|
577
|
+
return safeAll(db, `SELECT
|
|
578
|
+
sm.session_id,
|
|
579
|
+
COALESCE(NULLIF(MAX(CASE WHEN se.type = 'git' THEN se.project_dir END), ''), sm.project_dir, '${UNKNOWN_PROJECT_KEY}') as project_dir,
|
|
580
|
+
SUM(CASE WHEN se.type = 'git' AND se.data = 'commit' THEN 1 ELSE 0 END) as commits
|
|
581
|
+
FROM session_meta sm
|
|
582
|
+
LEFT JOIN session_events se ON se.session_id = sm.session_id
|
|
583
|
+
GROUP BY sm.session_id`);
|
|
584
|
+
}
|
|
585
|
+
return safeAll(db, `SELECT sm.session_id, COALESCE(sm.project_dir, '${UNKNOWN_PROJECT_KEY}') as project_dir,
|
|
431
586
|
SUM(CASE WHEN se.type = 'git' AND se.data = 'commit' THEN 1 ELSE 0 END) as commits
|
|
432
587
|
FROM session_meta sm
|
|
433
588
|
LEFT JOIN session_events se ON se.session_id = sm.session_id
|
|
434
|
-
GROUP BY sm.session_id`)
|
|
435
|
-
);
|
|
589
|
+
GROUP BY sm.session_id`);
|
|
590
|
+
});
|
|
436
591
|
|
|
437
592
|
// 3. Sandbox Adoption — context-mode MCP tool usage vs total
|
|
438
593
|
const sandboxAdoption = queryAllSessionDBs(db =>
|
|
@@ -467,6 +622,59 @@ function apiAnalytics() {
|
|
|
467
622
|
sandbox_calls: (a.sandbox_calls || 0) + (b.sandbox_calls || 0),
|
|
468
623
|
total_calls: (a.total_calls || 0) + (b.total_calls || 0),
|
|
469
624
|
}), { sandbox_calls: 0, total_calls: 0 });
|
|
625
|
+
const attributionSchemaCoverage = queryAllSessionDBs(db => [{
|
|
626
|
+
has_attribution_columns: hasColumn(db, "session_events", "project_dir") ? 1 : 0,
|
|
627
|
+
}]);
|
|
628
|
+
const fallbackOnly = attributionSchemaCoverage.length > 0
|
|
629
|
+
&& attributionSchemaCoverage.every((r) => !r.has_attribution_columns);
|
|
630
|
+
|
|
631
|
+
const mergedProjectActivity = mergeByKey(projectActivity, "project_dir", (a, b) => {
|
|
632
|
+
const aEvents = Number(a.events || 0);
|
|
633
|
+
const bEvents = Number(b.events || 0);
|
|
634
|
+
const aWeighted = Number(
|
|
635
|
+
(a.weighted_confidence_sum ?? (Number(a.avg_confidence || 0) * aEvents)) || 0,
|
|
636
|
+
);
|
|
637
|
+
const bWeighted = Number(
|
|
638
|
+
(b.weighted_confidence_sum ?? (Number(b.avg_confidence || 0) * bEvents)) || 0,
|
|
639
|
+
);
|
|
640
|
+
return {
|
|
641
|
+
project_dir: a.project_dir,
|
|
642
|
+
sessions: (a.sessions || 0) + (b.sessions || 0),
|
|
643
|
+
events: aEvents + bEvents,
|
|
644
|
+
compacts: (a.compacts || 0) + (b.compacts || 0),
|
|
645
|
+
weighted_confidence_sum: aWeighted + bWeighted,
|
|
646
|
+
high_conf_events: (a.high_conf_events || 0) + (b.high_conf_events || 0),
|
|
647
|
+
};
|
|
648
|
+
})
|
|
649
|
+
.map((p) => ({
|
|
650
|
+
project_dir: p.project_dir,
|
|
651
|
+
sessions: p.sessions || 0,
|
|
652
|
+
events: p.events || 0,
|
|
653
|
+
compacts: p.compacts || 0,
|
|
654
|
+
avg_confidence: (p.events || 0) > 0 ? (p.weighted_confidence_sum || 0) / p.events : 0,
|
|
655
|
+
high_conf_events: p.high_conf_events || 0,
|
|
656
|
+
}))
|
|
657
|
+
.sort((a, b) => (b.events || 0) - (a.events || 0));
|
|
658
|
+
const nonUnknownProjects = mergedProjectActivity.filter((p) => p.project_dir !== UNKNOWN_PROJECT_KEY);
|
|
659
|
+
|
|
660
|
+
const attributionAgg = attributionSummary.reduce((a, b) => ({
|
|
661
|
+
total_events: (a.total_events || 0) + (b.total_events || 0),
|
|
662
|
+
unknown_events: (a.unknown_events || 0) + (b.unknown_events || 0),
|
|
663
|
+
high_conf_events: (a.high_conf_events || 0) + (b.high_conf_events || 0),
|
|
664
|
+
// weighted sum for avg_confidence
|
|
665
|
+
weighted_confidence_sum: (a.weighted_confidence_sum || 0) + ((b.avg_confidence || 0) * (b.total_events || 0)),
|
|
666
|
+
}), { total_events: 0, unknown_events: 0, high_conf_events: 0, weighted_confidence_sum: 0 });
|
|
667
|
+
|
|
668
|
+
const attributedEvents = Math.max(0, attributionAgg.total_events - attributionAgg.unknown_events);
|
|
669
|
+
const unknownPct = attributionAgg.total_events > 0
|
|
670
|
+
? Math.round(1000 * attributionAgg.unknown_events / attributionAgg.total_events) / 10
|
|
671
|
+
: 100;
|
|
672
|
+
const avgConfidencePct = attributionAgg.total_events > 0
|
|
673
|
+
? Math.round(1000 * attributionAgg.weighted_confidence_sum / attributionAgg.total_events) / 10
|
|
674
|
+
: 0;
|
|
675
|
+
const highConfidencePct = attributionAgg.total_events > 0
|
|
676
|
+
? Math.round(1000 * attributionAgg.high_conf_events / attributionAgg.total_events) / 10
|
|
677
|
+
: 0;
|
|
470
678
|
|
|
471
679
|
return {
|
|
472
680
|
totals: {
|
|
@@ -483,7 +691,7 @@ function apiAnalytics() {
|
|
|
483
691
|
totalTasks: tasks.length, totalPrompts: prompts.length,
|
|
484
692
|
promptsPerSession: sessionDurations.length > 0
|
|
485
693
|
? Math.round(10 * prompts.length / sessionDurations.length) / 10 : 0,
|
|
486
|
-
uniqueProjects:
|
|
694
|
+
uniqueProjects: nonUnknownProjects.length,
|
|
487
695
|
totalCommits: commitRate.reduce((a, b) => a + (b.commits || 0), 0),
|
|
488
696
|
commitsPerSession: sessionDurations.length > 0
|
|
489
697
|
? Math.round(10 * commitRate.reduce((a, b) => a + (b.commits || 0), 0) / sessionDurations.length) / 10 : 0,
|
|
@@ -492,6 +700,15 @@ function apiAnalytics() {
|
|
|
492
700
|
totalRules: rulesFreshness.length,
|
|
493
701
|
totalEditTestCycles: editTestCycles.reduce((a, b) => a + (b.cycles || 0), 0),
|
|
494
702
|
},
|
|
703
|
+
attribution: {
|
|
704
|
+
totalEvents: attributionAgg.total_events,
|
|
705
|
+
attributedEvents,
|
|
706
|
+
unknownEvents: attributionAgg.unknown_events,
|
|
707
|
+
unknownPct,
|
|
708
|
+
avgConfidencePct,
|
|
709
|
+
highConfidencePct,
|
|
710
|
+
isFallbackOnly: fallbackOnly,
|
|
711
|
+
},
|
|
495
712
|
sessionsByDate: mergeByKey(sessionsByDate, "date", (a, b) => ({
|
|
496
713
|
date: a.date, count: a.count + b.count, events: a.events + b.events, compacts: a.compacts + b.compacts
|
|
497
714
|
})),
|
|
@@ -503,9 +720,7 @@ function apiAnalytics() {
|
|
|
503
720
|
timeToFirstCommit,
|
|
504
721
|
exploreExecRatio: exploreExecRatio.reduce((a, b) => ({ explore: (a.explore||0)+(b.explore||0), execute: (a.execute||0)+(b.execute||0), total: (a.total||0)+(b.total||0) }), { explore: 0, execute: 0, total: 0 }),
|
|
505
722
|
reworkData, gitActivity, subagents,
|
|
506
|
-
projectActivity:
|
|
507
|
-
project_dir: a.project_dir, sessions: a.sessions + b.sessions, events: a.events + b.events, compacts: (a.compacts||0)+(b.compacts||0)
|
|
508
|
-
})).sort((a, b) => b.events - a.events),
|
|
723
|
+
projectActivity: mergedProjectActivity,
|
|
509
724
|
hourlyPattern: mergeByKey(hourlyPattern, "hour", (a, b) => ({ hour: a.hour, count: a.count + b.count })),
|
|
510
725
|
weeklyTrend: mergeByKey(weeklyTrend, "week", (a, b) => ({ week: a.week, sessions: a.sessions + b.sessions, events: a.events + b.events })),
|
|
511
726
|
tasks, prompts,
|
|
@@ -530,6 +745,7 @@ function route(method, pathname, params) {
|
|
|
530
745
|
|
|
531
746
|
if (pathname.startsWith("/api/content/") && pathname.includes("/chunks/")) {
|
|
532
747
|
const parts = pathname.split("/");
|
|
748
|
+
if (!isValidHash(parts[3])) return { error: "invalid hash" };
|
|
533
749
|
return apiSourceChunks(parts[3], Number(parts[5]));
|
|
534
750
|
}
|
|
535
751
|
if (pathname === "/api/search") {
|
|
@@ -539,10 +755,12 @@ function route(method, pathname, params) {
|
|
|
539
755
|
}
|
|
540
756
|
if (pathname.startsWith("/api/sessions/") && pathname.includes("/events/")) {
|
|
541
757
|
const parts = pathname.split("/");
|
|
758
|
+
if (!isValidHash(parts[3])) return { error: "invalid hash" };
|
|
542
759
|
return apiSessionEvents(parts[3], decodeURIComponent(parts[5]));
|
|
543
760
|
}
|
|
544
761
|
if (method === "DELETE" && pathname.startsWith("/api/content/")) {
|
|
545
762
|
const parts = pathname.split("/");
|
|
763
|
+
if (!isValidHash(parts[3])) return { error: "invalid hash" };
|
|
546
764
|
return apiDeleteSource(parts[3], Number(parts[5]));
|
|
547
765
|
}
|
|
548
766
|
return null;
|
|
@@ -568,6 +786,7 @@ function serveStaticFile(pathname) {
|
|
|
568
786
|
// ── Server (dual runtime) ────────────────────────────────
|
|
569
787
|
|
|
570
788
|
const indexHTML = readFileSync(join(DIST_DIR, "index.html"), "utf8");
|
|
789
|
+
const API_JSON_HEADERS = { "Content-Type": "application/json" };
|
|
571
790
|
|
|
572
791
|
if (isBun) {
|
|
573
792
|
// Bun: use Bun.serve
|
|
@@ -579,7 +798,7 @@ if (isBun) {
|
|
|
579
798
|
const data = route(req.method, url.pathname, url.searchParams);
|
|
580
799
|
if (data !== null) {
|
|
581
800
|
return new Response(JSON.stringify(data), {
|
|
582
|
-
headers:
|
|
801
|
+
headers: API_JSON_HEADERS,
|
|
583
802
|
});
|
|
584
803
|
}
|
|
585
804
|
if (url.pathname.startsWith("/assets/") || url.pathname.match(/\.\w{2,4}$/)) {
|
|
@@ -595,9 +814,7 @@ if (isBun) {
|
|
|
595
814
|
// Node: use http.createServer
|
|
596
815
|
const server = createHttpServer((req, res) => {
|
|
597
816
|
const url = new URL(req.url, `http://localhost:${PORT}`);
|
|
598
|
-
|
|
599
|
-
res.setHeader("Access-Control-Allow-Methods", "GET, DELETE, OPTIONS");
|
|
600
|
-
if (req.method === "OPTIONS") { res.writeHead(204); res.end(); return; }
|
|
817
|
+
if (req.method === "OPTIONS") { res.writeHead(405); res.end(); return; }
|
|
601
818
|
|
|
602
819
|
const data = route(req.method, url.pathname, url.searchParams);
|
|
603
820
|
if (data !== null) {
|
|
@@ -619,6 +836,19 @@ if (isBun) {
|
|
|
619
836
|
server.listen(PORT, "127.0.0.1");
|
|
620
837
|
}
|
|
621
838
|
|
|
839
|
+
// Parent watchdog: exit when the MCP process that spawned us disappears.
|
|
840
|
+
// Fallback for SIGKILL / crash paths where shutdown() cannot run.
|
|
841
|
+
const PARENT_PID = Number(process.env.INSIGHT_PARENT_PID);
|
|
842
|
+
if (Number.isFinite(PARENT_PID) && PARENT_PID > 0) {
|
|
843
|
+
setInterval(() => {
|
|
844
|
+
try {
|
|
845
|
+
process.kill(PARENT_PID, 0);
|
|
846
|
+
} catch {
|
|
847
|
+
process.exit(0);
|
|
848
|
+
}
|
|
849
|
+
}, 5000).unref();
|
|
850
|
+
}
|
|
851
|
+
|
|
622
852
|
console.log(`\n context-mode Insight`);
|
|
623
853
|
console.log(` http://localhost:${PORT}`);
|
|
624
854
|
console.log(` Runtime: ${isBun ? "Bun" : "Node.js"}\n`);
|
package/insight/src/lib/api.ts
CHANGED
|
@@ -40,7 +40,8 @@ export interface AnalyticsData {
|
|
|
40
40
|
exploreExecRatio: { explore: number; execute: number; total: number };
|
|
41
41
|
reworkData: { session_id: string; file: string; edit_count: number }[];
|
|
42
42
|
gitActivity: { action: string; created_at: string; session_id: string; project_dir: string; session_start: string }[];
|
|
43
|
-
projectActivity: { project_dir: string; sessions: number; events: number }[];
|
|
43
|
+
projectActivity: { project_dir: string; sessions: number; events: number; avg_confidence?: number; high_conf_events?: number }[];
|
|
44
|
+
attribution?: { totalEvents: number; attributedEvents: number; unknownEvents: number; unknownPct: number; avgConfidencePct: number; highConfidencePct: number; isFallbackOnly: boolean };
|
|
44
45
|
hourlyPattern: { hour: number; count: number }[];
|
|
45
46
|
weeklyTrend: { week: string; sessions: number; events: number }[];
|
|
46
47
|
tasks: { task: string; created_at: string }[];
|
|
@@ -505,18 +505,31 @@ function Dashboard() {
|
|
|
505
505
|
<div className="grid grid-cols-3 gap-4 mb-4">
|
|
506
506
|
<Mini label="Projects" value={t.uniqueProjects} />
|
|
507
507
|
<Mini label="Top Project" value={topProject?.project_dir?.split("/").pop() || "-"} color="text-emerald-500" />
|
|
508
|
-
<Mini label="
|
|
508
|
+
<Mini label="Match Quality" value={data.attribution?.avgConfidencePct != null ? (data.attribution.avgConfidencePct >= 80 ? "Strong" : data.attribution.avgConfidencePct >= 55 ? "Fair" : "Weak") : "-"} color={data.attribution && data.attribution.avgConfidencePct >= 80 ? "text-emerald-500" : data.attribution && data.attribution.avgConfidencePct >= 55 ? "text-amber-500" : "text-red-400"} />
|
|
509
509
|
</div>
|
|
510
|
+
{data.attribution?.isFallbackOnly && (
|
|
511
|
+
<div className="mb-3 px-3 py-2 rounded-md bg-muted/50 border border-border text-xs text-muted-foreground flex items-center gap-1.5">
|
|
512
|
+
<Lightbulb className="h-3 w-3 shrink-0" />
|
|
513
|
+
Limited tracking detail — project time is estimated from session data
|
|
514
|
+
</div>
|
|
515
|
+
)}
|
|
510
516
|
<div className="space-y-2.5 pt-2 border-t border-border">
|
|
511
517
|
{data.projectActivity.slice(0, 6).map((p, i) => {
|
|
512
518
|
const maxEv = data.projectActivity[0]?.events || 1;
|
|
513
519
|
const pct = Math.round((p.events / maxEv) * 100);
|
|
514
520
|
const name = p.project_dir?.split("/").filter(Boolean).slice(-2).join("/") || "Unknown";
|
|
521
|
+
const conf = p.avg_confidence != null ? Math.round(p.avg_confidence * 100) : null;
|
|
522
|
+
const qualityLabel = conf != null ? (conf >= 80 ? "Strong" : conf >= 55 ? "Fair" : "Weak") : null;
|
|
523
|
+
const qualityIcon = conf != null ? (conf >= 80 ? "✓" : conf >= 55 ? "~" : "!") : null;
|
|
524
|
+
const qualityColor = conf != null ? (conf >= 80 ? "text-emerald-500" : conf >= 55 ? "text-amber-500" : "text-red-400") : "";
|
|
515
525
|
return (
|
|
516
526
|
<div key={i}>
|
|
517
527
|
<div className="flex justify-between text-xs mb-1">
|
|
518
|
-
<span className="font-mono truncate max-w-[
|
|
519
|
-
<span className="text-muted-foreground tabular-nums">
|
|
528
|
+
<span className="font-mono truncate max-w-[200px]">{name}</span>
|
|
529
|
+
<span className="text-muted-foreground tabular-nums">
|
|
530
|
+
{p.sessions} sessions · {p.events} events
|
|
531
|
+
{qualityLabel != null && <span className={`ml-1.5 text-[11px] font-medium ${qualityColor}`} title={`Match quality: ${conf}%`}>{qualityIcon} {qualityLabel}</span>}
|
|
532
|
+
</span>
|
|
520
533
|
</div>
|
|
521
534
|
<div className="h-1.5 bg-secondary rounded-full overflow-hidden">
|
|
522
535
|
<div className="h-full rounded-full transition-all" style={{ width: `${pct}%`, background: COLORS[i % COLORS.length] }} />
|
|
@@ -79,7 +79,7 @@ function SearchPage() {
|
|
|
79
79
|
<pre
|
|
80
80
|
className="text-xs text-muted-foreground whitespace-pre-wrap break-words font-mono leading-relaxed"
|
|
81
81
|
dangerouslySetInnerHTML={{
|
|
82
|
-
__html: (r.highlighted
|
|
82
|
+
__html: (r.highlighted ? esc(r.highlighted) : esc(r.content))
|
|
83
83
|
.replace(/«/g, '<mark class="bg-amber-500/20 text-foreground rounded px-0.5">')
|
|
84
84
|
.replace(/»/g, "</mark>"),
|
|
85
85
|
}}
|
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.90",
|
|
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.90",
|
|
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",
|
|
@@ -19,13 +19,22 @@
|
|
|
19
19
|
"sandbox",
|
|
20
20
|
"code-execution",
|
|
21
21
|
"fts5",
|
|
22
|
-
"bm25"
|
|
22
|
+
"bm25",
|
|
23
|
+
"pi-package"
|
|
23
24
|
],
|
|
24
25
|
"repository": {
|
|
25
26
|
"type": "git",
|
|
26
27
|
"url": "https://github.com/mksglu/context-mode"
|
|
27
28
|
},
|
|
28
29
|
"homepage": "https://github.com/mksglu/context-mode#readme",
|
|
30
|
+
"pi": {
|
|
31
|
+
"extensions": [
|
|
32
|
+
"./build/pi-extension.js"
|
|
33
|
+
],
|
|
34
|
+
"skills": [
|
|
35
|
+
"./skills"
|
|
36
|
+
]
|
|
37
|
+
},
|
|
29
38
|
"openclaw": {
|
|
30
39
|
"extensions": [
|
|
31
40
|
"./build/openclaw-plugin.js"
|