agenthud 0.5.2 → 0.5.4

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 (3) hide show
  1. package/README.md +53 -38
  2. package/dist/index.js +44 -85
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -3,37 +3,25 @@
3
3
  Terminal dashboard for AI agent development.
4
4
 
5
5
  ```
6
- ┌─ Git ────────────────────────────────────────────────────┐
7
- main · +1068 -166 · 12 commits · 23 files
8
- 1d00dc0 refactor: rename project from agent-dashboa...
9
- 3529727 fix: Decisions separator line width now mat...
10
- d7652c4 fix: use proper box character for Decisions...
11
- 1594987 feat: move progress bar to Plan title line
12
- d5a9f4d feat: improve Plan panel and Tests panel di...
13
- └──────────────────────────────────────────────────────────┘
14
-
15
- ┌─ Plan ─────────────────────────────────── 7/10 ███████░░░┐
16
- Build agenthud CLI tool
17
- Set up project (npm, TypeScript, Vitest)
18
- Implement git data collection module
19
- Create GitPanel UI component
20
- │ ✓ Add CLI entry point with watch mode │
21
- │ ✓ Fix getTodayStats bug │
22
- Add --dir flag for project directory │
23
- Add --json flag for JSON output
24
- │ ✓ Add PlanPanel component │
25
- │ ✓ Add TestPanel component │
26
- │ ○ Publish to npm │
27
- ├─ Decisions ──────────────────────────────────────────────┤
28
- │ • Use dependency injection for git module testing │
29
- │ • Use Ink for terminal UI │
30
- │ • Use git log --numstat for stats instead of git diff... │
31
- └──────────────────────────────────────────────────────────┘
32
-
33
- ┌─ Tests ──────────────────────────────────────────────────┐
34
- │ ⚠ Outdated (1 commit behind) │
35
- │ ✓ 70 passed · 3529727 · 40m ago │
36
- └──────────────────────────────────────────────────────────┘
6
+ ┌─ Claude ─────────────────────────────────────────────────────┐
7
+ [10:30:00] > User: Show me the project structure
8
+ [10:30:05] * Glob: src/**/*.ts
9
+ [10:30:10] Read: package.json
10
+ [10:30:15] < Response: Here's the project structure...
11
+ [10:30:20] $ Bash: npm run test
12
+ [10:30:25] ~ Edit: src/index.ts
13
+ └──────────────────────────────────────────────────────────────┘
14
+
15
+ ┌─ Git ────────────────────────────────────────────────────────┐
16
+ main · +1068 -166 · 12 commits · 23 files
17
+ 1d00dc0 refactor: rename project from agent-dashboa...
18
+ 3529727 fix: Decisions separator line width now mat...
19
+ d7652c4 fix: use proper box character for Decisions...
20
+ └──────────────────────────────────────────────────────────────┘
21
+
22
+ ┌─ Tests ──────────────────────────────────────────────────────┐
23
+ 301 passed · 3fd7988
24
+ └──────────────────────────────────────────────────────────────┘
37
25
  ```
38
26
 
39
27
  ## Install
@@ -43,17 +31,44 @@ npx agenthud
43
31
 
44
32
  ## Features
45
33
 
34
+ - **Claude**: Real-time Claude Code session monitoring
46
35
  - **Git**: Branch, commits, line changes
47
- - **Plan**: Progress, decisions from `.agenthud/plan.json`
48
36
  - **Tests**: Results with outdated detection
37
+ - **Project**: Package info, stack detection
38
+
39
+ ## Claude Panel Icons
40
+
41
+ | Symbol | Type |
42
+ |--------|------|
43
+ | `>` | User input |
44
+ | `<` | Response |
45
+ | `~` | Edit/Write |
46
+ | `○` | Read |
47
+ | `$` | Bash |
48
+ | `*` | Glob/Grep |
49
+ | `@` | Web |
50
+ | `▶` | Task |
51
+ | `?` | Question |
52
+
53
+ ## Configuration
49
54
 
50
- ## Setup
55
+ Create `.agenthud.yaml` in your project root:
51
56
 
