kairn-cli 1.9.0 → 1.10.0
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/cli.js +274 -169
- package/dist/cli.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -4,7 +4,7 @@ import chalk14 from "chalk";
|
|
|
4
4
|
|
|
5
5
|
// src/commands/init.ts
|
|
6
6
|
import { Command } from "commander";
|
|
7
|
-
import { password, select } from "@inquirer/prompts";
|
|
7
|
+
import { input, password, select } from "@inquirer/prompts";
|
|
8
8
|
import chalk3 from "chalk";
|
|
9
9
|
import Anthropic from "@anthropic-ai/sdk";
|
|
10
10
|
import OpenAI from "openai";
|
|
@@ -62,27 +62,158 @@ async function saveConfig(config) {
|
|
|
62
62
|
await fs.writeFile(CONFIG_PATH, JSON.stringify(config, null, 2), "utf-8");
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
+
// src/providers.ts
|
|
66
|
+
var PROVIDER_CONFIGS = {
|
|
67
|
+
anthropic: {
|
|
68
|
+
name: "Anthropic",
|
|
69
|
+
verifyModel: "claude-haiku-4-5-20251001",
|
|
70
|
+
cheapModel: "claude-haiku-4-5-20251001"
|
|
71
|
+
},
|
|
72
|
+
openai: {
|
|
73
|
+
name: "OpenAI",
|
|
74
|
+
verifyModel: "gpt-4.1-nano",
|
|
75
|
+
cheapModel: "gpt-4.1-nano"
|
|
76
|
+
},
|
|
77
|
+
google: {
|
|
78
|
+
name: "Google Gemini",
|
|
79
|
+
baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/",
|
|
80
|
+
verifyModel: "gemini-2.5-flash",
|
|
81
|
+
cheapModel: "gemini-2.5-flash"
|
|
82
|
+
},
|
|
83
|
+
xai: {
|
|
84
|
+
name: "xAI (Grok)",
|
|
85
|
+
baseURL: "https://api.x.ai/v1",
|
|
86
|
+
verifyModel: "grok-4-1-fast-non-reasoning",
|
|
87
|
+
cheapModel: "grok-4-1-fast-non-reasoning"
|
|
88
|
+
},
|
|
89
|
+
deepseek: {
|
|
90
|
+
name: "DeepSeek",
|
|
91
|
+
baseURL: "https://api.deepseek.com",
|
|
92
|
+
verifyModel: "deepseek-chat",
|
|
93
|
+
cheapModel: "deepseek-chat"
|
|
94
|
+
},
|
|
95
|
+
mistral: {
|
|
96
|
+
name: "Mistral",
|
|
97
|
+
baseURL: "https://api.mistral.ai/v1",
|
|
98
|
+
verifyModel: "mistral-small-latest",
|
|
99
|
+
cheapModel: "mistral-small-latest"
|
|
100
|
+
},
|
|
101
|
+
groq: {
|
|
102
|
+
name: "Groq (open-source models)",
|
|
103
|
+
baseURL: "https://api.groq.com/openai/v1",
|
|
104
|
+
verifyModel: "meta-llama/llama-4-scout-17b-16e-instruct",
|
|
105
|
+
cheapModel: "meta-llama/llama-4-scout-17b-16e-instruct"
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
var PROVIDER_MODELS = {
|
|
109
|
+
anthropic: [
|
|
110
|
+
{ name: "Claude Sonnet 4.6 (recommended)", value: "claude-sonnet-4-6" },
|
|
111
|
+
{ name: "Claude Opus 4.6 (highest quality)", value: "claude-opus-4-6" },
|
|
112
|
+
{ name: "Claude Haiku 4.5 (fastest, cheapest)", value: "claude-haiku-4-5-20251001" }
|
|
113
|
+
],
|
|
114
|
+
openai: [
|
|
115
|
+
{ name: "GPT-4.1 (recommended \u2014 smartest non-reasoning)", value: "gpt-4.1" },
|
|
116
|
+
{ name: "GPT-4.1 mini (faster, cheaper)", value: "gpt-4.1-mini" },
|
|
117
|
+
{ name: "o4-mini (reasoning, cost-efficient)", value: "o4-mini" },
|
|
118
|
+
{ name: "GPT-5 mini (frontier)", value: "gpt-5-mini" }
|
|
119
|
+
],
|
|
120
|
+
google: [
|
|
121
|
+
{ name: "Gemini 2.5 Flash (recommended \u2014 best value)", value: "gemini-2.5-flash" },
|
|
122
|
+
{ name: "Gemini 3 Flash (newest frontier)", value: "gemini-3-flash" },
|
|
123
|
+
{ name: "Gemini 2.5 Pro (highest quality)", value: "gemini-2.5-pro" },
|
|
124
|
+
{ name: "Gemini 3.1 Pro Preview (most advanced)", value: "gemini-3.1-pro-preview" }
|
|
125
|
+
],
|
|
126
|
+
xai: [
|
|
127
|
+
{ name: "Grok 4.1 Fast (recommended \u2014 $0.20/M, very fast)", value: "grok-4-1-fast-non-reasoning" },
|
|
128
|
+
{ name: "Grok 4.20 (frontier quality, 2M context)", value: "grok-4.20-0309-non-reasoning" }
|
|
129
|
+
],
|
|
130
|
+
deepseek: [
|
|
131
|
+
{ name: "DeepSeek V3.2 Chat (recommended \u2014 cheapest good model)", value: "deepseek-chat" },
|
|
132
|
+
{ name: "DeepSeek V3.2 Reasoner (with chain-of-thought)", value: "deepseek-reasoner" }
|
|
133
|
+
],
|
|
134
|
+
mistral: [
|
|
135
|
+
{ name: "Mistral Large 3 (recommended \u2014 open-weight flagship)", value: "mistral-large-latest" },
|
|
136
|
+
{ name: "Codestral (code-optimized, 256K context)", value: "codestral-latest" },
|
|
137
|
+
{ name: "Mistral Small 4 (cheapest)", value: "mistral-small-latest" }
|
|
138
|
+
],
|
|
139
|
+
groq: [
|
|
140
|
+
{ name: "Llama 4 Maverick (recommended \u2014 free, fast)", value: "meta-llama/llama-4-maverick-17b-128e-instruct" },
|
|
141
|
+
{ name: "Llama 4 Scout (free, fast)", value: "meta-llama/llama-4-scout-17b-16e-instruct" },
|
|
142
|
+
{ name: "DeepSeek R1 70B (free reasoning)", value: "deepseek-r1-distill-llama-70b" },
|
|
143
|
+
{ name: "Qwen 3 32B (free, multilingual)", value: "qwen/qwen3-32b" }
|
|
144
|
+
]
|
|
145
|
+
};
|
|
146
|
+
var PROVIDER_CHOICES = [
|
|
147
|
+
{ name: "Anthropic (Claude) \u2014 recommended", value: "anthropic" },
|
|
148
|
+
{ name: "OpenAI (GPT)", value: "openai" },
|
|
149
|
+
{ name: "Google (Gemini)", value: "google" },
|
|
150
|
+
{ name: "xAI (Grok)", value: "xai" },
|
|
151
|
+
{ name: "DeepSeek \u2014 cheapest", value: "deepseek" },
|
|
152
|
+
{ name: "Mistral \u2014 open-weight", value: "mistral" },
|
|
153
|
+
{ name: "Groq \u2014 free tier, open-source models", value: "groq" },
|
|
154
|
+
{ name: "Other (OpenAI-compatible endpoint)", value: "other" }
|
|
155
|
+
];
|
|
156
|
+
function getProviderName(provider) {
|
|
157
|
+
if (provider === "other") return "Custom endpoint";
|
|
158
|
+
return PROVIDER_CONFIGS[provider].name;
|
|
159
|
+
}
|
|
160
|
+
function getBaseURL(provider, customBaseURL) {
|
|
161
|
+
if (provider === "other") return customBaseURL;
|
|
162
|
+
return PROVIDER_CONFIGS[provider]?.baseURL;
|
|
163
|
+
}
|
|
164
|
+
function getCheapModel(provider, fallbackModel) {
|
|
165
|
+
if (provider === "other") return fallbackModel;
|
|
166
|
+
return PROVIDER_CONFIGS[provider].cheapModel;
|
|
167
|
+
}
|
|
168
|
+
function getVerifyModel(provider, fallbackModel) {
|
|
169
|
+
if (provider === "other") return fallbackModel;
|
|
170
|
+
return PROVIDER_CONFIGS[provider].verifyModel;
|
|
171
|
+
}
|
|
172
|
+
|
|
65
173
|
// src/ui.ts
|
|
66
174
|
import chalk from "chalk";
|
|
67
175
|
var maroon = chalk.rgb(139, 0, 0);
|
|
68
|
-
var
|
|
176
|
+
var darkMaroon = chalk.rgb(100, 0, 0);
|
|
177
|
+
var warmStone = chalk.rgb(212, 165, 116);
|
|
178
|
+
var lightStone = chalk.rgb(220, 190, 160);
|
|
179
|
+
var dimStone = chalk.rgb(140, 100, 70);
|
|
69
180
|
var ui = {
|
|
70
|
-
// Brand
|
|
181
|
+
// Brand colors
|
|
71
182
|
brand: (text) => maroon.bold(text),
|
|
72
|
-
accent: (text) =>
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
const
|
|
183
|
+
accent: (text) => warmStone(text),
|
|
184
|
+
// Logos and banners
|
|
185
|
+
fullBanner: (subtitle) => {
|
|
186
|
+
const KAIRN_WORDMARK2 = [
|
|
187
|
+
maroon("\u2588\u2588\u2557 \u2588\u2588\u2557") + " " + maroon("\u2588\u2588\u2588\u2588\u2588\u2557 ") + " " + maroon("\u2588\u2588\u2557") + " " + maroon("\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + " " + maroon("\u2588\u2588\u2588\u2557 \u2588\u2588\u2557"),
|
|
188
|
+
maroon("\u2588\u2588\u2551 \u2588\u2588\u2554\u255D") + " " + maroon("\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557") + " " + maroon("\u2588\u2588\u2551") + " " + maroon("\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557") + " " + maroon("\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551"),
|
|
189
|
+
warmStone("\u2588\u2588\u2588\u2588\u2588\u2554\u255D ") + " " + warmStone("\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551") + " " + warmStone("\u2588\u2588\u2551") + " " + warmStone("\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + " " + warmStone("\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551"),
|
|
190
|
+
warmStone("\u2588\u2588\u2554\u2550\u2588\u2588\u2557 ") + " " + warmStone("\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551") + " " + warmStone("\u2588\u2588\u2551") + " " + warmStone("\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557") + " " + warmStone("\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551"),
|
|
191
|
+
lightStone("\u2588\u2588\u2551 \u2588\u2588\u2557") + " " + lightStone("\u2588\u2588\u2551 \u2588\u2588\u2551") + " " + lightStone("\u2588\u2588\u2551") + " " + lightStone("\u2588\u2588\u2551 \u2588\u2588\u2551") + " " + lightStone("\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551"),
|
|
192
|
+
lightStone("\u255A\u2550\u255D \u255A\u2550\u255D") + " " + lightStone("\u255A\u2550\u255D \u255A\u2550\u255D") + " " + lightStone("\u255A\u2550\u255D") + " " + lightStone("\u255A\u2550\u255D \u255A\u2550\u255D") + " " + lightStone("\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D")
|
|
193
|
+
];
|
|
194
|
+
console.log("");
|
|
195
|
+
for (const line of KAIRN_WORDMARK2) {
|
|
196
|
+
console.log(" " + line);
|
|
197
|
+
}
|
|
198
|
+
if (subtitle) {
|
|
199
|
+
console.log(dimStone(` ${subtitle}`));
|
|
200
|
+
}
|
|
201
|
+
console.log("");
|
|
202
|
+
},
|
|
203
|
+
compactBanner: (subtitle) => {
|
|
204
|
+
const line = maroon("\u2501").repeat(52);
|
|
205
|
+
console.log(` ${line}`);
|
|
206
|
+
console.log(` ${maroon(" \u25C6")} ${chalk.bold.rgb(139, 0, 0)("KAIRN")}` + (subtitle ? ` ${dimStone("\u2014 " + subtitle)}` : ""));
|
|
207
|
+
console.log(` ${line}`);
|
|
208
|
+
},
|
|
209
|
+
// Section headers
|
|
210
|
+
section: (title) => {
|
|
211
|
+
const len = chalk.dim(title).length;
|
|
212
|
+
const line = "\u2501".repeat(Math.max(0, 48 - len));
|
|
76
213
|
return `
|
|
77
|
-
${
|
|
78
|
-
${maroon("\u2502")} ${maroon.bold(text.padEnd(49))}${maroon("\u2502")}
|
|
79
|
-
${maroon("\u2514" + line + "\u2518")}
|
|
80
|
-
`;
|
|
214
|
+
${warmStone("\u2501\u2501")} ${chalk.bold(title)} ${chalk.dim(warmStone(line))}`;
|
|
81
215
|
},
|
|
82
|
-
//
|
|
83
|
-
section: (title) => `
|
|
84
|
-
${warm("\u2501\u2501")} ${chalk.bold(title)} ${warm("\u2501".repeat(Math.max(0, 44 - title.length)))}`,
|
|
85
|
-
// Status
|
|
216
|
+
// Status messages
|
|
86
217
|
success: (text) => chalk.green(` \u2713 ${text}`),
|
|
87
218
|
warn: (text) => chalk.yellow(` \u26A0 ${text}`),
|
|
88
219
|
error: (text) => chalk.red(` \u2717 ${text}`),
|
|
@@ -92,65 +223,68 @@ var ui = {
|
|
|
92
223
|
// File list
|
|
93
224
|
file: (path15) => chalk.dim(` ${path15}`),
|
|
94
225
|
// Tool display
|
|
95
|
-
tool: (name, reason) => ` ${
|
|
226
|
+
tool: (name, reason) => ` ${warmStone("\u25CF")} ${chalk.bold(name)}
|
|
96
227
|
${chalk.dim(reason)}`,
|
|
97
228
|
// Divider
|
|
98
229
|
divider: () => chalk.dim(` ${"\u2500".repeat(50)}`),
|
|
99
230
|
// Command suggestion
|
|
100
231
|
cmd: (command) => ` ${chalk.bold.white("$ " + command)}`,
|
|
101
|
-
// Env var setup
|
|
102
|
-
|
|
103
|
-
let out = `
|
|
104
|
-
`;
|
|
105
|
-
out += chalk.dim(` ${desc}`);
|
|
232
|
+
// Env var setup with signupUrl
|
|
233
|
+
envVarPrompt: (name, desc, url) => {
|
|
234
|
+
let out = ` ${chalk.bold(name)}${chalk.dim(` (${desc})`)}`;
|
|
106
235
|
if (url) out += `
|
|
107
|
-
|
|
236
|
+
${chalk.dim("Get one at:")} ${warmStone(url)}`;
|
|
108
237
|
return out;
|
|
109
238
|
},
|
|
110
|
-
// Clarification question
|
|
111
|
-
question: (q, suggestion) =>
|
|
112
|
-
${
|
|
113
|
-
|
|
239
|
+
// Clarification question
|
|
240
|
+
question: (q, suggestion) => {
|
|
241
|
+
let msg = ` ${warmStone("?")} ${chalk.bold(q)}`;
|
|
242
|
+
if (suggestion) {
|
|
243
|
+
msg += `
|
|
244
|
+
${chalk.dim(`(suggested: ${suggestion})`)}`;
|
|
245
|
+
}
|
|
246
|
+
return msg;
|
|
247
|
+
},
|
|
248
|
+
// Error box for compile failures
|
|
114
249
|
errorBox: (title, message) => {
|
|
115
250
|
const line = "\u2500".repeat(50);
|
|
116
|
-
return `
|
|
117
|
-
${
|
|
118
|
-
|
|
119
|
-
${
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
`;
|
|
251
|
+
return chalk.red(`
|
|
252
|
+
\u250C${line}\u2510
|
|
253
|
+
\u2502 ${title.padEnd(49)}\u2502
|
|
254
|
+
\u2502 ${message.padEnd(49)}\u2502
|
|
255
|
+
\u2514${line}\u2518
|
|
256
|
+
`);
|
|
123
257
|
}
|
|
124
258
|
};
|
|
125
259
|
|
|
126
260
|
// src/logo.ts
|
|
127
261
|
import chalk2 from "chalk";
|
|
128
262
|
var maroon2 = chalk2.rgb(139, 0, 0);
|
|
129
|
-
var
|
|
130
|
-
var
|
|
131
|
-
var
|
|
132
|
-
var
|
|
263
|
+
var darkMaroon2 = chalk2.rgb(100, 0, 0);
|
|
264
|
+
var warmStone2 = chalk2.rgb(180, 120, 80);
|
|
265
|
+
var lightStone2 = chalk2.rgb(212, 165, 116);
|
|
266
|
+
var dimStone2 = chalk2.rgb(140, 100, 70);
|
|
133
267
|
var KAIRN_WORDMARK = [
|
|
134
|
-
maroon2("\u2588\u2588\u2557 \u2588\u2588\u2557") +
|
|
135
|
-
maroon2("\u2588\u2588\u2551 \u2588\u2588\u2554\u255D") +
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
268
|
+
maroon2("\u2588\u2588\u2557 \u2588\u2588\u2557") + darkMaroon2(" ") + maroon2("\u2588\u2588\u2588\u2588\u2588\u2557 ") + darkMaroon2(" ") + maroon2("\u2588\u2588\u2557") + darkMaroon2(" ") + maroon2("\u2588\u2588\u2588\u2588\u2588\u2588\u2557 ") + darkMaroon2(" ") + maroon2("\u2588\u2588\u2588\u2557 \u2588\u2588\u2557"),
|
|
269
|
+
maroon2("\u2588\u2588\u2551 \u2588\u2588\u2554\u255D") + darkMaroon2(" ") + maroon2("\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557") + darkMaroon2(" ") + maroon2("\u2588\u2588\u2551") + darkMaroon2(" ") + maroon2("\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557") + darkMaroon2(" ") + maroon2("\u2588\u2588\u2588\u2588\u2557 \u2588\u2588\u2551"),
|
|
270
|
+
warmStone2("\u2588\u2588\u2588\u2588\u2588\u2554\u255D ") + dimStone2(" ") + warmStone2("\u2588\u2588\u2588\u2588\u2588\u2588\u2588\u2551") + dimStone2(" ") + warmStone2("\u2588\u2588\u2551") + dimStone2(" ") + warmStone2("\u2588\u2588\u2588\u2588\u2588\u2588\u2554\u255D") + dimStone2(" ") + warmStone2("\u2588\u2588\u2554\u2588\u2588\u2557 \u2588\u2588\u2551"),
|
|
271
|
+
warmStone2("\u2588\u2588\u2554\u2550\u2588\u2588\u2557 ") + dimStone2(" ") + warmStone2("\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2551") + dimStone2(" ") + warmStone2("\u2588\u2588\u2551") + dimStone2(" ") + warmStone2("\u2588\u2588\u2554\u2550\u2550\u2588\u2588\u2557") + dimStone2(" ") + warmStone2("\u2588\u2588\u2551\u255A\u2588\u2588\u2557\u2588\u2588\u2551"),
|
|
272
|
+
lightStone2("\u2588\u2588\u2551 \u2588\u2588\u2557") + dimStone2(" ") + lightStone2("\u2588\u2588\u2551 \u2588\u2588\u2551") + dimStone2(" ") + lightStone2("\u2588\u2588\u2551") + dimStone2(" ") + lightStone2("\u2588\u2588\u2551 \u2588\u2588\u2551") + dimStone2(" ") + lightStone2("\u2588\u2588\u2551 \u255A\u2588\u2588\u2588\u2588\u2551"),
|
|
273
|
+
lightStone2("\u255A\u2550\u255D \u255A\u2550\u255D") + dimStone2(" ") + lightStone2("\u255A\u2550\u255D \u255A\u2550\u255D") + dimStone2(" ") + lightStone2("\u255A\u2550\u255D") + dimStone2(" ") + lightStone2("\u255A\u2550\u255D \u255A\u2550\u255D") + dimStone2(" ") + lightStone2("\u255A\u2550\u255D \u255A\u2550\u2550\u2550\u255D")
|
|
140
274
|
];
|
|
141
275
|
var CAIRN_ART = [
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
276
|
+
dimStone2(" \u28C0\u28C0\u28C0 "),
|
|
277
|
+
warmStone2(" \u28F4\u28FF\u28FF\u28FF\u28E6 "),
|
|
278
|
+
warmStone2(" \u2819\u283F\u283F\u280B "),
|
|
279
|
+
dimStone2(" \u28C0\u28E4\u28E4\u28E4\u28E4\u28C0 "),
|
|
280
|
+
lightStone2(" \u28F4\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28E6 "),
|
|
281
|
+
lightStone2(" \u2819\u283B\u283F\u283F\u283F\u281F\u280B "),
|
|
282
|
+
dimStone2(" \u28C0\u28E4\u28E4\u28F6\u28F6\u28F6\u28F6\u28E4\u28E4\u28C0 "),
|
|
283
|
+
warmStone2(" \u28F4\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28E6 "),
|
|
284
|
+
warmStone2(" \u2819\u283B\u283F\u283F\u283F\u283F\u283F\u283F\u281F\u280B "),
|
|
285
|
+
dimStone2(" \u28C0\u28E4\u28F6\u28F6\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28F6\u28F6\u28E4\u28C0 "),
|
|
286
|
+
lightStone2(" \u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF\u28FF "),
|
|
287
|
+
dimStone2(" \u2809\u2809\u2809\u2809\u2809\u2809\u2809\u2809\u2809\u2809\u2809\u2809\u2809\u2809\u2809 ")
|
|
154
288
|
];
|
|
155
289
|
function printFullBanner(subtitle) {
|
|
156
290
|
console.log("");
|
|
@@ -158,7 +292,7 @@ function printFullBanner(subtitle) {
|
|
|
158
292
|
console.log(" " + line);
|
|
159
293
|
}
|
|
160
294
|
if (subtitle) {
|
|
161
|
-
console.log(
|
|
295
|
+
console.log(dimStone2(` ${subtitle}`));
|
|
162
296
|
}
|
|
163
297
|
console.log("");
|
|
164
298
|
}
|
|
@@ -166,7 +300,7 @@ function printCompactBanner() {
|
|
|
166
300
|
const line = maroon2("\u2501").repeat(50);
|
|
167
301
|
console.log(`
|
|
168
302
|
${line}`);
|
|
169
|
-
console.log(` ${maroon2(" \u25C6")} ${chalk2.bold.rgb(139, 0, 0)("KAIRN")} ${
|
|
303
|
+
console.log(` ${maroon2(" \u25C6")} ${chalk2.bold.rgb(139, 0, 0)("KAIRN")} ${dimStone2("\u2014 Agent Environment Compiler")}`);
|
|
170
304
|
console.log(` ${line}
|
|
171
305
|
`);
|
|
172
306
|
}
|
|
@@ -208,62 +342,28 @@ async function installSeedTemplates() {
|
|
|
208
342
|
console.log(ui.success(`${installed} template${installed === 1 ? "" : "s"} installed`));
|
|
209
343
|
}
|
|
210
344
|
}
|
|
211
|
-
|
|
212
|
-
anthropic: {
|
|
213
|
-
name: "Anthropic",
|
|
214
|
-
models: [
|
|
215
|
-
{ name: "Claude Sonnet 4.6 (recommended \u2014 fast, smart)", value: "claude-sonnet-4-6" },
|
|
216
|
-
{ name: "Claude Opus 4.6 (highest quality)", value: "claude-opus-4-6" },
|
|
217
|
-
{ name: "Claude Haiku 4.5 (fastest, cheapest)", value: "claude-haiku-4-5-20251001" }
|
|
218
|
-
]
|
|
219
|
-
},
|
|
220
|
-
openai: {
|
|
221
|
-
name: "OpenAI",
|
|
222
|
-
models: [
|
|
223
|
-
{ name: "GPT-4o (recommended)", value: "gpt-4o" },
|
|
224
|
-
{ name: "GPT-4o mini (faster, cheaper)", value: "gpt-4o-mini" },
|
|
225
|
-
{ name: "o3 (reasoning)", value: "o3" }
|
|
226
|
-
]
|
|
227
|
-
},
|
|
228
|
-
google: {
|
|
229
|
-
name: "Google Gemini",
|
|
230
|
-
models: [
|
|
231
|
-
{ name: "Gemini 2.5 Flash (recommended)", value: "gemini-2.5-flash-preview-05-20" },
|
|
232
|
-
{ name: "Gemini 2.5 Pro (highest quality)", value: "gemini-2.5-pro-preview-05-06" }
|
|
233
|
-
]
|
|
234
|
-
}
|
|
235
|
-
};
|
|
236
|
-
async function verifyKey(provider, apiKey, model) {
|
|
345
|
+
async function verifyKey(provider, apiKey, baseURL, model) {
|
|
237
346
|
try {
|
|
238
347
|
if (provider === "anthropic") {
|
|
239
|
-
const
|
|
240
|
-
await
|
|
241
|
-
model: "claude-haiku-4-5-20251001",
|
|
242
|
-
max_tokens: 10,
|
|
243
|
-
messages: [{ role: "user", content: "ping" }]
|
|
244
|
-
});
|
|
245
|
-
return true;
|
|
246
|
-
} else if (provider === "openai") {
|
|
247
|
-
const client = new OpenAI({ apiKey });
|
|
248
|
-
await client.chat.completions.create({
|
|
249
|
-
model: "gpt-4o-mini",
|
|
250
|
-
max_tokens: 10,
|
|
251
|
-
messages: [{ role: "user", content: "ping" }]
|
|
252
|
-
});
|
|
253
|
-
return true;
|
|
254
|
-
} else if (provider === "google") {
|
|
255
|
-
const client = new OpenAI({
|
|
256
|
-
apiKey,
|
|
257
|
-
baseURL: "https://generativelanguage.googleapis.com/v1beta/openai/"
|
|
258
|
-
});
|
|
259
|
-
await client.chat.completions.create({
|
|
260
|
-
model: "gemini-2.5-flash-preview-05-20",
|
|
348
|
+
const client2 = new Anthropic({ apiKey });
|
|
349
|
+
await client2.messages.create({
|
|
350
|
+
model: getVerifyModel(provider, model || "claude-haiku-4-5-20251001"),
|
|
261
351
|
max_tokens: 10,
|
|
262
352
|
messages: [{ role: "user", content: "ping" }]
|
|
263
353
|
});
|
|
264
354
|
return true;
|
|
265
355
|
}
|
|
266
|
-
|
|
356
|
+
const verifyModel = provider === "other" ? model || "test" : getVerifyModel(provider, model || "");
|
|
357
|
+
const resolvedBaseURL = getBaseURL(provider, baseURL);
|
|
358
|
+
const clientOptions = { apiKey };
|
|
359
|
+
if (resolvedBaseURL) clientOptions.baseURL = resolvedBaseURL;
|
|
360
|
+
const client = new OpenAI(clientOptions);
|
|
361
|
+
await client.chat.completions.create({
|
|
362
|
+
model: verifyModel,
|
|
363
|
+
max_tokens: 10,
|
|
364
|
+
messages: [{ role: "user", content: "ping" }]
|
|
365
|
+
});
|
|
366
|
+
return true;
|
|
267
367
|
} catch {
|
|
268
368
|
return false;
|
|
269
369
|
}
|
|
@@ -285,42 +385,52 @@ var initCommand = new Command("init").description("Set up Kairn with your API ke
|
|
|
285
385
|
}
|
|
286
386
|
const provider = await select({
|
|
287
387
|
message: "LLM provider",
|
|
288
|
-
choices:
|
|
289
|
-
{ name: "Anthropic (Claude) \u2014 recommended", value: "anthropic" },
|
|
290
|
-
{ name: "OpenAI (GPT)", value: "openai" },
|
|
291
|
-
{ name: "Google (Gemini)", value: "google" }
|
|
292
|
-
]
|
|
293
|
-
});
|
|
294
|
-
const providerInfo = PROVIDER_MODELS[provider];
|
|
295
|
-
const model = await select({
|
|
296
|
-
message: "Compilation model",
|
|
297
|
-
choices: providerInfo.models
|
|
388
|
+
choices: PROVIDER_CHOICES
|
|
298
389
|
});
|
|
390
|
+
let model;
|
|
391
|
+
let baseURL;
|
|
392
|
+
let providerDisplayName;
|
|
393
|
+
if (provider === "other") {
|
|
394
|
+
providerDisplayName = "Custom endpoint";
|
|
395
|
+
baseURL = await input({ message: "Base URL" });
|
|
396
|
+
model = await input({ message: "Model name" });
|
|
397
|
+
} else {
|
|
398
|
+
providerDisplayName = getProviderName(provider);
|
|
399
|
+
model = await select({
|
|
400
|
+
message: "Compilation model",
|
|
401
|
+
choices: PROVIDER_MODELS[provider]
|
|
402
|
+
});
|
|
403
|
+
}
|
|
299
404
|
const apiKey = await password({
|
|
300
|
-
message: `${
|
|
405
|
+
message: `${providerDisplayName} API key${provider === "other" ? " (Enter to skip)" : ""}`,
|
|
301
406
|
mask: "*"
|
|
302
407
|
});
|
|
303
|
-
if (!apiKey) {
|
|
408
|
+
if (!apiKey && provider !== "other") {
|
|
304
409
|
console.log(ui.error("No API key provided. Aborting."));
|
|
305
410
|
process.exit(1);
|
|
306
411
|
}
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
412
|
+
if (apiKey) {
|
|
413
|
+
console.log(chalk3.dim("\n Verifying API key..."));
|
|
414
|
+
const valid = await verifyKey(provider, apiKey, baseURL, model);
|
|
415
|
+
if (!valid) {
|
|
416
|
+
console.log(ui.error("Invalid API key. Check your key and try again."));
|
|
417
|
+
process.exit(1);
|
|
418
|
+
}
|
|
419
|
+
console.log(ui.success("API key verified"));
|
|
420
|
+
} else {
|
|
421
|
+
console.log(ui.warn("No API key \u2014 skipping verification"));
|
|
312
422
|
}
|
|
313
|
-
console.log(ui.success("API key verified"));
|
|
314
423
|
const config = {
|
|
315
424
|
provider,
|
|
316
|
-
api_key: apiKey,
|
|
425
|
+
api_key: apiKey || "",
|
|
317
426
|
model,
|
|
427
|
+
...baseURL ? { base_url: baseURL } : {},
|
|
318
428
|
default_runtime: "claude-code",
|
|
319
429
|
created_at: (/* @__PURE__ */ new Date()).toISOString()
|
|
320
430
|
};
|
|
321
431
|
await saveConfig(config);
|
|
322
432
|
console.log(ui.success(`Config saved to ${chalk3.dim(getConfigPath())}`));
|
|
323
|
-
console.log(ui.kv("Provider",
|
|
433
|
+
console.log(ui.kv("Provider", providerDisplayName));
|
|
324
434
|
console.log(ui.kv("Model", model));
|
|
325
435
|
await installSeedTemplates();
|
|
326
436
|
const hasClaude = detectClaudeCode();
|
|
@@ -338,7 +448,7 @@ var initCommand = new Command("init").description("Set up Kairn with your API ke
|
|
|
338
448
|
|
|
339
449
|
// src/commands/describe.ts
|
|
340
450
|
import { Command as Command2 } from "commander";
|
|
341
|
-
import { input, confirm, select as select2 } from "@inquirer/prompts";
|
|
451
|
+
import { input as input2, confirm, select as select2 } from "@inquirer/prompts";
|
|
342
452
|
import chalk5 from "chalk";
|
|
343
453
|
import ora from "ora";
|
|
344
454
|
|
|
@@ -819,10 +929,11 @@ function classifyError(err, provider) {
|
|
|
819
929
|
return `${provider} API error: ${msg}`;
|
|
820
930
|
}
|
|
821
931
|
async function callLLM(config, userMessage) {
|
|
932
|
+
const providerName = getProviderName(config.provider);
|
|
822
933
|
if (config.provider === "anthropic") {
|
|
823
|
-
const
|
|
934
|
+
const client2 = new Anthropic2({ apiKey: config.api_key });
|
|
824
935
|
try {
|
|
825
|
-
const response = await
|
|
936
|
+
const response = await client2.messages.create({
|
|
826
937
|
model: config.model,
|
|
827
938
|
max_tokens: 8192,
|
|
828
939
|
system: SYSTEM_PROMPT,
|
|
@@ -833,35 +944,31 @@ async function callLLM(config, userMessage) {
|
|
|
833
944
|
throw new Error("No text response from compiler LLM");
|
|
834
945
|
}
|
|
835
946
|
return textBlock.text;
|
|
836
|
-
} catch (err) {
|
|
837
|
-
throw new Error(classifyError(err, "Anthropic"));
|
|
838
|
-
}
|
|
839
|
-
} else if (config.provider === "openai" || config.provider === "google") {
|
|
840
|
-
const providerName = config.provider === "google" ? "Google" : "OpenAI";
|
|
841
|
-
const clientOptions = { apiKey: config.api_key };
|
|
842
|
-
if (config.provider === "google") {
|
|
843
|
-
clientOptions.baseURL = "https://generativelanguage.googleapis.com/v1beta/openai/";
|
|
844
|
-
}
|
|
845
|
-
const client = new OpenAI2(clientOptions);
|
|
846
|
-
try {
|
|
847
|
-
const response = await client.chat.completions.create({
|
|
848
|
-
model: config.model,
|
|
849
|
-
max_tokens: 8192,
|
|
850
|
-
messages: [
|
|
851
|
-
{ role: "system", content: SYSTEM_PROMPT },
|
|
852
|
-
{ role: "user", content: userMessage }
|
|
853
|
-
]
|
|
854
|
-
});
|
|
855
|
-
const text = response.choices[0]?.message?.content;
|
|
856
|
-
if (!text) {
|
|
857
|
-
throw new Error("No text response from compiler LLM");
|
|
858
|
-
}
|
|
859
|
-
return text;
|
|
860
947
|
} catch (err) {
|
|
861
948
|
throw new Error(classifyError(err, providerName));
|
|
862
949
|
}
|
|
863
950
|
}
|
|
864
|
-
|
|
951
|
+
const resolvedBaseURL = getBaseURL(config.provider, config.base_url);
|
|
952
|
+
const clientOptions = { apiKey: config.api_key };
|
|
953
|
+
if (resolvedBaseURL) clientOptions.baseURL = resolvedBaseURL;
|
|
954
|
+
const client = new OpenAI2(clientOptions);
|
|
955
|
+
try {
|
|
956
|
+
const response = await client.chat.completions.create({
|
|
957
|
+
model: config.model,
|
|
958
|
+
max_tokens: 8192,
|
|
959
|
+
messages: [
|
|
960
|
+
{ role: "system", content: SYSTEM_PROMPT },
|
|
961
|
+
{ role: "user", content: userMessage }
|
|
962
|
+
]
|
|
963
|
+
});
|
|
964
|
+
const text = response.choices[0]?.message?.content;
|
|
965
|
+
if (!text) {
|
|
966
|
+
throw new Error("No text response from compiler LLM");
|
|
967
|
+
}
|
|
968
|
+
return text;
|
|
969
|
+
} catch (err) {
|
|
970
|
+
throw new Error(classifyError(err, providerName));
|
|
971
|
+
}
|
|
865
972
|
}
|
|
866
973
|
function validateSpec(spec, onProgress) {
|
|
867
974
|
const warnings = [];
|
|
@@ -913,9 +1020,7 @@ async function generateClarifications(intent, onProgress) {
|
|
|
913
1020
|
}
|
|
914
1021
|
onProgress?.("Analyzing your request...");
|
|
915
1022
|
const clarificationConfig = { ...config };
|
|
916
|
-
|
|
917
|
-
clarificationConfig.model = "claude-haiku-4-5-20251001";
|
|
918
|
-
}
|
|
1023
|
+
clarificationConfig.model = getCheapModel(config.provider, config.model);
|
|
919
1024
|
const response = await callLLM(clarificationConfig, CLARIFICATION_PROMPT + "\n\nUser description: " + intent);
|
|
920
1025
|
try {
|
|
921
1026
|
let cleaned = response.trim();
|
|
@@ -1627,7 +1732,7 @@ var describeCommand = new Command2("describe").description("Describe your workfl
|
|
|
1627
1732
|
);
|
|
1628
1733
|
process.exit(1);
|
|
1629
1734
|
}
|
|
1630
|
-
const intentRaw = intentArg || await
|
|
1735
|
+
const intentRaw = intentArg || await input2({
|
|
1631
1736
|
message: "What do you want your agent to do?"
|
|
1632
1737
|
});
|
|
1633
1738
|
if (!intentRaw.trim()) {
|
|
@@ -1647,7 +1752,7 @@ var describeCommand = new Command2("describe").description("Describe your workfl
|
|
|
1647
1752
|
if (clarifications.length > 0) {
|
|
1648
1753
|
const answers = [];
|
|
1649
1754
|
for (const c of clarifications) {
|
|
1650
|
-
const answer = await
|
|
1755
|
+
const answer = await input2({
|
|
1651
1756
|
message: c.question,
|
|
1652
1757
|
default: c.suggestion
|
|
1653
1758
|
});
|
|
@@ -2593,7 +2698,7 @@ var doctorCommand = new Command7("doctor").description(
|
|
|
2593
2698
|
// src/commands/registry.ts
|
|
2594
2699
|
import { Command as Command8 } from "commander";
|
|
2595
2700
|
import chalk11 from "chalk";
|
|
2596
|
-
import { input as
|
|
2701
|
+
import { input as input3, select as select3 } from "@inquirer/prompts";
|
|
2597
2702
|
var listCommand2 = new Command8("list").description("List tools in the registry").option("--category <cat>", "Filter by category").option("--user-only", "Show only user-defined tools").action(async (options) => {
|
|
2598
2703
|
printCompactBanner();
|
|
2599
2704
|
let all;
|
|
@@ -2653,7 +2758,7 @@ var listCommand2 = new Command8("list").description("List tools in the registry"
|
|
|
2653
2758
|
var addCommand = new Command8("add").description("Add a tool to the user registry").action(async () => {
|
|
2654
2759
|
let id;
|
|
2655
2760
|
try {
|
|
2656
|
-
id = await
|
|
2761
|
+
id = await input3({
|
|
2657
2762
|
message: "Tool ID (kebab-case)",
|
|
2658
2763
|
validate: (v) => {
|
|
2659
2764
|
if (!v) return "ID is required";
|
|
@@ -2661,8 +2766,8 @@ var addCommand = new Command8("add").description("Add a tool to the user registr
|
|
|
2661
2766
|
return true;
|
|
2662
2767
|
}
|
|
2663
2768
|
});
|
|
2664
|
-
const name = await
|
|
2665
|
-
const description = await
|
|
2769
|
+
const name = await input3({ message: "Display name" });
|
|
2770
|
+
const description = await input3({ message: "Description" });
|
|
2666
2771
|
const category = await select3({
|
|
2667
2772
|
message: "Category",
|
|
2668
2773
|
choices: [
|
|
@@ -2706,8 +2811,8 @@ var addCommand = new Command8("add").description("Add a tool to the user registr
|
|
|
2706
2811
|
if (auth === "api_key" || auth === "connection_string") {
|
|
2707
2812
|
let addMore = true;
|
|
2708
2813
|
while (addMore) {
|
|
2709
|
-
const varName = await
|
|
2710
|
-
const varDesc = await
|
|
2814
|
+
const varName = await input3({ message: "Env var name" });
|
|
2815
|
+
const varDesc = await input3({ message: "Env var description" });
|
|
2711
2816
|
env_vars.push({ name: varName, description: varDesc });
|
|
2712
2817
|
const another = await select3({
|
|
2713
2818
|
message: "Add another env var?",
|
|
@@ -2719,14 +2824,14 @@ var addCommand = new Command8("add").description("Add a tool to the user registr
|
|
|
2719
2824
|
addMore = another;
|
|
2720
2825
|
}
|
|
2721
2826
|
}
|
|
2722
|
-
const signup_url_raw = await
|
|
2827
|
+
const signup_url_raw = await input3({ message: "Signup URL (optional, press enter to skip)" });
|
|
2723
2828
|
const signup_url = signup_url_raw.trim() || void 0;
|
|
2724
|
-
const best_for_raw = await
|
|
2829
|
+
const best_for_raw = await input3({ message: "Best-for tags, comma-separated" });
|
|
2725
2830
|
const best_for = best_for_raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2726
2831
|
const install = {};
|
|
2727
2832
|
if (type === "mcp_server") {
|
|
2728
|
-
const command = await
|
|
2729
|
-
const args_raw = await
|
|
2833
|
+
const command = await input3({ message: "MCP command" });
|
|
2834
|
+
const args_raw = await input3({ message: "MCP args, comma-separated (leave blank for none)" });
|
|
2730
2835
|
const args = args_raw.split(",").map((s) => s.trim()).filter(Boolean);
|
|
2731
2836
|
install.mcp_config = { command, args };
|
|
2732
2837
|
}
|