agenthud 0.7.1 → 0.7.2

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 CHANGED
@@ -156,15 +156,15 @@ Shows activity from your other Claude Code projects:
156
156
 
157
157
  ```
158
158
  ┌─ Other Sessions ─────────────────────────────────────┐
159
- 📁 dotfiles, pain-radar, myapp +4 | 1 active
159
+ │ dotfiles, pain-radar, myapp +4 | * 1 active
160
160
  │ │
161
- 🔵 dotfiles (2m ago)
161
+ * dotfiles (2m ago)
162
162
  │ "Updated the config file as requested..." │
163
163
  └──────────────────────────────────────────────────────┘
164
164
  ```
165
165
 
166
166
  - **Project names**: Shows up to 3 recent projects, +N for more
167
- - **Active indicator**: 🔵 active (within 5 min), inactive
167
+ - **Active indicator**: `*` active (within 5 min), `o` inactive
168
168
  - **Last message**: Most recent assistant response from that session
169
169
 
170
170
  ## Keyboard
package/dist/index.js CHANGED
@@ -14,4 +14,4 @@ Error: Node.js ${MIN_NODE_VERSION}+ is required (current: ${process.version})
14
14
  console.error(" https://nodejs.org/\n");
15
15
  process.exit(1);
16
16
  }
17
- import("./main-LKANZYXL.js").then(({ main }) => main());
17
+ import("./main-I7XDIQE3.js").then(({ main }) => main());
@@ -199,16 +199,18 @@ function runInit(cwd = process.cwd()) {
199
199
  } else {
200
200
  result.skipped.push(".agenthud/config.yaml");
201
201
  }