52
- Add to your `CLAUDE.md`:
53
- ```markdown
54
- Maintain `.agenthud/` directory:
55
- - Update `plan.json` when plan changes
56
- - Update `decisions.json` for key decisions
57
+ ```yaml
58
+ panels:
59
+ git:
60
+ enabled: true
61
+ interval: 30s
62
+ claude:
63
+ enabled: true
64
+ interval: 5s
65
+ max_activities: 20
66
+ tests:
67
+ enabled: true
68
+ interval: manual
69
+ project:
70
+ enabled: true
71
+ interval: 60s
57
72
  ```
58
73
 
59
74
  ## Keyboard
package/dist/index.js CHANGED
@@ -13,6 +13,7 @@ import { Box as Box7, Text as Text7, useApp, useInput, useStdout } from "ink";
13
13
  import { Box, Text } from "ink";
14
14
 
15
15
  // src/ui/constants.ts
16
+ import stringWidth from "string-width";
16
17
  var DEFAULT_PANEL_WIDTH = 70;
17
18
  var MIN_TERMINAL_WIDTH = 50;
18
19
  var MAX_TERMINAL_WIDTH = 120;
@@ -57,35 +58,7 @@ function truncate(text, maxLength) {
57
58
  if (text.length <= maxLength) return text;
58
59
  return text.slice(0, maxLength - 3) + "...";
59
60
  }
60
- function getDisplayWidth(str) {
61
- let width = 0;
62
- for (const char of str) {
63
- const code = char.codePointAt(0) || 0;
64
- if (code >= 127744 && code <= 129535 || // Misc symbols, emoticons, etc.
65
- code >= 9728 && code <= 9983 || // Misc symbols
66
- code >= 9984 && code <= 10175 || // Dingbats (includes ✏)
67
- code >= 128512 && code <= 128591 || // Emoticons
68
- code >= 128640 && code <= 128767) {
69
- width += 2;
70
- } else if (code === 65039) {
71
- continue;
72
- } else if (
73
- // CJK characters (Korean, Chinese, Japanese) - 2 wide
74
- code >= 44032 && code <= 55215 || // Korean Hangul syllables
75
- code >= 4352 && code <= 4607 || // Korean Hangul Jamo
76
- code >= 12592 && code <= 12687 || // Korean Hangul Compatibility Jamo
77
- code >= 19968 && code <= 40959 || // CJK Unified Ideographs
78
- code >= 13312 && code <= 19903 || // CJK Extension A
79
- code >= 12288 && code <= 12351 || // CJK Symbols
80
- code >= 65280 && code <= 65519
81
- ) {
82
- width += 2;
83
- } else {
84
- width += 1;
85
- }
86
- }
87
- return width;
88
- }
61
+ var getDisplayWidth = stringWidth;
89
62
 
90
63
  // src/ui/GitPanel.tsx
91
64
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -520,13 +493,13 @@ function formatActivityParts(activity, maxWidth) {
520
493
  }
521
494
  detailDisplayWidth = currentWidth;
522
495
  }
523
- const content2 = `${icon} ${label}: ${truncatedDetail}`;
496
+ const labelContent2 = `${label}: ${truncatedDetail}`;
524
497
  const displayWidth2 = totalPrefixWidth + detailDisplayWidth;
525
- return { timestamp, content: content2, displayWidth: displayWidth2 };
498
+ return { timestamp, icon, labelContent: labelContent2, displayWidth: displayWidth2 };
526
499
  }
527
- const content = `${icon} ${label}`;
500
+ const labelContent = label;
528
501
  const displayWidth = totalPrefixWidth;
529
- return { timestamp, content, displayWidth };
502
+ return { timestamp, icon, labelContent, displayWidth };
530
503
  }
531
504
  function ClaudePanel({
532
505
  data,
@@ -588,17 +561,21 @@ function ClaudePanel({
588
561
  const lines = [];
589
562
  for (let i = 0; i < state.activities.length; i++) {
590
563
  const activity = state.activities[i];
591
- const { timestamp, content, displayWidth } = formatActivityParts(activity, contentWidth);
564
+ const { timestamp, icon, labelContent, displayWidth } = formatActivityParts(activity, contentWidth);
592
565
  const padding = Math.max(0, contentWidth - displayWidth);
593
566
  const style = getActivityStyle(activity);
567
+ const clearEOL = "\x1B[K";
594
568
  lines.push(
595
569
  /* @__PURE__ */ jsxs4(Text4, { children: [
596
570
  BOX.v,
597
571
  " ",
598
572
  /* @__PURE__ */ jsx4(Text4, { dimColor: true, children: timestamp }),
599
- /* @__PURE__ */ jsx4(Text4, { color: style.color, dimColor: style.dimColor, children: content }),
573
+ /* @__PURE__ */ jsx4(Text4, { color: "cyan", children: icon }),
574
+ " ",
575
+ /* @__PURE__ */ jsx4(Text4, { color: style.color, dimColor: style.dimColor, children: labelContent }),
600
576
  " ".repeat(padding),
601
- BOX.v
577
+ BOX.v,
578
+ clearEOL
602
579
  ] }, `activity-${i}`)
603
580
  );
604
581
  }
@@ -1331,6 +1308,29 @@ import {
1331
1308
  } from "fs";
1332
1309
  import { homedir } from "os";
1333
1310
  import { join as join2, basename as basename2 } from "path";
1311
+
1312
+ // src/types/index.ts
1313
+ var ICONS = {
1314
+ // Activity types
1315
+ User: ">",
1316
+ Response: "<",
1317
+ // Tools
1318
+ Edit: "~",
1319
+ Write: "~",
1320
+ Read: "\u25CB",
1321
+ Bash: "$",
1322
+ Glob: "*",
1323
+ Grep: "*",
1324
+ WebFetch: "@",
1325
+ WebSearch: "@",
1326
+ Task: "\u25B6",
1327
+ TodoWrite: "~",
1328
+ AskUserQuestion: "?",
1329
+ // Fallback
1330
+ Default: "$"
1331
+ };
1332
+
1333
+ // src/data/claude.ts
1334
1334
  var fs = {
1335
1335
  existsSync: nodeExistsSync2,
1336
1336
  readFileSync: (path) => nodeReadFileSync3(path, "utf-8"),
@@ -1341,20 +1341,6 @@ var FIVE_MINUTES_MS = 5 * 60 * 1e3;
1341
1341
  var THIRTY_SECONDS_MS = 30 * 1e3;
1342
1342
  var MAX_LINES_TO_SCAN = 200;
1343
1343
  var DEFAULT_MAX_ACTIVITIES = 10;
1344
- var MAX_DETAIL_LENGTH = 45;
1345
- var TOOL_ICONS = {
1346
- Edit: "\u{1F4DD}",
1347
- Write: "\u{1F4DD}",
1348
- Read: "\u{1F4D6}",
1349
- Bash: "\u{1F527}",
1350
- Glob: "\u{1F50D}",
1351
- Grep: "\u{1F50D}",
1352
- WebFetch: "\u{1F310}",
1353
- WebSearch: "\u{1F310}",
1354
- Task: "\u{1F4CB}",
1355
- TodoWrite: "\u{1F4DD}",
1356
- AskUserQuestion: "\u2753"
1357
- };
1358
1344
  function getClaudeSessionPath(projectPath) {
1359
1345
  const encoded = projectPath.replace(/\//g, "-");
1360
1346
  return join2(homedir(), ".claude", "projects", encoded);
@@ -1384,46 +1370,19 @@ function findActiveSession(sessionDir) {
1384
1370
  }
1385
1371
  return null;
1386
1372
  }
1387
- function getDisplayWidth2(str) {
1388
- let width = 0;
1389
- for (const char of str) {
1390
- const code = char.codePointAt(0) || 0;
1391
- if (code >= 127744 && code <= 129535 || code >= 9728 && code <= 9983 || code >= 9984 && code <= 10175 || code >= 128512 && code <= 128591 || code >= 128640 && code <= 128767 || code >= 44032 && code <= 55215 || code >= 4352 && code <= 4607 || code >= 12592 && code <= 12687 || code >= 19968 && code <= 40959 || code >= 13312 && code <= 19903 || code >= 12288 && code <= 12351 || code >= 65280 && code <= 65519) {
1392
- width += 2;
1393
- } else if (code !== 65039) {
1394
- width += 1;
1395
- }
1396
- }
1397
- return width;
1398
- }
1399
- function truncate2(str, maxDisplayWidth) {
1400
- const currentWidth = getDisplayWidth2(str);
1401
- if (currentWidth <= maxDisplayWidth) return str;
1402
- let result = "";
1403
- let width = 0;
1404
- for (const char of str) {
1405
- const charWidth = getDisplayWidth2(char);
1406
- if (width + charWidth > maxDisplayWidth - 3) {
1407
- return result + "...";
1408
- }
1409
- result += char;
1410
- width += charWidth;
1411
- }
1412
- return result;
1413
- }
1414
1373
  function getToolDetail(toolName, input) {
1415
1374
  if (!input) return "";
1416
1375
  if (input.command) {
1417
- return truncate2(input.command, MAX_DETAIL_LENGTH);
1376
+ return input.command.replace(/\n/g, " ");
1418
1377
  }
1419
1378
  if (input.file_path) {
1420
1379
  return basename2(input.file_path);
1421
1380
  }
1422
1381
  if (input.pattern) {
1423
- return truncate2(input.pattern, MAX_DETAIL_LENGTH);
1382
+ return input.pattern;
1424
1383
  }
1425
1384
  if (input.query) {
1426
- return truncate2(input.query, MAX_DETAIL_LENGTH);
1385
+ return input.query;
1427
1386
  }
1428
1387
  return "";
1429
1388
  }
@@ -1475,9 +1434,9 @@ function parseSessionState(sessionFile, maxActivities = DEFAULT_MAX_ACTIVITIES)
1475
1434
  activities.push({
1476
1435
  timestamp: lastTimestamp || /* @__PURE__ */ new Date(),
1477
1436
  type: "user",
1478
- icon: "\u{1F464}",
1437
+ icon: ICONS.User,
1479
1438
  label: "User",
1480
- detail: truncate2(userText.replace(/\n/g, " "), MAX_DETAIL_LENGTH)
1439
+ detail: userText.replace(/\n/g, " ")
1481
1440
  });
1482
1441
  }
1483
1442
  lastType = "user";
@@ -1492,7 +1451,7 @@ function parseSessionState(sessionFile, maxActivities = DEFAULT_MAX_ACTIVITIES)
1492
1451
  for (const block of messageContent) {
1493
1452
  if (block.type === "tool_use") {
1494
1453
  const toolName = block.name || "Tool";
1495
- const icon = TOOL_ICONS[toolName] || "\u{1F527}";
1454
+ const icon = ICONS[toolName] || ICONS.Default;
1496
1455
  const detail = getToolDetail(toolName, block.input);
1497
1456
  activities.push({
1498
1457
  timestamp: lastTimestamp || /* @__PURE__ */ new Date(),
@@ -1507,9 +1466,9 @@ function parseSessionState(sessionFile, maxActivities = DEFAULT_MAX_ACTIVITIES)
1507
1466
  activities.push({
1508
1467
  timestamp: lastTimestamp || /* @__PURE__ */ new Date(),
1509
1468
  type: "response",
1510
- icon: "\u{1F916}",
1469
+ icon: ICONS.Response,
1511
1470
  label: "Response",
1512
- detail: truncate2(block.text.replace(/\n/g, " "), MAX_DETAIL_LENGTH)
1471
+ detail: block.text.replace(/\n/g, " ")
1513
1472
  });
1514
1473
  lastType = "response";
1515
1474
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agenthud",
3
- "version": "0.5.2",
3
+ "version": "0.5.4",
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",
@@ -52,6 +52,7 @@
52
52
  "dependencies": {
53
53
  "ink": "^6.6.0",
54
54
  "react": "^19.2.3",
55
+ "string-width": "^8.1.0",
55
56
  "yaml": "^2.8.2"
56
57
  }
57
58
  }