@axplusb/kepler 1.0.5 → 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 +34 -0
- package/package.json +4 -4
- package/src/core/headless.mjs +68 -24
- package/src/core/project-artifacts.mjs +37 -0
- 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 +18 -0
- package/src/terminal/repl.mjs +26 -47
- package/src/terminal/skills.mjs +54 -0
- package/src/tools/bash.mjs +5 -2
- package/src/tools/project-overview.mjs +418 -0
- package/src/tools/registry.mjs +0 -16
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,6 +117,7 @@ Platform default included free. Bring your own API key for unlimited.
|
|
|
84
117
|
|
|
85
118
|
| Model | Score | Cost |
|
|
86
119
|
|-------|-------|------|
|
|
120
|
+
| DeepSeek V4 Flash + stagnation | 47.3% (142/300) | $0.046/instance |
|
|
87
121
|
| DeepSeek V4 Flash | 39.7% (119/300) | $0.046/instance |
|
|
88
122
|
| DeepSeek V4 Flash (baseline) | 30.7% (92/300) | $0.03/instance |
|
|
89
123
|
| DeepSeek V4 Pro | 50% (14/28 sample) | $0.48/instance |
|
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
|
}
|
package/src/core/headless.mjs
CHANGED
|
@@ -13,10 +13,9 @@
|
|
|
13
13
|
|
|
14
14
|
import { TarangStreamClient } from './stream-client.mjs';
|
|
15
15
|
import { createToolExecutor } from './tool-executor.mjs';
|
|
16
|
+
import { persistProjectArtifacts } from './project-artifacts.mjs';
|
|
16
17
|
import { TarangAuth } from '../auth/tarang-auth.mjs';
|
|
17
18
|
import { ApprovalManager } from './approval.mjs';
|
|
18
|
-
import { ContextRetriever } from '../context/retriever.mjs';
|
|
19
|
-
import { buildProjectSkeleton } from '../context/skeleton.mjs';
|
|
20
19
|
|
|
21
20
|
/**
|
|
22
21
|
* Run a single instruction in headless mode.
|
|
@@ -46,22 +45,8 @@ export async function runHeadless({ instruction, model, timeout = 300, maxCost,
|
|
|
46
45
|
process.exit(1);
|
|
47
46
|
}
|
|
48
47
|
|
|
49
|
-
//
|
|
50
|
-
|
|
51
|
-
const retriever = new ContextRetriever(process.cwd());
|
|
52
|
-
try {
|
|
53
|
-
const indexPromise = retriever.buildIndex();
|
|
54
|
-
const indexTimeout = new Promise((_, reject) =>
|
|
55
|
-
setTimeout(() => reject(new Error('Index timeout')), 15000)
|
|
56
|
-
);
|
|
57
|
-
await Promise.race([indexPromise, indexTimeout]);
|
|
58
|
-
log('Index ready');
|
|
59
|
-
} catch (e) {
|
|
60
|
-
log(`Index skipped: ${e.message || 'failed'}`);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
const skeleton = buildProjectSkeleton(process.cwd());
|
|
64
|
-
const toolExecutor = createToolExecutor({ retriever });
|
|
48
|
+
// Projects are registered and indexed only when the agent requests an overview.
|
|
49
|
+
const toolExecutor = createToolExecutor();
|
|
65
50
|
|
|
66
51
|
// Auto-approve everything — no prompts
|
|
67
52
|
const approval = new ApprovalManager({ autoApprove: true });
|
|
@@ -85,14 +70,24 @@ export async function runHeadless({ instruction, model, timeout = 300, maxCost,
|
|
|
85
70
|
// ── Execute ──
|
|
86
71
|
emit({ type: 'start', timestamp: Date.now(), instruction, model: model || 'default', cwd: process.cwd() });
|
|
87
72
|
|
|
88
|
-
const execContext = {
|
|
89
|
-
|
|
73
|
+
const execContext = {
|
|
74
|
+
cwd: process.cwd(),
|
|
75
|
+
freeswim: true,
|
|
76
|
+
project_resources: toolExecutor.getProjectResources(),
|
|
77
|
+
agent_context: toolExecutor.getAgentContext(),
|
|
78
|
+
};
|
|
90
79
|
if (model) execContext.model_override = model;
|
|
91
80
|
|
|
92
81
|
let toolCount = 0;
|
|
93
82
|
let finalContent = '';
|
|
94
83
|
let totalCost = 0;
|
|
95
84
|
|
|
85
|
+
// ── Telemetry collectors ──
|
|
86
|
+
const toolCalls = []; // { tool, duration_ms, success }
|
|
87
|
+
const subAgents = []; // { type, model, duration_s, tool_calls, success }
|
|
88
|
+
let stagnationCount = 0;
|
|
89
|
+
let usage = {}; // { input_tokens, output_tokens, cache_read, cache_write }
|
|
90
|
+
|
|
96
91
|
try {
|
|
97
92
|
for await (const event of client.execute(instruction, execContext)) {
|
|
98
93
|
const { type, data } = event;
|
|
@@ -101,6 +96,7 @@ export async function runHeadless({ instruction, model, timeout = 300, maxCost,
|
|
|
101
96
|
toolCount++;
|
|
102
97
|
const toolName = data?.tool || 'unknown';
|
|
103
98
|
const args = data?.args || {};
|
|
99
|
+
toolCalls.push({ tool: toolName, success: null, duration_ms: 0 });
|
|
104
100
|
emit({ type: 'tool_call', tool: toolName, args, approved: true });
|
|
105
101
|
log(`Tool: ${toolName}`);
|
|
106
102
|
}
|
|
@@ -108,9 +104,42 @@ export async function runHeadless({ instruction, model, timeout = 300, maxCost,
|
|
|
108
104
|
if (type === 'tool_result' || type === 'tool_done') {
|
|
109
105
|
const success = data?.success !== false;
|
|
110
106
|
const duration = data?.duration_s || 0;
|
|
107
|
+
// Update last tool call with result
|
|
108
|
+
const last = toolCalls.findLast(t => t.tool === (data?.tool || ''));
|
|
109
|
+
if (last) { last.success = success; last.duration_ms = Math.round(duration * 1000); }
|
|
111
110
|
emit({ type: 'tool_result', tool: data?.tool || '', success, duration_ms: Math.round(duration * 1000) });
|
|
112
111
|
}
|
|
113
112
|
|
|
113
|
+
if (type === 'sub_agent_start') {
|
|
114
|
+
log(`SubAgent: ${data?.type} (${data?.model})`);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (type === 'sub_agent_complete') {
|
|
118
|
+
subAgents.push({
|
|
119
|
+
type: data?.type || '',
|
|
120
|
+
model: data?.model || '',
|
|
121
|
+
duration_s: data?.duration_s || 0,
|
|
122
|
+
tool_calls: data?.tool_calls || 0,
|
|
123
|
+
success: data?.success !== false,
|
|
124
|
+
});
|
|
125
|
+
emit({ type: 'sub_agent', ...data });
|
|
126
|
+
log(`SubAgent done: ${data?.type} (${data?.tool_calls} tools, ${data?.duration_s}s)`);
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (type === 'plan_created' || type === 'goal_created') {
|
|
130
|
+
persistProjectArtifacts(
|
|
131
|
+
data,
|
|
132
|
+
toolExecutor.getProjectResources(),
|
|
133
|
+
log,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (type === 'stagnation' || type === 'stagnation_detected') {
|
|
138
|
+
stagnationCount++;
|
|
139
|
+
emit({ type: 'stagnation', reason: data?.reason || '', strategy: data?.recovery_strategy || '' });
|
|
140
|
+
log(`Stagnation: ${data?.reason || ''}`);
|
|
141
|
+
}
|
|
142
|
+
|
|
114
143
|
if (type === 'content') {
|
|
115
144
|
finalContent = data?.text || '';
|
|
116
145
|
}
|
|
@@ -122,10 +151,15 @@ export async function runHeadless({ instruction, model, timeout = 300, maxCost,
|
|
|
122
151
|
|
|
123
152
|
if (type === 'complete') {
|
|
124
153
|
totalCost = data?.cost || data?.total_cost || 0;
|
|
125
|
-
|
|
126
|
-
//
|
|
127
|
-
if (data?.usage
|
|
128
|
-
|
|
154
|
+
if (data?.usage?.total_cost) totalCost = data.usage.total_cost;
|
|
155
|
+
// Capture token usage
|
|
156
|
+
if (data?.usage) {
|
|
157
|
+
usage = {
|
|
158
|
+
input_tokens: data.usage.total_input_tokens || data.usage.input_tokens || 0,
|
|
159
|
+
output_tokens: data.usage.total_output_tokens || data.usage.output_tokens || 0,
|
|
160
|
+
cache_read: data.usage.cache_read_input_tokens || data.usage.cache_read || 0,
|
|
161
|
+
cache_write: data.usage.cache_creation_input_tokens || data.usage.cache_write || 0,
|
|
162
|
+
};
|
|
129
163
|
}
|
|
130
164
|
}
|
|
131
165
|
|
|
@@ -148,9 +182,19 @@ export async function runHeadless({ instruction, model, timeout = 300, maxCost,
|
|
|
148
182
|
|
|
149
183
|
const durationS = (Date.now() - startTime) / 1000;
|
|
150
184
|
|
|
185
|
+
// ── Tool breakdown ──
|
|
186
|
+
const toolBreakdown = {};
|
|
187
|
+
for (const t of toolCalls) {
|
|
188
|
+
toolBreakdown[t.tool] = (toolBreakdown[t.tool] || 0) + 1;
|
|
189
|
+
}
|
|
190
|
+
|
|
151
191
|
emit({
|
|
152
192
|
type: 'complete',
|
|
153
193
|
tools: toolCount,
|
|
194
|
+
tool_breakdown: toolBreakdown,
|
|
195
|
+
sub_agents: subAgents,
|
|
196
|
+
stagnation_triggers: stagnationCount,
|
|
197
|
+
usage,
|
|
154
198
|
duration_s: Math.round(durationS * 10) / 10,
|
|
155
199
|
cost_usd: totalCost,
|
|
156
200
|
model: model || 'default',
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import * as fs from 'node:fs';
|
|
2
|
+
import * as path from 'node:path';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Persist generated session artifacts only beneath roots registered by the CLI.
|
|
6
|
+
*/
|
|
7
|
+
export function persistProjectArtifacts(data, resources, log = () => {}) {
|
|
8
|
+
const projectIds = new Set(
|
|
9
|
+
Array.isArray(data?.project_ids) ? data.project_ids.filter(Boolean) : []
|
|
10
|
+
);
|
|
11
|
+
const registered = Array.isArray(resources) ? resources : [];
|
|
12
|
+
const targets = projectIds.size > 0
|
|
13
|
+
? registered.filter(resource => projectIds.has(resource.project_id))
|
|
14
|
+
: registered.length === 1 ? registered : [];
|
|
15
|
+
const artifacts = [
|
|
16
|
+
['goal', 'goal.md'],
|
|
17
|
+
['plan', 'plan.md'],
|
|
18
|
+
].filter(([field]) => typeof data?.[field] === 'string' && data[field].trim());
|
|
19
|
+
const written = [];
|
|
20
|
+
|
|
21
|
+
for (const resource of targets) {
|
|
22
|
+
for (const [field, filename] of artifacts) {
|
|
23
|
+
try {
|
|
24
|
+
const keplerDir = path.join(resource.root, '.kepler');
|
|
25
|
+
fs.mkdirSync(keplerDir, { recursive: true });
|
|
26
|
+
const artifactPath = path.join(keplerDir, filename);
|
|
27
|
+
fs.writeFileSync(artifactPath, data[field], 'utf-8');
|
|
28
|
+
resource[field] = data[field];
|
|
29
|
+
written.push(artifactPath);
|
|
30
|
+
log(`${field} saved -> ${artifactPath}`);
|
|
31
|
+
} catch {
|
|
32
|
+
// Persistence is helpful but must not stop the agent.
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
return written;
|
|
37
|
+
}
|