context-mode 1.0.80 → 1.0.82

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.
Files changed (43) hide show
  1. package/.claude-plugin/marketplace.json +2 -2
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.openclaw-plugin/openclaw.plugin.json +1 -1
  4. package/.openclaw-plugin/package.json +1 -1
  5. package/build/cli.js +57 -0
  6. package/build/server.js +94 -1
  7. package/cli.bundle.mjs +106 -99
  8. package/insight/components.json +25 -0
  9. package/insight/index.html +13 -0
  10. package/insight/package.json +54 -0
  11. package/insight/server.mjs +624 -0
  12. package/insight/src/components/analytics.tsx +112 -0
  13. package/insight/src/components/ui/badge.tsx +52 -0
  14. package/insight/src/components/ui/button.tsx +58 -0
  15. package/insight/src/components/ui/card.tsx +103 -0
  16. package/insight/src/components/ui/chart.tsx +371 -0
  17. package/insight/src/components/ui/collapsible.tsx +19 -0
  18. package/insight/src/components/ui/input.tsx +20 -0
  19. package/insight/src/components/ui/progress.tsx +83 -0
  20. package/insight/src/components/ui/scroll-area.tsx +55 -0
  21. package/insight/src/components/ui/separator.tsx +23 -0
  22. package/insight/src/components/ui/table.tsx +114 -0
  23. package/insight/src/components/ui/tabs.tsx +82 -0
  24. package/insight/src/components/ui/tooltip.tsx +64 -0
  25. package/insight/src/lib/api.ts +71 -0
  26. package/insight/src/lib/utils.ts +6 -0
  27. package/insight/src/main.tsx +22 -0
  28. package/insight/src/routeTree.gen.ts +189 -0
  29. package/insight/src/router.tsx +19 -0
  30. package/insight/src/routes/__root.tsx +55 -0
  31. package/insight/src/routes/enterprise.tsx +316 -0
  32. package/insight/src/routes/index.tsx +914 -0
  33. package/insight/src/routes/knowledge.tsx +221 -0
  34. package/insight/src/routes/knowledge_.$dbHash.$sourceId.tsx +137 -0
  35. package/insight/src/routes/search.tsx +97 -0
  36. package/insight/src/routes/sessions.tsx +179 -0
  37. package/insight/src/routes/sessions_.$dbHash.$sessionId.tsx +181 -0
  38. package/insight/src/styles.css +104 -0
  39. package/insight/tsconfig.json +29 -0
  40. package/insight/vite.config.ts +19 -0
  41. package/openclaw.plugin.json +1 -1
  42. package/package.json +2 -1
  43. package/server.bundle.mjs +76 -72
