@kibhq/cli 0.1.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/bin/kib.ts ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bun
2
+ import { loadCredentials } from "../src/ui/setup-provider.js";
3
+
4
+ // Load saved API keys before anything else
5
+ loadCredentials();
6
+
7
+ import { main } from "../src/index.js";
8
+
9
+ main();
package/package.json ADDED
@@ -0,0 +1,42 @@
1
+ {
2
+ "name": "@kibhq/cli",
3
+ "version": "0.1.0",
4
+ "type": "module",
5
+ "bin": {
6
+ "kib": "./bin/kib.ts"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "src",
11
+ "package.json"
12
+ ],
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/keeganthomp/kib.git",
16
+ "directory": "packages/cli"
17
+ },
18
+ "license": "MIT",
19
+ "keywords": [
20
+ "knowledge-base",
21
+ "wiki",
22
+ "llm",
23
+ "compiler",
24
+ "cli",
25
+ "rag",
26
+ "ai"
27
+ ],
28
+ "scripts": {
29
+ "dev": "bun run bin/kib.ts",
30
+ "test": "bun test"
31
+ },
32
+ "dependencies": {
33
+ "@kibhq/core": "workspace:*",
34
+ "commander": "^14.0.0",
35
+ "chalk": "^5.4.1",
36
+ "ora": "^8.2.0"
37
+ },
38
+ "devDependencies": {
39
+ "@types/bun": "latest",
40
+ "typescript": "^5.8.3"
41
+ }
42
+ }
@@ -0,0 +1,123 @@
1
+ import * as readline from "node:readline";
2
+ import type { Message } from "@kibhq/core";
3
+ import {
4
+ createProvider,
5
+ loadConfig,
6
+ NoProviderError,
7
+ resolveVaultRoot,
8
+ VaultNotFoundError,
9
+ } from "@kibhq/core";
10
+ import * as log from "../ui/logger.js";
11
+ import { setupProvider } from "../ui/setup-provider.js";
12
+
13
+ export async function chat() {
14
+ let root: string;
15
+ try {
16
+ root = resolveVaultRoot();
17
+ } catch (err) {
18
+ if (err instanceof VaultNotFoundError) {
19
+ log.error(err.message);
20
+ process.exit(1);
21
+ }
22
+ throw err;
23
+ }
24
+
25
+ const config = await loadConfig(root);
26
+
27
+ // Create provider
28
+ let provider;
29
+ try {
30
+ provider = await createProvider(config.provider.default, config.provider.model);
31
+ } catch (err) {
32
+ if (err instanceof NoProviderError) {
33
+ provider = await setupProvider(root);
34
+ } else {
35
+ log.error((err as Error).message);
36
+ process.exit(1);
37
+ }
38
+ }
39
+
40
+ const { queryVault } = await import("@kibhq/core");
41
+
42
+ log.header("interactive session (type /help for commands)");
43
+
44
+ const rl = readline.createInterface({
45
+ input: process.stdin,
46
+ output: process.stdout,
47
+ prompt: " you: ",
48
+ });
49
+
50
+ const history: Message[] = [];
51
+
52
+ rl.prompt();
53
+
54
+ rl.on("line", async (line) => {
55
+ const input = line.trim();
56
+
57
+ if (!input) {
58
+ rl.prompt();
59
+ return;
60
+ }
61
+
62
+ // Handle slash commands
63
+ if (input.startsWith("/")) {
64
+ const cmd = input.slice(1).toLowerCase();
65
+ if (cmd === "exit" || cmd === "quit" || cmd === "q") {
66
+ console.log();
67
+ log.dim("Session ended.");
68
+ rl.close();
69
+ process.exit(0);
70
+ }
71
+ if (cmd === "clear") {
72
+ history.length = 0;
73
+ log.dim("Conversation cleared.");
74
+ rl.prompt();
75
+ return;
76
+ }
77
+ if (cmd === "help") {
78
+ console.log();
79
+ log.dim("Commands:");
80
+ log.dim(" /clear — clear conversation history");
81
+ log.dim(" /exit — end the session");
82
+ log.dim(" /help — show this help");
83
+ console.log();
84
+ rl.prompt();
85
+ return;
86
+ }
87
+ log.warn(`Unknown command: ${input}. Type /help for available commands.`);
88
+ rl.prompt();
89
+ return;
90
+ }
91
+
92
+ // Query the vault
93
+ console.log();
94
+ process.stdout.write(" kib: ");
95
+
96
+ try {
97
+ const result = await queryVault(root, input, provider, {
98
+ history,
99
+ onChunk: (text) => process.stdout.write(text),
100
+ });
101
+
102
+ console.log("\n");
103
+
104
+ // Update conversation history
105
+ history.push({ role: "user", content: input });
106
+ history.push({ role: "assistant", content: result.answer });
107
+
108
+ // Keep history manageable (last 10 exchanges)
109
+ while (history.length > 20) {
110
+ history.shift();
111
+ }
112
+ } catch (err) {
113
+ console.log();
114
+ log.error((err as Error).message);
115
+ }
116
+
117
+ rl.prompt();
118
+ });
119
+
120
+ rl.on("close", () => {
121
+ process.exit(0);
122
+ });
123
+ }
@@ -0,0 +1,117 @@
1
+ import {
2
+ createProvider,
3
+ loadConfig,
4
+ NoProviderError,
5
+ resolveVaultRoot,
6
+ VaultNotFoundError,
7
+ } from "@kibhq/core";
8
+ import * as log from "../ui/logger.js";
9
+ import { setupProvider } from "../ui/setup-provider.js";
10
+ import { createSpinner } from "../ui/spinner.js";
11
+
12
+ interface CompileOpts {
13
+ force?: boolean;
14
+ dryRun?: boolean;
15
+ source?: string;
16
+ max?: number;
17
+ }
18
+
19
+ export async function compile(opts: CompileOpts) {
20
+ let root: string;
21
+ try {
22
+ root = resolveVaultRoot();
23
+ } catch (err) {
24
+ if (err instanceof VaultNotFoundError) {
25
+ log.error(err.message);
26
+ process.exit(1);
27
+ }
28
+ throw err;
29
+ }
30
+
31
+ const config = await loadConfig(root);
32
+
33
+ log.header("compiling wiki");
34
+
35
+ // Create LLM provider
36
+ let provider;
37
+ const providerSpinner = createSpinner("Connecting to LLM provider...");
38
+ providerSpinner.start();
39
+ try {
40
+ provider = await createProvider(config.provider.default, config.provider.model);
41
+ providerSpinner.succeed(`Connected to ${provider.name}`);
42
+ } catch (err) {
43
+ providerSpinner.stop();
44
+ if (err instanceof NoProviderError) {
45
+ provider = await setupProvider(root);
46
+ } else {
47
+ log.error((err as Error).message);
48
+ process.exit(1);
49
+ }
50
+ }
51
+
52
+ // Lazy import compile engine
53
+ const { compileVault } = await import("@kibhq/core");
54
+
55
+ const compileSpinner = createSpinner("Compiling sources...");
56
+ compileSpinner.start();
57
+
58
+ try {
59
+ const result = await compileVault(root, provider, config, {
60
+ force: opts.force,
61
+ dryRun: opts.dryRun,
62
+ sourceFilter: opts.source,
63
+ maxSources: opts.max,
64
+ onProgress: (msg) => {
65
+ compileSpinner.text = ` ${msg}`;
66
+ },
67
+ });
68
+
69
+ if (result.sourcesCompiled === 0) {
70
+ compileSpinner.info("No sources pending compilation");
71
+ log.blank();
72
+ log.dim("Use --force to recompile all sources");
73
+ log.blank();
74
+ return;
75
+ }
76
+
77
+ compileSpinner.succeed(
78
+ `Compiled ${result.sourcesCompiled} source${result.sourcesCompiled === 1 ? "" : "s"}`,
79
+ );
80
+
81
+ log.blank();
82
+ if (result.articlesCreated > 0) {
83
+ log.success(
84
+ `${result.articlesCreated} article${result.articlesCreated === 1 ? "" : "s"} created`,
85
+ );
86
+ }
87
+ if (result.articlesUpdated > 0) {
88
+ log.success(
89
+ `${result.articlesUpdated} article${result.articlesUpdated === 1 ? "" : "s"} updated`,
90
+ );
91
+ }
92
+ if (result.articlesDeleted > 0) {
93
+ log.info(
94
+ `${result.articlesDeleted} article${result.articlesDeleted === 1 ? "" : "s"} deleted`,
95
+ );
96
+ }
97
+
98
+ if (opts.dryRun) {
99
+ log.blank();
100
+ log.dim("(dry run — no files were written)");
101
+
102
+ if (result.operations.length > 0) {
103
+ log.blank();
104
+ for (const op of result.operations) {
105
+ const symbol = op.op === "create" ? "+" : op.op === "update" ? "~" : "-";
106
+ log.info(`${symbol} ${op.path}`);
107
+ }
108
+ }
109
+ }
110
+
111
+ log.blank();
112
+ } catch (err) {
113
+ compileSpinner.fail("Compilation failed");
114
+ log.error((err as Error).message);
115
+ process.exit(1);
116
+ }
117
+ }
@@ -0,0 +1,90 @@
1
+ import { loadConfig, resolveVaultRoot, saveConfig, VaultNotFoundError } from "@kibhq/core";
2
+ import * as log from "../ui/logger.js";
3
+
4
+ export async function config(key?: string, value?: string, opts?: { list?: boolean }) {
5
+ let root: string;
6
+ try {
7
+ root = resolveVaultRoot();
8
+ } catch (err) {
9
+ if (err instanceof VaultNotFoundError) {
10
+ log.error(err.message);
11
+ process.exit(1);
12
+ }
13
+ throw err;
14
+ }
15
+
16
+ const cfg = await loadConfig(root);
17
+
18
+ // List all config
19
+ if (opts?.list || (!key && !value)) {
20
+ log.header("configuration");
21
+ printConfig(cfg, "");
22
+ return;
23
+ }
24
+
25
+ // Get a value
26
+ if (key && !value) {
27
+ const val = getNestedValue(cfg, key);
28
+ if (val === undefined) {
29
+ log.error(`Unknown config key: ${key}`);
30
+ process.exit(1);
31
+ }
32
+ console.log(val);
33
+ return;
34
+ }
35
+
36
+ // Set a value
37
+ if (key && value) {
38
+ const updated = setNestedValue(cfg, key, parseValue(value));
39
+ if (!updated) {
40
+ log.error(`Unknown config key: ${key}`);
41
+ process.exit(1);
42
+ }
43
+ await saveConfig(root, cfg);
44
+ log.success(`Set ${key} = ${value}`);
45
+ }
46
+ }
47
+
48
+ function getNestedValue(obj: any, path: string): unknown {
49
+ const parts = path.split(".");
50
+ let current = obj;
51
+ for (const part of parts) {
52
+ if (current == null || typeof current !== "object") return undefined;
53
+ current = current[part];
54
+ }
55
+ return current;
56
+ }
57
+
58
+ function setNestedValue(obj: any, path: string, value: unknown): boolean {
59
+ const parts = path.split(".");
60
+ let current = obj;
61
+ for (let i = 0; i < parts.length - 1; i++) {
62
+ if (current == null || typeof current !== "object") return false;
63
+ current = current[parts[i]!];
64
+ }
65
+ const lastKey = parts[parts.length - 1]!;
66
+ if (current == null || typeof current !== "object" || !(lastKey in current)) {
67
+ return false;
68
+ }
69
+ current[lastKey] = value;
70
+ return true;
71
+ }
72
+
73
+ function parseValue(val: string): unknown {
74
+ if (val === "true") return true;
75
+ if (val === "false") return false;
76
+ const num = Number(val);
77
+ if (!Number.isNaN(num) && val.trim() !== "") return num;
78
+ return val;
79
+ }
80
+
81
+ function printConfig(obj: any, prefix: string) {
82
+ for (const [key, val] of Object.entries(obj)) {
83
+ const fullKey = prefix ? `${prefix}.${key}` : key;
84
+ if (val != null && typeof val === "object" && !Array.isArray(val)) {
85
+ printConfig(val, fullKey);
86
+ } else {
87
+ log.keyValue(fullKey, String(val));
88
+ }
89
+ }
90
+ }
@@ -0,0 +1,199 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { join, relative } from "node:path";
3
+ import { listWiki, resolveVaultRoot, VaultNotFoundError, WIKI_DIR } from "@kibhq/core";
4
+ import * as log from "../ui/logger.js";
5
+ import { createSpinner } from "../ui/spinner.js";
6
+
7
+ interface ExportOpts {
8
+ format: string;
9
+ output?: string;
10
+ }
11
+
12
+ export async function exportVault(opts: ExportOpts) {
13
+ let root: string;
14
+ try {
15
+ root = resolveVaultRoot();
16
+ } catch (err) {
17
+ if (err instanceof VaultNotFoundError) {
18
+ log.error(err.message);
19
+ process.exit(1);
20
+ }
21
+ throw err;
22
+ }
23
+
24
+ const format = opts.format ?? "markdown";
25
+ const outputDir = opts.output ?? join(root, "export");
26
+
27
+ log.header(`exporting wiki as ${format}`);
28
+
29
+ const spinner = createSpinner("Exporting...");
30
+ spinner.start();
31
+
32
+ try {
33
+ switch (format) {
34
+ case "markdown":
35
+ await exportMarkdown(root, outputDir);
36
+ break;
37
+ case "html":
38
+ await exportHtml(root, outputDir);
39
+ break;
40
+ default:
41
+ spinner.fail(`Unsupported format: ${format}`);
42
+ log.dim("Supported formats: markdown, html");
43
+ process.exit(1);
44
+ }
45
+
46
+ spinner.succeed(`Exported to ${outputDir}`);
47
+ log.blank();
48
+ } catch (err) {
49
+ spinner.fail("Export failed");
50
+ log.error((err as Error).message);
51
+ process.exit(1);
52
+ }
53
+ }
54
+
55
+ /**
56
+ * Export as clean markdown bundle (strip frontmatter, resolve links).
57
+ */
58
+ async function exportMarkdown(root: string, outputDir: string) {
59
+ const wikiDir = join(root, WIKI_DIR);
60
+ const files = await listWiki(root);
61
+
62
+ for (const filePath of files) {
63
+ const content = await readFile(filePath, "utf-8");
64
+ const relPath = relative(wikiDir, filePath);
65
+ const outPath = join(outputDir, relPath);
66
+
67
+ await mkdir(join(outPath, ".."), { recursive: true });
68
+
69
+ // Strip frontmatter
70
+ const cleaned = content.replace(/^---[\s\S]*?---\s*\n/, "");
71
+ // Resolve [[wikilinks]] to standard markdown links
72
+ const resolved = cleaned.replace(
73
+ /\[\[([^\]]+)\]\]/g,
74
+ (_, slug: string) => `[${slug}](${slug}.md)`,
75
+ );
76
+
77
+ await writeFile(outPath, resolved, "utf-8");
78
+ }
79
+ }
80
+
81
+ /**
82
+ * Export as a simple HTML static site.
83
+ */
84
+ async function exportHtml(root: string, outputDir: string) {
85
+ const wikiDir = join(root, WIKI_DIR);
86
+ const files = await listWiki(root);
87
+ const { parseFrontmatter } = await import("@kibhq/core");
88
+
89
+ await mkdir(outputDir, { recursive: true });
90
+
91
+ const articles: { title: string; relPath: string; htmlPath: string }[] = [];
92
+
93
+ for (const filePath of files) {
94
+ const content = await readFile(filePath, "utf-8");
95
+ const relPath = relative(wikiDir, filePath);
96
+ const { frontmatter, body } = parseFrontmatter(content);
97
+ const title = (frontmatter.title as string) ?? relPath.replace(/\.md$/, "");
98
+ const htmlPath = relPath.replace(/\.md$/, ".html");
99
+
100
+ // Simple markdown → HTML (headings, paragraphs, bold, italic, code, links)
101
+ const html = simpleMarkdownToHtml(body);
102
+
103
+ const page = `<!DOCTYPE html>
104
+ <html lang="en">
105
+ <head>
106
+ <meta charset="UTF-8">
107
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
108
+ <title>${escapeHtml(title)}</title>
109
+ <style>
110
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; max-width: 720px; margin: 2rem auto; padding: 0 1rem; line-height: 1.6; color: #1a1a1a; }
111
+ a { color: #0066cc; }
112
+ code { background: #f4f4f4; padding: 0.2em 0.4em; border-radius: 3px; font-size: 0.9em; }
113
+ pre { background: #f4f4f4; padding: 1rem; border-radius: 6px; overflow-x: auto; }
114
+ pre code { background: none; padding: 0; }
115
+ nav { margin-bottom: 2rem; padding-bottom: 1rem; border-bottom: 1px solid #eee; }
116
+ nav a { margin-right: 1rem; }
117
+ </style>
118
+ </head>
119
+ <body>
120
+ <nav><a href="index.html">Index</a></nav>
121
+ <h1>${escapeHtml(title)}</h1>
122
+ ${html}
123
+ </body>
124
+ </html>`;
125
+
126
+ const outPath = join(outputDir, htmlPath);
127
+ await mkdir(join(outPath, ".."), { recursive: true });
128
+ await writeFile(outPath, page, "utf-8");
129
+
130
+ articles.push({ title, relPath, htmlPath });
131
+ }
132
+
133
+ // Generate index.html
134
+ const indexHtml = `<!DOCTYPE html>
135
+ <html lang="en">
136
+ <head>
137
+ <meta charset="UTF-8">
138
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
139
+ <title>Knowledge Base</title>
140
+ <style>
141
+ body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif; max-width: 720px; margin: 2rem auto; padding: 0 1rem; line-height: 1.6; color: #1a1a1a; }
142
+ a { color: #0066cc; }
143
+ ul { list-style: none; padding: 0; }
144
+ li { margin: 0.5rem 0; }
145
+ </style>
146
+ </head>
147
+ <body>
148
+ <h1>Knowledge Base</h1>
149
+ <p>${articles.length} articles</p>
150
+ <ul>
151
+ ${articles
152
+ .sort((a, b) => a.title.localeCompare(b.title))
153
+ .map((a) => `<li><a href="${a.htmlPath}">${escapeHtml(a.title)}</a></li>`)
154
+ .join("\n ")}
155
+ </ul>
156
+ </body>
157
+ </html>`;
158
+
159
+ await writeFile(join(outputDir, "index.html"), indexHtml, "utf-8");
160
+ }
161
+
162
+ function simpleMarkdownToHtml(md: string): string {
163
+ return (
164
+ md
165
+ // Code blocks
166
+ .replace(/```(\w*)\n([\s\S]*?)```/g, "<pre><code>$2</code></pre>")
167
+ // Headers
168
+ .replace(/^### (.+)$/gm, "<h3>$1</h3>")
169
+ .replace(/^## (.+)$/gm, "<h2>$1</h2>")
170
+ .replace(/^# (.+)$/gm, "<h1>$1</h1>")
171
+ // Bold
172
+ .replace(/\*\*(.+?)\*\*/g, "<strong>$1</strong>")
173
+ // Italic
174
+ .replace(/\*(.+?)\*/g, "<em>$1</em>")
175
+ // Inline code
176
+ .replace(/`([^`]+)`/g, "<code>$1</code>")
177
+ // Wikilinks → HTML links
178
+ .replace(/\[\[([^\]]+)\]\]/g, '<a href="$1.html">$1</a>')
179
+ // Standard links
180
+ .replace(/\[([^\]]+)\]\(([^)]+)\)/g, '<a href="$2">$1</a>')
181
+ // Lists
182
+ .replace(/^- (.+)$/gm, "<li>$1</li>")
183
+ // Paragraphs
184
+ .replace(/\n\n/g, "</p><p>")
185
+ .replace(/^/, "<p>")
186
+ .replace(/$/, "</p>")
187
+ // Clean up list items
188
+ .replace(/<p><li>/g, "<ul><li>")
189
+ .replace(/<\/li><\/p>/g, "</li></ul>")
190
+ );
191
+ }
192
+
193
+ function escapeHtml(str: string): string {
194
+ return str
195
+ .replace(/&/g, "&amp;")
196
+ .replace(/</g, "&lt;")
197
+ .replace(/>/g, "&gt;")
198
+ .replace(/"/g, "&quot;");
199
+ }
@@ -0,0 +1,68 @@
1
+ import { resolveVaultRoot, VaultNotFoundError } from "@kibhq/core";
2
+ import * as log from "../ui/logger.js";
3
+ import { createSpinner } from "../ui/spinner.js";
4
+
5
+ interface IngestOpts {
6
+ category?: string;
7
+ tags?: string;
8
+ batch?: boolean;
9
+ }
10
+
11
+ export async function ingest(sources: string[], opts: IngestOpts) {
12
+ let root: string;
13
+ try {
14
+ root = resolveVaultRoot();
15
+ } catch (err) {
16
+ if (err instanceof VaultNotFoundError) {
17
+ log.error(err.message);
18
+ process.exit(1);
19
+ }
20
+ throw err;
21
+ }
22
+
23
+ // Lazy import — don't load ingest machinery for other commands
24
+ const { ingestSource } = await import("@kibhq/core");
25
+
26
+ log.header("ingesting sources");
27
+
28
+ const tags = opts.tags?.split(",").map((t) => t.trim());
29
+ let ingested = 0;
30
+ let skipped = 0;
31
+
32
+ for (const source of sources) {
33
+ const spinner = createSpinner(`Ingesting ${source}`);
34
+ spinner.start();
35
+
36
+ try {
37
+ const result = await ingestSource(root, source, {
38
+ category: opts.category,
39
+ tags,
40
+ });
41
+
42
+ if (result.skipped) {
43
+ spinner.warn(`Skipped: ${result.skipReason}`);
44
+ skipped++;
45
+ } else {
46
+ spinner.succeed(
47
+ `${result.title} → ${result.path} (${result.wordCount.toLocaleString()} words)`,
48
+ );
49
+ ingested++;
50
+ }
51
+ } catch (err) {
52
+ spinner.fail(`Failed: ${source}`);
53
+ log.error((err as Error).message);
54
+ }
55
+ }
56
+
57
+ log.blank();
58
+ if (ingested > 0) {
59
+ log.success(
60
+ `Ingested ${ingested} source${ingested === 1 ? "" : "s"}${skipped > 0 ? `, skipped ${skipped}` : ""}`,
61
+ );
62
+ log.blank();
63
+ log.dim("run kib compile to update the wiki");
64
+ } else if (skipped > 0) {
65
+ log.dim(`All ${skipped} source${skipped === 1 ? "" : "s"} already ingested`);
66
+ }
67
+ log.blank();
68
+ }
@@ -0,0 +1,54 @@
1
+ import { resolve } from "node:path";
2
+ import { detectProvider, initVault, VaultExistsError } from "@kibhq/core";
3
+ import * as log from "../ui/logger.js";
4
+
5
+ interface InitOpts {
6
+ name?: string;
7
+ provider?: string;
8
+ force?: boolean;
9
+ }
10
+
11
+ export async function init(opts: InitOpts) {
12
+ const cwd = resolve(process.cwd());
13
+
14
+ log.header("initializing vault");
15
+
16
+ try {
17
+ // Detect provider
18
+ const detected = detectProvider();
19
+ const provider = opts.provider ?? detected.name;
20
+ const model = detected.model;
21
+
22
+ const { root, manifest } = await initVault(cwd, {
23
+ name: opts.name,
24
+ provider,
25
+ model,
26
+ force: opts.force,
27
+ });
28
+
29
+ log.success("Created .kb/manifest.json");
30
+ log.success("Created .kb/config.toml");
31
+ log.success("Created raw/");
32
+ log.success("Created wiki/");
33
+ log.success("Created inbox/");
34
+
35
+ const providerLabel =
36
+ provider === "anthropic"
37
+ ? `anthropic (ANTHROPIC_API_KEY)`
38
+ : provider === "openai"
39
+ ? `openai (OPENAI_API_KEY)`
40
+ : `ollama (localhost:11434)`;
41
+
42
+ log.success(`Detected provider: ${providerLabel}`);
43
+ log.success(`Model: ${model}`);
44
+ log.blank();
45
+ log.dim(`vault ready — start with kib ingest <source>`);
46
+ log.blank();
47
+ } catch (err) {
48
+ if (err instanceof VaultExistsError) {
49
+ log.error(err.message);
50
+ process.exit(1);
51
+ }
52
+ throw err;
53
+ }
54
+ }