cueclaw 0.2.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.
@@ -510,9 +510,13 @@ async function stopDaemonBridge(bridge) {
510
510
  function stopExternalDaemon() {
511
511
  const pid = readPidFile();
512
512
  if (pid && isProcessAlive(pid)) {
513
- process.kill(pid, "SIGTERM");
513
+ try {
514
+ process.kill(pid, "SIGTERM");
515
+ logger.info({ pid }, "Stopped external daemon");
516
+ } catch (err) {
517
+ logger.warn({ err, pid }, "Failed to stop external daemon");
518
+ }
514
519
  removePidFile();
515
- logger.info({ pid }, "Stopped external daemon");
516
520
  }
517
521
  }
518
522
 
@@ -528,7 +532,9 @@ function useDaemonBridge(config, db, cwd, dispatch) {
528
532
  dispatch({ type: "ADD_MESSAGE", message: { type: "system", text: "Starting daemon..." } });
529
533
  initDaemonBridge(db, config, cwd, { skipBots: !hasConfiguredBots }).then((bridge) => {
530
534
  if (cancelled) {
531
- stopDaemonBridge(bridge);
535
+ stopDaemonBridge(bridge).catch((err) => {
536
+ logger.error({ err }, "Failed to stop daemon bridge after cancellation");
537
+ });
532
538
  return;
533
539
  }
534
540
  bridgeRef.current = bridge;
@@ -563,7 +569,9 @@ function useDaemonBridge(config, db, cwd, dispatch) {
563
569
  return () => {
564
570
  cancelled = true;
565
571
  if (bridgeRef.current) {
566
- stopDaemonBridge(bridgeRef.current);
572
+ stopDaemonBridge(bridgeRef.current).catch((err) => {
573
+ logger.error({ err }, "Failed to stop daemon bridge during cleanup");
574
+ });
567
575
  bridgeRef.current = null;
568
576
  }
569
577
  };
@@ -708,7 +716,7 @@ Guidelines:
708
716
  }
709
717
 
710
718
  // src/tui/hooks/use-planner-session.ts
711
- function usePlannerSession(config, dispatch, streamingText) {
719
+ function usePlannerSession(config, dispatch) {
712
720
  const plannerSessionRef = useRef3(null);
713
721
  const handleUserMessage = useCallback3(async (text) => {
714
722
  if (!config) return;
@@ -717,28 +725,25 @@ function usePlannerSession(config, dispatch, streamingText) {
717
725
  dispatch({ type: "SET_STREAMING_TEXT", text: "" });
718
726
  try {
719
727
  let result;
728
+ let accumulated = "";
729
+ const onToken = (token) => {
730
+ accumulated += token;
731
+ dispatch({ type: "SET_STREAMING_TEXT", text: accumulated });
732
+ };
720
733
  const tuiContext = { channel: "tui" };
721
734
  if (plannerSessionRef.current && plannerSessionRef.current.status === "conversing") {
722
735
  result = await continuePlannerSession(
723
736
  plannerSessionRef.current,
724
737
  text,
725
738
  config,
726
- {
727
- onToken: (token) => {
728
- dispatch({ type: "SET_STREAMING_TEXT", text: (streamingText || "") + token });
729
- }
730
- },
739
+ { onToken },
731
740
  tuiContext
732
741
  );
733
742
  } else {
734
743
  result = await startPlannerSession(
735
744
  text,
736
745
  config,
737
- {
738
- onToken: (token) => {
739
- dispatch({ type: "SET_STREAMING_TEXT", text: (streamingText || "") + token });
740
- }
741
- },
746
+ { onToken },
742
747
  tuiContext
743
748
  );
744
749
  }
@@ -774,7 +779,7 @@ function usePlannerSession(config, dispatch, streamingText) {
774
779
  plannerSessionRef.current = null;
775
780
  logger.error({ err }, "Planner session failed");
776
781
  }
777
- }, [config, streamingText]);
782
+ }, [config]);
778
783
  const handleCancelGeneration = useCallback3(() => {
779
784
  if (plannerSessionRef.current) {
780
785
  cancelPlannerSession(plannerSessionRef.current);
@@ -873,7 +878,11 @@ ${failedSteps.join("\n")}` : "";
873
878
  const handleCancel = useCallback4(() => {
874
879
  if (workflow) {
875
880
  const rejected = rejectPlan(workflow);
876
- updateWorkflowPhase(db, workflow.id, rejected.phase);
881
+ try {
882
+ updateWorkflowPhase(db, workflow.id, rejected.phase);
883
+ } catch (err) {
884
+ logger.error({ err }, "Failed to update workflow phase");
885
+ }
877
886
  }
878
887
  if (plannerSessionRef.current) {
879
888
  plannerSessionRef.current = null;
@@ -1663,7 +1672,7 @@ function AppProvider({ cwd, skipOnboarding, children }) {
1663
1672
  });
1664
1673
  }, []);
1665
1674
  const { bridgeRef, daemonStatus } = useDaemonBridge(config, db, cwd, dispatch);
1666
- const planner = usePlannerSession(config, dispatch, state.streamingText);
1675
+ const planner = usePlannerSession(config, dispatch);
1667
1676
  const execution = useWorkflowExecution(
1668
1677
  state.workflow,
1669
1678
  db,
@@ -2498,12 +2507,17 @@ function formatDuration2(ms) {
2498
2507
  }
2499
2508
 
2500
2509
  // src/tui/workflow-detail-view.tsx
2501
- import { useState as useState8, useCallback as useCallback14 } from "react";
2510
+ import { useState as useState8, useCallback as useCallback14, useEffect as useEffect7 } from "react";
2502
2511
  import { Box as Box18, Text as Text18 } from "ink";
2503
2512
  import { Fragment as Fragment4, jsx as jsx22, jsxs as jsxs18 } from "react/jsx-runtime";
2504
2513
  function WorkflowDetailView({ workflow, runs, latestStepRuns, onBack, onSelectRun, onStop }) {
2505
2514
  const [selectedRunIndex, setSelectedRunIndex] = useState8(0);
2506
2515
  const displayRuns = runs.slice(0, 5);
2516
+ useEffect7(() => {
2517
+ if (selectedRunIndex >= displayRuns.length && displayRuns.length > 0) {
2518
+ setSelectedRunIndex(displayRuns.length - 1);
2519
+ }
2520
+ }, [displayRuns.length, selectedRunIndex]);
2507
2521
  useKeypress("detail-view-actions", KeyPriority.Normal, useCallback14((input, key) => {
2508
2522
  if (keyBindings.escape(input, key) || keyBindings.quit(input, key)) {
2509
2523
  onBack();
@@ -2678,7 +2692,7 @@ function formatDuration3(ms) {
2678
2692
  }
2679
2693
 
2680
2694
  // src/tui/onboarding.tsx
2681
- import { useState as useState9, useCallback as useCallback15, useMemo as useMemo5 } from "react";
2695
+ import { useState as useState9, useCallback as useCallback15, useMemo as useMemo5, useRef as useRef9, useEffect as useEffect8 } from "react";
2682
2696
  import { Box as Box19, Text as Text19, useStdout as useStdout4 } from "ink";
2683
2697
  import { TextInput, PasswordInput, ConfirmInput, Spinner, StatusMessage } from "@inkjs/ui";
2684
2698
  import { jsx as jsx23, jsxs as jsxs19 } from "react/jsx-runtime";
@@ -2690,6 +2704,12 @@ function Onboarding({ onComplete, onCancel, issues }) {
2690
2704
  const { stdout } = useStdout4();
2691
2705
  const cols = stdout?.columns ?? 80;
2692
2706
  const existing = useMemo5(() => loadExistingConfig(), []);
2707
+ const completeTimerRef = useRef9(null);
2708
+ useEffect8(() => {
2709
+ return () => {
2710
+ if (completeTimerRef.current) clearTimeout(completeTimerRef.current);
2711
+ };
2712
+ }, []);
2693
2713
  const initialStep = useMemo5(() => {
2694
2714
  if (!issues || issues.length === 0) return "welcome";
2695
2715
  const errorFields = new Set(issues.filter((i) => i.severity === "error").map((i) => i.field));
@@ -2710,6 +2730,7 @@ function Onboarding({ onComplete, onCancel, issues }) {
2710
2730
  const env = checkEnvironment();
2711
2731
  useKeypress("onboarding-cancel", KeyPriority.Normal, useCallback15((input, key) => {
2712
2732
  if (onCancel && keyBindings.escape(input, key)) {
2733
+ if (completeTimerRef.current) clearTimeout(completeTimerRef.current);
2713
2734
  onCancel();
2714
2735
  return true;
2715
2736
  }
@@ -2848,7 +2869,7 @@ function Onboarding({ onComplete, onCancel, issues }) {
2848
2869
  }
2849
2870
  const config = loadConfig();
2850
2871
  setStep("done");
2851
- setTimeout(() => onComplete(config), 1500);
2872
+ completeTimerRef.current = setTimeout(() => onComplete(config), 1500);
2852
2873
  }, [onComplete]);
2853
2874
  const StepLayout = ({ children, input }) => /* @__PURE__ */ jsxs19(Box19, { flexDirection: "column", flexGrow: 1, children: [
2854
2875
  /* @__PURE__ */ jsx23(Box19, { flexDirection: "column", flexGrow: 1, children }),
@@ -3106,14 +3127,19 @@ function Onboarding({ onComplete, onCancel, issues }) {
3106
3127
  }
3107
3128
 
3108
3129
  // src/tui/status.tsx
3109
- import { useState as useState10, useCallback as useCallback16, useEffect as useEffect7 } from "react";
3130
+ import { useState as useState10, useCallback as useCallback16, useEffect as useEffect9 } from "react";
3110
3131
  import { Box as Box20, Text as Text20 } from "ink";
3111
3132
  import { Fragment as Fragment5, jsx as jsx24, jsxs as jsxs20 } from "react/jsx-runtime";
3112
3133
  function Status({ workflows, onSelect, onBack, onStop, onDelete }) {
3113
3134
  const [selectedIndex, setSelectedIndex] = useState10(0);
3114
3135
  const [confirm, setConfirm] = useState10(null);
3115
3136
  const [message, setMessage] = useState10(null);
3116
- useEffect7(() => {
3137
+ useEffect9(() => {
3138
+ if (selectedIndex >= workflows.length && workflows.length > 0) {
3139
+ setSelectedIndex(workflows.length - 1);
3140
+ }
3141
+ }, [workflows.length, selectedIndex]);
3142
+ useEffect9(() => {
3117
3143
  if (!message) return;
3118
3144
  const timer = setTimeout(() => setMessage(null), 3e3);
3119
3145
  return () => clearTimeout(timer);
@@ -3224,7 +3250,7 @@ function AppLayout({ cwd }) {
3224
3250
  const configIssues = useMemo6(() => {
3225
3251
  const validation = validateConfig();
3226
3252
  return validation.issues.filter((i) => i.severity === "error");
3227
- }, []);
3253
+ }, [config]);
3228
3254
  return /* @__PURE__ */ jsxs21(Box21, { flexDirection: "column", height: rows, children: [
3229
3255
  /* @__PURE__ */ jsx25(Static, { items: view !== "onboarding" ? ["banner"] : [], children: (item) => /* @__PURE__ */ jsx25(
3230
3256
  Banner,
package/dist/cli.js CHANGED
@@ -508,7 +508,7 @@ program.command("tui").description("Start interactive TUI").option("--skip-onboa
508
508
  enableTuiLogging();
509
509
  const React = await import("react");
510
510
  const { render } = await import("ink");
511
- const { App } = await import("./app-XQRFUTEX.js");
511
+ const { App } = await import("./app-P3ERBGJD.js");
512
512
  render(React.createElement(App, { cwd: process.cwd(), skipOnboarding: opts.skipOnboarding }), { exitOnCtrlC: false });
513
513
  } catch (err) {
514
514
  logger.error({ err }, "Failed to start TUI");
@@ -522,7 +522,7 @@ program.option("--skip-onboarding", "Skip first-run onboarding wizard").action(a
522
522
  enableTuiLogging();
523
523
  const React = await import("react");
524
524
  const { render } = await import("ink");
525
- const { App } = await import("./app-XQRFUTEX.js");
525
+ const { App } = await import("./app-P3ERBGJD.js");
526
526
  render(React.createElement(App, { cwd: process.cwd(), skipOnboarding }), { exitOnCtrlC: false });
527
527
  } catch (err) {
528
528
  logger.error({ err }, "Failed to start TUI");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cueclaw",
3
- "version": "0.2.0",
3
+ "version": "0.2.1",
4
4
  "description": "Orchestrate agent workflows with natural language. Natural language in, executable DAG out.",
5
5
  "type": "module",
6
6
  "bin": {