@@ -0,0 +1,181 @@
1
+ import { createFileRoute, Link } from "@tanstack/react-router";
2
+ import { useEffect, useState } from "react";
3
+ import { api, type SessionEventData } from "@/lib/api";
4
+ import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card";
5
+ import { Badge } from "@/components/ui/badge";
6
+ import { ScrollArea } from "@/components/ui/scroll-area";
7
+ import { Collapsible, CollapsibleContent, CollapsibleTrigger } from "@/components/ui/collapsible";
8
+ import { RatioBar, formatDuration, COLORS } from "@/components/analytics";
9
+ import { ArrowLeft, ChevronDown, ChevronRight, Zap, Clock, Layers } from "lucide-react";
10
+
11
+ export const Route = createFileRoute("/sessions_/$dbHash/$sessionId")({ component: EventView });
12
+
13
+ function EventRow({ e }: { e: { id: number; type: string; priority: number; data: string | null; created_at: string | null } }) {
14
+ const [expanded, setExpanded] = useState(false);
15
+ const raw = e.data || "-";
16
+ const truncated = raw.length > 100 ? raw.slice(0, 100) + "..." : raw;
17
+ const needsExpand = raw.length > 100;
18
+
19
+ return (
20
+ <div
21
+ className={`flex gap-3 py-2 border-b border-border/50 text-sm ${needsExpand ? "cursor-pointer hover:bg-accent/30" : ""}`}
22
+ onClick={() => needsExpand && setExpanded(prev => !prev)}
23
+ >
24
+ <span className="text-xs text-muted-foreground font-mono whitespace-nowrap min-w-[130px]">
25
+ {e.created_at || "-"}
26
+ </span>
27
+ <Badge variant={e.priority >= 3 ? "default" : "secondary"} className="text-[10px] shrink-0 h-5">
28
+ {e.type}
29
+ </Badge>
30
+ <div className="flex-1 min-w-0">
31
+ <span className={`text-xs ${expanded ? "whitespace-pre-wrap break-words" : "block truncate"} ${
32
+ e.priority >= 4 ? "text-red-500 font-semibold" : e.priority >= 3 ? "text-amber-500" : "text-muted-foreground"
33
+ }`}>
34
+ {expanded ? raw : truncated}
35
+ </span>
36
+ </div>
37
+ {needsExpand && (
38
+ <span className="shrink-0 text-muted-foreground">
39
+ {expanded ? <ChevronDown className="h-3 w-3" /> : <ChevronRight className="h-3 w-3" />}
40
+ </span>
41
+ )}
42
+ </div>
43
+ );
44
+ }
45
+
46
+ function EventView() {
47
+ const { dbHash, sessionId } = Route.useParams();
48
+ const [data, setData] = useState<SessionEventData | null>(null);
49
+ const [loading, setLoading] = useState(true);
50
+
51
+ useEffect(() => {
52
+ api.events(dbHash, sessionId).then(d => { setData(d); setLoading(false); });
53
+ }, [dbHash, sessionId]);
54
+
55
+ if (loading || !data) return <p className="text-muted-foreground animate-pulse">Loading session...</p>;
56
+
57
+ // Compute type counts
58
+ const typeCounts: Record<string, number> = {};
59
+ data.events.forEach(e => { typeCounts[e.type] = (typeCounts[e.type] || 0) + 1; });
60
+ const typeEntries = Object.entries(typeCounts).sort((a, b) => b[1] - a[1]);
61
+ const uniqueTypes = typeEntries.length;
62
+
63
+ // Compute duration from first to last event
64
+ let durationMin = 0;
65
+ if (data.events.length >= 2) {
66
+ const first = data.events[0]?.created_at;
67
+ const last = data.events[data.events.length - 1]?.created_at;
68
+ if (first && last) {
69
+ const start = new Date(first).getTime();
70
+ const end = new Date(last).getTime();
71
+ if (!isNaN(start) && !isNaN(end) && end > start) {
72
+ durationMin = (end - start) / 60000;
73
+ }
74
+ }
75
+ }
76
+
77
+ return (
78
+ <div className="space-y-6">
79
+ <Link to="/sessions" className="flex items-center gap-1.5 text-sm text-muted-foreground hover:text-foreground transition-colors">
80
+ <ArrowLeft className="h-4 w-4" /> Back to Sessions
81
+ </Link>
82
+
83
+ <div className="flex items-center gap-3">
84
+ <h2 className="text-2xl font-semibold">Session Detail</h2>
85
+ {data.resume && <Badge variant="outline" className="text-emerald-500 border-emerald-500/30">Resume available</Badge>}
86
+ </div>
87
+
88
+ {/* KPI Strip */}
89
+ <div className="grid grid-cols-3 gap-3">
90
+ <Card>
91
+ <CardHeader className="flex flex-row items-center justify-between pb-2">
92
+ <CardTitle className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider">Events</CardTitle>
93
+ <Zap className="h-4 w-4 text-blue-500" />
94
+ </CardHeader>
95
+ <CardContent>
96
+ <div className="text-2xl font-bold tabular-nums">{data.events.length}</div>
97
+ <p className="text-[11px] text-muted-foreground mt-0.5">total in session</p>
98
+ </CardContent>
99
+ </Card>
100
+ <Card>
101
+ <CardHeader className="flex flex-row items-center justify-between pb-2">
102
+ <CardTitle className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider">Duration</CardTitle>
103
+ <Clock className="h-4 w-4 text-purple-500" />
104
+ </CardHeader>
105
+ <CardContent>
106
+ <div className="text-2xl font-bold tabular-nums">{formatDuration(durationMin)}</div>
107
+ <p className="text-[11px] text-muted-foreground mt-0.5">first to last event</p>
108
+ </CardContent>
109
+ </Card>
110
+ <Card>
111
+ <CardHeader className="flex flex-row items-center justify-between pb-2">
112
+ <CardTitle className="text-[10px] font-medium text-muted-foreground uppercase tracking-wider">Types</CardTitle>
113
+ <Layers className="h-4 w-4 text-emerald-500" />
114
+ </CardHeader>
115
+ <CardContent>
116
+ <div className="text-2xl font-bold tabular-nums">{uniqueTypes}</div>
117
+ <p className="text-[11px] text-muted-foreground mt-0.5">unique event types</p>
118
+ </CardContent>
119
+ </Card>
120
+ </div>
121
+
122
+ {/* Event type breakdown as ratio bar */}
123
+ {typeEntries.length > 0 && (
124
+ <Card>
125
+ <CardHeader>
126
+ <CardTitle className="text-sm">Event Breakdown</CardTitle>
127
+ </CardHeader>
128
+ <CardContent>
129
+ <RatioBar items={typeEntries.slice(0, 8).map(([ type, count ], i) => ({
130
+ label: type, value: count, color: COLORS[i % COLORS.length],
131
+ }))} />
132
+ </CardContent>
133
+ </Card>
134
+ )}
135
+
136
+ {/* Event type badges */}
137
+ <div className="flex flex-wrap gap-1.5">
138
+ {typeEntries.map(([type, count]) => (
139
+ <Badge key={type} variant="secondary" className="text-[10px]">{type}: {count}</Badge>
140
+ ))}
141
+ </div>
142
+
143
+ {/* Event list */}
144
+ <Card>
145
+ <CardContent className="pt-4">
146
+ <ScrollArea className="h-[600px]">
147
+ <div className="space-y-0">
148
+ {data.events.map(e => (
149
+ <EventRow key={e.id} e={e} />
150
+ ))}
151
+ </div>
152
+ </ScrollArea>
153
+ </CardContent>
154
+ </Card>
155
+
156
+ {data.resume?.snapshot && (
157
+ <Collapsible>
158
+ <Card>
159
+ <CollapsibleTrigger className="w-full">
160
+ <CardHeader className="flex flex-row items-center justify-between cursor-pointer hover:bg-accent/50 transition-colors">
161
+ <CardTitle className="text-sm">
162
+ Resume Snapshot ({data.resume.event_count} events, {data.resume.consumed ? "consumed" : "pending"})
163
+ </CardTitle>
164
+ <ChevronDown className="h-4 w-4 text-muted-foreground" />
165
+ </CardHeader>
166
+ </CollapsibleTrigger>
167
+ <CollapsibleContent>
168
+ <CardContent>
169
+ <ScrollArea className="max-h-96">
170
+ <pre className="text-xs text-muted-foreground whitespace-pre-wrap break-words font-mono">
171
+ {data.resume.snapshot}
172
+ </pre>
173
+ </ScrollArea>
174
+ </CardContent>
175
+ </CollapsibleContent>
176
+ </Card>
177
+ </Collapsible>
178
+ )}
179
+ </div>
180
+ );
181
+ }
@@ -0,0 +1,104 @@
1
+ @import "tailwindcss";
2
+ @import "tw-animate-css";
3
+ @import "shadcn/tailwind.css";
4
+ @import "@fontsource-variable/geist";
5
+
6
+ @custom-variant dark (&:is(.dark *));
7
+
8
+ @theme inline {
9
+ --font-heading: var(--font-sans);
10
+ --font-sans: 'Geist Variable', sans-serif;
11
+ --color-sidebar-ring: var(--sidebar-ring);
12
+ --color-sidebar-border: var(--sidebar-border);
13
+ --color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
14
+ --color-sidebar-accent: var(--sidebar-accent);
15
+ --color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
16
+ --color-sidebar-primary: var(--sidebar-primary);
17
+ --color-sidebar-foreground: var(--sidebar-foreground);
18
+ --color-sidebar: var(--sidebar);
19
+ --color-chart-5: var(--chart-5);
20
+ --color-chart-4: var(--chart-4);
21
+ --color-chart-3: var(--chart-3);
22
+ --color-chart-2: var(--chart-2);
23
+ --color-chart-1: var(--chart-1);
24
+ --color-ring: var(--ring);
25
+ --color-input: var(--input);
26
+ --color-border: var(--border);
27
+ --color-destructive: var(--destructive);
28
+ --color-accent-foreground: var(--accent-foreground);
29
+ --color-accent: var(--accent);
30
+ --color-muted-foreground: var(--muted-foreground);
31
+ --color-muted: var(--muted);
32
+ --color-secondary-foreground: var(--secondary-foreground);
33
+ --color-secondary: var(--secondary);
34
+ --color-primary-foreground: var(--primary-foreground);
35
+ --color-primary: var(--primary);
36
+ --color-popover-foreground: var(--popover-foreground);
37
+ --color-popover: var(--popover);
38
+ --color-card-foreground: var(--card-foreground);
39
+ --color-card: var(--card);
40
+ --color-foreground: var(--foreground);
41
+ --color-background: var(--background);
42
+ --radius-sm: calc(var(--radius) * 0.6);
43
+ --radius-md: calc(var(--radius) * 0.8);
44
+ --radius-lg: var(--radius);
45
+ --radius-xl: calc(var(--radius) * 1.4);
46
+ --radius-2xl: calc(var(--radius) * 1.8);
47
+ --radius-3xl: calc(var(--radius) * 2.2);
48
+ --radius-4xl: calc(var(--radius) * 2.6);
49
+ }
50
+
51
+ :root {
52
+ --background: oklch(1 0 0);
53
+ --foreground: oklch(0.145 0 0);
54
+ --card: oklch(1 0 0);
55
+ --card-foreground: oklch(0.145 0 0);
56
+ --popover: oklch(1 0 0);
57
+ --popover-foreground: oklch(0.145 0 0);
58
+ --primary: oklch(0.205 0 0);
59
+ --primary-foreground: oklch(0.985 0 0);
60
+ --secondary: oklch(0.97 0 0);
61
+ --secondary-foreground: oklch(0.205 0 0);
62
+ --muted: oklch(0.97 0 0);
63
+ --muted-foreground: oklch(0.556 0 0);
64
+ --accent: oklch(0.97 0 0);
65
+ --accent-foreground: oklch(0.205 0 0);
66
+ --destructive: oklch(0.577 0.245 27.325);
67
+ --border: oklch(0.922 0 0);
68
+ --input: oklch(0.922 0 0);
69
+ --ring: oklch(0.708 0 0);
70
+ --radius: 0.625rem;
71
+ }
72
+
73
+ .dark {
74
+ --background: oklch(0.145 0 0);
75
+ --foreground: oklch(0.985 0 0);
76
+ --card: oklch(0.205 0 0);
77
+ --card-foreground: oklch(0.985 0 0);
78
+ --popover: oklch(0.205 0 0);
79
+ --popover-foreground: oklch(0.985 0 0);
80
+ --primary: oklch(0.922 0 0);
81
+ --primary-foreground: oklch(0.205 0 0);
82
+ --secondary: oklch(0.269 0 0);
83
+ --secondary-foreground: oklch(0.985 0 0);
84
+ --muted: oklch(0.269 0 0);
85
+ --muted-foreground: oklch(0.708 0 0);
86
+ --accent: oklch(0.269 0 0);
87
+ --accent-foreground: oklch(0.985 0 0);
88
+ --destructive: oklch(0.704 0.191 22.216);
89
+ --border: oklch(1 0 0 / 10%);
90
+ --input: oklch(1 0 0 / 15%);
91
+ --ring: oklch(0.556 0 0);
92
+ }
93
+
94
+ @layer base {
95
+ * {
96
+ @apply border-border outline-ring/50;
97
+ }
98
+ body {
99
+ @apply bg-background text-foreground;
100
+ }
101
+ html {
102
+ @apply font-sans;
103
+ }
104
+ }
@@ -0,0 +1,29 @@
1
+ {
2
+ "include": ["**/*.ts", "**/*.tsx"],
3
+ "compilerOptions": {
4
+ "target": "ES2022",
5
+ "jsx": "react-jsx",
6
+ "module": "ESNext",
7
+ "baseUrl": ".",
8
+ "paths": {
9
+ "#/*": ["./src/*"],
10
+ "@/*": ["./src/*"]
11
+ },
12
+ "lib": ["ES2022", "DOM", "DOM.Iterable"],
13
+ "types": ["vite/client"],
14
+
15
+ /* Bundler mode */
16
+ "moduleResolution": "bundler",
17
+ "allowImportingTsExtensions": true,
18
+ "verbatimModuleSyntax": true,
19
+ "noEmit": true,
20
+
21
+ /* Linting */
22
+ "skipLibCheck": true,
23
+ "strict": true,
24
+ "noUnusedLocals": true,
25
+ "noUnusedParameters": true,
26
+ "noFallthroughCasesInSwitch": true,
27
+ "noUncheckedSideEffectImports": true
28
+ }
29
+ }
@@ -0,0 +1,19 @@
1
+ import { defineConfig } from 'vite'
2
+ import { devtools } from '@tanstack/devtools-vite'
3
+
4
+ import { tanstackRouter } from '@tanstack/router-plugin/vite'
5
+
6
+ import viteReact from '@vitejs/plugin-react'
7
+ import tailwindcss from '@tailwindcss/vite'
8
+
9
+ const config = defineConfig({
10
+ resolve: { tsconfigPaths: true },
11
+ plugins: [
12
+ devtools(),
13
+ tailwindcss(),
14
+ tanstackRouter({ target: 'react', autoCodeSplitting: true }),
15
+ viteReact(),
16
+ ],
17
+ })
18
+
19
+ export default config
@@ -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.80",
6
+ "version": "1.0.82",
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.80",
3
+ "version": "1.0.82",
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",
@@ -46,6 +46,7 @@
46
46
  "build",
47
47
  "hooks",
48
48
  "configs",
49
+ "insight",
49
50
  "server.bundle.mjs",
50
51
  "cli.bundle.mjs",
51
52
  "skills",