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.
Files changed (2) hide show
  1. package/dist/cli.mjs +137 -39
  2. 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(prompt, config) {
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
- prompt,
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-input-available":
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-output-available":
291
+ case "tool-result":
261
292
  process.stdout.write(
262
- chalk2.green(`\u2705 tool`) + chalk2.gray(` \u2192 ${truncate(String(event.output), 120)}
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-output-error":
268
- process.stderr.write(chalk2.red(`
269
- \u274C Tool error: ${event.errorText}
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.errorText}
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 BANNER = chalk3.bold.cyan(`
309
- \u{1F988} Shark Code v0.1
310
- AI Coding Agent \u2014 Local First, Open Source
311
- `);
312
- var HELP = `${BANNER}
313
- ${chalk3.white("Usage:")}
314
- ${chalk3.green("sharkcode")} ${chalk3.yellow('"your prompt here"')}
315
-
316
- ${chalk3.white("Examples:")}
317
- sharkcode "explain this codebase"
318
- sharkcode "fix the null pointer bug in auth.ts"
319
- sharkcode "add error handling to the API routes"
320
-
321
- ${chalk3.white("Config:")}
322
- Set ${chalk3.yellow("DEEPSEEK_API_KEY")} env var, or edit ${chalk3.gray("~/.sharkcode/config.toml")}
323
- Get your key at ${chalk3.underline("https://platform.deepseek.com")}
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.length === 0 || args[0] === "--help" || args[0] === "-h") {
328
- console.log(HELP);
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.1.0");
403
+ console.log("sharkcode v0.2.0");
333
404
  return;
334
405
  }
335
- const prompt = args.join(" ");
336
406
  const config = loadConfig();
337
- process.stdout.write(
338
- chalk3.cyan("\u{1F988} Shark Code") + chalk3.gray(` | model: ${config.model}
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
- await runAgent(prompt, config);
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
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sharkcode",
3
- "version": "0.1.0",
3
+ "version": "0.2.1",
4
4
  "description": "Local First, open-source AI Coding Agent",
5
5
  "type": "module",
6
6
  "bin": {