agenthud 0.5.14 → 0.5.16
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/README.md +22 -1
- package/dist/index.js +34 -18
- package/dist/templates/config.yaml +3 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -12,7 +12,7 @@ When working with AI coding agents like Claude Code, you lose visibility into wh
|
|
|
12
12
|
|
|
13
13
|
## Install
|
|
14
14
|
|
|
15
|
-
Requires Node.js 20
|
|
15
|
+
Requires Node.js 20+. Tested on Ubuntu, Windows, macOS.
|
|
16
16
|
|
|
17
17
|
```bash
|
|
18
18
|
npx agenthud
|
|
@@ -26,6 +26,7 @@ Run this in a separate terminal while using Claude Code.
|
|
|
26
26
|
- **Track your git state** - Commits, branches, uncommitted changes at a glance
|
|
27
27
|
- **Know if tests pass** - Results update automatically, shows if outdated
|
|
28
28
|
- **Stay oriented** - Project info, dependencies, file counts
|
|
29
|
+
- **Monitor other sessions** - See what's happening in your other Claude Code projects
|
|
29
30
|
|
|
30
31
|
## Usage
|
|
31
32
|
|
|
@@ -61,8 +62,28 @@ panels:
|
|
|
61
62
|
project:
|
|
62
63
|
enabled: true
|
|
63
64
|
interval: 60s
|
|
65
|
+
other_sessions:
|
|
66
|
+
enabled: true
|
|
67
|
+
interval: 10s
|
|
64
68
|
```
|
|
65
69
|
|
|
70
|
+
### Other Sessions Panel
|
|
71
|
+
|
|
72
|
+
Shows activity from your other Claude Code projects:
|
|
73
|
+
|
|
74
|
+
```
|
|
75
|
+
┌─ Other Sessions ─────────────────────────────────────┐
|
|
76
|
+
│ 📁 dotfiles, pain-radar, myapp +4 | ⚡ 1 active │
|
|
77
|
+
│ │
|
|
78
|
+
│ 🔵 dotfiles (2m ago) │
|
|
79
|
+
│ "Updated the config file as requested..." │
|
|
80
|
+
└──────────────────────────────────────────────────────┘
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
- **Project names**: Shows up to 3 recent projects, +N for more
|
|
84
|
+
- **Active indicator**: 🔵 active (within 5 min), ⚪ inactive
|
|
85
|
+
- **Last message**: Most recent assistant response from that session
|
|
86
|
+
|
|
66
87
|
## Keyboard
|
|
67
88
|
|
|
68
89
|
- `q` quit
|
package/dist/index.js
CHANGED
|
@@ -956,6 +956,11 @@ function WelcomePanel() {
|
|
|
956
956
|
import { execSync as nodeExecSync, exec as nodeExec } from "child_process";
|
|
957
957
|
import { promisify } from "util";
|
|
958
958
|
var execAsync = promisify(nodeExec);
|
|
959
|
+
function cleanOutput(str) {
|
|
960
|
+
let result = str.replace(/^\uFEFF/, "");
|
|
961
|
+
result = result.replace(/^"|"$/g, "");
|
|
962
|
+
return result.trim();
|
|
963
|
+
}
|
|
959
964
|
var execFn = (command, options2) => nodeExecSync(command, options2);
|
|
960
965
|
function getUncommittedCount() {
|
|
961
966
|
try {
|
|
@@ -1041,11 +1046,12 @@ async function getGitDataAsync(config) {
|
|
|
1041
1046
|
let commits = [];
|
|
1042
1047
|
try {
|
|
1043
1048
|
const { stdout } = await execAsync(commands.commits);
|
|
1044
|
-
const lines = stdout
|
|
1049
|
+
const lines = cleanOutput(stdout).split("\n").filter(Boolean);
|
|
1045
1050
|
commits = lines.map((line) => {
|
|
1046
|
-
const
|
|
1051
|
+
const cleanLine = cleanOutput(line);
|
|
1052
|
+
const [hash, timestamp, ...messageParts] = cleanLine.split("|");
|
|
1047
1053
|
return {
|
|
1048
|
-
hash,
|
|
1054
|
+
hash: cleanOutput(hash),
|
|
1049
1055
|
message: messageParts.join("|"),
|
|
1050
1056
|
timestamp: new Date(timestamp)
|
|
1051
1057
|
};
|
|
@@ -1509,7 +1515,7 @@ var THIRTY_SECONDS_MS = 30 * 1e3;
|
|
|
1509
1515
|
var MAX_LINES_TO_SCAN = 200;
|
|
1510
1516
|
var DEFAULT_MAX_ACTIVITIES = 10;
|
|
1511
1517
|
function getClaudeSessionPath(projectPath) {
|
|
1512
|
-
const encoded = projectPath.replace(
|
|
1518
|
+
const encoded = projectPath.replace(/[/\\]/g, "-");
|
|
1513
1519
|
return join2(homedir(), ".claude", "projects", encoded);
|
|
1514
1520
|
}
|
|
1515
1521
|
function findActiveSession(sessionDir) {
|
|
@@ -1735,7 +1741,7 @@ import {
|
|
|
1735
1741
|
statSync as nodeStatSync2
|
|
1736
1742
|
} from "fs";
|
|
1737
1743
|
import { homedir as homedir2 } from "os";
|
|
1738
|
-
import { join as join3, basename as basename3 } from "path";
|
|
1744
|
+
import { join as join3, basename as basename3, sep } from "path";
|
|
1739
1745
|
var fs2 = {
|
|
1740
1746
|
existsSync: nodeExistsSync3,
|
|
1741
1747
|
readFileSync: (path) => nodeReadFileSync4(path, "utf-8"),
|
|
@@ -1748,7 +1754,7 @@ function getProjectsDir() {
|
|
|
1748
1754
|
return join3(homedir2(), ".claude", "projects");
|
|
1749
1755
|
}
|
|
1750
1756
|
function decodeProjectPath(encoded) {
|
|
1751
|
-
const naiveDecoded = encoded.replace(/-/g,
|
|
1757
|
+
const naiveDecoded = encoded.replace(/-/g, sep);
|
|
1752
1758
|
if (fs2.existsSync(naiveDecoded)) {
|
|
1753
1759
|
return naiveDecoded;
|
|
1754
1760
|
}
|
|
@@ -1762,7 +1768,7 @@ function decodeProjectPath(encoded) {
|
|
|
1762
1768
|
let found = false;
|
|
1763
1769
|
for (let j = segments.length; j > i; j--) {
|
|
1764
1770
|
const segment = segments.slice(i, j).join("-");
|
|
1765
|
-
const testPath = currentPath
|
|
1771
|
+
const testPath = currentPath ? join3(currentPath, segment) : sep + segment;
|
|
1766
1772
|
try {
|
|
1767
1773
|
if (fs2.existsSync(testPath)) {
|
|
1768
1774
|
const stat = fs2.statSync(testPath);
|
|
@@ -1777,7 +1783,7 @@ function decodeProjectPath(encoded) {
|
|
|
1777
1783
|
}
|
|
1778
1784
|
}
|
|
1779
1785
|
if (!found) {
|
|
1780
|
-
currentPath
|
|
1786
|
+
currentPath = currentPath ? join3(currentPath, segments[i]) : sep + segments[i];
|
|
1781
1787
|
i++;
|
|
1782
1788
|
}
|
|
1783
1789
|
}
|
|
@@ -1905,10 +1911,11 @@ function getOtherSessionsData(currentProjectPath, options2 = {}) {
|
|
|
1905
1911
|
}
|
|
1906
1912
|
const allProjects = getAllProjects();
|
|
1907
1913
|
defaultResult.totalProjects = allProjects.length;
|
|
1908
|
-
const normalizedCurrentPath = currentProjectPath.replace(
|
|
1914
|
+
const normalizedCurrentPath = currentProjectPath.replace(/[/\\]$/, "").replace(/\\/g, "/");
|
|
1909
1915
|
const otherSessions = [];
|
|
1910
1916
|
for (const project of allProjects) {
|
|
1911
|
-
|
|
1917
|
+
const normalizedDecodedPath = project.decodedPath.replace(/\\/g, "/");
|
|
1918
|
+
if (normalizedDecodedPath === normalizedCurrentPath) {
|
|
1912
1919
|
continue;
|
|
1913
1920
|
}
|
|
1914
1921
|
const projectDir = join3(projectsDir, project.encodedPath);
|
|
@@ -2509,22 +2516,31 @@ function DashboardApp({ mode }) {
|
|
|
2509
2516
|
const { exit } = useApp();
|
|
2510
2517
|
const { stdout } = useStdout();
|
|
2511
2518
|
const { config, warnings } = useMemo(() => parseConfig(), []);
|
|
2512
|
-
const
|
|
2519
|
+
const getEffectiveWidth = (terminalColumns) => {
|
|
2520
|
+
if (config.width) {
|
|
2521
|
+
return config.width;
|
|
2522
|
+
}
|
|
2523
|
+
return getClampedWidth(terminalColumns);
|
|
2524
|
+
};
|
|
2525
|
+
const [width, setWidth] = useState(() => getEffectiveWidth(stdout?.columns));
|
|
2513
2526
|
useEffect(() => {
|
|
2514
|
-
|
|
2515
|
-
|
|
2516
|
-
|
|
2527
|
+
if (!config.width) {
|
|
2528
|
+
const newWidth = getEffectiveWidth(stdout?.columns);
|
|
2529
|
+
if (newWidth !== width) {
|
|
2530
|
+
setWidth(newWidth);
|
|
2531
|
+
}
|
|
2517
2532
|
}
|
|
2518
|
-
}, [stdout?.columns, width]);
|
|
2533
|
+
}, [stdout?.columns, width, config.width]);
|
|
2519
2534
|
useEffect(() => {
|
|
2535
|
+
if (config.width) return;
|
|
2520
2536
|
const handleResize = () => {
|
|
2521
|
-
setWidth(
|
|
2537
|
+
setWidth(getEffectiveWidth(stdout?.columns));
|
|
2522
2538
|
};
|
|
2523
2539
|
stdout?.on("resize", handleResize);
|
|
2524
2540
|
return () => {
|
|
2525
2541
|
stdout?.off("resize", handleResize);
|
|
2526
2542
|
};
|
|
2527
|
-
}, [stdout]);
|
|
2543
|
+
}, [stdout, config.width]);
|
|
2528
2544
|
const projectIntervalSeconds = config.panels.project.interval ? config.panels.project.interval / 1e3 : null;
|
|
2529
2545
|
const gitIntervalSeconds = config.panels.git.interval ? config.panels.git.interval / 1e3 : null;
|
|
2530
2546
|
const claudeIntervalSeconds = config.panels.claude.interval ? config.panels.claude.interval / 1e3 : null;
|
|
@@ -3035,7 +3051,7 @@ function getDefaultConfig2() {
|
|
|
3035
3051
|
return nodeReadFileSync7(templatePath, "utf-8");
|
|
3036
3052
|
}
|
|
3037
3053
|
function getClaudeSessionPath2(projectPath) {
|
|
3038
|
-
const encoded = projectPath.replace(
|
|
3054
|
+
const encoded = projectPath.replace(/[/\\]/g, "-");
|
|
3039
3055
|
return join5(homedir3(), ".claude", "projects", encoded);
|
|
3040
3056
|
}
|
|
3041
3057
|
function runInit(cwd = process.cwd()) {
|
package/package.json
CHANGED