agenthud 0.8.0 → 0.8.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 +31 -0
- package/dist/index.js +1 -1
- package/dist/{main-CA4K47Y5.js → main-QZW2W2BX.js} +298 -39
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -105,6 +105,31 @@ Press `↵` on any activity to open a scrollable full-content view.
|
|
|
105
105
|
| `↓` / `j` | Scroll down |
|
|
106
106
|
| `↵` / `Esc` / `q` | Close |
|
|
107
107
|
|
|
108
|
+
## Report
|
|
109
|
+
|
|
110
|
+
Print a Markdown summary of all activity on a given date, suitable for piping to scripts or LLMs:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
agenthud report # today
|
|
114
|
+
agenthud report --date 2026-05-14 # specific date
|
|
115
|
+
agenthud report --date today --include all # all activity types
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
Output is written to stdout in Markdown format:
|
|
119
|
+
|
|
120
|
+
```
|
|
121
|
+
# AgentHUD Report: 2026-05-14
|
|
122
|
+
|
|
123
|
+
## myproject (10:23 – 14:45)
|
|
124
|
+
|
|
125
|
+
[10:23] $ npm test
|
|
126
|
+
[10:35] ~ src/ui/App.tsx
|
|
127
|
+
[11:15] < Added spinner hook to make the UI feel alive.
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
**`--include` types:** `response`, `bash`, `edit`, `thinking`, `read`, `glob`, `user`
|
|
131
|
+
Default: `response,bash,edit,thinking`
|
|
132
|
+
|
|
108
133
|
## Configuration
|
|
109
134
|
|
|
110
135
|
Optional. Create `~/.agenthud/config.yaml`:
|
|
@@ -120,6 +145,12 @@ hiddenSubAgents:
|
|
|
120
145
|
- code-reviewer
|
|
121
146
|
```
|
|
122
147
|
|
|
148
|
+
## Environment Variables
|
|
149
|
+
|
|
150
|
+
| Variable | Default | Description |
|
|
151
|
+
|----------|---------|-------------|
|
|
152
|
+
| `CLAUDE_PROJECTS_DIR` | `~/.claude/projects` | Path to Claude Code projects directory. Useful for backups or mounted volumes. |
|
|
153
|
+
|
|
123
154
|
## Feedback
|
|
124
155
|
|
|
125
156
|
Issues and PRs welcome at [GitHub](https://github.com/neochoon/agenthud).
|
package/dist/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/main.ts
|
|
2
|
-
import { createInterface } from "readline";
|
|
3
2
|
import { existsSync as existsSync5, rmSync } from "fs";
|
|
4
3
|
import { join as join5 } from "path";
|
|
4
|
+
import { createInterface } from "readline";
|
|
5
5
|
import { render } from "ink";
|
|
6
6
|
import React from "react";
|
|
7
7
|
|
|
@@ -9,16 +9,50 @@ import React from "react";
|
|
|
9
9
|
import { readFileSync } from "fs";
|
|
10
10
|
import { dirname, join } from "path";
|
|
11
11
|
import { fileURLToPath } from "url";
|
|
12
|
+
var ALL_TYPES = [
|
|
13
|
+
"response",
|
|
14
|
+
"bash",
|
|
15
|
+
"edit",
|
|
16
|
+
"thinking",
|
|
17
|
+
"read",
|
|
18
|
+
"glob",
|
|
19
|
+
"user"
|
|
20
|
+
];
|
|
21
|
+
var DEFAULT_TYPES = ["response", "bash", "edit", "thinking"];
|
|
22
|
+
var KNOWN_WATCH_FLAGS = /* @__PURE__ */ new Set([
|
|
23
|
+
"-w",
|
|
24
|
+
"--watch",
|
|
25
|
+
"--once",
|
|
26
|
+
"-V",
|
|
27
|
+
"--version",
|
|
28
|
+
"-h",
|
|
29
|
+
"--help"
|
|
30
|
+
]);
|
|
31
|
+
var KNOWN_REPORT_FLAGS = /* @__PURE__ */ new Set(["--date", "--include", "--format"]);
|
|
32
|
+
var KNOWN_SUBCOMMANDS = /* @__PURE__ */ new Set(["report"]);
|
|
12
33
|
function getHelp() {
|
|
13
34
|
return `Usage: agenthud [options]
|
|
14
35
|
|
|
15
36
|
Monitors all running Claude Code sessions in real-time.
|
|
16
37
|
|
|
17
38
|
Options:
|
|
18
|
-
-w, --watch
|
|
19
|
-
--once
|
|
20
|
-
-V, --version
|
|
21
|
-
-h, --help
|
|
39
|
+
-w, --watch Watch mode (default) \u2014 live updates
|
|
40
|
+
--once Print once and exit
|
|
41
|
+
-V, --version Show version number
|
|
42
|
+
-h, --help Show this help message
|
|
43
|
+
|
|
44
|
+
Commands:
|
|
45
|
+
report [--date DATE] [--include TYPES]
|
|
46
|
+
Print activity report for a date (default: today)
|
|
47
|
+
--date YYYY-MM-DD|today Date to report on
|
|
48
|
+
--include TYPES Comma-separated types or "all"
|
|
49
|
+
Types: response,bash,edit,thinking,read,glob,user
|
|
50
|
+
Default: response,bash,edit,thinking
|
|
51
|
+
--format FORMAT Output format: markdown (default) or json
|
|
52
|
+
|
|
53
|
+
Environment:
|
|
54
|
+
CLAUDE_PROJECTS_DIR Path to Claude projects directory
|
|
55
|
+
(default: ~/.claude/projects)
|
|
22
56
|
|
|
23
57
|
Config: ~/.agenthud/config.yaml
|
|
24
58
|
Logs: ~/.agenthud/logs/
|
|
@@ -34,6 +68,26 @@ function getVersion() {
|
|
|
34
68
|
function clearScreen() {
|
|
35
69
|
console.clear();
|
|
36
70
|
}
|
|
71
|
+
function parseUTCMidnight(dateStr) {
|
|
72
|
+
if (dateStr === "today") {
|
|
73
|
+
const now = /* @__PURE__ */ new Date();
|
|
74
|
+
return new Date(
|
|
75
|
+
Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
|
|
76
|
+
);
|
|
77
|
+
}
|
|
78
|
+
const match = dateStr.match(/^(\d{4})-(\d{2})-(\d{2})$/);
|
|
79
|
+
if (!match) return null;
|
|
80
|
+
const [, y, m, d] = match.map(Number);
|
|
81
|
+
const date = new Date(Date.UTC(y, m - 1, d));
|
|
82
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
83
|
+
return date;
|
|
84
|
+
}
|
|
85
|
+
function todayUTCMidnight() {
|
|
86
|
+
const now = /* @__PURE__ */ new Date();
|
|
87
|
+
return new Date(
|
|
88
|
+
Date.UTC(now.getUTCFullYear(), now.getUTCMonth(), now.getUTCDate())
|
|
89
|
+
);
|
|
90
|
+
}
|
|
37
91
|
function parseArgs(args) {
|
|
38
92
|
if (args.includes("--help") || args.includes("-h")) {
|
|
39
93
|
return { mode: "watch", command: "help" };
|
|
@@ -44,15 +98,77 @@ function parseArgs(args) {
|
|
|
44
98
|
if (args.includes("--once")) {
|
|
45
99
|
return { mode: "once" };
|
|
46
100
|
}
|
|
101
|
+
if (args[0] === "report") {
|
|
102
|
+
const rest = args.slice(1);
|
|
103
|
+
let reportDate = todayUTCMidnight();
|
|
104
|
+
let reportInclude = DEFAULT_TYPES;
|
|
105
|
+
let reportError;
|
|
106
|
+
for (const arg of rest) {
|
|
107
|
+
if (arg.startsWith("-") && !KNOWN_REPORT_FLAGS.has(arg)) {
|
|
108
|
+
reportError = `Unknown option: "${arg}". Run agenthud --help for usage.`;
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
const dateIdx = rest.indexOf("--date");
|
|
113
|
+
if (dateIdx !== -1) {
|
|
114
|
+
const dateStr = rest[dateIdx + 1];
|
|
115
|
+
if (!dateStr) {
|
|
116
|
+
reportError = "Invalid date: missing value for --date";
|
|
117
|
+
} else {
|
|
118
|
+
const parsed = parseUTCMidnight(dateStr);
|
|
119
|
+
if (!parsed) {
|
|
120
|
+
reportError = `Invalid date: "${dateStr}". Use YYYY-MM-DD or "today".`;
|
|
121
|
+
} else {
|
|
122
|
+
reportDate = parsed;
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
const includeIdx = rest.indexOf("--include");
|
|
127
|
+
if (includeIdx !== -1) {
|
|
128
|
+
const includeStr = rest[includeIdx + 1];
|
|
129
|
+
if (includeStr === "all") {
|
|
130
|
+
reportInclude = ALL_TYPES;
|
|
131
|
+
} else if (includeStr) {
|
|
132
|
+
reportInclude = includeStr.split(",").map((s) => s.trim()).filter(Boolean);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
let reportFormat = "markdown";
|
|
136
|
+
const formatIdx = rest.indexOf("--format");
|
|
137
|
+
if (formatIdx !== -1) {
|
|
138
|
+
const fmt = rest[formatIdx + 1];
|
|
139
|
+
if (fmt === "json" || fmt === "markdown") {
|
|
140
|
+
reportFormat = fmt;
|
|
141
|
+
} else if (fmt) {
|
|
142
|
+
reportError = `Invalid format: "${fmt}". Use "markdown" or "json".`;
|
|
143
|
+
} else {
|
|
144
|
+
reportError = "Invalid format: missing value for --format.";
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return {
|
|
148
|
+
mode: "report",
|
|
149
|
+
reportDate,
|
|
150
|
+
reportInclude,
|
|
151
|
+
reportFormat,
|
|
152
|
+
reportError
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
if (args[0] && !args[0].startsWith("-") && !KNOWN_SUBCOMMANDS.has(args[0])) {
|
|
156
|
+
return {
|
|
157
|
+
mode: "watch",
|
|
158
|
+
error: `Unknown command: "${args[0]}". Run agenthud --help for usage.`
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
for (const arg of args) {
|
|
162
|
+
if (arg.startsWith("-") && !KNOWN_WATCH_FLAGS.has(arg)) {
|
|
163
|
+
return {
|
|
164
|
+
mode: "watch",
|
|
165
|
+
error: `Unknown option: "${arg}". Run agenthud --help for usage.`
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
}
|
|
47
169
|
return { mode: "watch" };
|
|
48
170
|
}
|
|
49
171
|
|
|
50
|
-
// src/ui/App.tsx
|
|
51
|
-
import { existsSync as existsSync4, watch, writeFileSync as writeFileSync2 } from "fs";
|
|
52
|
-
import { join as join4 } from "path";
|
|
53
|
-
import { Box as Box4, Text as Text4, useApp, useInput, useStdout } from "ink";
|
|
54
|
-
import { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
|
|
55
|
-
|
|
56
172
|
// src/config/globalConfig.ts
|
|
57
173
|
import { existsSync, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "fs";
|
|
58
174
|
import { homedir } from "os";
|
|
@@ -145,6 +261,9 @@ function hasProjectLevelConfig() {
|
|
|
145
261
|
return existsSync(join2(process.cwd(), ".agenthud", "config.yaml"));
|
|
146
262
|
}
|
|
147
263
|
|
|
264
|
+
// src/data/reportGenerator.ts
|
|
265
|
+
import { statSync } from "fs";
|
|
266
|
+
|
|
148
267
|
// src/data/sessionHistory.ts
|
|
149
268
|
import { existsSync as existsSync2, readFileSync as readFileSync3 } from "fs";
|
|
150
269
|
|
|
@@ -295,8 +414,103 @@ function parseSessionHistory(filePath) {
|
|
|
295
414
|
return activities;
|
|
296
415
|
}
|
|
297
416
|
|
|
417
|
+
// src/data/reportGenerator.ts
|
|
418
|
+
function activityMatchesInclude(activity, include) {
|
|
419
|
+
const label = activity.label.toLowerCase();
|
|
420
|
+
const type = activity.type;
|
|
421
|
+
if (include.includes("response") && type === "response") return true;
|
|
422
|
+
if (include.includes("thinking") && type === "thinking") return true;
|
|
423
|
+
if (include.includes("user") && type === "user") return true;
|
|
424
|
+
if (include.includes("bash") && label === "bash") return true;
|
|
425
|
+
if (include.includes("edit") && (label === "edit" || label === "write" || label === "todowrite"))
|
|
426
|
+
return true;
|
|
427
|
+
if (include.includes("read") && (label === "read" || label === "glob" || label === "grep"))
|
|
428
|
+
return true;
|
|
429
|
+
if (include.includes("glob") && (label === "glob" || label === "grep"))
|
|
430
|
+
return true;
|
|
431
|
+
return false;
|
|
432
|
+
}
|
|
433
|
+
function isSameUTCDay(a, b) {
|
|
434
|
+
return a.getUTCFullYear() === b.getUTCFullYear() && a.getUTCMonth() === b.getUTCMonth() && a.getUTCDate() === b.getUTCDate();
|
|
435
|
+
}
|
|
436
|
+
function formatTime(date) {
|
|
437
|
+
return `${String(date.getUTCHours()).padStart(2, "0")}:${String(date.getUTCMinutes()).padStart(2, "0")}`;
|
|
438
|
+
}
|
|
439
|
+
function formatActivity(activity) {
|
|
440
|
+
const time = formatTime(activity.timestamp);
|
|
441
|
+
const detail = activity.detail.length > 120 ? activity.detail.slice(0, 120) : activity.detail;
|
|
442
|
+
const suffix = detail ? `: ${detail}` : "";
|
|
443
|
+
return `[${time}] ${activity.icon} ${activity.label}${suffix}`;
|
|
444
|
+
}
|
|
445
|
+
function formatDateString(date) {
|
|
446
|
+
return `${date.getUTCFullYear()}-${String(date.getUTCMonth() + 1).padStart(2, "0")}-${String(date.getUTCDate()).padStart(2, "0")}`;
|
|
447
|
+
}
|
|
448
|
+
function sessionIsOnDate(session, date, activities) {
|
|
449
|
+
try {
|
|
450
|
+
const mtime = new Date(statSync(session.filePath).mtimeMs);
|
|
451
|
+
if (isSameUTCDay(mtime, date)) return true;
|
|
452
|
+
} catch {
|
|
453
|
+
}
|
|
454
|
+
return activities.some((a) => isSameUTCDay(a.timestamp, date));
|
|
455
|
+
}
|
|
456
|
+
function generateReport(sessions, options2) {
|
|
457
|
+
const { date, include, format = "markdown" } = options2;
|
|
458
|
+
const dateStr = formatDateString(date);
|
|
459
|
+
const blocks = [];
|
|
460
|
+
for (const session of sessions) {
|
|
461
|
+
const allActivities = parseSessionHistory(session.filePath);
|
|
462
|
+
if (!sessionIsOnDate(session, date, allActivities)) continue;
|
|
463
|
+
const dayActivities = allActivities.filter((a) => isSameUTCDay(a.timestamp, date)).filter((a) => activityMatchesInclude(a, include));
|
|
464
|
+
if (dayActivities.length === 0) continue;
|
|
465
|
+
blocks.push({
|
|
466
|
+
session,
|
|
467
|
+
activities: dayActivities,
|
|
468
|
+
firstTime: dayActivities[0].timestamp.getTime()
|
|
469
|
+
});
|
|
470
|
+
}
|
|
471
|
+
if (blocks.length === 0) {
|
|
472
|
+
if (format === "json") {
|
|
473
|
+
return JSON.stringify({ date: dateStr, sessions: [] }, null, 2);
|
|
474
|
+
}
|
|
475
|
+
return `No activity found for ${dateStr}.`;
|
|
476
|
+
}
|
|
477
|
+
blocks.sort((a, b) => a.firstTime - b.firstTime);
|
|
478
|
+
if (format === "json") {
|
|
479
|
+
return JSON.stringify(
|
|
480
|
+
{
|
|
481
|
+
date: dateStr,
|
|
482
|
+
sessions: blocks.map(({ session, activities }) => ({
|
|
483
|
+
project: session.projectName,
|
|
484
|
+
start: formatTime(activities[0].timestamp),
|
|
485
|
+
end: formatTime(activities[activities.length - 1].timestamp),
|
|
486
|
+
activities: activities.map((a) => ({
|
|
487
|
+
time: formatTime(a.timestamp),
|
|
488
|
+
icon: a.icon,
|
|
489
|
+
label: a.label,
|
|
490
|
+
detail: a.detail.length > 120 ? a.detail.slice(0, 120) : a.detail
|
|
491
|
+
}))
|
|
492
|
+
}))
|
|
493
|
+
},
|
|
494
|
+
null,
|
|
495
|
+
2
|
|
496
|
+
);
|
|
497
|
+
}
|
|
498
|
+
const lines = [`# AgentHUD Report: ${dateStr}`, ""];
|
|
499
|
+
for (const { session, activities } of blocks) {
|
|
500
|
+
const first = formatTime(activities[0].timestamp);
|
|
501
|
+
const last = formatTime(activities[activities.length - 1].timestamp);
|
|
502
|
+
lines.push(`## ${session.projectName} (${first} \u2013 ${last})`);
|
|
503
|
+
lines.push("");
|
|
504
|
+
for (const activity of activities) {
|
|
505
|
+
lines.push(formatActivity(activity));
|
|
506
|
+
}
|
|
507
|
+
lines.push("");
|
|
508
|
+
}
|
|
509
|
+
return lines.join("\n").trimEnd();
|
|
510
|
+
}
|
|
511
|
+
|
|
298
512
|
// src/data/sessions.ts
|
|
299
|
-
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync4, statSync } from "fs";
|
|
513
|
+
import { existsSync as existsSync3, readdirSync, readFileSync as readFileSync4, statSync as statSync2 } from "fs";
|
|
300
514
|
import { homedir as homedir2 } from "os";
|
|
301
515
|
import { basename as basename2, join as join3 } from "path";
|
|
302
516
|
|
|
@@ -339,7 +553,7 @@ var getDisplayWidth = stringWidth;
|
|
|
339
553
|
|
|
340
554
|
// src/data/sessions.ts
|
|
341
555
|
function getProjectsDir() {
|
|
342
|
-
return join3(homedir2(), ".claude", "projects");
|
|
556
|
+
return process.env.CLAUDE_PROJECTS_DIR ?? join3(homedir2(), ".claude", "projects");
|
|
343
557
|
}
|
|
344
558
|
function decodeProjectPath(encoded) {
|
|
345
559
|
const windowsDriveMatch = encoded.match(/^([A-Za-z])--(.*)$/);
|
|
@@ -418,7 +632,7 @@ function buildSubAgents(parentId, projectDir, config, projectName) {
|
|
|
418
632
|
const hideKey = `${projectName}/${id}`;
|
|
419
633
|
const filePath = join3(subagentsDir, file);
|
|
420
634
|
try {
|
|
421
|
-
const stat =
|
|
635
|
+
const stat = statSync2(filePath);
|
|
422
636
|
const { agentId, taskDescription } = readSubAgentInfo(filePath);
|
|
423
637
|
return {
|
|
424
638
|
id,
|
|
@@ -449,7 +663,7 @@ function discoverSessions(config) {
|
|
|
449
663
|
try {
|
|
450
664
|
projectDirs = readdirSync(projectsDir).filter((entry) => {
|
|
451
665
|
try {
|
|
452
|
-
return
|
|
666
|
+
return statSync2(join3(projectsDir, entry)).isDirectory();
|
|
453
667
|
} catch {
|
|
454
668
|
return false;
|
|
455
669
|
}
|
|
@@ -475,7 +689,7 @@ function discoverSessions(config) {
|
|
|
475
689
|
const hideKey = `${projectName}/${id}`;
|
|
476
690
|
const filePath = join3(projectDir, file);
|
|
477
691
|
try {
|
|
478
|
-
const stat =
|
|
692
|
+
const stat = statSync2(filePath);
|
|
479
693
|
const subAgents = buildSubAgents(id, projectDir, config, projectName);
|
|
480
694
|
allSessions.push({
|
|
481
695
|
id,
|
|
@@ -514,6 +728,12 @@ function discoverSessions(config) {
|
|
|
514
728
|
};
|
|
515
729
|
}
|
|
516
730
|
|
|
731
|
+
// src/ui/App.tsx
|
|
732
|
+
import { existsSync as existsSync4, watch, writeFileSync as writeFileSync2 } from "fs";
|
|
733
|
+
import { join as join4 } from "path";
|
|
734
|
+
import { Box as Box4, Text as Text4, useApp, useInput, useStdout } from "ink";
|
|
735
|
+
import { useCallback, useEffect as useEffect2, useMemo, useRef, useState as useState2 } from "react";
|
|
736
|
+
|
|
517
737
|
// src/ui/ActivityViewerPanel.tsx
|
|
518
738
|
import { Box, Text } from "ink";
|
|
519
739
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -570,13 +790,14 @@ function ActivityViewerPanel({
|
|
|
570
790
|
visibleRows,
|
|
571
791
|
width,
|
|
572
792
|
cursorLine,
|
|
573
|
-
hasFocus
|
|
793
|
+
hasFocus,
|
|
794
|
+
spinner = ""
|
|
574
795
|
}) {
|
|
575
796
|
const innerWidth = getInnerWidth(width);
|
|
576
797
|
const contentWidth = innerWidth - 1;
|
|
577
798
|
let titleSuffix;
|
|
578
799
|
if (isLive) {
|
|
579
|
-
titleSuffix =
|
|
800
|
+
titleSuffix = `[LIVE ${spinner || "\u25BC"}]`;
|
|
580
801
|
} else {
|
|
581
802
|
const badge = newCount > 0 ? ` +${newCount}\u2191` : "";
|
|
582
803
|
titleSuffix = `[PAUSED \u2193${scrollOffset}${badge}]`;
|
|
@@ -711,8 +932,18 @@ function DetailViewPanel({
|
|
|
711
932
|
clampedOffset,
|
|
712
933
|
clampedOffset + visibleRows
|
|
713
934
|
);
|
|
714
|
-
const
|
|
935
|
+
const style = getActivityStyle(activity);
|
|
715
936
|
const scrollSuffix = totalLines > visibleRows ? `[${clampedOffset + 1}-${Math.min(clampedOffset + visibleRows, totalLines)}/${totalLines}]` : "";
|
|
937
|
+
const iconWidth = getDisplayWidth(activity.icon);
|
|
938
|
+
const labelWidth = activity.label.length;
|
|
939
|
+
const scrollPart = scrollSuffix ? ` ${scrollSuffix} ${BOX.h}` : "";
|
|
940
|
+
const scrollPartWidth = scrollSuffix ? getDisplayWidth(scrollPart) : 0;
|
|
941
|
+
const dashCount = Math.max(
|
|
942
|
+
0,
|
|
943
|
+
width - 3 - iconWidth - 1 - labelWidth - 1 - scrollPartWidth - 1
|
|
944
|
+
);
|
|
945
|
+
const dashes = BOX.h.repeat(dashCount);
|
|
946
|
+
const titleRight = `${dashes}${scrollPart}${BOX.tr}`;
|
|
716
947
|
const contentRows = [];
|
|
717
948
|
for (let i = 0; i < visibleRows; i++) {
|
|
718
949
|
const line = visibleSlice[i] ?? "";
|
|
@@ -728,7 +959,16 @@ function DetailViewPanel({
|
|
|
728
959
|
);
|
|
729
960
|
}
|
|
730
961
|
return /* @__PURE__ */ jsxs2(Box2, { flexDirection: "column", width, children: [
|
|
731
|
-
/* @__PURE__ */
|
|
962
|
+
/* @__PURE__ */ jsxs2(Text2, { children: [
|
|
963
|
+
BOX.tl,
|
|
964
|
+
BOX.h,
|
|
965
|
+
" ",
|
|
966
|
+
/* @__PURE__ */ jsx2(Text2, { color: "cyan", children: activity.icon }),
|
|
967
|
+
" ",
|
|
968
|
+
/* @__PURE__ */ jsx2(Text2, { color: style.color, dimColor: style.dimColor, children: activity.label }),
|
|
969
|
+
" ",
|
|
970
|
+
titleRight
|
|
971
|
+
] }),
|
|
732
972
|
contentRows,
|
|
733
973
|
/* @__PURE__ */ jsx2(Text2, { children: createBottomLine(width) })
|
|
734
974
|
] });
|
|
@@ -1352,11 +1592,10 @@ function App({ mode }) {
|
|
|
1352
1592
|
const selectedIndex = allFlat.findIndex((s) => s.id === selectedId);
|
|
1353
1593
|
const height = (stdout?.rows ?? 41) - 1;
|
|
1354
1594
|
const width = stdout?.columns ?? 80;
|
|
1355
|
-
const
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
);
|
|
1359
|
-
const treeRows = Math.max(3, height - viewerRows - 7);
|
|
1595
|
+
const maxTreeRows = Math.floor(height * (1 - VIEWER_HEIGHT_FRACTION));
|
|
1596
|
+
const naturalTreeRows = allFlat.length;
|
|
1597
|
+
const treeRows = Math.max(1, Math.min(naturalTreeRows, maxTreeRows));
|
|
1598
|
+
const viewerRows = Math.max(5, height - 7 - treeRows);
|
|
1360
1599
|
const saveLog = useCallback(() => {
|
|
1361
1600
|
if (!activities.length || !selectedId) return;
|
|
1362
1601
|
ensureLogDir(config.logDir);
|
|
@@ -1636,7 +1875,8 @@ function App({ mode }) {
|
|
|
1636
1875
|
visibleRows: viewerRows,
|
|
1637
1876
|
width,
|
|
1638
1877
|
cursorLine: viewerCursorLine,
|
|
1639
|
-
hasFocus: focus === "viewer"
|
|
1878
|
+
hasFocus: focus === "viewer",
|
|
1879
|
+
spinner
|
|
1640
1880
|
}
|
|
1641
1881
|
) })
|
|
1642
1882
|
] });
|
|
@@ -1644,6 +1884,11 @@ function App({ mode }) {
|
|
|
1644
1884
|
|
|
1645
1885
|
// src/main.ts
|
|
1646
1886
|
var options = parseArgs(process.argv.slice(2));
|
|
1887
|
+
if (options.error) {
|
|
1888
|
+
process.stderr.write(`agenthud: ${options.error}
|
|
1889
|
+
`);
|
|
1890
|
+
process.exit(1);
|
|
1891
|
+
}
|
|
1647
1892
|
if (options.command === "help") {
|
|
1648
1893
|
console.log(getHelp());
|
|
1649
1894
|
process.exit(0);
|
|
@@ -1660,22 +1905,36 @@ if (existsSync5(legacyConfig)) {
|
|
|
1660
1905
|
console.log("Settings have moved to ~/.agenthud/config.yaml.");
|
|
1661
1906
|
const rl = createInterface({ input: process.stdin, output: process.stdout });
|
|
1662
1907
|
await new Promise((resolve) => {
|
|
1663
|
-
rl.question(
|
|
1664
|
-
|
|
1665
|
-
(answer)
|
|
1666
|
-
|
|
1667
|
-
|
|
1668
|
-
|
|
1669
|
-
|
|
1670
|
-
|
|
1671
|
-
console.log("Aborted.");
|
|
1672
|
-
process.exit(0);
|
|
1673
|
-
}
|
|
1674
|
-
resolve();
|
|
1908
|
+
rl.question("Delete the old config file and continue? [y/N] ", (answer) => {
|
|
1909
|
+
rl.close();
|
|
1910
|
+
if (answer.trim().toLowerCase() === "y") {
|
|
1911
|
+
rmSync(legacyConfig);
|
|
1912
|
+
console.log("Deleted .agenthud/config.yaml.");
|
|
1913
|
+
} else {
|
|
1914
|
+
console.log("Aborted.");
|
|
1915
|
+
process.exit(0);
|
|
1675
1916
|
}
|
|
1676
|
-
|
|
1917
|
+
resolve();
|
|
1918
|
+
});
|
|
1677
1919
|
});
|
|
1678
1920
|
}
|
|
1921
|
+
if (options.mode === "report") {
|
|
1922
|
+
if (options.reportError) {
|
|
1923
|
+
process.stderr.write(`agenthud: ${options.reportError}
|
|
1924
|
+
`);
|
|
1925
|
+
process.exit(1);
|
|
1926
|
+
}
|
|
1927
|
+
const config = loadGlobalConfig();
|
|
1928
|
+
const tree = discoverSessions(config);
|
|
1929
|
+
const markdown = generateReport(tree.sessions, {
|
|
1930
|
+
date: options.reportDate,
|
|
1931
|
+
include: options.reportInclude,
|
|
1932
|
+
format: options.reportFormat
|
|
1933
|
+
});
|
|
1934
|
+
process.stdout.write(`${markdown}
|
|
1935
|
+
`);
|
|
1936
|
+
process.exit(0);
|
|
1937
|
+
}
|
|
1679
1938
|
if (options.mode === "watch") {
|
|
1680
1939
|
clearScreen();
|
|
1681
1940
|
}
|
package/package.json
CHANGED