klyro 0.1.4 → 0.1.5
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/dist/agent/anthropic-adapter.d.ts +1 -1
- package/dist/agent/anthropic-adapter.js +22 -20
- package/dist/agent/provider-adapter.js +30 -7
- package/dist/agent/registry.js +24 -7
- package/dist/agent/retry.js +54 -9
- package/dist/agent/runtime.d.ts +45 -1
- package/dist/agent/runtime.js +236 -23
- package/dist/chat.d.ts +3 -3
- package/dist/chat.js +137 -39
- package/dist/cli/auth.d.ts +9 -0
- package/dist/cli/auth.js +84 -0
- package/dist/cli/completion.d.ts +6 -0
- package/dist/cli/completion.js +66 -0
- package/dist/cli/config.d.ts +44 -0
- package/dist/cli/config.js +428 -0
- package/dist/cli/doctor.d.ts +8 -0
- package/dist/cli/doctor.js +136 -0
- package/dist/cli/errors.d.ts +5 -0
- package/dist/cli/errors.js +37 -0
- package/dist/cli/markdown.d.ts +9 -0
- package/dist/cli/markdown.js +77 -0
- package/dist/cli/repl.js +113 -5
- package/dist/cli/run.d.ts +9 -0
- package/dist/cli/run.js +97 -2
- package/dist/cli/slash/parser.d.ts +10 -0
- package/dist/cli/slash/parser.js +6 -1
- package/dist/cli/trace.d.ts +7 -0
- package/dist/cli/trace.js +63 -0
- package/dist/cli/update.d.ts +6 -0
- package/dist/cli/update.js +70 -0
- package/dist/context/system-prompt.d.ts +14 -0
- package/dist/context/system-prompt.js +47 -0
- package/dist/events/bus.d.ts +16 -0
- package/dist/events/bus.js +28 -0
- package/dist/events/catalog.d.ts +136 -0
- package/dist/events/catalog.js +5 -0
- package/dist/index.js +335 -22
- package/dist/persistence/session.d.ts +12 -0
- package/dist/persistence/session.js +45 -0
- package/dist/policy/engine.d.ts +11 -1
- package/dist/policy/engine.js +85 -2
- package/dist/providers/model-info.d.ts +18 -0
- package/dist/providers/model-info.js +18 -0
- package/dist/providers.js +3 -2
- package/dist/renderers/json.d.ts +11 -0
- package/dist/renderers/json.js +14 -0
- package/dist/renderers/terminal.d.ts +9 -0
- package/dist/renderers/terminal.js +41 -0
- package/dist/repl.js +52 -15
- package/dist/shared/errors.d.ts +18 -0
- package/dist/shared/errors.js +33 -0
- package/dist/shared/index.d.ts +2 -0
- package/dist/shared/index.js +2 -0
- package/dist/shared/types.d.ts +19 -0
- package/dist/shared/types.js +11 -0
- package/dist/tools/fs/read-file.d.ts +1 -31
- package/dist/tools/fs/read-file.js +45 -17
- package/dist/tools/fs/read-history.d.ts +3 -0
- package/dist/tools/fs/read-history.js +13 -0
- package/dist/tools/fs/write-file.d.ts +1 -4
- package/dist/tools/fs/write-file.js +35 -1
- package/dist/tools/normalize.d.ts +1 -1
- package/dist/tools/shell/shell-exec.d.ts +1 -26
- package/dist/tools/shell/shell-exec.js +121 -17
- package/dist/tools/types.d.ts +25 -0
- package/dist/trace/writer.d.ts +13 -0
- package/dist/trace/writer.js +53 -0
- package/dist/tui/app.d.ts +1 -15
- package/dist/tui/app.js +203 -22
- package/dist/tui/approval.js +25 -4
- package/dist/tui/approval.test.js +1 -1
- package/dist/tui/snapshot.test.js +2 -2
- package/dist/tui/status.js +5 -1
- package/dist/util/log.d.ts +12 -0
- package/dist/util/log.js +75 -0
- package/dist/verification/auto.d.ts +8 -0
- package/dist/verification/auto.js +52 -0
- package/package.json +3 -2
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 3.1 — klyro trace <session> pretty-print, --stats
|
|
3
|
+
*/
|
|
4
|
+
import * as fs from 'node:fs/promises';
|
|
5
|
+
import * as path from 'node:path';
|
|
6
|
+
async function readTraceFile(sessionId) {
|
|
7
|
+
// Try trace writer path first: .klyro/traces/<id>.jsonl
|
|
8
|
+
const tracePath = path.join(process.cwd(), '.klyro', 'traces', `${sessionId}.jsonl`);
|
|
9
|
+
try {
|
|
10
|
+
const raw = await fs.readFile(tracePath, 'utf-8');
|
|
11
|
+
return raw.split('\n').filter(Boolean).map((l) => JSON.parse(l));
|
|
12
|
+
}
|
|
13
|
+
catch {
|
|
14
|
+
// Fallback to session store messages
|
|
15
|
+
const { getDefaultSessionStore } = await import('../persistence/session.js');
|
|
16
|
+
const store = getDefaultSessionStore();
|
|
17
|
+
const msgs = await store.loadMessages(sessionId);
|
|
18
|
+
return msgs;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
export async function runTrace(sessionId, opts = {}) {
|
|
22
|
+
const { getDefaultSessionStore, resolveSessionId } = await import('../persistence/session.js');
|
|
23
|
+
const store = getDefaultSessionStore();
|
|
24
|
+
const full = await resolveSessionId(store, sessionId);
|
|
25
|
+
if (!full) {
|
|
26
|
+
// Try direct trace file
|
|
27
|
+
const events = await readTraceFile(sessionId);
|
|
28
|
+
if (events.length > 0) {
|
|
29
|
+
if (opts.json)
|
|
30
|
+
process.stdout.write(JSON.stringify(events, null, 2) + '\n');
|
|
31
|
+
else
|
|
32
|
+
for (const ev of events)
|
|
33
|
+
process.stdout.write(JSON.stringify(ev) + '\n');
|
|
34
|
+
return 0;
|
|
35
|
+
}
|
|
36
|
+
process.stderr.write(`session/trace not found: ${sessionId}\n`);
|
|
37
|
+
return 2;
|
|
38
|
+
}
|
|
39
|
+
const events = await readTraceFile(full);
|
|
40
|
+
if (opts.stats) {
|
|
41
|
+
const byType = {};
|
|
42
|
+
for (const ev of events) {
|
|
43
|
+
const t = ev.type ?? 'unknown';
|
|
44
|
+
byType[t] = (byType[t] ?? 0) + 1;
|
|
45
|
+
}
|
|
46
|
+
const out = { sessionId: full, total: events.length, byType };
|
|
47
|
+
process.stdout.write(JSON.stringify(out, null, 2) + '\n');
|
|
48
|
+
return 0;
|
|
49
|
+
}
|
|
50
|
+
if (opts.json) {
|
|
51
|
+
process.stdout.write(JSON.stringify(events, null, 2) + '\n');
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
for (const ev of events) {
|
|
55
|
+
const e = ev;
|
|
56
|
+
if (e.type === 'stream.delta' && e.text)
|
|
57
|
+
process.stdout.write(e.text);
|
|
58
|
+
else
|
|
59
|
+
process.stdout.write(JSON.stringify(ev) + '\n');
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
return 0;
|
|
63
|
+
}
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* klyro update — check registry for newer version, cached 24h.
|
|
3
|
+
* Env KLYRO_NO_UPDATE_CHECK=1 disables.
|
|
4
|
+
*/
|
|
5
|
+
import * as fs from 'node:fs/promises';
|
|
6
|
+
import * as path from 'node:path';
|
|
7
|
+
import * as os from 'node:os';
|
|
8
|
+
const CACHE_TTL_MS = 24 * 60 * 60 * 1000;
|
|
9
|
+
function cachePath() {
|
|
10
|
+
const home = os.homedir() || process.cwd();
|
|
11
|
+
return path.join(home, '.klyro', 'update-cache.json');
|
|
12
|
+
}
|
|
13
|
+
export async function checkForUpdate(current) {
|
|
14
|
+
if (process.env.KLYRO_NO_UPDATE_CHECK === '1')
|
|
15
|
+
return null;
|
|
16
|
+
const cache = cachePath();
|
|
17
|
+
try {
|
|
18
|
+
const raw = await fs.readFile(cache, 'utf-8');
|
|
19
|
+
const data = JSON.parse(raw);
|
|
20
|
+
if (Date.now() - data.at < CACHE_TTL_MS) {
|
|
21
|
+
return data.latest !== current ? data.latest : null;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
catch {
|
|
25
|
+
// ignore
|
|
26
|
+
}
|
|
27
|
+
try {
|
|
28
|
+
const ctrl = new AbortController();
|
|
29
|
+
const t = setTimeout(() => ctrl.abort(), 3000);
|
|
30
|
+
const res = await fetch('https://registry.npmjs.org/klyro/latest', { signal: ctrl.signal });
|
|
31
|
+
clearTimeout(t);
|
|
32
|
+
if (!res.ok)
|
|
33
|
+
return null;
|
|
34
|
+
const json = (await res.json());
|
|
35
|
+
const latest = json.version ?? '';
|
|
36
|
+
await fs.mkdir(path.dirname(cache), { recursive: true });
|
|
37
|
+
await fs.writeFile(cache, JSON.stringify({ at: Date.now(), latest }), 'utf-8');
|
|
38
|
+
if (latest && latest !== current)
|
|
39
|
+
return latest;
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
// network failure — silent
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
export async function runUpdate() {
|
|
47
|
+
const here = await import('../index.js').then(() => '');
|
|
48
|
+
// Get version from package.json via dynamic import
|
|
49
|
+
const { readFileSync } = await import('node:fs');
|
|
50
|
+
const { resolve, dirname } = await import('node:path');
|
|
51
|
+
const { fileURLToPath } = await import('node:url');
|
|
52
|
+
try {
|
|
53
|
+
const pkgPath = resolve(dirname(fileURLToPath(import.meta.url)), '../../package.json');
|
|
54
|
+
const pkg = JSON.parse(readFileSync(pkgPath, 'utf-8'));
|
|
55
|
+
const cur = pkg.version ?? '0.0.0';
|
|
56
|
+
const latest = await checkForUpdate(cur);
|
|
57
|
+
if (latest) {
|
|
58
|
+
process.stdout.write(`Update available: ${cur} → ${latest}\n npm i -g klyro@latest\n`);
|
|
59
|
+
}
|
|
60
|
+
else {
|
|
61
|
+
process.stdout.write(`klyro ${cur} is latest\n`);
|
|
62
|
+
}
|
|
63
|
+
return 0;
|
|
64
|
+
}
|
|
65
|
+
catch (err) {
|
|
66
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
67
|
+
process.stderr.write(`klyro update: ${msg}\n`);
|
|
68
|
+
return 1;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 2.3 — Layered system prompt (stable first): identity → environment → global instructions
|
|
3
|
+
*/
|
|
4
|
+
export declare function buildSystemPrompt(opts: {
|
|
5
|
+
cwd: string;
|
|
6
|
+
model: string;
|
|
7
|
+
extraSystem?: string;
|
|
8
|
+
appendSystem?: string;
|
|
9
|
+
thinkingEnabled?: boolean;
|
|
10
|
+
}): string;
|
|
11
|
+
export declare function parseImageInput(text: string): {
|
|
12
|
+
text: string;
|
|
13
|
+
images: string[];
|
|
14
|
+
};
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 2.3 — Layered system prompt (stable first): identity → environment → global instructions
|
|
3
|
+
*/
|
|
4
|
+
import * as os from 'node:os';
|
|
5
|
+
import { spawnSync } from 'node:child_process';
|
|
6
|
+
function getBranch(cwd) {
|
|
7
|
+
try {
|
|
8
|
+
const r = spawnSync('git', ['branch', '--show-current'], { cwd, encoding: 'utf-8', timeout: 800, windowsHide: true });
|
|
9
|
+
if (r.status === 0)
|
|
10
|
+
return r.stdout.trim();
|
|
11
|
+
}
|
|
12
|
+
catch { /* ignore */ }
|
|
13
|
+
return '';
|
|
14
|
+
}
|
|
15
|
+
export function buildSystemPrompt(opts) {
|
|
16
|
+
const identity = `You are Klyro, an autonomous coding harness. You solve the user's task by calling tools in a loop. Prefer the smallest change that solves the task. When you have finished, produce a short final text answer (no tool calls). Do not invent file paths. Do not call tools outside the working directory.`;
|
|
17
|
+
const env = [
|
|
18
|
+
`OS: ${os.type()} ${os.release()} ${os.arch()}`,
|
|
19
|
+
`Shell: ${process.env.SHELL ?? process.env.COMSPEC ?? 'unknown'}`,
|
|
20
|
+
`CWD: ${opts.cwd}`,
|
|
21
|
+
`Date: ${new Date().toISOString().slice(0, 10)}`,
|
|
22
|
+
`Branch: ${getBranch(opts.cwd) || '(no git)'}`,
|
|
23
|
+
`Model: ${opts.model}`,
|
|
24
|
+
].join('\n');
|
|
25
|
+
const global = `Global instructions: Be concise, verify after edits, and never say "Done" without running verification.`;
|
|
26
|
+
const parts = [identity, `Environment:\n${env}`, global];
|
|
27
|
+
if (opts.extraSystem)
|
|
28
|
+
parts.splice(1, 0, opts.extraSystem);
|
|
29
|
+
if (opts.appendSystem)
|
|
30
|
+
parts.push(opts.appendSystem);
|
|
31
|
+
if (opts.thinkingEnabled)
|
|
32
|
+
parts.push('Thinking: enabled — use internal reasoning before tool calls.');
|
|
33
|
+
return parts.join('\n\n');
|
|
34
|
+
}
|
|
35
|
+
export function parseImageInput(text) {
|
|
36
|
+
// Handle @img.png, drag-drop path, clipboard stub
|
|
37
|
+
const images = [];
|
|
38
|
+
const imgRe = /@([^\s]+\.(png|jpg|jpeg|gif|webp))/gi;
|
|
39
|
+
let m;
|
|
40
|
+
while ((m = imgRe.exec(text))) {
|
|
41
|
+
if (m[1])
|
|
42
|
+
images.push(m[1]);
|
|
43
|
+
}
|
|
44
|
+
// Remove @img refs from text for now (real impl would attach as image block)
|
|
45
|
+
const clean = text.replace(imgRe, '').trim();
|
|
46
|
+
return { text: clean, images };
|
|
47
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 3.1 — core/events emitter
|
|
3
|
+
* In-memory pub/sub for KlyroEvents. Sync delivery, no buffering.
|
|
4
|
+
*/
|
|
5
|
+
import type { KlyroEvent } from './catalog.js';
|
|
6
|
+
type Listener = (ev: KlyroEvent) => void;
|
|
7
|
+
export declare class EventBus {
|
|
8
|
+
private listeners;
|
|
9
|
+
private history;
|
|
10
|
+
emit(ev: KlyroEvent): void;
|
|
11
|
+
on(listener: Listener): () => void;
|
|
12
|
+
getHistory(): KlyroEvent[];
|
|
13
|
+
clear(): void;
|
|
14
|
+
}
|
|
15
|
+
export declare const globalBus: EventBus;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 3.1 — core/events emitter
|
|
3
|
+
* In-memory pub/sub for KlyroEvents. Sync delivery, no buffering.
|
|
4
|
+
*/
|
|
5
|
+
export class EventBus {
|
|
6
|
+
listeners = new Set();
|
|
7
|
+
history = [];
|
|
8
|
+
emit(ev) {
|
|
9
|
+
this.history.push(ev);
|
|
10
|
+
for (const l of [...this.listeners]) {
|
|
11
|
+
try {
|
|
12
|
+
l(ev);
|
|
13
|
+
}
|
|
14
|
+
catch { /* ignore listener error */ }
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
on(listener) {
|
|
18
|
+
this.listeners.add(listener);
|
|
19
|
+
return () => this.listeners.delete(listener);
|
|
20
|
+
}
|
|
21
|
+
getHistory() {
|
|
22
|
+
return [...this.history];
|
|
23
|
+
}
|
|
24
|
+
clear() {
|
|
25
|
+
this.history = [];
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export const globalBus = new EventBus();
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 3.1 — KlyroEvent catalog (Appendix C)
|
|
3
|
+
* Every observable action in the harness is a typed event.
|
|
4
|
+
*/
|
|
5
|
+
export type KlyroEvent = {
|
|
6
|
+
type: 'session.start';
|
|
7
|
+
ts: number;
|
|
8
|
+
sessionId: string;
|
|
9
|
+
task: string;
|
|
10
|
+
cwd: string;
|
|
11
|
+
model: string;
|
|
12
|
+
} | {
|
|
13
|
+
type: 'session.end';
|
|
14
|
+
ts: number;
|
|
15
|
+
sessionId: string;
|
|
16
|
+
status: string;
|
|
17
|
+
durationMs: number;
|
|
18
|
+
} | {
|
|
19
|
+
type: 'turn.start';
|
|
20
|
+
ts: number;
|
|
21
|
+
sessionId: string;
|
|
22
|
+
turn: number;
|
|
23
|
+
model: string;
|
|
24
|
+
} | {
|
|
25
|
+
type: 'turn.end';
|
|
26
|
+
ts: number;
|
|
27
|
+
sessionId: string;
|
|
28
|
+
turn: number;
|
|
29
|
+
finishReason?: string;
|
|
30
|
+
} | {
|
|
31
|
+
type: 'stream.delta';
|
|
32
|
+
ts: number;
|
|
33
|
+
sessionId: string;
|
|
34
|
+
text: string;
|
|
35
|
+
} | {
|
|
36
|
+
type: 'stream.thinking';
|
|
37
|
+
ts: number;
|
|
38
|
+
sessionId: string;
|
|
39
|
+
text: string;
|
|
40
|
+
} | {
|
|
41
|
+
type: 'tool.call';
|
|
42
|
+
ts: number;
|
|
43
|
+
sessionId: string;
|
|
44
|
+
callId: string;
|
|
45
|
+
name: string;
|
|
46
|
+
input: unknown;
|
|
47
|
+
} | {
|
|
48
|
+
type: 'tool.result';
|
|
49
|
+
ts: number;
|
|
50
|
+
sessionId: string;
|
|
51
|
+
callId: string;
|
|
52
|
+
name: string;
|
|
53
|
+
output: unknown;
|
|
54
|
+
isError: boolean;
|
|
55
|
+
latencyMs: number;
|
|
56
|
+
} | {
|
|
57
|
+
type: 'permission.ask';
|
|
58
|
+
ts: number;
|
|
59
|
+
sessionId: string;
|
|
60
|
+
callId: string;
|
|
61
|
+
name: string;
|
|
62
|
+
reason?: string;
|
|
63
|
+
} | {
|
|
64
|
+
type: 'permission.decision';
|
|
65
|
+
ts: number;
|
|
66
|
+
sessionId: string;
|
|
67
|
+
callId: string;
|
|
68
|
+
action: 'allow' | 'deny' | 'ask';
|
|
69
|
+
reason?: string;
|
|
70
|
+
} | {
|
|
71
|
+
type: 'policy.decision';
|
|
72
|
+
ts: number;
|
|
73
|
+
sessionId: string;
|
|
74
|
+
callId: string;
|
|
75
|
+
name: string;
|
|
76
|
+
action: 'allow' | 'deny' | 'ask';
|
|
77
|
+
reason?: string;
|
|
78
|
+
} | {
|
|
79
|
+
type: 'file.changed';
|
|
80
|
+
ts: number;
|
|
81
|
+
sessionId: string;
|
|
82
|
+
path: string;
|
|
83
|
+
op: 'created' | 'modified' | 'deleted';
|
|
84
|
+
} | {
|
|
85
|
+
type: 'phase.changed';
|
|
86
|
+
ts: number;
|
|
87
|
+
sessionId: string;
|
|
88
|
+
phase: string;
|
|
89
|
+
} | {
|
|
90
|
+
type: 'verification.started';
|
|
91
|
+
ts: number;
|
|
92
|
+
sessionId: string;
|
|
93
|
+
command: string;
|
|
94
|
+
} | {
|
|
95
|
+
type: 'verification.succeeded';
|
|
96
|
+
ts: number;
|
|
97
|
+
sessionId: string;
|
|
98
|
+
command: string;
|
|
99
|
+
} | {
|
|
100
|
+
type: 'verification.failed';
|
|
101
|
+
ts: number;
|
|
102
|
+
sessionId: string;
|
|
103
|
+
command: string;
|
|
104
|
+
reason: string;
|
|
105
|
+
} | {
|
|
106
|
+
type: 'repair.started';
|
|
107
|
+
ts: number;
|
|
108
|
+
sessionId: string;
|
|
109
|
+
attempt: number;
|
|
110
|
+
maxAttempts: number;
|
|
111
|
+
reason: string;
|
|
112
|
+
} | {
|
|
113
|
+
type: 'checkpoint.saved';
|
|
114
|
+
ts: number;
|
|
115
|
+
sessionId: string;
|
|
116
|
+
} | {
|
|
117
|
+
type: 'usage';
|
|
118
|
+
ts: number;
|
|
119
|
+
sessionId: string;
|
|
120
|
+
input: number;
|
|
121
|
+
output: number;
|
|
122
|
+
cost?: number;
|
|
123
|
+
} | {
|
|
124
|
+
type: 'error';
|
|
125
|
+
ts: number;
|
|
126
|
+
sessionId: string;
|
|
127
|
+
code: string;
|
|
128
|
+
message: string;
|
|
129
|
+
retryable?: boolean;
|
|
130
|
+
} | {
|
|
131
|
+
type: 'abort';
|
|
132
|
+
ts: number;
|
|
133
|
+
sessionId: string;
|
|
134
|
+
reason: string;
|
|
135
|
+
};
|
|
136
|
+
export type KlyroEventType = KlyroEvent['type'];
|