gnhf 0.1.20 → 0.1.21
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 +9 -8
- package/dist/cli.mjs +54 -13
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,6 +46,7 @@ You wake up to a branch full of clean work and a log of everything that happened
|
|
|
46
46
|
|
|
47
47
|
- **Dead simple** — one command starts an autonomous loop that runs until you Ctrl+C or a configured runtime cap is reached
|
|
48
48
|
- **Long running** — each iteration is committed on success, rolled back on failure, with sensible retries and exponential backoff
|
|
49
|
+
- **Live terminal title** — interactive runs keep your terminal title updated with live status, token totals, and commit count, then restore the previous title on exit
|
|
49
50
|
- **Agent-agnostic** — works with Claude Code, Codex, Rovo Dev, or OpenCode out of the box
|
|
50
51
|
|
|
51
52
|
## Quick Start
|
|
@@ -166,14 +167,14 @@ Pass `--worktree` to run each agent in an isolated [git worktree](https://git-sc
|
|
|
166
167
|
|
|
167
168
|
### Flags
|
|
168
169
|
|
|
169
|
-
| Flag | Description
|
|
170
|
-
| ------------------------ |
|
|
171
|
-
| `--agent <agent>` | Agent to use (`claude`, `codex`, `rovodev`, or `opencode`)
|
|
172
|
-
| `--max-iterations <n>` | Abort after `n` total iterations
|
|
173
|
-
| `--max-tokens <n>` | Abort after `n` total input+output tokens
|
|
174
|
-
| `--prevent-sleep <mode>` | Prevent system sleep during the run (`on`/`off` or `true`/`false`)
|
|
175
|
-
| `--worktree` | Run in a separate git worktree (enables multiple agents concurrently) | `false`
|
|
176
|
-
| `--version` | Show version
|
|
170
|
+
| Flag | Description | Default |
|
|
171
|
+
| ------------------------ | --------------------------------------------------------------------- | ---------------------- |
|
|
172
|
+
| `--agent <agent>` | Agent to use (`claude`, `codex`, `rovodev`, or `opencode`) | config file (`claude`) |
|
|
173
|
+
| `--max-iterations <n>` | Abort after `n` total iterations | unlimited |
|
|
174
|
+
| `--max-tokens <n>` | Abort after `n` total input+output tokens | unlimited |
|
|
175
|
+
| `--prevent-sleep <mode>` | Prevent system sleep during the run (`on`/`off` or `true`/`false`) | config file (`on`) |
|
|
176
|
+
| `--worktree` | Run in a separate git worktree (enables multiple agents concurrently) | `false` |
|
|
177
|
+
| `--version` | Show version | |
|
|
177
178
|
|
|
178
179
|
## Configuration
|
|
179
180
|
|
package/dist/cli.mjs
CHANGED
|
@@ -3530,6 +3530,24 @@ const DONE_HINT = "[ctrl+c to exit]";
|
|
|
3530
3530
|
function spacedLabel(text) {
|
|
3531
3531
|
return text.split("").join(" ");
|
|
3532
3532
|
}
|
|
3533
|
+
function formatTokenCount(tokens, direction) {
|
|
3534
|
+
return `${formatTokens(tokens)} ${direction}`;
|
|
3535
|
+
}
|
|
3536
|
+
function formatCommitCount(commitCount) {
|
|
3537
|
+
return `${commitCount} ${commitCount === 1 ? "commit" : "commits"}`;
|
|
3538
|
+
}
|
|
3539
|
+
function buildTerminalTitle(state, now) {
|
|
3540
|
+
return `gnhf ${state.status === "running" || state.status === "waiting" ? getMoonPhase("active", now, MOON_PHASE_PERIOD) : state.status} · ${formatTokenCount(state.totalInputTokens, "in")} · ${formatTokenCount(state.totalOutputTokens, "out")} · ${formatCommitCount(state.commitCount)}`;
|
|
3541
|
+
}
|
|
3542
|
+
function emitTerminalTitle(title) {
|
|
3543
|
+
return `\x1b]2;${title}\x07`;
|
|
3544
|
+
}
|
|
3545
|
+
function saveTerminalTitle() {
|
|
3546
|
+
return "\x1B[22;0t";
|
|
3547
|
+
}
|
|
3548
|
+
function restoreTerminalTitle() {
|
|
3549
|
+
return "\x1B[23;0t";
|
|
3550
|
+
}
|
|
3533
3551
|
function renderTitleCells(agentName) {
|
|
3534
3552
|
return [
|
|
3535
3553
|
[...textToCells(spacedLabel("gnhf"), "dim"), ...agentName ? [
|
|
@@ -3545,21 +3563,20 @@ function renderTitleCells(agentName) {
|
|
|
3545
3563
|
];
|
|
3546
3564
|
}
|
|
3547
3565
|
function renderStatsCells(elapsed, inputTokens, outputTokens, commitCount) {
|
|
3548
|
-
const commitLabel = commitCount === 1 ? "commit" : "commits";
|
|
3549
3566
|
return [
|
|
3550
3567
|
...textToCells(elapsed, "bold"),
|
|
3551
3568
|
...textToCells(" ", "normal"),
|
|
3552
3569
|
...textToCells("·", "dim"),
|
|
3553
3570
|
...textToCells(" ", "normal"),
|
|
3554
|
-
...textToCells(
|
|
3571
|
+
...textToCells(formatTokenCount(inputTokens, "in"), "normal"),
|
|
3555
3572
|
...textToCells(" ", "normal"),
|
|
3556
3573
|
...textToCells("·", "dim"),
|
|
3557
3574
|
...textToCells(" ", "normal"),
|
|
3558
|
-
...textToCells(
|
|
3575
|
+
...textToCells(formatTokenCount(outputTokens, "out"), "normal"),
|
|
3559
3576
|
...textToCells(" ", "normal"),
|
|
3560
3577
|
...textToCells("·", "dim"),
|
|
3561
3578
|
...textToCells(" ", "normal"),
|
|
3562
|
-
...textToCells(
|
|
3579
|
+
...textToCells(formatCommitCount(commitCount), "normal")
|
|
3563
3580
|
];
|
|
3564
3581
|
}
|
|
3565
3582
|
function renderAgentMessageCells(message, status) {
|
|
@@ -3775,10 +3792,22 @@ var Renderer = class {
|
|
|
3775
3792
|
cachedWidth = 0;
|
|
3776
3793
|
cachedHeight = 0;
|
|
3777
3794
|
prevCells = [];
|
|
3795
|
+
prevTitle = null;
|
|
3796
|
+
titleSaved = false;
|
|
3778
3797
|
isFirstFrame = true;
|
|
3779
3798
|
seedTop;
|
|
3780
3799
|
seedBottom;
|
|
3781
3800
|
seedSide;
|
|
3801
|
+
handleState = (newState) => {
|
|
3802
|
+
this.state = {
|
|
3803
|
+
...newState,
|
|
3804
|
+
iterations: [...newState.iterations]
|
|
3805
|
+
};
|
|
3806
|
+
this.updateTerminalTitle();
|
|
3807
|
+
};
|
|
3808
|
+
handleStopped = () => {
|
|
3809
|
+
this.stop("stopped");
|
|
3810
|
+
};
|
|
3782
3811
|
constructor(orchestrator, prompt, agentName) {
|
|
3783
3812
|
this.orchestrator = orchestrator;
|
|
3784
3813
|
this.prompt = prompt;
|
|
@@ -3792,15 +3821,8 @@ var Renderer = class {
|
|
|
3792
3821
|
});
|
|
3793
3822
|
}
|
|
3794
3823
|
start() {
|
|
3795
|
-
this.orchestrator.on("state",
|
|
3796
|
-
|
|
3797
|
-
...newState,
|
|
3798
|
-
iterations: [...newState.iterations]
|
|
3799
|
-
};
|
|
3800
|
-
});
|
|
3801
|
-
this.orchestrator.on("stopped", () => {
|
|
3802
|
-
this.stop("stopped");
|
|
3803
|
-
});
|
|
3824
|
+
this.orchestrator.on("state", this.handleState);
|
|
3825
|
+
this.orchestrator.on("stopped", this.handleStopped);
|
|
3804
3826
|
if (process$1.stdin.isTTY) {
|
|
3805
3827
|
process$1.stdin.setRawMode(true);
|
|
3806
3828
|
process$1.stdin.resume();
|
|
@@ -3819,11 +3841,18 @@ var Renderer = class {
|
|
|
3819
3841
|
clearInterval(this.interval);
|
|
3820
3842
|
this.interval = null;
|
|
3821
3843
|
}
|
|
3844
|
+
this.orchestrator.off("state", this.handleState);
|
|
3845
|
+
this.orchestrator.off("stopped", this.handleStopped);
|
|
3822
3846
|
if (process$1.stdin.isTTY) {
|
|
3823
3847
|
process$1.stdin.setRawMode(false);
|
|
3824
3848
|
process$1.stdin.pause();
|
|
3825
3849
|
process$1.stdin.removeAllListeners("data");
|
|
3826
3850
|
}
|
|
3851
|
+
if (this.titleSaved) {
|
|
3852
|
+
process$1.stdout.write(restoreTerminalTitle());
|
|
3853
|
+
this.titleSaved = false;
|
|
3854
|
+
this.prevTitle = null;
|
|
3855
|
+
}
|
|
3827
3856
|
this.exitResolve(reason);
|
|
3828
3857
|
}
|
|
3829
3858
|
waitUntilExit() {
|
|
@@ -3862,6 +3891,7 @@ var Renderer = class {
|
|
|
3862
3891
|
const w = process$1.stdout.columns || 80;
|
|
3863
3892
|
const h = process$1.stdout.rows || 24;
|
|
3864
3893
|
const resized = this.ensureStarFields(w, h);
|
|
3894
|
+
this.updateTerminalTitle(now);
|
|
3865
3895
|
const nextCells = buildFrameCells(this.prompt, this.agentName, this.state, this.topStars, this.bottomStars, this.sideStars, now, w, h);
|
|
3866
3896
|
if (this.isFirstFrame || resized) {
|
|
3867
3897
|
process$1.stdout.write("\x1B[H" + nextCells.map(rowToString).join("\n"));
|
|
@@ -3872,6 +3902,17 @@ var Renderer = class {
|
|
|
3872
3902
|
}
|
|
3873
3903
|
this.prevCells = nextCells;
|
|
3874
3904
|
}
|
|
3905
|
+
updateTerminalTitle(now = Date.now()) {
|
|
3906
|
+
if (!process$1.stdout.isTTY) return;
|
|
3907
|
+
const nextTitle = buildTerminalTitle(this.state, now);
|
|
3908
|
+
if (!this.titleSaved) {
|
|
3909
|
+
process$1.stdout.write(saveTerminalTitle());
|
|
3910
|
+
this.titleSaved = true;
|
|
3911
|
+
}
|
|
3912
|
+
if (nextTitle === this.prevTitle) return;
|
|
3913
|
+
process$1.stdout.write(emitTerminalTitle(nextTitle));
|
|
3914
|
+
this.prevTitle = nextTitle;
|
|
3915
|
+
}
|
|
3875
3916
|
};
|
|
3876
3917
|
//#endregion
|
|
3877
3918
|
//#region src/utils/slugify.ts
|