@udara-kavindu/hik-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/README.md +17 -0
- package/dist/auth.js +30 -0
- package/dist/chat.js +138 -0
- package/dist/fs.js +17 -0
- package/dist/git.js +18 -0
- package/dist/index.js +165 -0
- package/dist/interactive.js +92 -0
- package/package.json +42 -0
package/README.md
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
# hik-cli
|
|
2
|
+
|
|
3
|
+
To install dependencies and build the CLI:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
bun install
|
|
7
|
+
bun run build
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
To make the `hik` command available locally:
|
|
11
|
+
|
|
12
|
+
```bash
|
|
13
|
+
bun link
|
|
14
|
+
hik --help
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
This project was created using `bun init` in bun v1.4.2. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
|
package/dist/auth.js
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
const CONFIG_DIR = path.join(os.homedir(), '.hik');
|
|
5
|
+
const CONFIG_FILE = path.join(CONFIG_DIR, 'config.json');
|
|
6
|
+
export function getConfig() {
|
|
7
|
+
if (!fs.existsSync(CONFIG_FILE))
|
|
8
|
+
return {};
|
|
9
|
+
try {
|
|
10
|
+
return JSON.parse(fs.readFileSync(CONFIG_FILE, 'utf-8'));
|
|
11
|
+
}
|
|
12
|
+
catch {
|
|
13
|
+
return {};
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
export function saveConfig(config) {
|
|
17
|
+
if (!fs.existsSync(CONFIG_DIR)) {
|
|
18
|
+
fs.mkdirSync(CONFIG_DIR, { recursive: true });
|
|
19
|
+
}
|
|
20
|
+
const current = getConfig();
|
|
21
|
+
fs.writeFileSync(CONFIG_FILE, JSON.stringify({ ...current, ...config }, null, 2));
|
|
22
|
+
}
|
|
23
|
+
export function requireApiKey() {
|
|
24
|
+
const config = getConfig();
|
|
25
|
+
if (!config.apiKey) {
|
|
26
|
+
console.error('[Error] No API key found. Please run: hik login <your-api-key>');
|
|
27
|
+
process.exit(1);
|
|
28
|
+
}
|
|
29
|
+
return config.apiKey;
|
|
30
|
+
}
|
package/dist/chat.js
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import fetch from "node-fetch";
|
|
2
|
+
import { requireApiKey, getConfig } from "./auth.js";
|
|
3
|
+
export async function streamChat(message, model) {
|
|
4
|
+
const apiKey = requireApiKey();
|
|
5
|
+
const config = getConfig();
|
|
6
|
+
const apiUrl = config.apiUrl || "http://localhost:3000";
|
|
7
|
+
// For MVP, we'll use a random UUID for session ID.
|
|
8
|
+
// In a real app, you might want to persist sessions in a local file.
|
|
9
|
+
const sessionId = crypto.randomUUID();
|
|
10
|
+
const response = await fetch(`${apiUrl}/api/v1/chat`, {
|
|
11
|
+
method: "POST",
|
|
12
|
+
headers: {
|
|
13
|
+
"Content-Type": "application/json",
|
|
14
|
+
"x-api-key": apiKey,
|
|
15
|
+
},
|
|
16
|
+
body: JSON.stringify({
|
|
17
|
+
sessionId,
|
|
18
|
+
messages: [{ role: "user", content: message }],
|
|
19
|
+
model: model || "qwen2.5-coder-7b-instruct",
|
|
20
|
+
}),
|
|
21
|
+
});
|
|
22
|
+
if (!response.ok) {
|
|
23
|
+
const err = await response.text();
|
|
24
|
+
throw new Error(`API Error: ${err}`);
|
|
25
|
+
}
|
|
26
|
+
const decoder = new TextDecoder();
|
|
27
|
+
if (!response.body)
|
|
28
|
+
throw new Error("No response body");
|
|
29
|
+
let buffer = "";
|
|
30
|
+
for await (const chunk of response.body) {
|
|
31
|
+
buffer +=
|
|
32
|
+
typeof chunk === "string"
|
|
33
|
+
? chunk
|
|
34
|
+
: decoder.decode(chunk, { stream: true });
|
|
35
|
+
const lines = buffer.split("\n");
|
|
36
|
+
buffer = lines.pop() || "";
|
|
37
|
+
for (const line of lines) {
|
|
38
|
+
if (line.startsWith("data: ")) {
|
|
39
|
+
try {
|
|
40
|
+
const data = JSON.parse(line.slice(6));
|
|
41
|
+
if (data.type === "text") {
|
|
42
|
+
process.stdout.write(data.content);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
catch (e) {
|
|
46
|
+
// Ignore parse errors
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
buffer += decoder.decode();
|
|
52
|
+
console.log("\n"); // New line after stream ends
|
|
53
|
+
}
|
|
54
|
+
async function fetchCompletion(messages, model) {
|
|
55
|
+
const apiKey = requireApiKey();
|
|
56
|
+
const config = getConfig();
|
|
57
|
+
const apiUrl = config.apiUrl || "http://localhost:3000";
|
|
58
|
+
const sessionId = crypto.randomUUID();
|
|
59
|
+
const response = await fetch(`${apiUrl}/api/v1/chat`, {
|
|
60
|
+
method: "POST",
|
|
61
|
+
headers: {
|
|
62
|
+
"Content-Type": "application/json",
|
|
63
|
+
"x-api-key": apiKey,
|
|
64
|
+
},
|
|
65
|
+
body: JSON.stringify({
|
|
66
|
+
sessionId,
|
|
67
|
+
messages,
|
|
68
|
+
model,
|
|
69
|
+
}),
|
|
70
|
+
});
|
|
71
|
+
if (!response.ok)
|
|
72
|
+
throw new Error(await response.text());
|
|
73
|
+
const decoder = new TextDecoder();
|
|
74
|
+
let fullText = "";
|
|
75
|
+
let buffer = "";
|
|
76
|
+
if (response.body) {
|
|
77
|
+
for await (const chunk of response.body) {
|
|
78
|
+
buffer +=
|
|
79
|
+
typeof chunk === "string"
|
|
80
|
+
? chunk
|
|
81
|
+
: decoder.decode(chunk, { stream: true });
|
|
82
|
+
const lines = buffer.split("\n");
|
|
83
|
+
buffer = lines.pop() || "";
|
|
84
|
+
for (const line of lines) {
|
|
85
|
+
if (line.startsWith("data: ")) {
|
|
86
|
+
try {
|
|
87
|
+
const data = JSON.parse(line.slice(6));
|
|
88
|
+
if (data.type === "text")
|
|
89
|
+
fullText += data.content;
|
|
90
|
+
}
|
|
91
|
+
catch { }
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
return fullText;
|
|
97
|
+
}
|
|
98
|
+
export async function generateCommitMessage(diff) {
|
|
99
|
+
const prompt = `Generate a conventional commit message for this diff. Only return the message, nothing else.\n\n${diff}`;
|
|
100
|
+
return fetchCompletion([{ role: "user", content: prompt }], "qwen2.5-coder-7b-instruct");
|
|
101
|
+
}
|
|
102
|
+
export async function explainCode(content, fileName) {
|
|
103
|
+
const prompt = `You are an expert developer. Please explain the following code from the file "${fileName}".
|
|
104
|
+
Focus on:
|
|
105
|
+
1. What the code does at a high level.
|
|
106
|
+
2. Key functions or logic flows.
|
|
107
|
+
3. Any potential improvements or "gotchas".
|
|
108
|
+
|
|
109
|
+
Keep the explanation concise and easy to understand.
|
|
110
|
+
|
|
111
|
+
Code:
|
|
112
|
+
\`\`\`
|
|
113
|
+
${content}
|
|
114
|
+
\`\`\``;
|
|
115
|
+
return fetchCompletion([{ role: "user", content: prompt }], "qwen2.5-coder-7b-instruct");
|
|
116
|
+
}
|
|
117
|
+
export async function fixIssue(input, contextType, fileName) {
|
|
118
|
+
let prompt = "";
|
|
119
|
+
if (contextType === "error") {
|
|
120
|
+
prompt = `You are an expert debugger. I am encountering the following error or issue.
|
|
121
|
+
Please analyze it and provide:
|
|
122
|
+
1. A brief explanation of what is likely causing the error.
|
|
123
|
+
2. The corrected code or solution.
|
|
124
|
+
|
|
125
|
+
Issue/Error:
|
|
126
|
+
${input}`;
|
|
127
|
+
}
|
|
128
|
+
else {
|
|
129
|
+
prompt = `You are an expert code reviewer. I have the following code in "${fileName || "a file"}" that needs fixing or improvement.
|
|
130
|
+
Please identify any bugs, performance issues, or bad practices and provide the corrected version.
|
|
131
|
+
|
|
132
|
+
Code:
|
|
133
|
+
\`\`\`
|
|
134
|
+
${input}
|
|
135
|
+
\`\`\``;
|
|
136
|
+
}
|
|
137
|
+
return fetchCompletion([{ role: "user", content: prompt }], "qwen2.5-coder-7b-instruct");
|
|
138
|
+
}
|
package/dist/fs.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import * as fs from "fs";
|
|
2
|
+
import * as path from "path";
|
|
3
|
+
export function readFileContent(filePath) {
|
|
4
|
+
const absolutePath = path.resolve(filePath);
|
|
5
|
+
if (!fs.existsSync(absolutePath)) {
|
|
6
|
+
throw new Error(`File not found: ${filePath}`);
|
|
7
|
+
}
|
|
8
|
+
const stats = fs.statSync(absolutePath);
|
|
9
|
+
if (stats.isDirectory()) {
|
|
10
|
+
throw new Error(`Path is a directory, not a file: ${filePath}`);
|
|
11
|
+
}
|
|
12
|
+
// Limit file size to prevent sending massive files to the LLM (e.g., 100KB limit)
|
|
13
|
+
if (stats.size > 100 * 1024) {
|
|
14
|
+
throw new Error("File is too large to explain (max 100KB).");
|
|
15
|
+
}
|
|
16
|
+
return fs.readFileSync(absolutePath, "utf-8");
|
|
17
|
+
}
|
package/dist/git.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { execSync } from "child_process";
|
|
2
|
+
export function getStagedDiff() {
|
|
3
|
+
try {
|
|
4
|
+
// Get the diff of all staged files
|
|
5
|
+
return execSync("git diff --cached").toString();
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
throw new Error("No staged changes found or not in a git repository.");
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function getCurrentBranch() {
|
|
12
|
+
try {
|
|
13
|
+
return execSync("git branch --show-current").toString().trim();
|
|
14
|
+
}
|
|
15
|
+
catch {
|
|
16
|
+
return "unknown";
|
|
17
|
+
}
|
|
18
|
+
}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command } from 'commander';
|
|
3
|
+
import { execSync } from "node:child_process";
|
|
4
|
+
import chalk from "chalk";
|
|
5
|
+
import { saveConfig } from "./auth.js";
|
|
6
|
+
import { explainCode, fixIssue, generateCommitMessage, streamChat, } from "./chat.js";
|
|
7
|
+
import pkg from "../package.json" with { type: "json" };
|
|
8
|
+
import { getStagedDiff } from "./git.js";
|
|
9
|
+
import { readFileContent } from "./fs.js";
|
|
10
|
+
import * as path from "path";
|
|
11
|
+
import { startInteractiveChat } from "./interactive.js";
|
|
12
|
+
const program = new Command();
|
|
13
|
+
program
|
|
14
|
+
.name("hik")
|
|
15
|
+
.description("Your unified AI workspace CLI")
|
|
16
|
+
.version(pkg.version);
|
|
17
|
+
program
|
|
18
|
+
.command("login")
|
|
19
|
+
.argument("<api-key>", "Your Hik API key (hik_...)")
|
|
20
|
+
.description("Save your API key locally")
|
|
21
|
+
.action((apiKey) => {
|
|
22
|
+
if (!apiKey.startsWith("hik_")) {
|
|
23
|
+
console.error("[ERROR] Invalid API key format. It should start with hik_");
|
|
24
|
+
process.exit(1);
|
|
25
|
+
}
|
|
26
|
+
saveConfig({ apiKey });
|
|
27
|
+
console.log("[SUCCESS] API key saved successfully!");
|
|
28
|
+
});
|
|
29
|
+
program
|
|
30
|
+
.command("chat")
|
|
31
|
+
.argument("<message>", "The message to send to Hik")
|
|
32
|
+
.option("-m, --model <model>", "Specify the LLM model", "qwen2.5-coder-7b-instruct")
|
|
33
|
+
.description("Start a chat with Hik")
|
|
34
|
+
.action(async (message, options) => {
|
|
35
|
+
try {
|
|
36
|
+
console.log("[INFO] Hik: ");
|
|
37
|
+
await streamChat(message, options.model);
|
|
38
|
+
}
|
|
39
|
+
catch (error) {
|
|
40
|
+
console.error("[ERROR]:", error.message);
|
|
41
|
+
process.exit(1);
|
|
42
|
+
}
|
|
43
|
+
});
|
|
44
|
+
program
|
|
45
|
+
.command("config")
|
|
46
|
+
.option("--url <url>", "Set the backend API URL")
|
|
47
|
+
.description("Manage CLI configuration")
|
|
48
|
+
.action((options) => {
|
|
49
|
+
if (options.url) {
|
|
50
|
+
saveConfig({ apiUrl: options.url });
|
|
51
|
+
console.log(`[SUCCESS] Backend URL set to: ${options.url}`);
|
|
52
|
+
}
|
|
53
|
+
else {
|
|
54
|
+
console.error("[ERROR] No URL provided.");
|
|
55
|
+
console.log("Usage: hik config --url <http://localhost:3000>");
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
program
|
|
59
|
+
.command("commit")
|
|
60
|
+
.description("Generate a commit message from staged changes")
|
|
61
|
+
.option("-y, --yes", "Automatically commit with the generated message")
|
|
62
|
+
.action(async (options) => {
|
|
63
|
+
try {
|
|
64
|
+
console.log(chalk.blue("[INFO] Analyzing staged changes..."));
|
|
65
|
+
const diff = getStagedDiff();
|
|
66
|
+
if (!diff.trim()) {
|
|
67
|
+
console.log(chalk.yellow("[WARNING] No staged changes found."));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
console.log(chalk.blue("✨ Generating commit message..."));
|
|
71
|
+
const message = await generateCommitMessage(diff);
|
|
72
|
+
console.log(chalk.green("\nProposed Commit Message:"));
|
|
73
|
+
console.log(chalk.bold(`"${message}"\n`));
|
|
74
|
+
if (options.yes) {
|
|
75
|
+
execSync(`git commit -m "${message}"`, { stdio: "inherit" });
|
|
76
|
+
console.log(chalk.green("[SUCCESS] Committed successfully!"));
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
console.log(chalk.gray("[INFO] To commit automatically next time, use: hik commit -y"));
|
|
80
|
+
console.log(chalk.gray(`[INFO] Or run: git commit -m "${message}"`));
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
catch (error) {
|
|
84
|
+
console.error(chalk.red("[ERROR] Error:"), error.message);
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
program
|
|
89
|
+
.command("explain")
|
|
90
|
+
.argument("<file>", "Path to the file you want to explain")
|
|
91
|
+
.description("Explain the contents of a code file")
|
|
92
|
+
.action(async (filePath) => {
|
|
93
|
+
try {
|
|
94
|
+
console.log(chalk.blue(`[INFO] Reading ${filePath}...`));
|
|
95
|
+
const content = readFileContent(filePath);
|
|
96
|
+
console.log(chalk.blue("✨ Analyzing code..."));
|
|
97
|
+
const explanation = await explainCode(content, path.basename(filePath));
|
|
98
|
+
console.log(chalk.green("\n💡 Explanation:"));
|
|
99
|
+
console.log(explanation);
|
|
100
|
+
console.log("\n");
|
|
101
|
+
}
|
|
102
|
+
catch (error) {
|
|
103
|
+
console.error(chalk.red("[ERROR] Error:"), error.message);
|
|
104
|
+
process.exit(1);
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
program
|
|
108
|
+
.command("fix")
|
|
109
|
+
.argument("[file]", "Path to the file containing the code or error log")
|
|
110
|
+
.description("Get a fix for an error message or buggy code. Can also read from stdin.")
|
|
111
|
+
.action(async (filePath) => {
|
|
112
|
+
try {
|
|
113
|
+
let input = "";
|
|
114
|
+
let contextType = "error";
|
|
115
|
+
let sourceName = "stdin";
|
|
116
|
+
// Check if data is being piped (e.g., cat error.log | hik fix)
|
|
117
|
+
if (process.stdin.isTTY) {
|
|
118
|
+
// If no file argument and not piped, ask for manual paste (MVP: just throw error for now)
|
|
119
|
+
if (!filePath) {
|
|
120
|
+
console.error(chalk.red("[ERROR] Please provide a file path or pipe input. Example:"));
|
|
121
|
+
console.error(chalk.gray(" hik fix error.log"));
|
|
122
|
+
console.error(chalk.gray(" cat error.log | hik fix"));
|
|
123
|
+
console.error(chalk.gray(" hik fix src/broken-code.ts"));
|
|
124
|
+
process.exit(1);
|
|
125
|
+
}
|
|
126
|
+
// It's a file
|
|
127
|
+
input = readFileContent(filePath);
|
|
128
|
+
sourceName = filePath;
|
|
129
|
+
// Simple heuristic: if it looks like code, treat as code
|
|
130
|
+
if (filePath.match(/\.(ts|js|py|go|rs|java|cpp|c)$/)) {
|
|
131
|
+
contextType = "code";
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
else {
|
|
135
|
+
// Reading from stdin
|
|
136
|
+
const chunks = [];
|
|
137
|
+
for await (const chunk of process.stdin) {
|
|
138
|
+
chunks.push(chunk);
|
|
139
|
+
}
|
|
140
|
+
input = Buffer.concat(chunks).toString();
|
|
141
|
+
contextType = "error"; // Default piped input to error/log analysis
|
|
142
|
+
}
|
|
143
|
+
if (!input.trim()) {
|
|
144
|
+
console.error(chalk.red("[ERROR] No input provided."));
|
|
145
|
+
process.exit(1);
|
|
146
|
+
}
|
|
147
|
+
console.log(chalk.blue(`🔍 Analyzing ${sourceName}...`));
|
|
148
|
+
const solution = await fixIssue(input, contextType, sourceName);
|
|
149
|
+
console.log(chalk.green("\n🛠️ Suggested Fix:"));
|
|
150
|
+
console.log(solution);
|
|
151
|
+
console.log("\n");
|
|
152
|
+
}
|
|
153
|
+
catch (error) {
|
|
154
|
+
console.error(chalk.red("[ERROR] Error:"), error.message);
|
|
155
|
+
process.exit(1);
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
program
|
|
159
|
+
.command("interactive")
|
|
160
|
+
.alias("i")
|
|
161
|
+
.description("Start an interactive chat session")
|
|
162
|
+
.action(() => {
|
|
163
|
+
startInteractiveChat();
|
|
164
|
+
});
|
|
165
|
+
program.parse();
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
import * as readline from 'readline';
|
|
2
|
+
import { requireApiKey, getConfig } from './auth.js';
|
|
3
|
+
import chalk from 'chalk';
|
|
4
|
+
export async function startInteractiveChat() {
|
|
5
|
+
const apiKey = requireApiKey();
|
|
6
|
+
const config = getConfig();
|
|
7
|
+
const apiUrl = config.apiUrl || 'http://localhost:3000';
|
|
8
|
+
const rl = readline.createInterface({
|
|
9
|
+
input: process.stdin,
|
|
10
|
+
output: process.stdout,
|
|
11
|
+
prompt: chalk.green('hik> '),
|
|
12
|
+
});
|
|
13
|
+
const history = [];
|
|
14
|
+
const sessionId = crypto.randomUUID();
|
|
15
|
+
console.log(chalk.blue('\n🤖 Hik Interactive Mode'));
|
|
16
|
+
console.log(chalk.gray('Type your message and press Enter. Type /exit to quit.\n'));
|
|
17
|
+
rl.prompt();
|
|
18
|
+
rl.on('line', async (line) => {
|
|
19
|
+
const input = line.trim();
|
|
20
|
+
if (input.toLowerCase() === '/exit' || input.toLowerCase() === '/quit') {
|
|
21
|
+
console.log(chalk.yellow('Goodbye! 👋'));
|
|
22
|
+
rl.close();
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if (!input) {
|
|
26
|
+
rl.prompt();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// Add user message to history
|
|
30
|
+
history.push({ role: 'user', content: input });
|
|
31
|
+
try {
|
|
32
|
+
// Show a small indicator that we are thinking
|
|
33
|
+
process.stdout.write(chalk.dim('Hik is typing...\r'));
|
|
34
|
+
const response = await fetch(`${apiUrl}/api/v1/chat`, {
|
|
35
|
+
method: 'POST',
|
|
36
|
+
headers: {
|
|
37
|
+
'Content-Type': 'application/json',
|
|
38
|
+
'x-api-key': apiKey,
|
|
39
|
+
},
|
|
40
|
+
body: JSON.stringify({
|
|
41
|
+
sessionId,
|
|
42
|
+
messages: history,
|
|
43
|
+
model: 'qwen2.5-coder-7b-instruct',
|
|
44
|
+
}),
|
|
45
|
+
});
|
|
46
|
+
if (!response.ok)
|
|
47
|
+
throw new Error(await response.text());
|
|
48
|
+
// Clear the "typing" indicator
|
|
49
|
+
process.stdout.write(' '.repeat(20) + '\r');
|
|
50
|
+
// Stream the response
|
|
51
|
+
const reader = response.body?.getReader();
|
|
52
|
+
const decoder = new TextDecoder();
|
|
53
|
+
let assistantResponse = '';
|
|
54
|
+
if (reader) {
|
|
55
|
+
let buffer = '';
|
|
56
|
+
while (true) {
|
|
57
|
+
const { done, value } = await reader.read();
|
|
58
|
+
if (done)
|
|
59
|
+
break;
|
|
60
|
+
buffer += decoder.decode(value, { stream: true });
|
|
61
|
+
const lines = buffer.split('\n');
|
|
62
|
+
buffer = lines.pop() || '';
|
|
63
|
+
for (const line of lines) {
|
|
64
|
+
if (line.startsWith('data: ')) {
|
|
65
|
+
try {
|
|
66
|
+
const data = JSON.parse(line.slice(6));
|
|
67
|
+
if (data.type === 'text') {
|
|
68
|
+
process.stdout.write(data.content);
|
|
69
|
+
assistantResponse += data.content;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
catch { }
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
console.log('\n'); // New line after response
|
|
78
|
+
// Add assistant response to history
|
|
79
|
+
history.push({ role: 'assistant', content: assistantResponse });
|
|
80
|
+
}
|
|
81
|
+
catch (error) {
|
|
82
|
+
console.error(chalk.red('\n[ERROR] Error:'), error.message);
|
|
83
|
+
}
|
|
84
|
+
finally {
|
|
85
|
+
rl.prompt();
|
|
86
|
+
}
|
|
87
|
+
});
|
|
88
|
+
rl.on('close', () => {
|
|
89
|
+
console.log('\n');
|
|
90
|
+
process.exit(0);
|
|
91
|
+
});
|
|
92
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@udara-kavindu/hik-cli",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Your unified AI workspace CLI",
|
|
5
|
+
"module": "index.ts",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "dist/index.js",
|
|
8
|
+
"scripts": {
|
|
9
|
+
"dev": "bun run src/index.ts",
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"prepublishOnly": "bun run build"
|
|
12
|
+
},
|
|
13
|
+
"bin": {
|
|
14
|
+
"hik": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"keywords": [
|
|
17
|
+
"ai",
|
|
18
|
+
"cli",
|
|
19
|
+
"llm",
|
|
20
|
+
"obsidian",
|
|
21
|
+
"hik"
|
|
22
|
+
],
|
|
23
|
+
"author": "Kavindu Udara <karunasinghesampath@gmail.com>",
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"files": [
|
|
26
|
+
"dist"
|
|
27
|
+
],
|
|
28
|
+
"engines": {
|
|
29
|
+
"node": ">=18.0.0"
|
|
30
|
+
},
|
|
31
|
+
"devDependencies": {
|
|
32
|
+
"@types/bun": "latest"
|
|
33
|
+
},
|
|
34
|
+
"peerDependencies": {
|
|
35
|
+
"typescript": "^7"
|
|
36
|
+
},
|
|
37
|
+
"dependencies": {
|
|
38
|
+
"chalk": "^6.0.0",
|
|
39
|
+
"commander": "^15.0.0",
|
|
40
|
+
"node-fetch": "^3.3.2"
|
|
41
|
+
}
|
|
42
|
+
}
|