agenthud 0.6.5 → 0.7.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.
Files changed (2) hide show
  1. package/dist/index.js +225 -131
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -355,7 +355,9 @@ function getDefaultConfig2() {
355
355
  }
356
356
  },
357
357
  panelOrder: ["project", "git", "tests", "claude", "other_sessions"],
358
- width: DEFAULT_WIDTH
358
+ width: DEFAULT_WIDTH,
359
+ wideLayoutThreshold: null
360
+ // disabled by default
359
361
  };
360
362
  }
361
363
  var BUILTIN_PANELS = ["project", "git", "tests", "claude", "other_sessions"];
@@ -410,6 +412,18 @@ function parseConfig() {
410
412
  config.width = parsed.width;
411
413
  }
412
414
  }
415
+ const MIN_WIDE_THRESHOLD = 140;
416
+ const wideThresholdValue = parsed.wideLayoutThreshold ?? parsed.wide_layout_threshold;
417
+ if (typeof wideThresholdValue === "number") {
418
+ if (wideThresholdValue < MIN_WIDE_THRESHOLD) {
419
+ warnings.push(
420
+ `wideLayoutThreshold ${wideThresholdValue} is too small, using minimum of ${MIN_WIDE_THRESHOLD}`
421
+ );
422
+ config.wideLayoutThreshold = MIN_WIDE_THRESHOLD;
423
+ } else {
424
+ config.wideLayoutThreshold = wideThresholdValue;
425
+ }
426
+ }
413
427
  const panels = parsed.panels;
