@axplusb/kepler 1.0.0 → 1.0.1
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/package.json +1 -1
- package/src/auth/tarang-auth.mjs +9 -9
- package/src/config/env.mjs +2 -2
- package/src/config/settings.mjs +2 -2
- package/src/core/checkpoints.mjs +1 -1
- package/src/core/headless.mjs +4 -4
- package/src/core/jsonl-writer.mjs +7 -7
- package/src/core/local-agent.mjs +1 -1
- package/src/core/local-store.mjs +6 -6
- package/src/core/paths.mjs +24 -21
- package/src/core/session-manager.mjs +4 -4
- package/src/core/settings-sync.mjs +4 -4
- package/src/core/stream-client.mjs +1 -1
- package/src/index.mjs +12 -12
- package/src/terminal/analytics.mjs +11 -11
- package/src/terminal/main.mjs +16 -16
- package/src/terminal/repl.mjs +12 -12
- package/src/tools/agent.mjs +2 -2
- package/src/ui/banner.mjs +15 -15
- package/src/ui/slash-commands.mjs +4 -4
package/package.json
CHANGED
package/src/auth/tarang-auth.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
* Reads/writes ~/.
|
|
2
|
+
* Kepler Authentication — GitHub OAuth + config management.
|
|
3
|
+
* Reads/writes ~/.kepler/config.json.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import * as fs from 'node:fs';
|
|
@@ -10,7 +10,7 @@ import * as http from 'node:http';
|
|
|
10
10
|
import { getLoginSuccessHTML } from '../ui/banner.mjs';
|
|
11
11
|
import { resolveBackendUrl } from '../core/backend-url.mjs';
|
|
12
12
|
|
|
13
|
-
const CONFIG_DIR = path.join(os.homedir(), '.
|
|
13
|
+
const CONFIG_DIR = process.env.KEPLER_HOME || process.env.ORCA_HOME || path.join(os.homedir(), '.kepler');
|
|
14
14
|
const CONFIG_PATH = path.join(CONFIG_DIR, 'config.json');
|
|
15
15
|
|
|
16
16
|
export class TarangAuth {
|
|
@@ -18,7 +18,7 @@ export class TarangAuth {
|
|
|
18
18
|
this._config = null;
|
|
19
19
|
}
|
|
20
20
|
|
|
21
|
-
/** Ensure ~/.
|
|
21
|
+
/** Ensure ~/.kepler/ directory exists with secure permissions. */
|
|
22
22
|
_ensureConfigDir() {
|
|
23
23
|
if (!fs.existsSync(CONFIG_DIR)) {
|
|
24
24
|
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
@@ -134,7 +134,7 @@ export class TarangAuth {
|
|
|
134
134
|
async syncSettings() {
|
|
135
135
|
const { fetchRemoteSettings, mergeRemoteSettings } = await import('../core/settings-sync.mjs');
|
|
136
136
|
const creds = this.loadCredentials();
|
|
137
|
-
if (!creds.token) throw new Error('Not logged in. Run `
|
|
137
|
+
if (!creds.token) throw new Error('Not logged in. Run `kepler login` first.');
|
|
138
138
|
|
|
139
139
|
const remote = await fetchRemoteSettings(creds.token);
|
|
140
140
|
if (!remote) throw new Error('Failed to fetch settings from server.');
|
|
@@ -162,7 +162,7 @@ export class TarangAuth {
|
|
|
162
162
|
|
|
163
163
|
const env = process.env.TARANG_ENV || process.env.NODE_ENV || 'production';
|
|
164
164
|
|
|
165
|
-
process.stderr.write(`\n${BOLD}
|
|
165
|
+
process.stderr.write(`\n${BOLD}Kepler Configuration${RESET}\n`);
|
|
166
166
|
process.stderr.write(`${'─'.repeat(50)}\n`);
|
|
167
167
|
process.stderr.write(` Auth: ${creds.token ? `${check} logged in` : `${cross} not logged in ${DIM}(/login)${RESET}`}\n`);
|
|
168
168
|
process.stderr.write(` Environment: ${DIM}${env}${RESET}\n`);
|
|
@@ -188,8 +188,8 @@ export class TarangAuth {
|
|
|
188
188
|
process.stderr.write(` Last synced: ${DIM}${new Date(raw.last_synced_at).toLocaleString()}${RESET}\n`);
|
|
189
189
|
}
|
|
190
190
|
|
|
191
|
-
process.stderr.write(`\n ${DIM}Run ${RESET}${CYAN}
|
|
192
|
-
process.stderr.write(` ${DIM}Run ${RESET}${CYAN}
|
|
191
|
+
process.stderr.write(`\n ${DIM}Run ${RESET}${CYAN}kepler sync${RESET}${DIM} to sync settings from web.${RESET}\n`);
|
|
192
|
+
process.stderr.write(` ${DIM}Run ${RESET}${CYAN}kepler configure${RESET}${DIM} to open settings in browser.${RESET}\n`);
|
|
193
193
|
process.stderr.write('\n');
|
|
194
194
|
}
|
|
195
195
|
|
|
@@ -202,7 +202,7 @@ export class TarangAuth {
|
|
|
202
202
|
* 3. Web checks Supabase session (if none → GitHub OAuth → Supabase)
|
|
203
203
|
* 4. Web generates CLI token via /api/cli/token
|
|
204
204
|
* 5. Web redirects browser to CLI callback with token
|
|
205
|
-
* 6. CLI receives token, saves to ~/.
|
|
205
|
+
* 6. CLI receives token, saves to ~/.kepler/config.json
|
|
206
206
|
*/
|
|
207
207
|
async login() {
|
|
208
208
|
const { resolveWebUrl } = await import('../core/backend-url.mjs');
|
package/src/config/env.mjs
CHANGED
|
@@ -27,8 +27,8 @@ export const ENV_SCHEMA = {
|
|
|
27
27
|
CLAUDE_CODE_BRIEF: { type: 'boolean', default: false, description: 'Brief output mode' },
|
|
28
28
|
CLAUDE_CODE_DISABLE_CRON: { type: 'boolean', default: false, description: 'Disable cron tasks' },
|
|
29
29
|
CLAUDE_CODE_ENABLE_TASKS: { type: 'boolean', default: false, description: 'Enable task system' },
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
KEPLER_STAGNATION_DETECTION: { type: 'boolean', default: false, description: 'Detect consecutive identical tool-call loops' },
|
|
31
|
+
KEPLER_STAGNATION_THRESHOLD: { type: 'number', default: 3, description: 'Consecutive identical calls before warning' },
|
|
32
32
|
CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS: { type: 'boolean', default: false, description: 'Enable agent teams' },
|
|
33
33
|
CLAUDE_CODE_DEBUG: { type: 'boolean', default: false, description: 'Debug mode' },
|
|
34
34
|
CLAUDE_CODE_DISABLE_TELEMETRY: { type: 'boolean', default: false, description: 'Disable telemetry' },
|
package/src/config/settings.mjs
CHANGED
|
@@ -118,13 +118,13 @@ function applyEnvOverrides(settings) {
|
|
|
118
118
|
if (process.env.CLAUDE_CODE_THINKING === '1') settings.alwaysThinkingEnabled = true;
|
|
119
119
|
if (process.env.CLAUDE_CODE_DISABLE_CRON === '1') settings.cronEnabled = false;
|
|
120
120
|
if (process.env.CLAUDE_CODE_ENABLE_TASKS === '1') settings.enableTeams = true;
|
|
121
|
-
const stagnationEnabled = process.env.
|
|
121
|
+
const stagnationEnabled = process.env.KEPLER_STAGNATION_DETECTION;
|
|
122
122
|
if (stagnationEnabled !== undefined) {
|
|
123
123
|
settings.stagnationDetection = ['1', 'true', 'yes', 'on'].includes(
|
|
124
124
|
stagnationEnabled.toLowerCase(),
|
|
125
125
|
);
|
|
126
126
|
}
|
|
127
|
-
const stagnationThreshold = process.env.
|
|
127
|
+
const stagnationThreshold = process.env.KEPLER_STAGNATION_THRESHOLD;
|
|
128
128
|
if (stagnationThreshold) {
|
|
129
129
|
const n = parseInt(stagnationThreshold, 10);
|
|
130
130
|
if (Number.isInteger(n) && n >= 2) settings.stagnationThreshold = n;
|
package/src/core/checkpoints.mjs
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* Before any file edit, a checkpoint is created containing the
|
|
5
5
|
* original file content. The /undo command restores the last checkpoint.
|
|
6
|
-
* Checkpoints are stored in ~/.
|
|
6
|
+
* Checkpoints are stored in ~/.kepler/projects/{hash}/checkpoints/
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
9
|
import fs from 'fs';
|
package/src/core/headless.mjs
CHANGED
|
@@ -6,9 +6,9 @@
|
|
|
6
6
|
* stderr gets minimal progress (optional with --verbose).
|
|
7
7
|
*
|
|
8
8
|
* Usage:
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
9
|
+
* kepler --headless "Fix the bug in auth.py"
|
|
10
|
+
* kepler --headless --timeout 300 --max-cost 2.00 "Refactor the login flow"
|
|
11
|
+
* kepler --headless --model deepseek/deepseek-chat-v3-0324 "Add tests"
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { TarangStreamClient } from './stream-client.mjs';
|
|
@@ -42,7 +42,7 @@ export async function runHeadless({ instruction, model, timeout = 300, maxCost,
|
|
|
42
42
|
const auth = new TarangAuth();
|
|
43
43
|
const creds = auth.loadCredentials();
|
|
44
44
|
if (!creds.token) {
|
|
45
|
-
emit({ type: 'error', error: 'Not logged in. Run:
|
|
45
|
+
emit({ type: 'error', error: 'Not logged in. Run: kepler login' });
|
|
46
46
|
process.exit(1);
|
|
47
47
|
}
|
|
48
48
|
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* JSONL Writer — writes cc-lens compatible session transcripts to ~/.
|
|
2
|
+
* JSONL Writer — writes cc-lens compatible session transcripts to ~/.kepler/.
|
|
3
3
|
*
|
|
4
4
|
* Format mirrors Claude Code's ~/.claude/ JSONL structure so that
|
|
5
5
|
* cc-lens (CLAUDE_CONFIG_DIR=~/.orca npx cc-lens) can read Orca sessions.
|
|
@@ -17,13 +17,13 @@ import * as os from 'node:os';
|
|
|
17
17
|
import { randomUUID } from 'node:crypto';
|
|
18
18
|
import * as childProcessModule from 'node:child_process';
|
|
19
19
|
|
|
20
|
-
const
|
|
20
|
+
const KEPLER_DIR = process.env.KEPLER_HOME || path.join(os.homedir(), '.kepler');
|
|
21
21
|
const FLUSH_INTERVAL_MS = 500;
|
|
22
22
|
|
|
23
23
|
/**
|
|
24
24
|
* Sanitize a cwd path into a project slug for the directory name.
|
|
25
25
|
* /Users/sree/Sites/myproject → -Users-sree-Sites-myproject
|
|
26
|
-
* Mirrors the Claude Code /
|
|
26
|
+
* Mirrors the Claude Code / kepler-cli convention.
|
|
27
27
|
*/
|
|
28
28
|
function sanitizePath(p) {
|
|
29
29
|
return p.replace(/\//g, '-').replace(/^-/, '-');
|
|
@@ -50,7 +50,7 @@ export class JsonlWriter {
|
|
|
50
50
|
this.version = version;
|
|
51
51
|
this.sessionId = null; // set by setSessionId() when backend assigns it
|
|
52
52
|
this.slug = sanitizePath(cwd);
|
|
53
|
-
this.projectDir = path.join(
|
|
53
|
+
this.projectDir = path.join(KEPLER_DIR, 'projects', this.slug);
|
|
54
54
|
|
|
55
55
|
// UUID chain for parent linking (cc-lens replay)
|
|
56
56
|
this.lastUuid = null;
|
|
@@ -238,7 +238,7 @@ export class JsonlWriter {
|
|
|
238
238
|
}
|
|
239
239
|
|
|
240
240
|
/**
|
|
241
|
-
* Write a prompt entry to ~/.
|
|
241
|
+
* Write a prompt entry to ~/.kepler/history.jsonl.
|
|
242
242
|
*/
|
|
243
243
|
writeHistory(prompt) {
|
|
244
244
|
const entry = {
|
|
@@ -248,7 +248,7 @@ export class JsonlWriter {
|
|
|
248
248
|
project: this.cwd,
|
|
249
249
|
sessionId: this.sessionId,
|
|
250
250
|
};
|
|
251
|
-
const historyPath = path.join(
|
|
251
|
+
const historyPath = path.join(KEPLER_DIR, 'history.jsonl');
|
|
252
252
|
fs.promises.appendFile(historyPath, JSON.stringify(entry) + '\n', { mode: 0o600 })
|
|
253
253
|
.catch(() => {}); // best effort
|
|
254
254
|
}
|
|
@@ -327,7 +327,7 @@ export class JsonlWriter {
|
|
|
327
327
|
fs.mkdirSync(this.projectDir, { recursive: true, mode: 0o700 });
|
|
328
328
|
} catch { /* ignore */ }
|
|
329
329
|
try {
|
|
330
|
-
fs.mkdirSync(path.join(
|
|
330
|
+
fs.mkdirSync(path.join(KEPLER_DIR, 'projects'), { recursive: true, mode: 0o700 });
|
|
331
331
|
} catch { /* ignore */ }
|
|
332
332
|
}
|
|
333
333
|
|
package/src/core/local-agent.mjs
CHANGED
|
@@ -402,7 +402,7 @@ export class LocalAgent {
|
|
|
402
402
|
}
|
|
403
403
|
|
|
404
404
|
const parts = [
|
|
405
|
-
'You are
|
|
405
|
+
'You are Kepler, an AI coding agent running in local mode.',
|
|
406
406
|
'You have access to tools for reading, writing, and executing code.',
|
|
407
407
|
'Use tools to accomplish the user\'s request. Be concise and direct.',
|
|
408
408
|
];
|
package/src/core/local-store.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Local Store Reader — scans ~/.
|
|
2
|
+
* Local Store Reader — scans ~/.kepler/ JSONL files for historical stats.
|
|
3
3
|
*
|
|
4
4
|
* Provides read helpers for CLI commands (/stats, /history, /tokens, /tools, /sessions).
|
|
5
5
|
* All data comes from local JSONL files — no cloud dependency.
|
|
@@ -10,8 +10,8 @@ import * as path from 'node:path';
|
|
|
10
10
|
import * as os from 'node:os';
|
|
11
11
|
import * as readline from 'node:readline';
|
|
12
12
|
|
|
13
|
-
const
|
|
14
|
-
const PROJECTS_DIR = path.join(
|
|
13
|
+
const KEPLER_DIR = process.env.KEPLER_HOME || path.join(os.homedir(), '.kepler');
|
|
14
|
+
const PROJECTS_DIR = path.join(KEPLER_DIR, 'projects');
|
|
15
15
|
|
|
16
16
|
function normalizeBlock(block) {
|
|
17
17
|
if (!block || typeof block !== 'object') {
|
|
@@ -302,7 +302,7 @@ export async function getModelBreakdown(days = 30) {
|
|
|
302
302
|
* @param {number} n — max entries to return (most recent first)
|
|
303
303
|
*/
|
|
304
304
|
export function getHistory(n = 50) {
|
|
305
|
-
const historyPath = path.join(
|
|
305
|
+
const historyPath = path.join(KEPLER_DIR, 'history.jsonl');
|
|
306
306
|
try {
|
|
307
307
|
const content = fs.readFileSync(historyPath, 'utf-8');
|
|
308
308
|
const lines = content.trim().split('\n').filter(Boolean);
|
|
@@ -318,8 +318,8 @@ export function getHistory(n = 50) {
|
|
|
318
318
|
|
|
319
319
|
export function getStorePaths() {
|
|
320
320
|
return {
|
|
321
|
-
|
|
321
|
+
keplerDir: KEPLER_DIR,
|
|
322
322
|
projectsDir: PROJECTS_DIR,
|
|
323
|
-
historyPath: path.join(
|
|
323
|
+
historyPath: path.join(KEPLER_DIR, 'history.jsonl'),
|
|
324
324
|
};
|
|
325
325
|
}
|
package/src/core/paths.mjs
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Kepler Paths — centralized path resolution for all Kepler data.
|
|
3
3
|
*
|
|
4
|
-
* Everything lives under ~/.
|
|
5
|
-
* ~/.
|
|
4
|
+
* Everything lives under ~/.kepler/:
|
|
5
|
+
* ~/.kepler/
|
|
6
6
|
* config.json — auth credentials + settings
|
|
7
7
|
* history.jsonl — prompt history
|
|
8
8
|
* hooks.json — global hooks
|
|
@@ -22,7 +22,7 @@ import * as path from 'node:path';
|
|
|
22
22
|
import * as os from 'node:os';
|
|
23
23
|
import * as crypto from 'node:crypto';
|
|
24
24
|
|
|
25
|
-
const
|
|
25
|
+
const KEPLER_HOME = process.env.KEPLER_HOME || process.env.ORCA_HOME || path.join(os.homedir(), '.kepler');
|
|
26
26
|
|
|
27
27
|
/**
|
|
28
28
|
* Hash a project path to a short directory name.
|
|
@@ -42,57 +42,60 @@ export function projectHash(projectDir) {
|
|
|
42
42
|
.slice(0, 16);
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
/** Root ~/.
|
|
46
|
-
export function
|
|
47
|
-
return
|
|
45
|
+
/** Root ~/.kepler/ directory. */
|
|
46
|
+
export function keplerHome() {
|
|
47
|
+
return KEPLER_HOME;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
-
/**
|
|
50
|
+
/** @deprecated Use keplerHome() */
|
|
51
|
+
export const orcaHome = keplerHome;
|
|
52
|
+
|
|
53
|
+
/** ~/.kepler/projects/{hash}/ for a given project path. */
|
|
51
54
|
export function projectDir(projectPath) {
|
|
52
|
-
return path.join(
|
|
55
|
+
return path.join(KEPLER_HOME, 'projects', projectHash(projectPath));
|
|
53
56
|
}
|
|
54
57
|
|
|
55
|
-
/** ~/.
|
|
58
|
+
/** ~/.kepler/projects/{hash}/index/ — BM25 search index. */
|
|
56
59
|
export function indexDir(projectPath) {
|
|
57
60
|
return path.join(projectDir(projectPath), 'index');
|
|
58
61
|
}
|
|
59
62
|
|
|
60
|
-
/** ~/.
|
|
63
|
+
/** ~/.kepler/projects/{hash}/checkpoints/ — file undo. */
|
|
61
64
|
export function checkpointsDir(projectPath) {
|
|
62
65
|
return path.join(projectDir(projectPath), 'checkpoints');
|
|
63
66
|
}
|
|
64
67
|
|
|
65
|
-
/** ~/.
|
|
68
|
+
/** ~/.kepler/projects/{hash}/state.json — current session. */
|
|
66
69
|
export function statePath(projectPath) {
|
|
67
70
|
return path.join(projectDir(projectPath), 'state.json');
|
|
68
71
|
}
|
|
69
72
|
|
|
70
|
-
/** ~/.
|
|
73
|
+
/** ~/.kepler/projects/{hash}/sessions/ — session archive. */
|
|
71
74
|
export function sessionsDir(projectPath) {
|
|
72
75
|
return path.join(projectDir(projectPath), 'sessions');
|
|
73
76
|
}
|
|
74
77
|
|
|
75
|
-
/** ~/.
|
|
78
|
+
/** ~/.kepler/projects/{hash}/hooks.json — project hooks. */
|
|
76
79
|
export function projectHooksPath(projectPath) {
|
|
77
80
|
return path.join(projectDir(projectPath), 'hooks.json');
|
|
78
81
|
}
|
|
79
82
|
|
|
80
|
-
/** ~/.
|
|
83
|
+
/** ~/.kepler/conversations/ — central conversation storage. */
|
|
81
84
|
export function conversationsDir() {
|
|
82
|
-
return path.join(
|
|
85
|
+
return path.join(KEPLER_HOME, 'conversations');
|
|
83
86
|
}
|
|
84
87
|
|
|
85
|
-
/** ~/.
|
|
88
|
+
/** ~/.kepler/conversations/{sessionId}.jsonl */
|
|
86
89
|
export function conversationPath(sessionId) {
|
|
87
90
|
return path.join(conversationsDir(), `${sessionId}.jsonl`);
|
|
88
91
|
}
|
|
89
92
|
|
|
90
|
-
/** ~/.
|
|
93
|
+
/** ~/.kepler/hooks.json — global hooks. */
|
|
91
94
|
export function globalHooksPath() {
|
|
92
|
-
return path.join(
|
|
95
|
+
return path.join(KEPLER_HOME, 'hooks.json');
|
|
93
96
|
}
|
|
94
97
|
|
|
95
|
-
/** ~/.
|
|
98
|
+
/** ~/.kepler/history.jsonl — prompt history. */
|
|
96
99
|
export function historyPath() {
|
|
97
|
-
return path.join(
|
|
100
|
+
return path.join(KEPLER_HOME, 'history.jsonl');
|
|
98
101
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Session Manager — persist session state, history, and conversation messages.
|
|
3
3
|
*
|
|
4
|
-
* All data lives under ~/.
|
|
5
|
-
* ~/.
|
|
4
|
+
* All data lives under ~/.kepler/:
|
|
5
|
+
* ~/.kepler/
|
|
6
6
|
* projects/{hash}/
|
|
7
7
|
* state.json — current session metadata
|
|
8
8
|
* sessions/ — session metadata archive
|
|
@@ -29,7 +29,7 @@ const MAX_SESSIONS = 100;
|
|
|
29
29
|
export class SessionManager {
|
|
30
30
|
constructor(projectPath = process.cwd()) {
|
|
31
31
|
this.projectPath = projectPath;
|
|
32
|
-
this.
|
|
32
|
+
this.projectKeplerDir = getProjectDir(projectPath);
|
|
33
33
|
this.statePath = getStatePath(projectPath);
|
|
34
34
|
this.sessionsDir = getSessionsDir(projectPath);
|
|
35
35
|
this.conversationsDir = getConversationsDir();
|
|
@@ -37,7 +37,7 @@ export class SessionManager {
|
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
_ensureDirs() {
|
|
40
|
-
fs.mkdirSync(this.
|
|
40
|
+
fs.mkdirSync(this.projectKeplerDir, { recursive: true });
|
|
41
41
|
fs.mkdirSync(this.sessionsDir, { recursive: true });
|
|
42
42
|
fs.mkdirSync(this.conversationsDir, { recursive: true });
|
|
43
43
|
}
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
* Settings Sync — fetch user settings from Tarang web and cache locally.
|
|
3
3
|
*
|
|
4
4
|
* Syncs: gateway_type, model preferences, configured providers.
|
|
5
|
-
* Cached in ~/.
|
|
5
|
+
* Cached in ~/.kepler/config.json alongside auth token.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
8
|
import { resolveWebUrl } from './backend-url.mjs';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Fetch user settings from the web API using CLI token.
|
|
12
|
-
* @param {string} token - CLI auth token (
|
|
12
|
+
* @param {string} token - CLI auth token (kepler_xxx)
|
|
13
13
|
* @returns {Promise<Object|null>} Settings object or null on failure
|
|
14
14
|
*/
|
|
15
15
|
export async function fetchRemoteSettings(token) {
|
|
@@ -29,7 +29,7 @@ export async function fetchRemoteSettings(token) {
|
|
|
29
29
|
|
|
30
30
|
if (!resp.ok) {
|
|
31
31
|
if (resp.status === 401) {
|
|
32
|
-
process.stderr.write('\x1b[33mSettings sync: token expired or invalid. Run `
|
|
32
|
+
process.stderr.write('\x1b[33mSettings sync: token expired or invalid. Run `kepler login` to re-authenticate.\x1b[0m\n');
|
|
33
33
|
}
|
|
34
34
|
return null;
|
|
35
35
|
}
|
|
@@ -44,7 +44,7 @@ export async function fetchRemoteSettings(token) {
|
|
|
44
44
|
/**
|
|
45
45
|
* Merge remote settings into local config.
|
|
46
46
|
* Remote settings override local only for fields that are set.
|
|
47
|
-
* @param {Object} localConfig - Current ~/.
|
|
47
|
+
* @param {Object} localConfig - Current ~/.kepler/config.json content
|
|
48
48
|
* @param {Object} remote - Settings from fetchRemoteSettings()
|
|
49
49
|
* @returns {Object} Merged config to save
|
|
50
50
|
*/
|
|
@@ -106,7 +106,7 @@ export class TarangStreamClient {
|
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
if (response.status === 401) {
|
|
109
|
-
yield { type: EVENT_TYPES.ERROR, data: { message: 'Authentication failed. Run `
|
|
109
|
+
yield { type: EVENT_TYPES.ERROR, data: { message: 'Authentication failed. Run `kepler login` to re-authenticate.', fatal: true } };
|
|
110
110
|
return;
|
|
111
111
|
}
|
|
112
112
|
if (!response.ok) {
|
package/src/index.mjs
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
* @
|
|
3
|
+
* @axplusb/kepler v1.0.1 — Kepler AI Coding Agent CLI
|
|
4
4
|
*
|
|
5
5
|
* Phase 3: Hybrid local/remote/auto + advanced features.
|
|
6
6
|
*/
|
|
7
7
|
|
|
8
|
-
// Load .env file from cwd or ~/.
|
|
8
|
+
// Load .env file from cwd or ~/.kepler/.env
|
|
9
9
|
import { readFileSync, existsSync } from 'node:fs';
|
|
10
10
|
import { join } from 'node:path';
|
|
11
11
|
import { homedir } from 'node:os';
|
|
12
12
|
|
|
13
|
-
for (const envPath of [join(process.cwd(), '.env'), join(homedir(), '.
|
|
13
|
+
for (const envPath of [join(process.cwd(), '.env'), join(homedir(), '.kepler', '.env')]) {
|
|
14
14
|
if (existsSync(envPath)) {
|
|
15
15
|
for (const line of readFileSync(envPath, 'utf-8').split('\n')) {
|
|
16
16
|
const match = line.match(/^\s*([\w]+)\s*=\s*(.+?)\s*$/);
|
|
@@ -117,12 +117,12 @@ function printUsage() {
|
|
|
117
117
|
const B = '\x1b[1m', C = '\x1b[36m', D = '\x1b[2m', G = '\x1b[32m', R = '\x1b[0m';
|
|
118
118
|
|
|
119
119
|
process.stderr.write(`${B}USAGE${R}\n`);
|
|
120
|
-
process.stderr.write(` ${C}
|
|
121
|
-
process.stderr.write(` ${C}
|
|
122
|
-
process.stderr.write(` ${C}
|
|
123
|
-
process.stderr.write(` ${C}
|
|
124
|
-
process.stderr.write(` ${C}
|
|
125
|
-
process.stderr.write(` ${C}
|
|
120
|
+
process.stderr.write(` ${C}kepler "instruction"${R} Execute instruction\n`);
|
|
121
|
+
process.stderr.write(` ${C}kepler${R} Interactive mode (REPL)\n`);
|
|
122
|
+
process.stderr.write(` ${C}kepler login${R} Authenticate via GitHub OAuth\n`);
|
|
123
|
+
process.stderr.write(` ${C}kepler configure${R} Open settings in browser\n`);
|
|
124
|
+
process.stderr.write(` ${C}kepler config --show${R} Display local configuration\n`);
|
|
125
|
+
process.stderr.write(` ${C}kepler resume${R} Resume a paused session\n`);
|
|
126
126
|
process.stderr.write('\n');
|
|
127
127
|
process.stderr.write(`${B}MODE FLAGS${R}\n`);
|
|
128
128
|
process.stderr.write(` ${G}--local${R} Direct LLM API ${D}(<100ms, offline)${R}\n`);
|
|
@@ -133,7 +133,7 @@ function printUsage() {
|
|
|
133
133
|
process.stderr.write(`${B}MODEL FLAGS${R}\n`);
|
|
134
134
|
process.stderr.write(` ${G}--system-prompt <text>${R} Override system prompt\n`);
|
|
135
135
|
process.stderr.write(` ${G}--max-turns <n>${R} Maximum conversation turns\n`);
|
|
136
|
-
process.stderr.write(` ${D}Models are configured via:
|
|
136
|
+
process.stderr.write(` ${D}Models are configured via: kepler configure${R}\n`);
|
|
137
137
|
process.stderr.write('\n');
|
|
138
138
|
process.stderr.write(`${B}PERMISSION FLAGS${R}\n`);
|
|
139
139
|
process.stderr.write(` ${G}--yes, -y${R} Auto-approve all operations\n`);
|
|
@@ -187,7 +187,7 @@ import { startTerminalRepl } from './terminal/repl.mjs';
|
|
|
187
187
|
|
|
188
188
|
async function main() {
|
|
189
189
|
const args = parseArgs(process.argv.slice(2));
|
|
190
|
-
if (args.version) { console.log(`@
|
|
190
|
+
if (args.version) { console.log(`@axplusb/kepler ${VERSION}`); process.exit(0); }
|
|
191
191
|
if (args.help) { printUsage(); process.exit(0); }
|
|
192
192
|
|
|
193
193
|
const auth = new TarangAuth();
|
|
@@ -219,7 +219,7 @@ async function main() {
|
|
|
219
219
|
if (remote.models?.reasoning) process.stderr.write(`\x1b[32m✓ Coding:\x1b[0m ${remote.models.reasoning}\n`);
|
|
220
220
|
if (remote.models?.local) process.stderr.write(`\x1b[32m✓ Local:\x1b[0m ${remote.models.local}\n`);
|
|
221
221
|
if (remote.configured_providers?.length) process.stderr.write(`\x1b[32m✓ Providers:\x1b[0m ${remote.configured_providers.join(', ')}\n`);
|
|
222
|
-
process.stderr.write('\n\x1b[32m✓ Settings saved to ~/.
|
|
222
|
+
process.stderr.write('\n\x1b[32m✓ Settings saved to ~/.kepler/config.json\x1b[0m\n');
|
|
223
223
|
} catch (err) {
|
|
224
224
|
process.stderr.write(`\x1b[31m✗ ${err.message}\x1b[0m\n`);
|
|
225
225
|
}
|
|
@@ -72,12 +72,12 @@ function extractPrompt(entry) {
|
|
|
72
72
|
|
|
73
73
|
export function formatSessionsReport(sessions, limit) {
|
|
74
74
|
if (!sessions.length) {
|
|
75
|
-
return 'No local sessions found in ~/.
|
|
75
|
+
return 'No local sessions found in ~/.kepler/projects.\n';
|
|
76
76
|
}
|
|
77
77
|
|
|
78
78
|
const lines = [];
|
|
79
|
-
lines.push(`
|
|
80
|
-
lines.push(`Recent local transcripts from ~/.
|
|
79
|
+
lines.push(`KEPLER SESSIONS ${sessions.length} shown`);
|
|
80
|
+
lines.push(`Recent local transcripts from ~/.kepler/projects (limit ${limit})`);
|
|
81
81
|
lines.push('');
|
|
82
82
|
|
|
83
83
|
for (const session of sessions) {
|
|
@@ -96,8 +96,8 @@ export function formatSessionsReport(sessions, limit) {
|
|
|
96
96
|
|
|
97
97
|
export function formatStatsReport(stats, tools, models, days, paths) {
|
|
98
98
|
const lines = [];
|
|
99
|
-
lines.push(`
|
|
100
|
-
lines.push(`Store: ${paths.
|
|
99
|
+
lines.push(`KEPLER STATS ${relativeDaysLabel(days)}`);
|
|
100
|
+
lines.push(`Store: ${paths.keplerDir}`);
|
|
101
101
|
lines.push('');
|
|
102
102
|
lines.push(`Sessions ${formatNumber(stats.totalSessions)}`);
|
|
103
103
|
lines.push(`Messages ${formatNumber(stats.totalUserMessages + stats.totalAssistantMessages)} (${formatNumber(stats.totalUserMessages)} user, ${formatNumber(stats.totalAssistantMessages)} assistant)`);
|
|
@@ -131,12 +131,12 @@ export function formatStatsReport(stats, tools, models, days, paths) {
|
|
|
131
131
|
|
|
132
132
|
export function formatHistoryReport(entries, limit) {
|
|
133
133
|
if (!entries.length) {
|
|
134
|
-
return 'No local prompt history found in ~/.
|
|
134
|
+
return 'No local prompt history found in ~/.kepler/history.jsonl.\n';
|
|
135
135
|
}
|
|
136
136
|
|
|
137
137
|
const lines = [];
|
|
138
|
-
lines.push(`
|
|
139
|
-
lines.push(`Recent prompts from ~/.
|
|
138
|
+
lines.push(`KEPLER HISTORY ${entries.length} shown`);
|
|
139
|
+
lines.push(`Recent prompts from ~/.kepler/history.jsonl (limit ${limit})`);
|
|
140
140
|
lines.push('');
|
|
141
141
|
|
|
142
142
|
for (const entry of entries) {
|
|
@@ -202,7 +202,7 @@ export async function runHistoryCommand(args = []) {
|
|
|
202
202
|
process.stdout.write(formatHistoryReport(history, limit));
|
|
203
203
|
}
|
|
204
204
|
|
|
205
|
-
// Dashboard removed — now using
|
|
205
|
+
// Dashboard removed — now using Kepler Pulse (pulse/cli.js)
|
|
206
206
|
|
|
207
207
|
export async function _runDashboardCommand_REMOVED() { /* see pulse/cli.js */ }
|
|
208
208
|
|
|
@@ -272,8 +272,8 @@ export async function _OLD_runDashboardCommand(args = []) {
|
|
|
272
272
|
const actualPort = typeof address === 'object' && address ? address.port : requestedPort;
|
|
273
273
|
const url = `http://${host}:${actualPort}`;
|
|
274
274
|
|
|
275
|
-
process.stderr.write(`\x1b[
|
|
276
|
-
process.stderr.write(`\x1b[2mReading local analytics from ${getStorePaths().
|
|
275
|
+
process.stderr.write(`\x1b[36mKepler dashboard\x1b[0m ${url}\n`);
|
|
276
|
+
process.stderr.write(`\x1b[2mReading local analytics from ${getStorePaths().keplerDir}\x1b[0m\n`);
|
|
277
277
|
process.stderr.write(`\x1b[2mPress Ctrl+C to stop the dashboard server.\x1b[0m\n`);
|
|
278
278
|
|
|
279
279
|
if (shouldOpen) {
|
package/src/terminal/main.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* Kepler CLI — ANSI Terminal UI.
|
|
4
4
|
* Zero React. Zero Ink. Zero flickering.
|
|
5
5
|
*/
|
|
6
6
|
|
|
@@ -19,7 +19,7 @@ const subcommandArgs = process.argv.slice(3);
|
|
|
19
19
|
|
|
20
20
|
async function main() {
|
|
21
21
|
if (subcommand === 'dashboard') {
|
|
22
|
-
// Launch
|
|
22
|
+
// Launch Kepler Pulse Next.js dashboard
|
|
23
23
|
const { spawn } = await import('node:child_process');
|
|
24
24
|
const { fileURLToPath } = await import('node:url');
|
|
25
25
|
const path = await import('node:path');
|
|
@@ -76,28 +76,28 @@ async function main() {
|
|
|
76
76
|
const { createRequire } = await import('node:module');
|
|
77
77
|
const require = createRequire(import.meta.url);
|
|
78
78
|
const { version } = require('../../package.json');
|
|
79
|
-
process.stdout.write(`
|
|
79
|
+
process.stdout.write(`kepler v${version}\n`);
|
|
80
80
|
return;
|
|
81
81
|
}
|
|
82
82
|
|
|
83
83
|
if (subcommand === 'help' || subcommand === '--help' || subcommand === '-h') {
|
|
84
84
|
process.stderr.write(`
|
|
85
|
-
\x1b[1m\x1b[
|
|
85
|
+
\x1b[1m\x1b[36mkepler\x1b[0m — AI Coding Agent — codekepler.ai
|
|
86
86
|
|
|
87
87
|
\x1b[1mUsage:\x1b[0m
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
88
|
+
kepler Start interactive REPL
|
|
89
|
+
kepler "instruction" Run a single instruction
|
|
90
|
+
kepler --headless -p "x" Non-interactive: auto-approve, JSONL output
|
|
91
|
+
kepler --resume Resume last conversation
|
|
92
|
+
kepler dashboard Open Kepler Pulse analytics dashboard
|
|
93
|
+
kepler login Sign in via browser
|
|
94
|
+
kepler logout Sign out and clear credentials
|
|
95
|
+
kepler version Show version
|
|
96
96
|
|
|
97
97
|
\x1b[1mAnalytics:\x1b[0m
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
98
|
+
kepler sessions List recent local sessions
|
|
99
|
+
kepler stats Show aggregate local session stats
|
|
100
|
+
kepler history Show recent prompt history
|
|
101
101
|
|
|
102
102
|
\x1b[1mREPL Commands:\x1b[0m
|
|
103
103
|
/help Show available commands
|
|
@@ -121,7 +121,7 @@ async function main() {
|
|
|
121
121
|
TARANG_ENV Set backend (local, development, production)
|
|
122
122
|
ANTHROPIC_API_KEY Direct Anthropic API key
|
|
123
123
|
OPENROUTER_API_KEY OpenRouter API key
|
|
124
|
-
|
|
124
|
+
KEPLER_CONFIG_DIR Override config directory (default: ~/.kepler)
|
|
125
125
|
|
|
126
126
|
\x1b[2mDocs: https://devtarang.ai/docs\x1b[0m
|
|
127
127
|
`);
|
package/src/terminal/repl.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Kepler REPL — Full Claude-like terminal UX.
|
|
3
3
|
*
|
|
4
4
|
* Pure ANSI. No React. No Ink. No flickering.
|
|
5
5
|
*
|
|
@@ -148,12 +148,12 @@ function printBanner(auth) {
|
|
|
148
148
|
//
|
|
149
149
|
// ✓ 3 tools · 1.2s · $0.02 ctx 21% · 42k tok
|
|
150
150
|
// ╶─────────────────────────────────────────────────────────────────╴
|
|
151
|
-
//
|
|
151
|
+
// kepler ›
|
|
152
152
|
//
|
|
153
153
|
// Layout on first prompt (no stats yet):
|
|
154
154
|
//
|
|
155
155
|
// ╶─────────────────────────────────────────────────────────────────╴
|
|
156
|
-
//
|
|
156
|
+
// kepler ›
|
|
157
157
|
|
|
158
158
|
/**
|
|
159
159
|
* Build the contextual status strip — compact, one line.
|
|
@@ -765,7 +765,7 @@ async function handleCommand(input, ctx) {
|
|
|
765
765
|
|
|
766
766
|
switch (cmd) {
|
|
767
767
|
case '/help':
|
|
768
|
-
process.stderr.write(`\n ${c.bold('
|
|
768
|
+
process.stderr.write(`\n ${c.bold('Kepler Commands')}\n`);
|
|
769
769
|
process.stderr.write(` ${c.gray('─'.repeat(44))}\n`);
|
|
770
770
|
for (const [name, desc] of Object.entries(COMMANDS)) {
|
|
771
771
|
process.stderr.write(` ${c.brand(name.padEnd(14))} ${desc}\n`);
|
|
@@ -937,7 +937,7 @@ async function handleCommand(input, ctx) {
|
|
|
937
937
|
process.stderr.write(`\n ${c.bold('Conversation')} (${session.history.length} messages)\n`);
|
|
938
938
|
process.stderr.write(` ${c.gray('─'.repeat(40))}\n`);
|
|
939
939
|
for (const msg of session.history.slice(-20)) {
|
|
940
|
-
const role = msg.role === 'user' ? c.white('You') : c.brand('
|
|
940
|
+
const role = msg.role === 'user' ? c.white('You') : c.brand('Kepler');
|
|
941
941
|
process.stderr.write(` ${role}: ${msg.content.slice(0, 80)}${msg.content.length > 80 ? '...' : ''}\n`);
|
|
942
942
|
}
|
|
943
943
|
process.stderr.write('\n');
|
|
@@ -1016,7 +1016,7 @@ async function handleCommand(input, ctx) {
|
|
|
1016
1016
|
const instr = s.instruction ? s.instruction.slice(0, 40) : '(no instruction)';
|
|
1017
1017
|
process.stderr.write(` ${c.brand(s.sessionId)} ${c.dim(date)} ${s.messageCount} msgs ${c.dim(instr)}\n`);
|
|
1018
1018
|
}
|
|
1019
|
-
process.stderr.write(`\n ${c.dim('Resume with:')}
|
|
1019
|
+
process.stderr.write(`\n ${c.dim('Resume with:')} kepler --resume <sessionId>\n`);
|
|
1020
1020
|
return;
|
|
1021
1021
|
}
|
|
1022
1022
|
|
|
@@ -1123,7 +1123,7 @@ async function handleCommand(input, ctx) {
|
|
|
1123
1123
|
case '/logout': {
|
|
1124
1124
|
const success = ctx.auth.logout();
|
|
1125
1125
|
if (success) {
|
|
1126
|
-
process.stderr.write(` ${c.green('✓')} ${c.dim('Signed out. Credentials cleared from ~/.
|
|
1126
|
+
process.stderr.write(` ${c.green('✓')} ${c.dim('Signed out. Credentials cleared from ~/.kepler/config.json')}\n`);
|
|
1127
1127
|
process.stderr.write(` ${c.dim('Run /login to sign in again.')}\n`);
|
|
1128
1128
|
} else {
|
|
1129
1129
|
process.stderr.write(` ${c.yellow('!')} ${c.dim('No credentials to clear.')}\n`);
|
|
@@ -1172,11 +1172,11 @@ export async function startTerminalRepl() {
|
|
|
1172
1172
|
const skipPerms = cliArgs.freeswim;
|
|
1173
1173
|
const approval = new ApprovalManager({ autoApprove: skipPerms });
|
|
1174
1174
|
|
|
1175
|
-
// Session manager — persists conversation messages to .
|
|
1175
|
+
// Session manager — persists conversation messages to .kepler/conversations/
|
|
1176
1176
|
const sessionMgr = new SessionManager(safeCwd());
|
|
1177
1177
|
_sessionMgr = sessionMgr; // expose to renderEvent
|
|
1178
1178
|
|
|
1179
|
-
// Local JSONL writer — writes cc-lens compatible session data to ~/.
|
|
1179
|
+
// Local JSONL writer — writes cc-lens compatible session data to ~/.kepler/
|
|
1180
1180
|
const jsonlWriter = new JsonlWriter(safeCwd(), VERSION);
|
|
1181
1181
|
|
|
1182
1182
|
// Persistent stream client — session_id captured from backend on first turn
|
|
@@ -1261,7 +1261,7 @@ export async function startTerminalRepl() {
|
|
|
1261
1261
|
|
|
1262
1262
|
process.stderr.write(`\n ${c.dim('Press')} ${c.brand('Enter')} ${c.dim('to start, or type a prompt below.')}\n`);
|
|
1263
1263
|
|
|
1264
|
-
const PROMPT = `${c.brand('
|
|
1264
|
+
const PROMPT = `${c.brand('kepler')} ${c.dim('›')} `;
|
|
1265
1265
|
|
|
1266
1266
|
const rl = readline.createInterface({
|
|
1267
1267
|
input: process.stdin,
|
|
@@ -1326,8 +1326,8 @@ export async function startTerminalRepl() {
|
|
|
1326
1326
|
return;
|
|
1327
1327
|
}
|
|
1328
1328
|
|
|
1329
|
-
//
|
|
1330
|
-
process.stderr.write(`\n${c.bold(c.brand('
|
|
1329
|
+
// Kepler response label
|
|
1330
|
+
process.stderr.write(`\n${c.bold(c.brand('kepler'))}\n`);
|
|
1331
1331
|
|
|
1332
1332
|
// Create or reuse stream client — sessionId persists across turns
|
|
1333
1333
|
if (!streamClient || streamClient.baseUrl !== creds.backendUrl || streamClient.token !== creds.token) {
|
package/src/tools/agent.mjs
CHANGED
|
@@ -87,10 +87,10 @@ export const AgentTool = {
|
|
|
87
87
|
settings: {
|
|
88
88
|
stream: false,
|
|
89
89
|
stagnationDetection: !['0', 'false', 'no', 'off'].includes(
|
|
90
|
-
(process.env.
|
|
90
|
+
(process.env.KEPLER_STAGNATION_DETECTION ?? '0').toLowerCase(),
|
|
91
91
|
),
|
|
92
92
|
stagnationThreshold: Number.parseInt(
|
|
93
|
-
process.env.
|
|
93
|
+
process.env.KEPLER_STAGNATION_THRESHOLD
|
|
94
94
|
?? '3',
|
|
95
95
|
10,
|
|
96
96
|
),
|
package/src/ui/banner.mjs
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Banner & Branding —
|
|
2
|
+
* Banner & Branding — Kepler CLI startup display.
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import { execSync } from 'node:child_process';
|
|
@@ -17,13 +17,13 @@ const BLUE = '\x1b[34m';
|
|
|
17
17
|
const BOLD_GREEN = '\x1b[1;32m';
|
|
18
18
|
const BOLD_CYAN = '\x1b[1;36m';
|
|
19
19
|
|
|
20
|
-
const
|
|
21
|
-
' ██████╗ ██████╗
|
|
22
|
-
'
|
|
23
|
-
'
|
|
24
|
-
'██║
|
|
25
|
-
'
|
|
26
|
-
'
|
|
20
|
+
const BANNER_KEPLER = [
|
|
21
|
+
'██╗ ██╗ ███████╗ ██████╗ ██╗ ███████╗ ██████╗ ',
|
|
22
|
+
'██║ ██╔╝ ██╔════╝ ██╔══██╗ ██║ ██╔════╝ ██╔══██╗',
|
|
23
|
+
'█████╔╝ █████╗ ██████╔╝ ██║ █████╗ ██████╔╝',
|
|
24
|
+
'██╔═██╗ ██╔══╝ ██╔═══╝ ██║ ██╔══╝ ██╔══██╗',
|
|
25
|
+
'██║ ██╗ ███████╗ ██║ ███████╗ ███████╗ ██║ ██║',
|
|
26
|
+
'╚═╝ ╚═╝ ╚══════╝ ╚═╝ ╚══════╝ ╚══════╝ ╚═╝ ╚═╝',
|
|
27
27
|
];
|
|
28
28
|
|
|
29
29
|
/**
|
|
@@ -31,10 +31,10 @@ const BANNER_ORCA = [
|
|
|
31
31
|
*/
|
|
32
32
|
export function printBanner() {
|
|
33
33
|
process.stderr.write('\n');
|
|
34
|
-
for (const line of
|
|
34
|
+
for (const line of BANNER_KEPLER) {
|
|
35
35
|
process.stderr.write(` ${BOLD_CYAN}${line}${RESET}\n`);
|
|
36
36
|
}
|
|
37
|
-
process.stderr.write(` ${DIM}
|
|
37
|
+
process.stderr.write(` ${DIM}AI Coding Agent — codekepler.ai${RESET}\n`);
|
|
38
38
|
process.stderr.write('\n');
|
|
39
39
|
}
|
|
40
40
|
|
|
@@ -105,7 +105,7 @@ export function printStyledConfig(creds) {
|
|
|
105
105
|
return `${check} ${val.slice(0, 6)}...${val.slice(-4)}`;
|
|
106
106
|
};
|
|
107
107
|
|
|
108
|
-
process.stderr.write(`\n${BOLD}
|
|
108
|
+
process.stderr.write(`\n${BOLD}Kepler Configuration${RESET} ${DIM}(~/.kepler/config.json)${RESET}\n`);
|
|
109
109
|
process.stderr.write(`${'─'.repeat(50)}\n`);
|
|
110
110
|
process.stderr.write(` Token: ${mask(creds.token)}\n`);
|
|
111
111
|
process.stderr.write(` OpenRouter: ${mask(creds.openRouterKey)}\n`);
|
|
@@ -167,7 +167,7 @@ export function getLoginSuccessHTML() {
|
|
|
167
167
|
<html>
|
|
168
168
|
<head>
|
|
169
169
|
<meta charset="utf-8">
|
|
170
|
-
<title>
|
|
170
|
+
<title>Kepler - Login Successful</title>
|
|
171
171
|
<style>
|
|
172
172
|
body {
|
|
173
173
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
@@ -189,7 +189,7 @@ export function getLoginSuccessHTML() {
|
|
|
189
189
|
margin-bottom: 8px;
|
|
190
190
|
}
|
|
191
191
|
.dev { color: #3fb950; }
|
|
192
|
-
.
|
|
192
|
+
.kepler { color: #58a6ff; }
|
|
193
193
|
.check {
|
|
194
194
|
font-size: 64px;
|
|
195
195
|
color: #3fb950;
|
|
@@ -222,14 +222,14 @@ export function getLoginSuccessHTML() {
|
|
|
222
222
|
<body>
|
|
223
223
|
<div class="container">
|
|
224
224
|
<div class="logo">
|
|
225
|
-
<span class="
|
|
225
|
+
<span class="kepler">KEPLER</span>
|
|
226
226
|
</div>
|
|
227
227
|
<div class="check">✓</div>
|
|
228
228
|
<h1>Login Successful!</h1>
|
|
229
229
|
<p>You can close this tab and return to your terminal.</p>
|
|
230
230
|
<div class="hint">
|
|
231
231
|
<p>Next step: set your API key</p>
|
|
232
|
-
<code>
|
|
232
|
+
<code>kepler config --openrouter-key YOUR_KEY</code>
|
|
233
233
|
</div>
|
|
234
234
|
</div>
|
|
235
235
|
</body>
|
|
@@ -41,7 +41,7 @@ function cmdHelp(ctx) {
|
|
|
41
41
|
const BOLD = '\x1b[1m', CYAN = '\x1b[36m', DIM = '\x1b[2m', GREEN = '\x1b[32m', BLUE = '\x1b[34m', RESET = '\x1b[0m';
|
|
42
42
|
|
|
43
43
|
process.stderr.write(`\n${BLUE}┌──────────────────────────────────────────────┐${RESET}\n`);
|
|
44
|
-
process.stderr.write(`${BLUE}│${RESET} ${BOLD}
|
|
44
|
+
process.stderr.write(`${BLUE}│${RESET} ${BOLD}Kepler Help${RESET} ${BLUE}│${RESET}\n`);
|
|
45
45
|
process.stderr.write(`${BLUE}├──────────────────────────────────────────────┤${RESET}\n`);
|
|
46
46
|
process.stderr.write(`${BLUE}│${RESET} ${BLUE}│${RESET}\n`);
|
|
47
47
|
process.stderr.write(`${BLUE}│${RESET} ${BOLD}Commands:${RESET} ${BLUE}│${RESET}\n`);
|
|
@@ -94,7 +94,7 @@ function cmdClear(ctx) {
|
|
|
94
94
|
}
|
|
95
95
|
|
|
96
96
|
function cmdSessions() {
|
|
97
|
-
const sessDir = path.join(process.cwd(), '.
|
|
97
|
+
const sessDir = path.join(process.cwd(), '.kepler', 'sessions');
|
|
98
98
|
if (!fs.existsSync(sessDir)) {
|
|
99
99
|
process.stderr.write('No sessions found.\n');
|
|
100
100
|
return;
|
|
@@ -131,7 +131,7 @@ async function cmdIndex() {
|
|
|
131
131
|
const retriever = new ContextRetriever(cwd);
|
|
132
132
|
const result = await retriever.buildIndex();
|
|
133
133
|
process.stderr.write(`\x1b[32m✓ Index built: ${result.fileCount} files, ${result.chunkCount} chunks\x1b[0m\n`);
|
|
134
|
-
process.stderr.write(`\x1b[2m Stored at: ${path.join(cwd, '.
|
|
134
|
+
process.stderr.write(`\x1b[2m Stored at: ${path.join(cwd, '.kepler', 'index')}\x1b[0m\n`);
|
|
135
135
|
} catch (err) {
|
|
136
136
|
process.stderr.write(`\x1b[31mIndex build failed: ${err.message}\x1b[0m\n`);
|
|
137
137
|
}
|
|
@@ -186,7 +186,7 @@ function cmdRefresh(ctx) {
|
|
|
186
186
|
process.stderr.write('\x1b[31mNo auth module available.\x1b[0m\n');
|
|
187
187
|
return;
|
|
188
188
|
}
|
|
189
|
-
// Force re-read from ~/.
|
|
189
|
+
// Force re-read from ~/.kepler/config.json
|
|
190
190
|
ctx.auth._config = null;
|
|
191
191
|
const creds = ctx.auth.loadCredentials();
|
|
192
192
|
const GREEN = '\x1b[32m', DIM = '\x1b[2m', CYAN = '\x1b[36m', RESET = '\x1b[0m';
|