klyro 0.1.3 → 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.d.ts +2 -0
- package/dist/cli/repl.js +280 -52
- 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 +369 -23
- 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 +7 -14
- package/dist/tui/app.js +228 -26
- package/dist/tui/approval.js +25 -4
- package/dist/tui/approval.test.js +1 -1
- package/dist/tui/snapshot.test.d.ts +6 -0
- package/dist/tui/snapshot.test.js +87 -0
- 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,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LEVEL 1 — klyro doctor
|
|
3
|
+
* Runs quick diagnostics: node version, config, provider reachability,
|
|
4
|
+
* persistence dir, git, tools.
|
|
5
|
+
*/
|
|
6
|
+
import * as fs from 'node:fs/promises';
|
|
7
|
+
import * as fsSync from 'node:fs';
|
|
8
|
+
import * as path from 'node:path';
|
|
9
|
+
import { spawn } from 'node:child_process';
|
|
10
|
+
import { getConfigPath, loadConfig } from './config.js';
|
|
11
|
+
import { resolveProvider, providerHelp } from '../providers.js';
|
|
12
|
+
import { getDefaultSessionsDir } from '../persistence/session.js';
|
|
13
|
+
import { builtinRegistry } from '../tools/registry.js';
|
|
14
|
+
function checkNode() {
|
|
15
|
+
const v = process.versions.node;
|
|
16
|
+
const major = Number(v.split('.')[0] ?? 0);
|
|
17
|
+
const ok = major >= 20;
|
|
18
|
+
return {
|
|
19
|
+
name: 'Node.js',
|
|
20
|
+
ok,
|
|
21
|
+
detail: `${v} ${ok ? '✓' : '✗ requires >=20'} (${process.execPath})`,
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
async function checkConfig() {
|
|
25
|
+
const p = getConfigPath();
|
|
26
|
+
try {
|
|
27
|
+
await loadConfig();
|
|
28
|
+
const exists = fsSync.existsSync(p);
|
|
29
|
+
return { name: 'Config', ok: true, detail: exists ? `${p} ✓` : `${p} (not yet created) ✓` };
|
|
30
|
+
}
|
|
31
|
+
catch (err) {
|
|
32
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
33
|
+
return { name: 'Config', ok: false, detail: `${p} ✗ ${msg}` };
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
async function checkProvider() {
|
|
37
|
+
try {
|
|
38
|
+
const prov = await resolveProvider();
|
|
39
|
+
if (!prov) {
|
|
40
|
+
return { name: 'Provider', ok: false, detail: `none ${providerHelp(null)}` };
|
|
41
|
+
}
|
|
42
|
+
const src = prov.source === 'local-probe' ? 'local-probe' : prov.source === 'env' ? 'env' : 'manual';
|
|
43
|
+
const auth = prov.apiKey ? 'key set' : 'no key (local)';
|
|
44
|
+
return { name: 'Provider', ok: true, detail: `${prov.baseURL} model=${prov.model} (${src}, ${auth}) ✓` };
|
|
45
|
+
}
|
|
46
|
+
catch (err) {
|
|
47
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
48
|
+
return { name: 'Provider', ok: false, detail: `error: ${msg}` };
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
async function checkSessions() {
|
|
52
|
+
const dir = getDefaultSessionsDir();
|
|
53
|
+
try {
|
|
54
|
+
await fs.mkdir(dir, { recursive: true });
|
|
55
|
+
const test = path.join(dir, `.doctor-${Date.now()}.tmp`);
|
|
56
|
+
await fs.writeFile(test, 'ok', 'utf-8');
|
|
57
|
+
await fs.unlink(test);
|
|
58
|
+
return { name: 'Sessions dir', ok: true, detail: `${dir} writable ✓` };
|
|
59
|
+
}
|
|
60
|
+
catch (err) {
|
|
61
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
62
|
+
return { name: 'Sessions dir', ok: false, detail: `${dir} ✗ ${msg}` };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
function checkGit() {
|
|
66
|
+
return new Promise((resolve) => {
|
|
67
|
+
const child = spawn('git', ['--version'], { shell: false, windowsHide: true });
|
|
68
|
+
let out = '';
|
|
69
|
+
let done = false;
|
|
70
|
+
const timer = setTimeout(() => {
|
|
71
|
+
if (done)
|
|
72
|
+
return;
|
|
73
|
+
done = true;
|
|
74
|
+
child.kill();
|
|
75
|
+
resolve({ name: 'git', ok: false, detail: 'timeout' });
|
|
76
|
+
}, 3000);
|
|
77
|
+
child.stdout.on('data', (b) => { out += b.toString(); });
|
|
78
|
+
child.on('error', () => {
|
|
79
|
+
if (done)
|
|
80
|
+
return;
|
|
81
|
+
done = true;
|
|
82
|
+
clearTimeout(timer);
|
|
83
|
+
resolve({ name: 'git', ok: false, detail: 'not found (install git)' });
|
|
84
|
+
});
|
|
85
|
+
child.on('close', (code) => {
|
|
86
|
+
if (done)
|
|
87
|
+
return;
|
|
88
|
+
done = true;
|
|
89
|
+
clearTimeout(timer);
|
|
90
|
+
if (code === 0)
|
|
91
|
+
resolve({ name: 'git', ok: true, detail: out.trim() + ' ✓' });
|
|
92
|
+
else
|
|
93
|
+
resolve({ name: 'git', ok: false, detail: `exit ${code}` });
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
function checkTools() {
|
|
98
|
+
const reg = builtinRegistry();
|
|
99
|
+
return { name: 'Tools', ok: true, detail: `${reg.list().length} tools: ${reg.list().map((t) => t.name).join(', ')}` };
|
|
100
|
+
}
|
|
101
|
+
function checkPlatform() {
|
|
102
|
+
const ok = ['win32', 'linux', 'darwin'].includes(process.platform);
|
|
103
|
+
return { name: 'Platform', ok, detail: `${process.platform} ${process.arch} ${ok ? '✓' : '✗ unsupported'}` };
|
|
104
|
+
}
|
|
105
|
+
export async function runDoctor(opts = {}) {
|
|
106
|
+
const checks = [];
|
|
107
|
+
checks.push(checkNode());
|
|
108
|
+
checks.push(await checkConfig());
|
|
109
|
+
checks.push(await checkProvider());
|
|
110
|
+
checks.push(await checkSessions());
|
|
111
|
+
checks.push(await checkGit());
|
|
112
|
+
checks.push(checkTools());
|
|
113
|
+
checks.push(checkPlatform());
|
|
114
|
+
const allOk = checks.every((c) => c.ok);
|
|
115
|
+
if (opts.json) {
|
|
116
|
+
process.stdout.write(JSON.stringify({ ok: allOk, checks }, null, 2) + '\n');
|
|
117
|
+
return allOk ? 0 : 1;
|
|
118
|
+
}
|
|
119
|
+
process.stdout.write('\nklyro doctor\n');
|
|
120
|
+
process.stdout.write('─'.repeat(40) + '\n');
|
|
121
|
+
for (const c of checks) {
|
|
122
|
+
const glyph = c.ok ? '✓' : '✗';
|
|
123
|
+
const color = c.ok ? '' : '';
|
|
124
|
+
process.stdout.write(`${glyph} ${c.name.padEnd(14)} ${c.detail}\n`);
|
|
125
|
+
}
|
|
126
|
+
process.stdout.write('─'.repeat(40) + '\n');
|
|
127
|
+
if (allOk) {
|
|
128
|
+
process.stdout.write('All checks passed.\n');
|
|
129
|
+
}
|
|
130
|
+
else {
|
|
131
|
+
process.stdout.write('Some checks failed — see ✗ above. Fix env/config and re-run.\n');
|
|
132
|
+
process.stdout.write(`Config path: ${getConfigPath()}\n`);
|
|
133
|
+
process.stdout.write(`Sessions : ${getDefaultSessionsDir()}\n`);
|
|
134
|
+
}
|
|
135
|
+
return allOk ? 0 : 1;
|
|
136
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 1.5 — handleFatal: ✖ message / hint: / "run with --debug", mapped exit codes
|
|
3
|
+
*/
|
|
4
|
+
import { KlyroError } from '../shared/errors.js';
|
|
5
|
+
export function handleFatal(err) {
|
|
6
|
+
const isDebug = !!process.env.KLYRO_LOG_LEVEL || !!process.env.DEBUG || process.argv.includes('--debug');
|
|
7
|
+
let code = 1;
|
|
8
|
+
let message = err instanceof Error ? err.message : String(err);
|
|
9
|
+
let hint;
|
|
10
|
+
if (err instanceof KlyroError) {
|
|
11
|
+
code = err.exitCode;
|
|
12
|
+
hint = err.hint;
|
|
13
|
+
}
|
|
14
|
+
else if (err instanceof Error && err.code === 'ENOENT') {
|
|
15
|
+
code = 2;
|
|
16
|
+
hint = 'Check the file path exists';
|
|
17
|
+
}
|
|
18
|
+
// Never print stack without --debug
|
|
19
|
+
if (!isDebug) {
|
|
20
|
+
// Strip stack, only print message
|
|
21
|
+
process.stderr.write(`✖ ${message}\n`);
|
|
22
|
+
}
|
|
23
|
+
else {
|
|
24
|
+
process.stderr.write(`✖ ${message}\n`);
|
|
25
|
+
if (err instanceof Error && err.stack)
|
|
26
|
+
process.stderr.write(err.stack + '\n');
|
|
27
|
+
}
|
|
28
|
+
if (hint)
|
|
29
|
+
process.stderr.write(` hint: ${hint}\n`);
|
|
30
|
+
if (!isDebug)
|
|
31
|
+
process.stderr.write(' run with --debug for stack trace\n');
|
|
32
|
+
process.exit(code);
|
|
33
|
+
}
|
|
34
|
+
export function setupGlobalHandlers() {
|
|
35
|
+
process.on('uncaughtException', (err) => handleFatal(err));
|
|
36
|
+
process.on('unhandledRejection', (reason) => handleFatal(reason));
|
|
37
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 1.5 — incremental markdown renderer (headers, code w/ highlight, lists, tables)
|
|
3
|
+
* Minimal: handles headers, code blocks, lists, tables, width-aware wrap, plain-text for non-TTY.
|
|
4
|
+
*/
|
|
5
|
+
export declare function renderMarkdown(md: string, opts?: {
|
|
6
|
+
width?: number;
|
|
7
|
+
isTTY?: boolean;
|
|
8
|
+
}): string;
|
|
9
|
+
export declare function renderIncremental(prev: string, nextChunk: string): string;
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 1.5 — incremental markdown renderer (headers, code w/ highlight, lists, tables)
|
|
3
|
+
* Minimal: handles headers, code blocks, lists, tables, width-aware wrap, plain-text for non-TTY.
|
|
4
|
+
*/
|
|
5
|
+
export function renderMarkdown(md, opts = {}) {
|
|
6
|
+
const width = opts.width ?? (process.stdout.columns || 80);
|
|
7
|
+
const isTTY = opts.isTTY ?? !!process.stdout.isTTY;
|
|
8
|
+
if (!isTTY || process.env.KLYRO_JSON === '1' || process.env.NO_COLOR === '1') {
|
|
9
|
+
// Plain text: strip markdown decorations
|
|
10
|
+
return stripMarkdown(md);
|
|
11
|
+
}
|
|
12
|
+
let out = '';
|
|
13
|
+
const lines = md.split('\n');
|
|
14
|
+
let inCodeBlock = false;
|
|
15
|
+
for (let line of lines) {
|
|
16
|
+
if (line.startsWith('```')) {
|
|
17
|
+
inCodeBlock = !inCodeBlock;
|
|
18
|
+
out += (inCodeBlock ? '┌ code ──\n' : '└──────\n');
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
if (inCodeBlock) {
|
|
22
|
+
out += '│ ' + line + '\n';
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
if (line.startsWith('# ')) {
|
|
26
|
+
out += '\n' + line.replace(/^#\s+/, '').toUpperCase() + '\n' + '─'.repeat(Math.min(width, 40)) + '\n';
|
|
27
|
+
continue;
|
|
28
|
+
}
|
|
29
|
+
if (line.startsWith('## ')) {
|
|
30
|
+
out += '\n' + line.replace(/^##\s+/, '') + '\n';
|
|
31
|
+
continue;
|
|
32
|
+
}
|
|
33
|
+
if (line.startsWith('- ') || line.startsWith('* ')) {
|
|
34
|
+
out += '• ' + line.slice(2) + '\n';
|
|
35
|
+
continue;
|
|
36
|
+
}
|
|
37
|
+
if (line.startsWith('|') && line.endsWith('|')) {
|
|
38
|
+
// Table row: replace | with │ and trim
|
|
39
|
+
const cells = line.split('|').filter(Boolean).map((c) => c.trim());
|
|
40
|
+
out += '│ ' + cells.join(' │ ') + ' │\n';
|
|
41
|
+
continue;
|
|
42
|
+
}
|
|
43
|
+
// Wrap long lines
|
|
44
|
+
if (line.length > width) {
|
|
45
|
+
const words = line.split(' ');
|
|
46
|
+
let cur = '';
|
|
47
|
+
for (const w of words) {
|
|
48
|
+
if ((cur + ' ' + w).length > width) {
|
|
49
|
+
out += cur.trimEnd() + '\n';
|
|
50
|
+
cur = w + ' ';
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
cur += w + ' ';
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (cur.trim())
|
|
57
|
+
out += cur.trimEnd() + '\n';
|
|
58
|
+
}
|
|
59
|
+
else {
|
|
60
|
+
out += line + '\n';
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return out;
|
|
64
|
+
}
|
|
65
|
+
function stripMarkdown(md) {
|
|
66
|
+
return md
|
|
67
|
+
.replace(/^#+\s+/gm, '')
|
|
68
|
+
.replace(/```[\s\S]*?```/g, (m) => m.replace(/```/g, '').trim())
|
|
69
|
+
.replace(/\*\*(.+?)\*\*/g, '$1')
|
|
70
|
+
.replace(/\*(.+?)\*/g, '$1')
|
|
71
|
+
.replace(/`(.+?)`/g, '$1')
|
|
72
|
+
.replace(/\[([^\]]+)\]\([^)]+\)/g, '$1');
|
|
73
|
+
}
|
|
74
|
+
export function renderIncremental(prev, nextChunk) {
|
|
75
|
+
// For streaming, just append and re-render last chunk
|
|
76
|
+
return renderMarkdown(prev + nextChunk);
|
|
77
|
+
}
|
package/dist/cli/repl.d.ts
CHANGED
|
@@ -11,5 +11,7 @@ export interface ReplOptions {
|
|
|
11
11
|
maxSteps?: number;
|
|
12
12
|
model?: string;
|
|
13
13
|
nonInteractive?: boolean;
|
|
14
|
+
/** Force TUI even when stdin is not a TTY (e.g. for testing or explicit flag). */
|
|
15
|
+
forceTty?: boolean;
|
|
14
16
|
}
|
|
15
17
|
export declare function startRepl(opts?: ReplOptions): Promise<number>;
|