gnhf 0.1.0 → 0.1.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.
Files changed (3) hide show
  1. package/README.md +12 -9
  2. package/dist/cli.mjs +30 -17
  3. package/package.json +5 -1
package/README.md CHANGED
@@ -1,4 +1,6 @@
1
- <h1 align="center">gnhf</h1>
1
+ <p align="center">Before I go to bed, I tell my agents:</p>
2
+ <h1 align="center">good night, have fun</h1>
3
+
2
4
  <p align="center">
3
5
  <a href="https://www.npmjs.com/package/gnhf"
4
6
  ><img
@@ -33,14 +35,15 @@
33
35
  /></a>
34
36
  </p>
35
37
 
36
- <h3 align="center">Before I go to bed, I tell my agents: good night, have fun.</h3>
37
-
38
- You have a backlog of improvements you'll never get to. Tests that need writing, complexity that needs reducing, types that need fixing. You could spend a Saturday on it, or you could hand it to a coding agent and go to sleep.
38
+ <p align="center">
39
+ <img src="docs/splash.png" alt="gnhf — Good Night, Have Fun" width="800">
40
+ </p>
39
41
 
40
- gnhf is an orchestrator that runs your coding agent in a loop — each iteration makes one small, committed, documented change towards an objective. You wake up to a branch full of clean work and a log of everything that happened.
42
+ gnhf is a [ralph loop](https://ghuntley.com/ralph/) orchestrator that keeps your agents running while you sleep — each iteration makes one small, committed, documented change towards an objective.
43
+ You wake up to a branch full of clean work and a log of everything that happened.
41
44
 
42
- - **Set it and forget it** — one command starts an autonomous loop that runs until you Ctrl+C
43
- - **Safe by design** — each iteration is committed on success, rolled back on failure, with exponential backoff and auto-abort after consecutive failures
45
+ - **Dead simple** — one command starts an autonomous loop that runs until you Ctrl+C
46
+ - **Autonomous by design** — each iteration is committed on success, rolled back on failure, with exponential backoff and auto-abort after consecutive failures
44
47
  - **Agent-agnostic** — works with Claude Code or Codex out of the box
45
48
 
46
49
  ## Quick Start
@@ -105,7 +108,7 @@ npm link
105
108
  │ ┌──────────┘ │
106
109
  ▼ ▼ │
107
110
  ┌────────────┐ yes ┌──────────┐ │
108
- 5 consec. ├─────────►│ abort │ │
111
+ 3 consec. ├─────────►│ abort │ │
109
112
  │ failures? │ └──────────┘ │
110
113
  └─────┬──────┘ │
111
114
  no │ │
@@ -140,7 +143,7 @@ Config lives at `~/.gnhf/config.yml`:
140
143
  agent: claude
141
144
 
142
145
  # Abort after this many consecutive failures
143
- maxConsecutiveFailures: 5
146
+ maxConsecutiveFailures: 3
144
147
  ```
145
148
 
146
149
  CLI flags override config file values.
package/dist/cli.mjs CHANGED
@@ -12,7 +12,7 @@ import { createHash } from "node:crypto";
12
12
  //#region src/core/config.ts
13
13
  const DEFAULT_CONFIG = {
14
14
  agent: "claude",
15
- maxConsecutiveFailures: 5
15
+ maxConsecutiveFailures: 3
16
16
  };
17
17
  function loadConfig(overrides) {
18
18
  const configPath = join(homedir(), ".gnhf", "config.yml");
@@ -401,11 +401,18 @@ This is iteration ${params.n} of an ongoing loop to fully accomplish the objecti
401
401
  ## Instructions
402
402
 
403
403
  1. Read .gnhf/runs/${params.runId}/notes.md first to understand what has been done in previous iterations.
404
- 2. Focus on one smallest logical unit of work that's individually testable and would make incremental progress towards the objective. Do NOT try to do everything at once.
405
- 3. Run build/tests/linters/formatters if available to validate your work.
404
+ 2. Focus on the next smallest logical unit of work that's individually testable and would make incremental progress towards the objective - that's the scope of this iteration.
405
+ 3. If you made code changes, run build/tests/linters/formatters if available to validate your work.
406
406
  4. Do NOT make any git commits. Commits will be handled automatically by the gnhf orchestrator.
407
407
  5. When you are done, respond with a JSON object according to the provided schema.
408
408
 
409
+ ## Output
410
+
411
+ - success: whether you were able to complete your iteration. set to false only if something made it impossible for you to do your work
412
+ - summary: a concise one-sentence summary of the accomplishment in this iteration
413
+ - key_changes_made: an array of descriptions for key changes you made. don't group this by file - group by logical units of work. don't describe activities - describe material outcomes
414
+ - key_learnings: an array of new learnings that were surprising and weren't captured by previous notes
415
+
409
416
  ## Objective
410
417
 
411
418
  ${params.prompt}`;
@@ -767,24 +774,30 @@ function formatTokens(count) {
767
774
  //#region src/utils/wordwrap.ts
768
775
  function wordWrap(text, width, maxLines) {
769
776
  if (!text) return [];
770
- const words = text.split(/\s+/);
771
777
  const lines = [];
772
- let current = "";
773
- for (const word of words) {
774
- if (word.length > width) {
775
- if (current) {
776
- lines.push(current);
777
- current = "";
778
- }
779
- for (let i = 0; i < word.length; i += width) lines.push(word.slice(i, i + width));
778
+ for (const paragraph of text.split("\n")) {
779
+ const words = paragraph.split(/\s+/).filter(Boolean);
780
+ if (words.length === 0) {
781
+ lines.push("");
780
782
  continue;
781
783
  }
782
- if (current && current.length + 1 + word.length > width) {
783
- lines.push(current);
784
- current = word;
785
- } else current = current ? current + " " + word : word;
784
+ let current = "";
785
+ for (const word of words) {
786
+ if (word.length > width) {
787
+ if (current) {
788
+ lines.push(current);
789
+ current = "";
790
+ }
791
+ for (let i = 0; i < word.length; i += width) lines.push(word.slice(i, i + width));
792
+ continue;
793
+ }
794
+ if (current && current.length + 1 + word.length > width) {
795
+ lines.push(current);
796
+ current = word;
797
+ } else current = current ? current + " " + word : word;
798
+ }
799
+ if (current) lines.push(current);
786
800
  }
787
- if (current) lines.push(current);
788
801
  if (maxLines && lines.length > maxLines) {
789
802
  const capped = lines.slice(0, maxLines);
790
803
  const last = capped[maxLines - 1];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnhf",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Before I go to bed, I tell my agents: good night, have fun",
5
5
  "type": "module",
6
6
  "bin": {
@@ -40,6 +40,10 @@
40
40
  "publishConfig": {
41
41
  "access": "public"
42
42
  },
43
+ "repository": {
44
+ "type": "git",
45
+ "url": "https://github.com/kunchenguid/gnhf"
46
+ },
43
47
  "engines": {
44
48
  "node": ">=20"
45
49
  }