@axplusb/kepler 1.0.4 → 1.0.9
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/KEPLER-README.md +128 -2
- package/package.json +4 -4
- package/pulse/app/activity/page.tsx +1 -1
- package/pulse/app/api/import/route.ts +1 -1
- package/pulse/app/api/memory/route.ts +2 -2
- package/pulse/app/costs/page.tsx +1 -1
- package/pulse/app/export/page.tsx +3 -3
- package/pulse/app/globals.css +3 -3
- package/pulse/app/help/page.tsx +11 -11
- package/pulse/app/history/page.tsx +2 -2
- package/pulse/app/layout.tsx +2 -2
- package/pulse/app/memory/page.tsx +2 -2
- package/pulse/app/overview-client.tsx +1 -1
- package/pulse/app/page.tsx +2 -2
- package/pulse/app/plans/page.tsx +2 -2
- package/pulse/app/projects/page.tsx +1 -1
- package/pulse/app/sessions/page.tsx +1 -1
- package/pulse/app/settings/page.tsx +4 -4
- package/pulse/app/todos/page.tsx +2 -2
- package/pulse/app/tools/page.tsx +1 -1
- package/pulse/cli.js +15 -25
- package/pulse/components/layout/sidebar.tsx +2 -2
- package/pulse/components/sessions/replay/user-tool-result.tsx +1 -1
- package/pulse/lib/claude-reader.ts +1 -1
- package/pulse/lib/decode.ts +1 -1
- package/pulse/package.json +3 -3
- package/src/auth/tarang-auth.mjs +1 -1
- package/src/config/cli-args.mjs +5 -0
- package/src/context/retriever.mjs +1 -1
- package/src/context/skeleton.mjs +1 -1
- package/src/core/approval.mjs +22 -53
- package/src/core/headless.mjs +68 -24
- package/src/core/paths.mjs +1 -1
- package/src/core/project-artifacts.mjs +37 -0
- package/src/core/stream-client.mjs +6 -1
- package/src/core/tool-executor.mjs +163 -55
- package/src/skills/installer.mjs +188 -0
- package/src/skills/loader.mjs +217 -112
- package/src/terminal/main.mjs +19 -1
- package/src/terminal/repl.mjs +40 -105
- package/src/terminal/skills.mjs +54 -0
- package/src/terminal/tool-display.mjs +82 -0
- package/src/tools/bash.mjs +5 -2
- package/src/tools/project-overview.mjs +418 -0
- package/src/tools/registry.mjs +0 -16
- package/src/ui/banner.mjs +7 -14
- package/src/ui/formatter.mjs +6 -40
- package/README.md.orca +0 -82
package/KEPLER-README.md
CHANGED
|
@@ -26,9 +26,42 @@ kepler dashboard Open Kepler Pulse analytics dashboard
|
|
|
26
26
|
kepler sessions List recent local sessions
|
|
27
27
|
kepler stats Aggregate session stats (tokens, cost, tools)
|
|
28
28
|
kepler history Recent prompt history
|
|
29
|
+
kepler skills list List discovered project and global skills
|
|
30
|
+
kepler skills view NAME View a skill procedure or bundled resource
|
|
31
|
+
kepler skills install SRC Install skills from a directory or Git repository
|
|
32
|
+
kepler skills update NAME Update a locked installed skill
|
|
33
|
+
kepler skills remove NAME Remove an installed skill
|
|
29
34
|
kepler version Show version
|
|
30
35
|
```
|
|
31
36
|
|
|
37
|
+
## Agent Skills
|
|
38
|
+
|
|
39
|
+
Kepler supports portable `SKILL.md` bundles and Claude-compatible skill
|
|
40
|
+
directories. Skill metadata is included in the cached agent context; full
|
|
41
|
+
instructions and references are loaded only when the agent calls
|
|
42
|
+
`skill_view`.
|
|
43
|
+
|
|
44
|
+
Discovery precedence:
|
|
45
|
+
|
|
46
|
+
1. `<project>/.kepler/skills`
|
|
47
|
+
2. `<project>/.claude/skills`
|
|
48
|
+
3. `~/.kepler/skills`
|
|
49
|
+
4. `~/.claude/skills`
|
|
50
|
+
|
|
51
|
+
Install a local bundle or a repository containing one or more skills:
|
|
52
|
+
|
|
53
|
+
```bash
|
|
54
|
+
kepler skills install ./my-skill
|
|
55
|
+
kepler skills install github:owner/skills-repository
|
|
56
|
+
kepler skills install https://github.com/owner/skills.git
|
|
57
|
+
kepler skills install ./team-skills --project
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
Global installations are recorded in `~/.kepler/skills.lock.json`. Project
|
|
61
|
+
installations use `<project>/.kepler/skills.lock.json`. Kepler copies skill
|
|
62
|
+
files as data, rejects symlinks, and does not execute bundled scripts during
|
|
63
|
+
installation.
|
|
64
|
+
|
|
32
65
|
## REPL Commands
|
|
33
66
|
|
|
34
67
|
```
|
|
@@ -84,8 +117,101 @@ Platform default included free. Bring your own API key for unlimited.
|
|
|
84
117
|
|
|
85
118
|
| Model | Score | Cost |
|
|
86
119
|
|-------|-------|------|
|
|
87
|
-
| DeepSeek V4 Flash |
|
|
88
|
-
| DeepSeek V4
|
|
120
|
+
| DeepSeek V4 Flash + stagnation | 47.3% (142/300) | $0.046/instance |
|
|
121
|
+
| DeepSeek V4 Flash | 39.7% (119/300) | $0.046/instance |
|
|
122
|
+
| DeepSeek V4 Flash (baseline) | 30.7% (92/300) | $0.03/instance |
|
|
123
|
+
| DeepSeek V4 Pro | 50% (14/28 sample) | $0.48/instance |
|
|
124
|
+
|
|
125
|
+
### Running SWE-bench
|
|
126
|
+
|
|
127
|
+
Benchmarks run on an Azure VM (`swebench-eval-vm`, D8s_v3) with a local backend + CLI.
|
|
128
|
+
|
|
129
|
+
**Setup (one-time):**
|
|
130
|
+
|
|
131
|
+
```bash
|
|
132
|
+
# Deploy backend + CLI + harness to VM
|
|
133
|
+
./benchmark/vm-setup.sh
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
**Run benchmark:**
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
# SSH into VM
|
|
140
|
+
ssh azureuser@20.9.77.9
|
|
141
|
+
|
|
142
|
+
# Start backend
|
|
143
|
+
source ~/.tarang-benchmark.env
|
|
144
|
+
cd ~/tarang-backend
|
|
145
|
+
source ~/backend-env/bin/activate
|
|
146
|
+
uvicorn app.main:app --port 8000 &
|
|
147
|
+
|
|
148
|
+
# Run all 300 instances (3 parallel workers, skip already-done)
|
|
149
|
+
source ~/swebench-env/bin/activate
|
|
150
|
+
cd ~/tarang-npm
|
|
151
|
+
python3 benchmark/swe-bench/harness.py \
|
|
152
|
+
--dataset lite \
|
|
153
|
+
--model deepseek/deepseek-v4-flash \
|
|
154
|
+
--parallel 3 \
|
|
155
|
+
--skip-done \
|
|
156
|
+
--timeout 600
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
**Flags:**
|
|
160
|
+
|
|
161
|
+
| Flag | Description |
|
|
162
|
+
|------|-------------|
|
|
163
|
+
| `--dataset lite\|verified\|full` | SWE-bench split (default: lite, 300 instances) |
|
|
164
|
+
| `--model <id>` | OpenRouter model ID |
|
|
165
|
+
| `--parallel <n>` | Number of parallel workers (default: 1) |
|
|
166
|
+
| `--skip-done` | Skip instances already in the output file |
|
|
167
|
+
| `--timeout <s>` | Per-instance timeout in seconds (default: 600) |
|
|
168
|
+
| `--limit <n>` | Only run first N instances |
|
|
169
|
+
| `--instance <id>` | Run a single instance by ID |
|
|
170
|
+
| `--gen-only` | Generate patches only (skip test evaluation) |
|
|
171
|
+
| `--output <path>` | Custom output file path |
|
|
172
|
+
|
|
173
|
+
**Results** are saved incrementally to `benchmark/results/<model>_<dataset>.json` — no data loss on kill.
|
|
174
|
+
|
|
175
|
+
**Convenience scripts on VM:**
|
|
176
|
+
|
|
177
|
+
```bash
|
|
178
|
+
# Watchdog (auto-restarts on crash)
|
|
179
|
+
tmux new-session -d -s bench "bash /tmp/start-parallel.sh"
|
|
180
|
+
|
|
181
|
+
# Check progress
|
|
182
|
+
tmux capture-pane -t bench -p | tail -20
|
|
183
|
+
|
|
184
|
+
# Or use the pre-built start script
|
|
185
|
+
./start-benchmark.sh deepseek/deepseek-v4-flash
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Terminal-Bench
|
|
189
|
+
|
|
190
|
+
Use the benchmark-only adapter under `benchmark/terminal-bench/` when you want to
|
|
191
|
+
evaluate Kepler in Terminal-Bench without touching the production CLI.
|
|
192
|
+
|
|
193
|
+
It runs with:
|
|
194
|
+
|
|
195
|
+
- a separate backend venv
|
|
196
|
+
- a separate backend port (`8001` by default)
|
|
197
|
+
- a custom `--agent-import-path kepler_agent:KeplerAgent`
|
|
198
|
+
- per-trial workspace isolation so memory does not leak across tasks
|
|
199
|
+
|
|
200
|
+
Reusable entrypoints:
|
|
201
|
+
|
|
202
|
+
```bash
|
|
203
|
+
benchmark/terminal-bench/setup-kepler-backend.sh
|
|
204
|
+
benchmark/terminal-bench/run-kepler-backend.sh
|
|
205
|
+
benchmark/terminal-bench/run-kepler-benchmark.sh --task-id hello-world
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Environment variables that can be overridden in another machine or CI setup:
|
|
209
|
+
|
|
210
|
+
`BACKEND_DIR`, `FRAMEWORK_DIR`, `VENV_DIR`, `TB_VENV`, `TARANG_NPM_DIR`,
|
|
211
|
+
`ENV_FILE`, `BACKEND_URL`, `PORT`, `MODEL`, `DATASET`, `CONCURRENCY`.
|
|
212
|
+
|
|
213
|
+
Create a local `.tarang-tbench.env` with the benchmark credentials and backend
|
|
214
|
+
auth token, then point `ENV_FILE` at it if needed.
|
|
89
215
|
|
|
90
216
|
## Links
|
|
91
217
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@axplusb/kepler",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Kepler — AI coding agent
|
|
3
|
+
"version": "1.0.9",
|
|
4
|
+
"description": "Kepler — AI coding agent with operating brief, preflight planning, and sub-agents. SWE-bench Lite evaluated.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
7
7
|
"kepler": "src/terminal/main.mjs"
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"readme": "KEPLER-README.md",
|
|
15
15
|
"scripts": {
|
|
16
16
|
"start": "node src/terminal/main.mjs",
|
|
17
|
-
"test": "node test/test-sse-client.mjs && node test/test-tool-executor.mjs && node test/test-callback.mjs && node test/test-formatter.mjs && node test/test-terminal-rendering.mjs && node test/test-slash-commands.mjs && node test/test-approval.mjs && node test/test-session-manager.mjs && node test/test-safety.mjs && node test/test-jsonl-writer.mjs && node test/test-analytics.mjs && node test/test-stagnation.mjs"
|
|
17
|
+
"test": "node test/test-sse-client.mjs && node test/test-tool-executor.mjs && node test/test-project-artifacts.mjs && node test/test-skills.mjs && node test/test-callback.mjs && node test/test-formatter.mjs && node test/test-terminal-rendering.mjs && node test/test-slash-commands.mjs && node test/test-approval.mjs && node test/test-session-manager.mjs && node test/test-safety.mjs && node test/test-jsonl-writer.mjs && node test/test-analytics.mjs && node test/test-stagnation.mjs"
|
|
18
18
|
},
|
|
19
19
|
"engines": {
|
|
20
20
|
"node": ">=18.0.0"
|
|
@@ -38,7 +38,7 @@
|
|
|
38
38
|
"homepage": "https://codekepler.ai",
|
|
39
39
|
"repository": {
|
|
40
40
|
"type": "git",
|
|
41
|
-
"url": "git+https://github.com/raviakasapu/codekepler-
|
|
41
|
+
"url": "git+https://github.com/raviakasapu/codekepler-npm.git"
|
|
42
42
|
},
|
|
43
43
|
"dependencies": {}
|
|
44
44
|
}
|
|
@@ -175,7 +175,7 @@ export default function ActivityPage() {
|
|
|
175
175
|
<CalendarDays className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
176
176
|
Day of Week
|
|
177
177
|
</CardTitle>
|
|
178
|
-
<CardDescription>Which days you use
|
|
178
|
+
<CardDescription>Which days you use Kepler most</CardDescription>
|
|
179
179
|
</CardHeader>
|
|
180
180
|
<CardContent className="flex flex-1 flex-col justify-between pt-3 pb-0">
|
|
181
181
|
<DayOfWeekChart data={data.dow_counts} />
|
|
@@ -24,7 +24,7 @@ export async function POST(req: Request) {
|
|
|
24
24
|
}
|
|
25
25
|
|
|
26
26
|
// Note: actual file writing is intentionally not implemented here
|
|
27
|
-
// to prevent accidental corruption of ~/.
|
|
27
|
+
// to prevent accidental corruption of ~/.kepler/ live data.
|
|
28
28
|
// The import feature shows a diff preview only.
|
|
29
29
|
|
|
30
30
|
return NextResponse.json(diff)
|
|
@@ -6,7 +6,7 @@ import { readMemories } from '@/lib/claude-reader'
|
|
|
6
6
|
|
|
7
7
|
export const dynamic = 'force-dynamic'
|
|
8
8
|
|
|
9
|
-
const CLAUDE_DIR = path.join(os.homedir(), '.
|
|
9
|
+
const CLAUDE_DIR = path.join(os.homedir(), '.kepler')
|
|
10
10
|
|
|
11
11
|
export async function GET() {
|
|
12
12
|
const memories = await readMemories()
|
|
@@ -37,7 +37,7 @@ export async function PATCH(req: Request) {
|
|
|
37
37
|
|
|
38
38
|
const filePath = path.join(CLAUDE_DIR, 'projects', projectSlug, 'memory', file)
|
|
39
39
|
|
|
40
|
-
// Ensure the resolved path stays within ~/.
|
|
40
|
+
// Ensure the resolved path stays within ~/.kepler/projects/
|
|
41
41
|
const allowedRoot = path.join(CLAUDE_DIR, 'projects')
|
|
42
42
|
if (!filePath.startsWith(allowedRoot + path.sep)) {
|
|
43
43
|
return NextResponse.json({ error: 'Path outside allowed directory' }, { status: 403 })
|
package/pulse/app/costs/page.tsx
CHANGED
|
@@ -23,7 +23,7 @@ export default function CostsPage() {
|
|
|
23
23
|
|
|
24
24
|
return (
|
|
25
25
|
<div className="flex flex-col min-h-screen">
|
|
26
|
-
<TopBar title="Costs" subtitle="Estimated spend from ~/.
|
|
26
|
+
<TopBar title="Costs" subtitle="Estimated spend from ~/.kepler/" />
|
|
27
27
|
<div className="p-6 space-y-6">
|
|
28
28
|
|
|
29
29
|
{error && (
|
|
@@ -146,7 +146,7 @@ export default function ExportPage() {
|
|
|
146
146
|
<div className="flex flex-col min-h-screen">
|
|
147
147
|
<TopBar
|
|
148
148
|
title="Export & import"
|
|
149
|
-
subtitle="Download a portable backup of ~/.
|
|
149
|
+
subtitle="Download a portable backup of ~/.kepler/ analytics or merge data from another machine"
|
|
150
150
|
/>
|
|
151
151
|
|
|
152
152
|
<div className="p-6 space-y-6 flex-1">
|
|
@@ -239,7 +239,7 @@ export default function ExportPage() {
|
|
|
239
239
|
</CardTitle>
|
|
240
240
|
</CardHeader>
|
|
241
241
|
<CardContent className="pt-0">
|
|
242
|
-
<p className="text-xs text-muted-foreground">From ~/.
|
|
242
|
+
<p className="text-xs text-muted-foreground">From ~/.kepler/ when available</p>
|
|
243
243
|
</CardContent>
|
|
244
244
|
</Card>
|
|
245
245
|
</>
|
|
@@ -436,7 +436,7 @@ export default function ExportPage() {
|
|
|
436
436
|
<Alert className="border-amber-500/40 bg-amber-500/5">
|
|
437
437
|
<AlertTriangle className="h-4 w-4 text-amber-600" />
|
|
438
438
|
<AlertDescription className="text-xs text-amber-800 dark:text-amber-200/90">
|
|
439
|
-
Writing merged data to ~/.
|
|
439
|
+
Writing merged data to ~/.kepler/ is not implemented in this build — this is a preview only.
|
|
440
440
|
</AlertDescription>
|
|
441
441
|
</Alert>
|
|
442
442
|
<div className="max-h-36 overflow-auto rounded-md border border-border/60 bg-background/50 space-y-1 p-2 font-mono text-[11px] text-muted-foreground">
|
package/pulse/app/globals.css
CHANGED
|
@@ -86,9 +86,9 @@
|
|
|
86
86
|
--radius-lg: var(--radius);
|
|
87
87
|
--radius-xl: calc(var(--radius) + 4px);
|
|
88
88
|
|
|
89
|
-
/*
|
|
90
|
-
--color-
|
|
91
|
-
--color-
|
|
89
|
+
/* Kepler accent colors */
|
|
90
|
+
--color-kepler-cyan: #06b6d4;
|
|
91
|
+
--color-kepler-cyan-light: #22d3ee;
|
|
92
92
|
--color-terminal-cyan: #06b6d4;
|
|
93
93
|
--color-terminal-green: #34d399;
|
|
94
94
|
--color-terminal-amber: #f59e0b;
|
package/pulse/app/help/page.tsx
CHANGED
|
@@ -2,14 +2,14 @@ import { TopBar } from '@/components/layout/top-bar'
|
|
|
2
2
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
3
3
|
|
|
4
4
|
const CLI_COMMANDS = [
|
|
5
|
-
{ cmd: '
|
|
6
|
-
{ cmd: '
|
|
7
|
-
{ cmd: '
|
|
8
|
-
{ cmd: '
|
|
9
|
-
{ cmd: '
|
|
10
|
-
{ cmd: '
|
|
11
|
-
{ cmd: '
|
|
12
|
-
{ cmd: '
|
|
5
|
+
{ cmd: 'kepler', desc: 'Start interactive REPL' },
|
|
6
|
+
{ cmd: 'kepler "instruction"', desc: 'Run a single instruction and exit' },
|
|
7
|
+
{ cmd: 'kepler dashboard', desc: 'Open Kepler Pulse analytics dashboard' },
|
|
8
|
+
{ cmd: 'kepler sessions', desc: 'List recent local sessions' },
|
|
9
|
+
{ cmd: 'kepler stats', desc: 'Show aggregate local session stats' },
|
|
10
|
+
{ cmd: 'kepler history', desc: 'Show recent prompt history' },
|
|
11
|
+
{ cmd: 'kepler login', desc: 'Sign in via browser' },
|
|
12
|
+
{ cmd: 'kepler version', desc: 'Show version' },
|
|
13
13
|
]
|
|
14
14
|
|
|
15
15
|
const REPL_COMMANDS = [
|
|
@@ -38,7 +38,7 @@ const ENV_VARS = [
|
|
|
38
38
|
{ name: 'TARANG_ENV', desc: 'Set backend environment (local, development, production)' },
|
|
39
39
|
{ name: 'ANTHROPIC_API_KEY', desc: 'Direct Anthropic API key for local mode' },
|
|
40
40
|
{ name: 'OPENROUTER_API_KEY', desc: 'OpenRouter API key' },
|
|
41
|
-
{ name: '
|
|
41
|
+
{ name: 'KEPLER_CONFIG_DIR', desc: 'Override config directory (default: ~/.kepler)' },
|
|
42
42
|
{ name: 'TARANG_BACKEND_URL', desc: 'Override backend URL explicitly' },
|
|
43
43
|
]
|
|
44
44
|
|
|
@@ -46,7 +46,7 @@ const FEATURES = [
|
|
|
46
46
|
{ title: 'Multi-Agent Orchestration', desc: 'Primary agent spawns explore, plan, and review sub-agents for complex tasks.' },
|
|
47
47
|
{ title: 'Smart Tool Routing', desc: 'Write operations routed to CLI for local execution. Read tools run server-side.' },
|
|
48
48
|
{ title: 'Safety Guardrails', desc: 'Destructive commands (rm, delete) always require approval. Path validation on all file operations.' },
|
|
49
|
-
{ title: 'Local JSONL Analytics', desc: 'Every session is recorded to ~/.
|
|
49
|
+
{ title: 'Local JSONL Analytics', desc: 'Every session is recorded to ~/.kepler/ for offline analytics via Kepler Pulse.' },
|
|
50
50
|
{ title: 'Conversation History', desc: 'Multi-turn conversations within a REPL session. Context preserved across turns.' },
|
|
51
51
|
{ title: 'BM25 Code Index', desc: 'Project files indexed on startup for fast semantic code search.' },
|
|
52
52
|
]
|
|
@@ -67,7 +67,7 @@ function CommandTable({ commands }: { commands: { cmd: string; desc: string }[]
|
|
|
67
67
|
export default function HelpPage() {
|
|
68
68
|
return (
|
|
69
69
|
<div className="flex flex-col min-h-screen">
|
|
70
|
-
<TopBar title="Help" subtitle="
|
|
70
|
+
<TopBar title="Help" subtitle="Kepler CLI commands, shortcuts, and configuration" />
|
|
71
71
|
<div className="px-6 py-6 space-y-6">
|
|
72
72
|
|
|
73
73
|
<Card>
|
|
@@ -55,7 +55,7 @@ export default function HistoryPage() {
|
|
|
55
55
|
|
|
56
56
|
return (
|
|
57
57
|
<div className="flex flex-col min-h-screen">
|
|
58
|
-
<TopBar title="History" subtitle="~/.
|
|
58
|
+
<TopBar title="History" subtitle="~/.kepler/history.jsonl" />
|
|
59
59
|
<div className="p-4 md:p-6 space-y-4">
|
|
60
60
|
|
|
61
61
|
{error && (
|
|
@@ -92,7 +92,7 @@ export default function HistoryPage() {
|
|
|
92
92
|
{pageEntries.length === 0 ? (
|
|
93
93
|
<div className="text-center py-16 text-muted-foreground text-sm">
|
|
94
94
|
{(data.history?.length ?? 0) === 0
|
|
95
|
-
? 'No history found in ~/.
|
|
95
|
+
? 'No history found in ~/.kepler/history.jsonl'
|
|
96
96
|
: 'No entries match your search.'}
|
|
97
97
|
</div>
|
|
98
98
|
) : (
|
package/pulse/app/layout.tsx
CHANGED
|
@@ -20,8 +20,8 @@ const pressStart2P = Press_Start_2P({
|
|
|
20
20
|
})
|
|
21
21
|
|
|
22
22
|
export const metadata: Metadata = {
|
|
23
|
-
title: '
|
|
24
|
-
description: 'Real-time analytics for your
|
|
23
|
+
title: 'Kepler Pulse',
|
|
24
|
+
description: 'Real-time analytics for your Kepler AI agent sessions',
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export default function RootLayout({
|
|
@@ -260,7 +260,7 @@ export default function MemoryPage() {
|
|
|
260
260
|
|
|
261
261
|
return (
|
|
262
262
|
<div className="flex flex-col min-h-screen">
|
|
263
|
-
<TopBar title="
|
|
263
|
+
<TopBar title="kepler-pulse · memory" subtitle="~/.kepler/projects/*/memory/" />
|
|
264
264
|
<div className="p-4 md:p-6 space-y-5">
|
|
265
265
|
|
|
266
266
|
{error && <p className="text-[#f87171] text-sm font-mono">Error loading memories.</p>}
|
|
@@ -338,7 +338,7 @@ export default function MemoryPage() {
|
|
|
338
338
|
<p className="text-3xl mb-3">🧠</p>
|
|
339
339
|
<p className="text-muted-foreground/60 text-sm font-mono">
|
|
340
340
|
{memories.length === 0
|
|
341
|
-
? 'No memory files found in ~/.
|
|
341
|
+
? 'No memory files found in ~/.kepler/projects/*/memory/'
|
|
342
342
|
: 'No memories match your filter.'}
|
|
343
343
|
</p>
|
|
344
344
|
</div>
|
|
@@ -381,7 +381,7 @@ export function OverviewClient() {
|
|
|
381
381
|
<Card>
|
|
382
382
|
<CardHeader>
|
|
383
383
|
<CardTitle>Recent Sessions</CardTitle>
|
|
384
|
-
<CardDescription>Your latest
|
|
384
|
+
<CardDescription>Your latest Kepler sessions</CardDescription>
|
|
385
385
|
</CardHeader>
|
|
386
386
|
<CardContent>
|
|
387
387
|
<OverviewConversationTable sessions={sessions} />
|
package/pulse/app/page.tsx
CHANGED
|
@@ -5,8 +5,8 @@ export default function OverviewPage() {
|
|
|
5
5
|
return (
|
|
6
6
|
<div className="flex flex-col min-h-screen">
|
|
7
7
|
<TopBar
|
|
8
|
-
title="
|
|
9
|
-
subtitle="Real-time analytics for your
|
|
8
|
+
title="Kepler Pulse"
|
|
9
|
+
subtitle="Real-time analytics for your Kepler AI agent sessions"
|
|
10
10
|
/>
|
|
11
11
|
<OverviewClient />
|
|
12
12
|
</div>
|
package/pulse/app/plans/page.tsx
CHANGED
|
@@ -251,7 +251,7 @@ export default function PlansPage() {
|
|
|
251
251
|
|
|
252
252
|
return (
|
|
253
253
|
<div className="flex flex-col min-h-screen">
|
|
254
|
-
<TopBar title="
|
|
254
|
+
<TopBar title="kepler-pulse · plans" subtitle="~/.kepler/plans/" />
|
|
255
255
|
<div className="p-4 md:p-6 space-y-5">
|
|
256
256
|
|
|
257
257
|
{error && <p className="text-[#f87171] text-sm font-mono">Error: {String(error)}</p>}
|
|
@@ -289,7 +289,7 @@ export default function PlansPage() {
|
|
|
289
289
|
<p className="text-[#d97706] text-2xl mb-3">📋</p>
|
|
290
290
|
<p className="text-muted-foreground/60 text-sm font-mono">
|
|
291
291
|
{plans.length === 0
|
|
292
|
-
? 'No plans found in ~/.
|
|
292
|
+
? 'No plans found in ~/.kepler/plans/'
|
|
293
293
|
: 'No plans match your search.'}
|
|
294
294
|
</p>
|
|
295
295
|
</div>
|
|
@@ -101,7 +101,7 @@ export default function ProjectsPage() {
|
|
|
101
101
|
|
|
102
102
|
{!isLoading && sorted.length === 0 && (
|
|
103
103
|
<div className="text-center py-16 text-muted-foreground text-sm">
|
|
104
|
-
{search ? 'No projects match your search.' : 'No projects found in ~/.
|
|
104
|
+
{search ? 'No projects match your search.' : 'No projects found in ~/.kepler/'}
|
|
105
105
|
</div>
|
|
106
106
|
)}
|
|
107
107
|
</div>
|
|
@@ -18,7 +18,7 @@ export default function SessionsPage() {
|
|
|
18
18
|
return (
|
|
19
19
|
<div className="flex flex-col min-h-screen">
|
|
20
20
|
<TopBar
|
|
21
|
-
title="
|
|
21
|
+
title="Kepler Pulse · Sessions"
|
|
22
22
|
subtitle={data ? `${data.total} total sessions` : 'loading...'}
|
|
23
23
|
/>
|
|
24
24
|
<div className="p-6">
|
|
@@ -83,7 +83,7 @@ export default function SettingsPage() {
|
|
|
83
83
|
|
|
84
84
|
return (
|
|
85
85
|
<div className="flex flex-col min-h-screen">
|
|
86
|
-
<TopBar title="
|
|
86
|
+
<TopBar title="kepler-pulse · settings" subtitle="~/.kepler/settings.json" />
|
|
87
87
|
<div className="p-4 md:p-6 space-y-6">
|
|
88
88
|
{error && <p className="text-[#f87171] text-sm font-mono">Error: {String(error)}</p>}
|
|
89
89
|
{isLoading && (
|
|
@@ -100,13 +100,13 @@ export default function SettingsPage() {
|
|
|
100
100
|
<span className="text-primary text-2xl font-mono font-bold">
|
|
101
101
|
{formatBytes(data.storageBytes)}
|
|
102
102
|
</span>
|
|
103
|
-
<span className="text-muted-foreground text-sm font-mono">used by ~/.
|
|
103
|
+
<span className="text-muted-foreground text-sm font-mono">used by ~/.kepler/</span>
|
|
104
104
|
</div>
|
|
105
105
|
</Section>
|
|
106
106
|
|
|
107
107
|
<Section title="Settings">
|
|
108
108
|
{Object.keys(data.settings).length === 0 ? (
|
|
109
|
-
<p className="text-muted-foreground/60 text-sm font-mono">No settings found in ~/.
|
|
109
|
+
<p className="text-muted-foreground/60 text-sm font-mono">No settings found in ~/.kepler/settings.json</p>
|
|
110
110
|
) : (
|
|
111
111
|
<div className="font-mono text-sm leading-relaxed overflow-x-auto">
|
|
112
112
|
<JsonValue value={data.settings} />
|
|
@@ -139,7 +139,7 @@ export default function SettingsPage() {
|
|
|
139
139
|
|
|
140
140
|
<Section title={`Skills (${data.skills.length})`}>
|
|
141
141
|
{data.skills.length === 0 ? (
|
|
142
|
-
<p className="text-muted-foreground/60 text-sm font-mono">No skills found in ~/.
|
|
142
|
+
<p className="text-muted-foreground/60 text-sm font-mono">No skills found in ~/.kepler/skills/</p>
|
|
143
143
|
) : (
|
|
144
144
|
<div className="grid gap-2">
|
|
145
145
|
{data.skills.map(skill => (
|
package/pulse/app/todos/page.tsx
CHANGED
|
@@ -126,7 +126,7 @@ export default function TodosPage() {
|
|
|
126
126
|
|
|
127
127
|
return (
|
|
128
128
|
<div className="flex flex-col min-h-screen">
|
|
129
|
-
<TopBar title="Todos" subtitle="~/.
|
|
129
|
+
<TopBar title="Todos" subtitle="~/.kepler/todos/" />
|
|
130
130
|
<div className="p-4 md:p-6 space-y-5">
|
|
131
131
|
|
|
132
132
|
{error && (
|
|
@@ -192,7 +192,7 @@ export default function TodosPage() {
|
|
|
192
192
|
<CircleCheck className="w-8 h-8 mx-auto mb-3 text-muted-foreground/40" />
|
|
193
193
|
<p className="text-muted-foreground text-sm">
|
|
194
194
|
{allItems.length === 0
|
|
195
|
-
? 'No todos found in ~/.
|
|
195
|
+
? 'No todos found in ~/.kepler/todos/'
|
|
196
196
|
: 'No todos match your filter.'}
|
|
197
197
|
</p>
|
|
198
198
|
</div>
|
package/pulse/app/tools/page.tsx
CHANGED
|
@@ -201,7 +201,7 @@ export default function ToolsPage() {
|
|
|
201
201
|
{data.versions.length > 0 && (
|
|
202
202
|
<Card>
|
|
203
203
|
<CardHeader>
|
|
204
|
-
<CardTitle>
|
|
204
|
+
<CardTitle>Kepler Version History</CardTitle>
|
|
205
205
|
<CardDescription>Versions seen across your sessions</CardDescription>
|
|
206
206
|
</CardHeader>
|
|
207
207
|
<CardContent>
|
package/pulse/cli.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
* Launches a Next.js dev server from ~/.
|
|
3
|
+
* Kepler Pulse — local analytics dashboard launcher.
|
|
4
|
+
* Launches a Next.js dev server from ~/.kepler-pulse/ cache directory.
|
|
5
5
|
*/
|
|
6
6
|
|
|
7
7
|
const { spawn, exec } = require('child_process')
|
|
@@ -11,9 +11,9 @@ const path = require('path')
|
|
|
11
11
|
const fs = require('fs')
|
|
12
12
|
|
|
13
13
|
const PKG_DIR = __dirname
|
|
14
|
-
const CACHE_DIR = path.join(os.homedir(), '.
|
|
14
|
+
const CACHE_DIR = path.join(os.homedir(), '.kepler-pulse')
|
|
15
15
|
|
|
16
|
-
// ANSI helpers —
|
|
16
|
+
// ANSI helpers — Kepler cyan palette
|
|
17
17
|
const C = '\x1b[36m' // cyan
|
|
18
18
|
const C2 = '\x1b[96m' // bright cyan
|
|
19
19
|
const DIM = '\x1b[2m'
|
|
@@ -22,20 +22,10 @@ const R = '\x1b[0m'
|
|
|
22
22
|
const G = '\x1b[32m'
|
|
23
23
|
|
|
24
24
|
function printBanner() {
|
|
25
|
-
const art = [
|
|
26
|
-
`${C}${B} ██████╗ ██████╗ ██████╗ █████╗ ██████╗ ██╗ ██╗██╗ ███████╗███████╗${R}`,
|
|
27
|
-
`${C}${B}██╔═══██╗██╔══██╗██╔════╝██╔══██╗ ██╔══██╗██║ ██║██║ ██╔════╝██╔════╝${R}`,
|
|
28
|
-
`${C2}${B}██║ ██║██████╔╝██║ ███████║ ██████╔╝██║ ██║██║ ███████╗█████╗ ${R}`,
|
|
29
|
-
`${C2}${B}██║ ██║██╔══██╗██║ ██╔══██║ ██╔═══╝ ██║ ██║██║ ╚════██║██╔══╝ ${R}`,
|
|
30
|
-
`${C}${B}╚██████╔╝██║ ██║╚██████╗██║ ██║ ██║ ╚██████╔╝███████╗███████║███████╗${R}`,
|
|
31
|
-
`${C}${B} ╚═════╝ ╚═╝ ╚═╝ ╚═════╝╚═╝ ╚═╝ ╚═╝ ╚═════╝ ╚══════╝╚══════╝╚══════╝${R}`,
|
|
32
|
-
]
|
|
33
|
-
|
|
34
|
-
console.log()
|
|
35
|
-
art.forEach((line) => console.log(' ' + line))
|
|
36
25
|
console.log()
|
|
37
|
-
const configDir = process.env.
|
|
38
|
-
console.log(` ${B}${C}
|
|
26
|
+
const configDir = process.env.KEPLER_CONFIG_DIR ?? path.join(os.homedir(), '.kepler')
|
|
27
|
+
console.log(` ${B}${C}K · E · P · L · E · R${R}`)
|
|
28
|
+
console.log(` ${B}${C2}Kepler Pulse${R} ${DIM}real-time agent analytics${R}`)
|
|
39
29
|
console.log()
|
|
40
30
|
console.log(` ${DIM}Data dir:${R} ${C2}${configDir}${R}`)
|
|
41
31
|
console.log()
|
|
@@ -58,7 +48,7 @@ function openBrowser(url) {
|
|
|
58
48
|
exec(cmd)
|
|
59
49
|
}
|
|
60
50
|
|
|
61
|
-
// Source dirs/files to mirror into ~/.
|
|
51
|
+
// Source dirs/files to mirror into ~/.kepler-pulse/
|
|
62
52
|
const SRC_DIRS = ['app', 'components', 'lib', 'types', 'public']
|
|
63
53
|
const SRC_FILES = ['next.config.ts', 'tsconfig.json', 'postcss.config.mjs', 'components.json']
|
|
64
54
|
|
|
@@ -78,7 +68,7 @@ function syncSource(pkg) {
|
|
|
78
68
|
}
|
|
79
69
|
// Write a minimal package.json with only runtime dependencies
|
|
80
70
|
fs.writeFileSync(path.join(CACHE_DIR, 'package.json'), JSON.stringify({
|
|
81
|
-
name: '
|
|
71
|
+
name: 'kepler-pulse-runtime',
|
|
82
72
|
version: pkg.version,
|
|
83
73
|
dependencies: pkg.dependencies,
|
|
84
74
|
}, null, 2))
|
|
@@ -89,8 +79,8 @@ async function main() {
|
|
|
89
79
|
|
|
90
80
|
const pkg = require(path.join(PKG_DIR, 'package.json'))
|
|
91
81
|
|
|
92
|
-
// Check whether ~/.
|
|
93
|
-
const versionFile = path.join(CACHE_DIR, '.
|
|
82
|
+
// Check whether ~/.kepler-pulse/ is up-to-date for this version
|
|
83
|
+
const versionFile = path.join(CACHE_DIR, '.kepler-pulse-version')
|
|
94
84
|
const cachedVersion = fs.existsSync(versionFile)
|
|
95
85
|
? fs.readFileSync(versionFile, 'utf8').trim()
|
|
96
86
|
: null
|
|
@@ -120,13 +110,13 @@ async function main() {
|
|
|
120
110
|
const port = await findFreePort(3000)
|
|
121
111
|
const url = `http://localhost:${port}`
|
|
122
112
|
|
|
123
|
-
// Pass
|
|
124
|
-
const
|
|
113
|
+
// Pass KEPLER_CONFIG_DIR to the Next.js process so it reads from ~/.kepler/
|
|
114
|
+
const keplerDir = process.env.KEPLER_CONFIG_DIR ?? path.join(os.homedir(), '.kepler')
|
|
125
115
|
const env = {
|
|
126
116
|
...process.env,
|
|
127
117
|
PORT: String(port),
|
|
128
|
-
|
|
129
|
-
CLAUDE_CONFIG_DIR:
|
|
118
|
+
KEPLER_CONFIG_DIR: keplerDir,
|
|
119
|
+
CLAUDE_CONFIG_DIR: keplerDir,
|
|
130
120
|
}
|
|
131
121
|
|
|
132
122
|
console.log(` ${DIM}Starting server on${R} ${C2}${B}${url}${R}\n`)
|
|
@@ -101,7 +101,7 @@ function SidebarContents({
|
|
|
101
101
|
<span
|
|
102
102
|
className="dark:[text-shadow:0_1px_0_#0e4f5c,0_2px_0_#083344,0_3px_6px_rgba(0,0,0,0.35)] [text-shadow:0_1px_0_rgba(255,255,255,0.4)]"
|
|
103
103
|
>
|
|
104
|
-
|
|
104
|
+
Kepler Pulse
|
|
105
105
|
</span>
|
|
106
106
|
</span>
|
|
107
107
|
)}
|
|
@@ -140,7 +140,7 @@ function SidebarContents({
|
|
|
140
140
|
)}>
|
|
141
141
|
{!collapsed && (
|
|
142
142
|
<span className="text-xs text-sidebar-foreground/50">
|
|
143
|
-
|
|
143
|
+
Kepler Pulse
|
|
144
144
|
</span>
|
|
145
145
|
)}
|
|
146
146
|
<button
|
|
@@ -19,7 +19,7 @@ function restAfterAction(s: string, idx: number, needleLen: number): string | un
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
/**
|
|
22
|
-
* Best-effort parse of common
|
|
22
|
+
* Best-effort parse of common Kepler / tool sandbox result strings.
|
|
23
23
|
*/
|
|
24
24
|
export function parseToolResultMessage(raw: string): ParsedToolResult {
|
|
25
25
|
const s = raw.trim()
|
|
@@ -32,7 +32,7 @@ export async function resolveProjectPath(slug: string): Promise<string> {
|
|
|
32
32
|
return slugToPath(slug)
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
const CLAUDE_DIR = process.env.
|
|
35
|
+
const CLAUDE_DIR = process.env.KEPLER_CONFIG_DIR ?? process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), '.kepler')
|
|
36
36
|
|
|
37
37
|
export function claudePath(...segments: string[]): string {
|
|
38
38
|
return path.join(CLAUDE_DIR, ...segments)
|
package/pulse/lib/decode.ts
CHANGED
|
@@ -10,7 +10,7 @@ export function slugToPath(slug: string): string {
|
|
|
10
10
|
}
|
|
11
11
|
|
|
12
12
|
/**
|
|
13
|
-
* Encode a filesystem path to the slug format used by
|
|
13
|
+
* Encode a filesystem path to the slug format used by Kepler.
|
|
14
14
|
*/
|
|
15
15
|
export function pathToSlug(path: string): string {
|
|
16
16
|
return path.replace(/\//g, '-')
|