414
428
  if (!panels || typeof panels !== "object") {
415
429
  return { config, warnings };
@@ -550,6 +564,9 @@ var ICONS = {
550
564
  };
551
565
 
552
566
  // src/data/claude.ts
567
+ function stripAnsi(text) {
568
+ return text.replace(/\x1b\[[0-9;]*m/g, "");
569
+ }
553
570
  var MAX_LINES_TO_SCAN = 200;
554
571
  var DEFAULT_MAX_ACTIVITIES = 10;
555
572
  function getClaudeSessionPath2(projectPath) {
@@ -586,19 +603,19 @@ function findActiveSession(sessionDir, sessionTimeout) {
586
603
  function getToolDetail(_toolName, input) {
587
604
  if (!input) return "";
588
605
  if (input.command) {
589
- return input.command.replace(/\n/g, " ");
606
+ return stripAnsi(input.command.replace(/\n/g, " "));
590
607
  }
591
608
  if (input.file_path) {
592
609
  return basename(input.file_path);
593
610
  }
594
611
  if (input.pattern) {
595
- return input.pattern;
612
+ return stripAnsi(input.pattern);
596
613
  }
597
614
  if (input.query) {
598
- return input.query;
615
+ return stripAnsi(input.query);
599
616
  }
600
617
  if (input.description) {
601
- return input.description;
618
+ return stripAnsi(input.description);
602
619
  }
603
620
  return "";
604
621
  }
@@ -2126,7 +2143,8 @@ function ClaudePanel({
2126
2143
  countdown,
2127
2144
  width = DEFAULT_PANEL_WIDTH,
2128
2145
  isRunning = false,
2129
- justRefreshed = false
2146
+ justRefreshed = false,
2147
+ maxActivities
2130
2148
  }) {
2131
2149
  const countdownSuffix = isRunning ? "running..." : formatCountdown(countdown);
2132
2150
  const innerWidth = getInnerWidth(width);
@@ -2185,8 +2203,9 @@ function ClaudePanel({
2185
2203
  ] });
2186
2204
  }
2187
2205
  const lines = [];
2188
- for (let i = 0; i < state.activities.length; i++) {
2189
- const activity = state.activities[i];
2206
+ const displayActivities = maxActivities !== void 0 ? state.activities.slice(0, maxActivities) : state.activities;
2207
+ for (let i = 0; i < displayActivities.length; i++) {
2208
+ const activity = displayActivities[i];
2190
2209
  let modifiedActivity = activity;
2191
2210
  if (activity.label === "Task" && activity.subActivityCount && activity.subActivityCount > 0) {
2192
2211
  modifiedActivity = {
@@ -2200,7 +2219,6 @@ function ClaudePanel({
2200
2219
  );
2201
2220
  const padding = Math.max(0, contentWidth - displayWidth);
2202
2221
  const style = getActivityStyle(activity);
2203
- const clearEOL = "\x1B[K";
2204
2222
  lines.push(
2205
2223
  /* @__PURE__ */ jsxs(Text, { children: [
2206
2224
  BOX.v,
@@ -2210,8 +2228,7 @@ function ClaudePanel({
2210
2228
  " ",
2211
2229
  /* @__PURE__ */ jsx(Text, { color: style.color, dimColor: style.dimColor, children: labelContent }),
2212
2230
  " ".repeat(padding),
2213
- BOX.v,
2214
- clearEOL
2231
+ BOX.v
2215
2232
  ] }, `activity-${i}`)
2216
2233
  );
2217
2234
  if (activity.subActivities && activity.subActivities.length > 0) {
@@ -2262,8 +2279,7 @@ function ClaudePanel({
2262
2279
  " ",
2263
2280
  /* @__PURE__ */ jsx(Text, { color: subStyle.color, dimColor: subStyle.dimColor, children: subLabelContent }),
2264
2281
  " ".repeat(subPadding),
2265
- BOX.v,
2266
- clearEOL
2282
+ BOX.v
2267
2283
  ] }, `activity-${i}-sub-${j}`)
2268
2284
  );
2269
2285
  }
@@ -3354,6 +3370,18 @@ function DashboardApp({
3354
3370
  },
3355
3371
  [config.width]
3356
3372
  );
3373
+ const getEffectiveMaxActivities = useCallback4(
3374
+ (terminalRows, todoCount = 0, isWideLayout = false) => {
3375
+ const configMax = config.panels.claude.maxActivities ?? 10;
3376
+ if (!terminalRows || !isWideLayout) {
3377
+ return configMax;
3378
+ }
3379
+ const todoHeight = todoCount > 0 ? 1 + todoCount : 0;
3380
+ const heightBasedMax = Math.max(5, terminalRows - 13 - todoHeight);
3381
+ return Math.max(configMax, heightBasedMax);
3382
+ },
3383
+ [config.panels.claude.maxActivities]
3384
+ );
3357
3385
  const [width, setWidth] = useState4(() => getEffectiveWidth(stdout?.columns));
3358
3386
  useEffect4(() => {
3359
3387
  if (!config.width) {
@@ -3413,12 +3441,12 @@ function DashboardApp({
3413
3441
  const [gitData, setGitData] = useState4(
3414
3442
  () => getGitData(config.panels.git)
3415
3443
  );
3444
+ const fetchMaxActivities = Math.max(
3445
+ config.panels.claude.maxActivities ?? 10,
3446
+ stdout?.rows ?? 50
3447
+ );
3416
3448
  const [claudeData, setClaudeData] = useState4(
3417
- () => getClaudeData(
3418
- cwd,
3419
- config.panels.claude.maxActivities,
3420
- config.panels.claude.sessionTimeout
3421
- )
3449
+ () => getClaudeData(cwd, fetchMaxActivities, config.panels.claude.sessionTimeout)
3422
3450
  );
3423
3451
  const [otherSessionsData, setOtherSessionsData] = useState4(
3424
3452
  () => getOtherSessionsData(cwd, {
@@ -3431,7 +3459,6 @@ function DashboardApp({
3431
3459
  }
3432
3460
  return getTestData();
3433
3461
  }, [config.panels.tests.command]);
3434
- const [testsDisabled, setTestsDisabled] = useState4(false);
3435
3462
  const [testData, setTestData] = useState4({
3436
3463
  results: null,
3437
3464
  isOutdated: false,
@@ -3442,14 +3469,10 @@ function DashboardApp({
3442
3469
  if (testsInitializedRef.current) return;
3443
3470
  testsInitializedRef.current = true;
3444
3471
  const timer = setTimeout(() => {
3445
- const data = getTestDataFromConfig();
3446
- setTestData(data);
3447
- if (data.error && !config.panels.tests.command) {
3448
- setTestsDisabled(true);
3449
- }
3472
+ setTestData(getTestDataFromConfig());
3450
3473
  }, 0);
3451
3474
  return () => clearTimeout(timer);
3452
- }, [getTestDataFromConfig, config.panels.tests.command]);
3475
+ }, [getTestDataFromConfig]);
3453
3476
  const [customPanelData, setCustomPanelData] = useState4(() => {
3454
3477
  const data = {};
3455
3478
  if (config.customPanels) {
@@ -3481,30 +3504,27 @@ function DashboardApp({
3481
3504
  try {
3482
3505
  await new Promise((resolve) => {
3483
3506
  setTimeout(() => {
3484
- const data = getTestDataFromConfig();
3485
- if (config.panels.tests.command) {
3486
- setTestsDisabled(!!data.error);
3487
- }
3488
- setTestData(data);
3507
+ setTestData(getTestDataFromConfig());
3489
3508
  resolve();
3490
3509
  }, 0);
3491
3510
  });
3492
3511
  } finally {
3493
3512
  visualFeedback.endAsync("tests", { completed: true });
3494
3513
  }
3495
- }, [getTestDataFromConfig, config.panels.tests.command, visualFeedback]);
3514
+ }, [getTestDataFromConfig, visualFeedback]);
3496
3515
  const refreshClaude = useCallback4(() => {
3516
+ const maxFetch = Math.max(
3517
+ config.panels.claude.maxActivities ?? 10,
3518
+ stdout?.rows ?? 50
3519
+ );
3497
3520
  setClaudeData(
3498
- getClaudeData(
3499
- cwd,
3500
- config.panels.claude.maxActivities,
3501
- config.panels.claude.sessionTimeout
3502
- )
3521
+ getClaudeData(cwd, maxFetch, config.panels.claude.sessionTimeout)
3503
3522
  );
3504
3523
  visualFeedback.setRefreshed("claude");
3505
3524
  resetCountdown("claude");
3506
3525
  }, [
3507
3526
  cwd,
3527
+ stdout?.rows,
3508
3528
  config.panels.claude.maxActivities,
3509
3529
  config.panels.claude.sessionTimeout,
3510
3530
  visualFeedback,
@@ -3665,106 +3685,180 @@ function DashboardApp({
3665
3685
  }
3666
3686
  return () => timers.forEach((t) => clearInterval(t));
3667
3687
  }, [isWatchMode, config]);
3688
+ const terminalWidth = stdout?.columns ?? 0;
3689
+ const terminalHeight = stdout?.rows ?? 0;
3690
+ const columnGap = 2;
3691
+ const effectiveThreshold = config.wideLayoutThreshold ?? MIN_TERMINAL_WIDTH * 2 + columnGap;
3692
+ const useWideLayout = terminalWidth >= effectiveThreshold;
3693
+ const singleColumnWidth = getClampedWidth(terminalWidth);
3694
+ const leftColumnWidth = useWideLayout ? Math.floor((terminalWidth - columnGap) / 2) : singleColumnWidth;
3695
+ const rightColumnWidth = useWideLayout ? terminalWidth - leftColumnWidth - columnGap : singleColumnWidth;
3696
+ const claudeMaxActivities = useMemo3(() => {
3697
+ if (!useWideLayout) return void 0;
3698
+ const todos = claudeData.state.todos;
3699
+ const hasTodos = todos && todos.length > 0;
3700
+ const allCompleted = hasTodos && todos.every((t) => t.status === "completed");
3701
+ const activeTodoCount = hasTodos && !allCompleted ? todos.length : 0;
3702
+ return getEffectiveMaxActivities(terminalHeight, activeTodoCount, true);
3703
+ }, [
3704
+ useWideLayout,
3705
+ claudeData.state.todos,
3706
+ terminalHeight,
3707
+ getEffectiveMaxActivities
3708
+ ]);
3709
+ const renderPanel = (panelName, panelWidth, marginTop) => {
3710
+ if (panelName === "project" && config.panels.project.enabled) {
3711
+ const vs = visualFeedback.getState("project");
3712
+ return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3713
+ ProjectPanel,
3714
+ {
3715
+ data: projectData,
3716
+ countdown: isWatchMode ? countdowns.project : null,
3717
+ width: panelWidth,
3718
+ justRefreshed: vs.justRefreshed
3719
+ }
3720
+ ) }, `panel-${panelName}`);
3721
+ }
3722
+ if (panelName === "git" && config.panels.git.enabled) {
3723
+ const vs = visualFeedback.getState("git");
3724
+ return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3725
+ GitPanel,
3726
+ {
3727
+ branch: gitData.branch,
3728
+ commits: gitData.commits,
3729
+ stats: gitData.stats,
3730
+ uncommitted: gitData.uncommitted,
3731
+ countdown: isWatchMode ? countdowns.git : null,
3732
+ width: panelWidth,
3733
+ isRunning: vs.isRunning,
3734
+ justRefreshed: vs.justRefreshed
3735
+ }
3736
+ ) }, `panel-${panelName}`);
3737
+ }
3738
+ if (panelName === "tests" && config.panels.tests.enabled) {
3739
+ const vs = visualFeedback.getState("tests");
3740
+ return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3741
+ TestPanel,
3742
+ {
3743
+ results: testData.results,
3744
+ isOutdated: testData.isOutdated,
3745
+ commitsBehind: testData.commitsBehind,
3746
+ error: testData.error,
3747
+ width: panelWidth,
3748
+ isRunning: vs.isRunning,
3749
+ justCompleted: vs.justCompleted
3750
+ }
3751
+ ) }, `panel-${panelName}`);
3752
+ }
3753
+ if (panelName === "claude" && config.panels.claude.enabled) {
3754
+ const vs = visualFeedback.getState("claude");
3755
+ return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3756
+ ClaudePanel,
3757
+ {
3758
+ data: claudeData,
3759
+ countdown: isWatchMode ? countdowns.claude : null,
3760
+ width: panelWidth,
3761
+ justRefreshed: vs.justRefreshed,
3762
+ maxActivities: claudeMaxActivities
3763
+ }
3764
+ ) }, `panel-${panelName}`);
3765
+ }
3766
+ if (panelName === "other_sessions" && config.panels.other_sessions.enabled) {
3767
+ const vs = visualFeedback.getState("other_sessions");
3768
+ return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3769
+ OtherSessionsPanel,
3770
+ {
3771
+ data: otherSessionsData,
3772
+ countdown: isWatchMode ? countdowns.other_sessions : null,
3773
+ width: panelWidth,
3774
+ isRunning: vs.isRunning,
3775
+ messageMaxLength: config.panels.other_sessions.messageMaxLength
3776
+ }
3777
+ ) }, `panel-${panelName}`);
3778
+ }
3779
+ const customConfig = config.customPanels?.[panelName];
3780
+ if (customConfig?.enabled) {
3781
+ const result = customPanelData[panelName];
3782
+ if (!result) return null;
3783
+ const vs = visualFeedback.getState(panelName);
3784
+ const isManual = customConfig.interval === null;
3785
+ const relativeTime = isManual ? formatRelativeTime3(result.timestamp) : void 0;
3786
+ const countdown = !isManual && isWatchMode ? countdowns[panelName] : null;
3787
+ return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3788
+ GenericPanel,
3789
+ {
3790
+ data: result.data,
3791
+ renderer: customConfig.renderer,
3792
+ countdown,
3793
+ relativeTime,
3794
+ error: result.error,
3795
+ width: panelWidth,
3796
+ isRunning: vs.isRunning,
3797
+ justRefreshed: vs.justRefreshed
3798
+ }
3799
+ ) }, `panel-${panelName}`);
3800
+ }
3801
+ return null;
3802
+ };
3803
+ if (useWideLayout) {
3804
+ const leftPanels = ["claude", "other_sessions"];
3805
+ const rightPanels = config.panelOrder.filter(
3806
+ (name) => !leftPanels.includes(name)
3807
+ );
3808
+ return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
3809
+ warnings.length > 0 && /* @__PURE__ */ jsx8(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
3810
+ "\u26A0 ",
3811
+ warnings.join(", ")
3812
+ ] }) }),
3813
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "row", children: [
3814
+ /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", width: leftColumnWidth, children: [
3815
+ renderPanel("claude", leftColumnWidth, 0),
3816
+ renderPanel("other_sessions", leftColumnWidth, 1)
3817
+ ] }),
3818
+ /* @__PURE__ */ jsx8(
3819
+ Box8,
3820
+ {
3821
+ flexDirection: "column",
3822
+ width: rightColumnWidth,
3823
+ marginLeft: columnGap,
3824
+ children: rightPanels.map(
3825
+ (panelName, index) => renderPanel(panelName, rightColumnWidth, index === 0 ? 0 : 1)
3826
+ )
3827
+ }
3828
+ )
3829
+ ] }),
3830
+ isWatchMode && /* @__PURE__ */ jsxs8(
3831
+ Box8,
3832
+ {
3833
+ marginTop: 1,
3834
+ width: terminalWidth,
3835
+ justifyContent: "space-between",
3836
+ children: [
3837
+ /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: statusBarItems.map((item, index) => /* @__PURE__ */ jsxs8(React.Fragment, { children: [
3838
+ index > 0 && " \xB7 ",
3839
+ /* @__PURE__ */ jsxs8(Text8, { color: "cyan", children: [
3840
+ item.split(":")[0],
3841
+ ":"
3842
+ ] }),
3843
+ item.split(":").slice(1).join(":")
3844
+ ] }, index)) }),
3845
+ /* @__PURE__ */ jsxs8(Text8, { dimColor: true, children: [
3846
+ "AgentHUD v",
3847
+ getVersion()
3848
+ ] })
3849
+ ]
3850
+ }
3851
+ )
3852
+ ] });
3853
+ }
3668
3854
  return /* @__PURE__ */ jsxs8(Box8, { flexDirection: "column", children: [
3669
3855
  warnings.length > 0 && /* @__PURE__ */ jsx8(Box8, { marginBottom: 1, children: /* @__PURE__ */ jsxs8(Text8, { color: "yellow", children: [
3670
3856
  "\u26A0 ",
3671
3857
  warnings.join(", ")
3672
3858
  ] }) }),
3673
- config.panelOrder.map((panelName, index) => {
3674
- const isFirst = index === 0;
3675
- const marginTop = isFirst ? 0 : 1;
3676
- if (panelName === "project" && config.panels.project.enabled) {
3677
- const vs = visualFeedback.getState("project");
3678
- return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3679
- ProjectPanel,
3680
- {
3681
- data: projectData,
3682
- countdown: isWatchMode ? countdowns.project : null,
3683
- width,
3684
- justRefreshed: vs.justRefreshed
3685
- }
3686
- ) }, `panel-${panelName}-${index}`);
3687
- }
3688
- if (panelName === "git" && config.panels.git.enabled) {
3689
- const vs = visualFeedback.getState("git");
3690
- return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3691
- GitPanel,
3692
- {
3693
- branch: gitData.branch,
3694
- commits: gitData.commits,
3695
- stats: gitData.stats,
3696
- uncommitted: gitData.uncommitted,
3697
- countdown: isWatchMode ? countdowns.git : null,
3698
- width,
3699
- isRunning: vs.isRunning,
3700
- justRefreshed: vs.justRefreshed
3701
- }
3702
- ) }, `panel-${panelName}-${index}`);
3703
- }
3704
- if (panelName === "tests" && config.panels.tests.enabled && !testsDisabled) {
3705
- const vs = visualFeedback.getState("tests");
3706
- return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3707
- TestPanel,
3708
- {
3709
- results: testData.results,
3710
- isOutdated: testData.isOutdated,
3711
- commitsBehind: testData.commitsBehind,
3712
- error: testData.error,
3713
- width,
3714
- isRunning: vs.isRunning,
3715
- justCompleted: vs.justCompleted
3716
- }
3717
- ) }, `panel-${panelName}-${index}`);
3718
- }
3719
- if (panelName === "claude" && config.panels.claude.enabled) {
3720
- const vs = visualFeedback.getState("claude");
3721
- return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3722
- ClaudePanel,
3723
- {
3724
- data: claudeData,
3725
- countdown: isWatchMode ? countdowns.claude : null,
3726
- width,
3727
- justRefreshed: vs.justRefreshed
3728
- }
3729
- ) }, `panel-${panelName}-${index}`);
3730
- }
3731
- if (panelName === "other_sessions" && config.panels.other_sessions.enabled) {
3732
- const vs = visualFeedback.getState("other_sessions");
3733
- return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3734
- OtherSessionsPanel,
3735
- {
3736
- data: otherSessionsData,
3737
- countdown: isWatchMode ? countdowns.other_sessions : null,
3738
- width,
3739
- isRunning: vs.isRunning,
3740
- messageMaxLength: config.panels.other_sessions.messageMaxLength
3741
- }
3742
- ) }, `panel-${panelName}-${index}`);
3743
- }
3744
- const customConfig = config.customPanels?.[panelName];
3745
- if (customConfig?.enabled) {
3746
- const result = customPanelData[panelName];
3747
- if (!result) return null;
3748
- const vs = visualFeedback.getState(panelName);
3749
- const isManual = customConfig.interval === null;
3750
- const relativeTime = isManual ? formatRelativeTime3(result.timestamp) : void 0;
3751
- const countdown = !isManual && isWatchMode ? countdowns[panelName] : null;
3752
- return /* @__PURE__ */ jsx8(Box8, { marginTop, children: /* @__PURE__ */ jsx8(
3753
- GenericPanel,
3754
- {
3755
- data: result.data,
3756
- renderer: customConfig.renderer,
3757
- countdown,
3758
- relativeTime,
3759
- error: result.error,
3760
- width,
3761
- isRunning: vs.isRunning,
3762
- justRefreshed: vs.justRefreshed
3763
- }
3764
- ) }, `panel-${panelName}-${index}`);
3765
- }
3766
- return null;
3767
- }),
3859
+ config.panelOrder.map(
3860
+ (panelName, index) => renderPanel(panelName, width, index === 0 ? 0 : 1)
3861
+ ),
3768
3862
  isWatchMode && /* @__PURE__ */ jsxs8(Box8, { marginTop: 1, width, justifyContent: "space-between", children: [
3769
3863
  /* @__PURE__ */ jsx8(Text8, { dimColor: true, children: statusBarItems.map((item, index) => /* @__PURE__ */ jsxs8(React.Fragment, { children: [
3770
3864
  index > 0 && " \xB7 ",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenthud",
3
- "version": "0.6.5",
3
+ "version": "0.7.0",
4
4
  "description": "CLI tool to monitor agent status in real-time. Works with Claude Code, multi-agent workflows, and any AI agent system.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",