sharkbait 1.0.12 → 1.0.14

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.js +63 -32
  2. package/package.json +1 -1
package/dist/cli.js CHANGED
@@ -746,14 +746,6 @@ var REQUIRES_CONFIRMATION = [
746
746
  undoHint: "chown with original owner"
747
747
  }
748
748
  ];
749
- var SHELL_CHAINING_PATTERNS = [
750
- /;/,
751
- /\|\|/,
752
- /&&/,
753
- /\$\(/,
754
- /`[^`]+`/,
755
- /\|\s*\w/
756
- ];
757
749
  var DANGEROUS_PREFIXES = ["sudo", "doas", "pkexec", "exec"];
758
750
  function classifyCommand(command) {
759
751
  const trimmed = command.trim();
@@ -780,17 +772,30 @@ function classifyCommand(command) {
780
772
  reversibility: "effort"
781
773
  };
782
774
  }
783
- for (const pattern of SHELL_CHAINING_PATTERNS) {
784
- if (pattern.test(trimmed)) {
785
- return {
786
- status: "requires_confirmation",
787
- reason: "Command contains shell operators (;, &&, ||, |, $()) — each sub-command cannot be independently verified",
788
- reversibility: "effort"
789
- };
775
+ if (/\$\(/.test(trimmed) || /`[^`]+`/.test(trimmed)) {
776
+ return {
777
+ status: "requires_confirmation",
778
+ reason: "Command contains command substitution ($() or backticks)",
779
+ reversibility: "effort"
780
+ };
781
+ }
782
+ const hasChaining = /;|&&|\|\||(\|\s*\w)/.test(trimmed);
783
+ if (hasChaining) {
784
+ const subCommands = trimmed.split(/\s*(?:;|&&|\|\|)\s*/).flatMap((segment) => segment.split(/\s*\|\s*/)).map((s) => s.trim()).filter(Boolean);
785
+ for (const sub of subCommands) {
786
+ const subSafety = classifySimpleCommand(sub);
787
+ if (subSafety.status === "blocked")
788
+ return subSafety;
789
+ if (subSafety.status === "requires_confirmation")
790
+ return subSafety;
790
791
  }
792
+ return { status: "allowed" };
791
793
  }
792
- const baseCommand = extractBaseCommand(trimmed);
793
- if (isOnAllowlist(baseCommand, trimmed)) {
794
+ return classifySimpleCommand(trimmed);
795
+ }
796
+ function classifySimpleCommand(command) {
797
+ const baseCommand = extractBaseCommand(command);
798
+ if (isOnAllowlist(baseCommand, command)) {
794
799
  return { status: "allowed" };
795
800
  }
796
801
  return {
@@ -6423,7 +6428,7 @@ import React3 from "react";
6423
6428
  import { render } from "ink";
6424
6429
 
6425
6430
  // src/ui/app.tsx
6426
- import React2, { useState as useState2, useRef } from "react";
6431
+ import React2, { useState as useState2, useEffect as useEffect2, useCallback, useRef } from "react";
6427
6432
  import { Box as Box12, Text as Text12, useInput, useApp } from "ink";
6428
6433
 
6429
6434
  // src/ui/message.tsx
@@ -6700,7 +6705,7 @@ function Spinner({
6700
6705
  useEffect(() => {
6701
6706
  const timer = setInterval(() => {
6702
6707
  setFrameIndex((prev) => (prev + 1) % frames.length);
6703
- }, 100);
6708
+ }, 200);
6704
6709
  return () => clearInterval(timer);
6705
6710
  }, [frames.length]);
6706
6711
  return /* @__PURE__ */ jsxDEV3(Box3, {
@@ -8262,8 +8267,28 @@ function App({ contextFiles: initialContextFiles, enableBeads: initialBeadsEnabl
8262
8267
  return config.azure.deployment;
8263
8268
  });
8264
8269
  const abortControllerRef = useRef(null);
8270
+ const pendingOutputRef = useRef("");
8271
+ const outputTimerRef = useRef(null);
8265
8272
  const { exit } = useApp();
8266
8273
  const workingDir = currentDir;
8274
+ const flushOutput = useCallback(() => {
8275
+ if (pendingOutputRef.current) {
8276
+ setCurrentOutput(pendingOutputRef.current);
8277
+ }
8278
+ outputTimerRef.current = null;
8279
+ }, []);
8280
+ const throttledSetOutput = useCallback((content) => {
8281
+ pendingOutputRef.current = content;
8282
+ if (!outputTimerRef.current) {
8283
+ outputTimerRef.current = setTimeout(flushOutput, 50);
8284
+ }
8285
+ }, [flushOutput]);
8286
+ useEffect2(() => {
8287
+ return () => {
8288
+ if (outputTimerRef.current)
8289
+ clearTimeout(outputTimerRef.current);
8290
+ };
8291
+ }, []);
8267
8292
  const agent = React2.useMemo(() => new Agent({
8268
8293
  contextFiles,
8269
8294
  enableBeads: beadsEnabled
@@ -8427,7 +8452,7 @@ function App({ contextFiles: initialContextFiles, enableBeads: initialBeadsEnabl
8427
8452
  switch (event.type) {
8428
8453
  case "text":
8429
8454
  assistantContent += event.content;
8430
- setCurrentOutput(assistantContent);
8455
+ throttledSetOutput(assistantContent);
8431
8456
  const chunkTokens = estimateTokens(event.content);
8432
8457
  setTokenCount((prev) => prev + chunkTokens);
8433
8458
  setSessionCost((prev) => prev + chunkTokens * 0.00003);
@@ -8497,18 +8522,18 @@ ${event.consolidated}`,
8497
8522
  }
8498
8523
  case "tool_result": {
8499
8524
  const duration = event.duration;
8500
- setActiveToolCalls((prev) => prev.map((tc) => tc.name === event.name && tc.status === "running" ? {
8501
- ...tc,
8502
- status: "success",
8503
- duration,
8504
- result: typeof event.result === "string" ? event.result.slice(0, 100) : JSON.stringify(event.result).slice(0, 100)
8505
- } : tc));
8506
8525
  setActiveToolCalls((prev) => {
8507
- const completed = prev.find((tc) => tc.name === event.name && tc.status === "success");
8526
+ const updated = prev.map((tc) => tc.name === event.name && tc.status === "running" ? {
8527
+ ...tc,
8528
+ status: "success",
8529
+ duration,
8530
+ result: typeof event.result === "string" ? event.result.slice(0, 100) : JSON.stringify(event.result).slice(0, 100)
8531
+ } : tc);
8532
+ const completed = updated.find((tc) => tc.name === event.name && tc.status === "success");
8508
8533
  if (completed) {
8509
8534
  completedToolCalls.push(completed);
8510
8535
  }
8511
- return prev.filter((tc) => !(tc.name === event.name && tc.status === "success"));
8536
+ return updated.filter((tc) => !(tc.name === event.name && tc.status === "success"));
8512
8537
  });
8513
8538
  break;
8514
8539
  }
@@ -8526,6 +8551,11 @@ ${event.consolidated}`,
8526
8551
  setTokenCount(event.totalTokens);
8527
8552
  break;
8528
8553
  case "done":
8554
+ if (outputTimerRef.current) {
8555
+ clearTimeout(outputTimerRef.current);
8556
+ outputTimerRef.current = null;
8557
+ }
8558
+ pendingOutputRef.current = "";
8529
8559
  if (assistantContent.trim()) {
8530
8560
  setMessages((prev) => [...prev, {
8531
8561
  role: "assistant",
@@ -8636,7 +8666,8 @@ ${event.consolidated}`,
8636
8666
  }, undefined, false, undefined, this),
8637
8667
  currentOutput && /* @__PURE__ */ jsxDEV12(MessageView, {
8638
8668
  role: "assistant",
8639
- content: currentOutput
8669
+ content: currentOutput,
8670
+ enableHighlighting: false
8640
8671
  }, undefined, false, undefined, this)
8641
8672
  ]
8642
8673
  }, undefined, true, undefined, this),
@@ -8699,7 +8730,7 @@ ${event.consolidated}`,
8699
8730
  }
8700
8731
 
8701
8732
  // src/version.ts
8702
- var VERSION = "1.0.12";
8733
+ var VERSION = "1.0.14";
8703
8734
 
8704
8735
  // src/agent/start-chat.ts
8705
8736
  async function startChat(options = {}) {
@@ -8714,7 +8745,7 @@ async function startChat(options = {}) {
8714
8745
  enableBeads: options.beads ?? true,
8715
8746
  version: VERSION,
8716
8747
  workingDir
8717
- }));
8748
+ }), { patchConsole: true });
8718
8749
  await waitUntilExit();
8719
8750
  }
8720
8751
  // src/commands/init.ts
@@ -9876,7 +9907,7 @@ ${"━".repeat(60)}`);
9876
9907
  }
9877
9908
 
9878
9909
  // src/version.ts
9879
- var VERSION2 = "1.0.12";
9910
+ var VERSION2 = "1.0.14";
9880
9911
 
9881
9912
  // src/ui/logo.tsx
9882
9913
  import { Box as Box14, Text as Text14 } from "ink";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sharkbait",
3
- "version": "1.0.12",
3
+ "version": "1.0.14",
4
4
  "description": "AI-powered coding assistant for the command line. Uses OpenAI Responses API (not Chat). Autonomous agents, parallel code reviews, 36 tools.",
5
5
  "type": "module",
6
6
  "main": "./dist/cli.js",