202
- if (!existsSync2(".gitignore")) {
203
- writeFileSync(".gitignore", ".agenthud/\n");
204
- result.created.push(".gitignore");
205
- } else {
206
- const content = readFileSync3(".gitignore", "utf-8");
207
- if (!content.includes(".agenthud/")) {
208
- appendFileSync(".gitignore", "\n.agenthud/\n");
202
+ if (existsSync2(".git")) {
203
+ if (!existsSync2(".gitignore")) {
204
+ writeFileSync(".gitignore", ".agenthud/\n");
209
205
  result.created.push(".gitignore");
210
206
  } else {
211
- result.skipped.push(".gitignore");
207
+ const content = readFileSync3(".gitignore", "utf-8");
208
+ if (!content.includes(".agenthud/")) {
209
+ appendFileSync(".gitignore", "\n.agenthud/\n");
210
+ result.created.push(".gitignore");
211
+ } else {
212
+ result.skipped.push(".gitignore");
213
+ }
212
214
  }
213
215
  }
214
216
  if (!existsSync2(".git")) {
@@ -226,7 +228,7 @@ function runInit(cwd = process.cwd()) {
226
228
  }
227
229
 
228
230
  // src/data/sessionAvailability.ts
229
- import { existsSync as existsSync4 } from "fs";
231
+ import { existsSync as existsSync4, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
230
232
  import { homedir as homedir3 } from "os";
231
233
  import { basename as basename2, join as join4 } from "path";
232
234
 
@@ -507,10 +509,82 @@ function getOtherSessionsData(currentProjectPath, options = {}) {
507
509
  }
508
510
 
509
511
  // src/data/sessionAvailability.ts
512
+ function shortenPath(path) {
513
+ const home = homedir3();
514
+ if (path === home) {
515
+ return "~";
516
+ }
517
+ if (path.startsWith(home + "/") || path.startsWith(home + "\\")) {
518
+ return "~" + path.slice(home.length);
519
+ }
520
+ return path;
521
+ }
522
+ var PROJECT_INDICATORS = [
523
+ ".git",
524
+ // Git repository
525
+ "package.json",
526
+ // Node.js
527
+ "Cargo.toml",
528
+ // Rust
529
+ "pyproject.toml",
530
+ // Python (modern)
531
+ "setup.py",
532
+ // Python (legacy)
533
+ "go.mod",
534
+ // Go
535
+ "Makefile",
536
+ // Make-based projects
537
+ "CMakeLists.txt",
538
+ // CMake projects
539
+ "pom.xml",
540
+ // Java Maven
541
+ "build.gradle",
542
+ // Java Gradle
543
+ "Gemfile",
544
+ // Ruby
545
+ "composer.json"
546
+ // PHP
547
+ ];
548
+ function isDevProject(projectPath) {
549
+ for (const indicator of PROJECT_INDICATORS) {
550
+ if (existsSync4(join4(projectPath, indicator))) {
551
+ return true;
552
+ }
553
+ }
554
+ return false;
555
+ }
510
556
  function getSessionPath(projectPath) {
511
557
  const encoded = projectPath.replace(/[/\\]/g, "-");
512
558
  return join4(homedir3(), ".claude", "projects", encoded);
513
559
  }
560
+ function getProjectMostRecentMtime(encodedPath) {
561
+ const projectDir = join4(homedir3(), ".claude", "projects", encodedPath);
562
+ if (!existsSync4(projectDir)) {
563
+ return 0;
564
+ }
565
+ let files;
566
+ try {
567
+ files = readdirSync2(projectDir);
568
+ } catch {
569
+ return 0;
570
+ }
571
+ const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
572
+ if (jsonlFiles.length === 0) {
573
+ return 0;
574
+ }
575
+ let latestMtime = 0;
576
+ for (const file of jsonlFiles) {
577
+ const filePath = join4(projectDir, file);
578
+ try {
579
+ const stat = statSync2(filePath);
580
+ if (stat.mtimeMs && stat.mtimeMs > latestMtime) {
581
+ latestMtime = stat.mtimeMs;
582
+ }
583
+ } catch {
584
+ }
585
+ }
586
+ return latestMtime;
587
+ }
514
588
  function hasCurrentProjectSession(cwd) {
515
589
  const sessionPath = getSessionPath(cwd);
516
590
  return existsSync4(sessionPath);
@@ -518,7 +592,13 @@ function hasCurrentProjectSession(cwd) {
518
592
  function getProjectsWithSessions(currentPath) {
519
593
  const allProjects = getAllProjects();
520
594
  const currentEncoded = currentPath.replace(/[/\\]/g, "-");
521
- return allProjects.filter((p) => p.encodedPath !== currentEncoded).map((p) => basename2(p.decodedPath));
595
+ const projectsWithMtime = allProjects.filter((p) => p.encodedPath !== currentEncoded).filter((p) => existsSync4(p.decodedPath)).filter((p) => isDevProject(p.decodedPath)).map((p) => ({
596
+ name: basename2(p.decodedPath),
597
+ path: p.decodedPath,
598
+ mtime: getProjectMostRecentMtime(p.encodedPath)
599
+ }));
600
+ projectsWithMtime.sort((a, b) => b.mtime - a.mtime);
601
+ return projectsWithMtime.map(({ name, path }) => ({ name, path }));
522
602
  }
523
603
  function checkSessionAvailability(cwd) {
524
604
  const hasCurrentSession = hasCurrentProjectSession(cwd);
@@ -787,7 +867,7 @@ function parseConfig() {
787
867
  }
788
868
 
789
869
  // src/data/claude.ts
790
- import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync6, statSync as statSync2 } from "fs";
870
+ import { existsSync as existsSync6, readdirSync as readdirSync3, readFileSync as readFileSync6, statSync as statSync3 } from "fs";
791
871
  import { homedir as homedir4 } from "os";
792
872
  import { basename as basename3, join as join5 } from "path";
793
873
 
@@ -826,7 +906,7 @@ function findActiveSession(sessionDir, sessionTimeout) {
826
906
  if (!existsSync6(sessionDir)) {
827
907
  return null;
828
908
  }
829
- const files = readdirSync2(sessionDir);
909
+ const files = readdirSync3(sessionDir);
830
910
  const jsonlFiles = files.filter((f) => f.endsWith(".jsonl"));
831
911
  if (jsonlFiles.length === 0) {
832
912
  return null;
@@ -836,7 +916,7 @@ function findActiveSession(sessionDir, sessionTimeout) {
836
916
  let latestSize = 0;
837
917
  for (const file of jsonlFiles) {
838
918
  const filePath = join5(sessionDir, file);
839
- const stat = statSync2(filePath);
919
+ const stat = statSync3(filePath);
840
920
  if (stat.mtimeMs > latestMtime || stat.mtimeMs === latestMtime && stat.size > latestSize) {
841
921
  latestMtime = stat.mtimeMs;
842
922
  latestSize = stat.size;
@@ -875,12 +955,12 @@ function getSubagentFiles(sessionFile) {
875
955
  return [];
876
956
  }
877
957
  try {
878
- const files = readdirSync2(subagentsDir).filter(
958
+ const files = readdirSync3(subagentsDir).filter(
879
959
  (f) => f.endsWith(".jsonl")
880
960
  );
881
961
  const fileInfos = files.map((file) => {
882
962
  const filePath = join5(subagentsDir, file);
883
- const stat = statSync2(filePath);
963
+ const stat = statSync3(filePath);
884
964
  return { filePath, mtimeMs: stat.mtimeMs };
885
965
  });
886
966
  fileInfos.sort((a, b) => b.mtimeMs - a.mtimeMs);
@@ -1084,7 +1164,7 @@ function parseSessionState(sessionFile, maxActivities = DEFAULT_MAX_ACTIVITIES)
1084
1164
  const subagentsDir = join5(sessionFile.replace(/\.jsonl$/, ""), "subagents");
1085
1165
  if (existsSync6(subagentsDir)) {
1086
1166
  try {
1087
- const subagentFiles2 = readdirSync2(subagentsDir).filter(
1167
+ const subagentFiles2 = readdirSync3(subagentsDir).filter(
1088
1168
  (f) => f.endsWith(".jsonl")
1089
1169
  );
1090
1170
  for (const file of subagentFiles2) {
@@ -3985,12 +4065,16 @@ function main() {
3985
4065
  const sessionAvailability = checkSessionAvailability(cwd);
3986
4066
  if (!sessionAvailability.hasCurrentSession) {
3987
4067
  if (sessionAvailability.otherProjects.length > 0) {
3988
- console.log("\nNo Claude Code session found in current directory.\n");
3989
- console.log("Projects with Claude Code sessions:");
3990
- sessionAvailability.otherProjects.forEach(
3991
- (name) => console.log(` - ${name}`)
3992
- );
3993
- console.log("\nRun agenthud from one of these project directories.\n");
4068
+ console.log("\nProjects with Claude Code sessions:");
4069
+ sessionAvailability.otherProjects.forEach((project, index) => {
4070
+ const shortPath = shortenPath(project.path);
4071
+ console.log(` ${index + 1}. ${project.name} (${shortPath})`);
4072
+ });
4073
+ const firstProject = sessionAvailability.otherProjects[0];
4074
+ const firstShortPath = shortenPath(firstProject.path);
4075
+ console.log(`
4076
+ Run: cd ${firstShortPath} && agenthud
4077
+ `);
3994
4078
  } else {
3995
4079
  console.log("\nCould not find any projects with Claude Code sessions.\n");
3996
4080
  console.log("Start a Claude Code session in a project directory first:");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenthud",
3
- "version": "0.7.1",
3
+ "version": "0.7.2",
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",