sharkcode 0.1.0 → 0.2.1
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.mjs +137 -39
- package/package.json +1 -1
package/dist/cli.mjs
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/cli.ts
|
|
4
4
|
import chalk3 from "chalk";
|
|
5
|
+
import * as readline2 from "readline";
|
|
5
6
|
|
|
6
7
|
// src/config.ts
|
|
7
8
|
import { parse } from "smol-toml";
|
|
@@ -215,6 +216,29 @@ function createProvider(config) {
|
|
|
215
216
|
}
|
|
216
217
|
|
|
217
218
|
// src/agent.ts
|
|
219
|
+
var PURPLE = chalk2.hex("#a855f7");
|
|
220
|
+
function createSpinner() {
|
|
221
|
+
const frames = ["\u280B", "\u2819", "\u2839", "\u2838", "\u283C", "\u2834", "\u2826", "\u2827", "\u2807", "\u280F"];
|
|
222
|
+
let i = 0;
|
|
223
|
+
let timer = null;
|
|
224
|
+
return {
|
|
225
|
+
start() {
|
|
226
|
+
process.stdout.write("\n");
|
|
227
|
+
timer = setInterval(() => {
|
|
228
|
+
process.stdout.write(
|
|
229
|
+
`\r${PURPLE(frames[i++ % frames.length])} ${chalk2.gray("thinking...")}`
|
|
230
|
+
);
|
|
231
|
+
}, 80);
|
|
232
|
+
},
|
|
233
|
+
stop() {
|
|
234
|
+
if (timer) {
|
|
235
|
+
clearInterval(timer);
|
|
236
|
+
timer = null;
|
|
237
|
+
process.stdout.write("\r\x1B[K");
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
};
|
|
241
|
+
}
|
|
218
242
|
function buildSystemPrompt() {
|
|
219
243
|
return `You are Shark Code, an AI coding assistant. You help users with coding tasks by reading, writing, and editing files, and running shell commands.
|
|
220
244
|
|
|
@@ -235,50 +259,62 @@ Guidelines:
|
|
|
235
259
|
- Keep changes minimal and focused on the user's request
|
|
236
260
|
- When done, summarize what you changed`;
|
|
237
261
|
}
|
|
238
|
-
async function runAgent(
|
|
262
|
+
async function runAgent(messages, config) {
|
|
239
263
|
const model = createProvider(config);
|
|
240
264
|
const result = streamText({
|
|
241
265
|
model,
|
|
242
266
|
system: buildSystemPrompt(),
|
|
243
|
-
|
|
267
|
+
messages,
|
|
244
268
|
tools,
|
|
245
269
|
stopWhen: stepCountIs(30)
|
|
246
270
|
});
|
|
247
271
|
let currentStep = 0;
|
|
272
|
+
const spinner = createSpinner();
|
|
273
|
+
let spinnerStopped = false;
|
|
274
|
+
spinner.start();
|
|
248
275
|
for await (const event of result.fullStream) {
|
|
276
|
+
if (!spinnerStopped && (event.type === "text-delta" || event.type === "tool-call" || event.type === "error")) {
|
|
277
|
+
spinner.stop();
|
|
278
|
+
spinnerStopped = true;
|
|
279
|
+
}
|
|
249
280
|
switch (event.type) {
|
|
250
281
|
case "text-delta":
|
|
251
282
|
process.stdout.write(event.text);
|
|
252
283
|
break;
|
|
253
|
-
case "tool-
|
|
284
|
+
case "tool-call":
|
|
254
285
|
process.stdout.write(
|
|
255
286
|
chalk2.cyan(`
|
|
256
287
|
\u{1F527} ${event.toolName}`) + chalk2.gray(`(${formatArgs(event.input)})
|
|
257
288
|
`)
|
|
258
289
|
);
|
|
259
290
|
break;
|
|
260
|
-
case "tool-
|
|
291
|
+
case "tool-result":
|
|
261
292
|
process.stdout.write(
|
|
262
|
-
chalk2.green(`\u2705
|
|
293
|
+
chalk2.green(`\u2705 done`) + chalk2.gray(` \u2192 ${truncate(String(event.output), 120)}
|
|
263
294
|
|
|
264
295
|
`)
|
|
265
296
|
);
|
|
266
297
|
break;
|
|
267
|
-
case "tool-
|
|
268
|
-
process.stderr.write(
|
|
269
|
-
|
|
270
|
-
|
|
298
|
+
case "tool-error":
|
|
299
|
+
process.stderr.write(
|
|
300
|
+
chalk2.red(`
|
|
301
|
+
\u274C Tool error [${event.toolName}]: ${String(event.error)}
|
|
302
|
+
`)
|
|
303
|
+
);
|
|
271
304
|
break;
|
|
272
305
|
case "finish-step":
|
|
273
306
|
currentStep++;
|
|
274
307
|
break;
|
|
275
308
|
case "error":
|
|
276
309
|
process.stderr.write(chalk2.red(`
|
|
277
|
-
\u274C Error: ${event.
|
|
310
|
+
\u274C Error: ${String(event.error)}
|
|
278
311
|
`));
|
|
279
312
|
break;
|
|
280
313
|
}
|
|
281
314
|
}
|
|
315
|
+
if (!spinnerStopped) {
|
|
316
|
+
spinner.stop();
|
|
317
|
+
}
|
|
282
318
|
process.stdout.write("\n");
|
|
283
319
|
const usage = await result.totalUsage;
|
|
284
320
|
if (usage) {
|
|
@@ -290,6 +326,12 @@ async function runAgent(prompt, config) {
|
|
|
290
326
|
)
|
|
291
327
|
);
|
|
292
328
|
}
|
|
329
|
+
try {
|
|
330
|
+
const { messages: responseMessages } = await result.response;
|
|
331
|
+
return [...messages, ...responseMessages];
|
|
332
|
+
} catch {
|
|
333
|
+
return messages;
|
|
334
|
+
}
|
|
293
335
|
}
|
|
294
336
|
function formatArgs(args) {
|
|
295
337
|
if (typeof args !== "object" || args === null) return String(args);
|
|
@@ -305,47 +347,103 @@ function truncate(str, max) {
|
|
|
305
347
|
}
|
|
306
348
|
|
|
307
349
|
// src/cli.ts
|
|
308
|
-
var
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
350
|
+
var PURPLE2 = chalk3.hex("#a855f7");
|
|
351
|
+
var GLYPHS = {
|
|
352
|
+
S: [[0, 1, 1, 1, 1], [1, 0, 0, 0, 0], [0, 1, 1, 1, 0], [0, 0, 0, 0, 1], [1, 1, 1, 1, 0]],
|
|
353
|
+
H: [[1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1]],
|
|
354
|
+
A: [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 1, 1, 1, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1]],
|
|
355
|
+
R: [[1, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 1, 1, 1, 0], [1, 0, 1, 0, 0], [1, 0, 0, 1, 0]],
|
|
356
|
+
K: [[1, 0, 0, 1, 0], [1, 0, 1, 0, 0], [1, 1, 0, 0, 0], [1, 0, 1, 0, 0], [1, 0, 0, 1, 0]],
|
|
357
|
+
C: [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 0], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]],
|
|
358
|
+
O: [[0, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [0, 1, 1, 1, 0]],
|
|
359
|
+
D: [[1, 1, 1, 1, 0], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 0, 0, 0, 1], [1, 1, 1, 1, 0]],
|
|
360
|
+
E: [[1, 1, 1, 1, 1], [1, 0, 0, 0, 0], [1, 1, 1, 1, 0], [1, 0, 0, 0, 0], [1, 1, 1, 1, 1]]
|
|
361
|
+
};
|
|
362
|
+
function renderWord(word, padLeft = 2) {
|
|
363
|
+
const letters = word.toUpperCase().split("").map((c) => GLYPHS[c]);
|
|
364
|
+
const rows = [];
|
|
365
|
+
for (let row = 0; row < 5; row++) {
|
|
366
|
+
let line = " ".repeat(padLeft);
|
|
367
|
+
for (let i = 0; i < letters.length; i++) {
|
|
368
|
+
const letter = letters[i];
|
|
369
|
+
for (let col = 0; col < letter[row].length; col++) {
|
|
370
|
+
line += letter[row][col] ? "\u2588\u2588" : " ";
|
|
371
|
+
}
|
|
372
|
+
if (i < letters.length - 1) line += " ";
|
|
373
|
+
}
|
|
374
|
+
rows.push(PURPLE2(line));
|
|
375
|
+
}
|
|
376
|
+
return rows;
|
|
377
|
+
}
|
|
378
|
+
var BANNER = ["", ...renderWord("shark", 2), "", ...renderWord("code", 8), ""].join("\n");
|
|
379
|
+
async function readLine(prompt) {
|
|
380
|
+
return new Promise((resolve4) => {
|
|
381
|
+
const rl = readline2.createInterface({ input: process.stdin, output: process.stdout });
|
|
382
|
+
let answered = false;
|
|
383
|
+
rl.question(prompt, (answer) => {
|
|
384
|
+
answered = true;
|
|
385
|
+
rl.close();
|
|
386
|
+
resolve4(answer);
|
|
387
|
+
});
|
|
388
|
+
rl.on("close", () => {
|
|
389
|
+
if (!answered) resolve4(null);
|
|
390
|
+
});
|
|
391
|
+
});
|
|
392
|
+
}
|
|
325
393
|
async function main() {
|
|
326
394
|
const args = process.argv.slice(2);
|
|
327
|
-
if (args
|
|
328
|
-
console.log(
|
|
395
|
+
if (args[0] === "--help" || args[0] === "-h") {
|
|
396
|
+
console.log(BANNER);
|
|
397
|
+
console.log(PURPLE2(" Usage:"));
|
|
398
|
+
console.log(" " + PURPLE2("sharkcode") + chalk3.gray(" \u2014 interactive mode"));
|
|
399
|
+
console.log(" " + PURPLE2("sharkcode") + chalk3.yellow(' "prompt"') + chalk3.gray(" \u2014 single-shot mode\n"));
|
|
329
400
|
return;
|
|
330
401
|
}
|
|
331
402
|
if (args[0] === "--version" || args[0] === "-v") {
|
|
332
|
-
console.log("sharkcode v0.
|
|
403
|
+
console.log("sharkcode v0.2.0");
|
|
333
404
|
return;
|
|
334
405
|
}
|
|
335
|
-
const prompt = args.join(" ");
|
|
336
406
|
const config = loadConfig();
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
407
|
+
if (args.length > 0) {
|
|
408
|
+
console.log(PURPLE2("\n\u{1F988} SharkCode") + chalk3.gray(` | model: ${config.model}
|
|
409
|
+
`));
|
|
410
|
+
const messages2 = [{ role: "user", content: args.join(" ") }];
|
|
411
|
+
await runAgent(messages2, config);
|
|
412
|
+
return;
|
|
413
|
+
}
|
|
414
|
+
console.log(BANNER);
|
|
415
|
+
console.log(
|
|
416
|
+
PURPLE2(" \u25C6") + chalk3.gray(` model: ${config.model}`) + chalk3.gray(' type "exit" to quit\n')
|
|
341
417
|
);
|
|
342
|
-
|
|
418
|
+
let messages = [];
|
|
419
|
+
while (true) {
|
|
420
|
+
const input = await readLine(PURPLE2("\n\u25C6 "));
|
|
421
|
+
if (input === null) {
|
|
422
|
+
console.log(chalk3.gray("\nBye! \u{1F988}"));
|
|
423
|
+
break;
|
|
424
|
+
}
|
|
425
|
+
const trimmed = input.trim();
|
|
426
|
+
if (!trimmed) continue;
|
|
427
|
+
if (trimmed === "exit" || trimmed === "quit" || trimmed === "/exit") {
|
|
428
|
+
console.log(chalk3.gray("Bye! \u{1F988}"));
|
|
429
|
+
break;
|
|
430
|
+
}
|
|
431
|
+
messages.push({ role: "user", content: trimmed });
|
|
432
|
+
try {
|
|
433
|
+
messages = await runAgent(messages, config);
|
|
434
|
+
} catch (err) {
|
|
435
|
+
const error = err;
|
|
436
|
+
console.error(chalk3.red(`
|
|
437
|
+
\u274C Error: ${error.message}`));
|
|
438
|
+
if (error.message?.includes("401") || error.message?.includes("Unauthorized")) {
|
|
439
|
+
console.error(chalk3.yellow(" Check your API key in ~/.sharkcode/config.toml"));
|
|
440
|
+
}
|
|
441
|
+
messages = messages.slice(0, -1);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
343
444
|
}
|
|
344
445
|
main().catch((err) => {
|
|
345
446
|
console.error(chalk3.red(`
|
|
346
447
|
\u274C Fatal: ${err.message}`));
|
|
347
|
-
if (err.message?.includes("401") || err.message?.includes("Unauthorized")) {
|
|
348
|
-
console.error(chalk3.yellow(" Check your API key in ~/.sharkcode/config.toml"));
|
|
349
|
-
}
|
|
350
448
|
process.exit(1);
|
|
351
449
|
});
|