Skip to content

Commit d635f42

Browse files
committed
fix: repo field in package json
1 parent 414dd98 commit d635f42

10 files changed

Lines changed: 166 additions & 65 deletions

File tree

package.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@
4040
"publishConfig": {
4141
"access": "public"
4242
},
43+
"repository": {
44+
"type": "git",
45+
"url": "https://github.com/kunchenguid/gnhf"
46+
},
4347
"engines": {
4448
"node": ">=20"
4549
}

src/cli.ts

Lines changed: 4 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,7 @@ import {
99
commitAll,
1010
getCurrentBranch,
1111
} from "./core/git.js";
12-
import {
13-
setupRun,
14-
resumeRun,
15-
getLastIterationNumber,
16-
} from "./core/run.js";
12+
import { setupRun, resumeRun, getLastIterationNumber } from "./core/run.js";
1713
import { createAgent } from "./core/agents/factory.js";
1814
import { Orchestrator } from "./core/orchestrator.js";
1915
import { MockOrchestrator } from "./mock-orchestrator.js";
@@ -37,9 +33,7 @@ const program = new Command();
3733

3834
program
3935
.name("gnhf")
40-
.description(
41-
"Before I go to bed, I tell my agents: good night, have fun",
42-
)
36+
.description("Before I go to bed, I tell my agents: good night, have fun")
4337
.version("0.1.0")
4438
.argument("[prompt]", "The objective for the coding agent")
4539
.option("--agent <agent>", "Agent to use (claude or codex)", "claude")
@@ -88,10 +82,7 @@ program
8882
if (onGnhfBranch) {
8983
const existingRunId = currentBranch.slice("gnhf/".length);
9084
const existing = resumeRun(existingRunId, cwd);
91-
const existingPrompt = readFileSync(
92-
existing.promptPath,
93-
"utf-8",
94-
);
85+
const existingPrompt = readFileSync(existing.promptPath, "utf-8");
9586

9687
if (!prompt || prompt === existingPrompt) {
9788
prompt = existingPrompt;
@@ -109,10 +100,7 @@ program
109100
if (answer === "o") {
110101
ensureCleanWorkingTree(cwd);
111102
runInfo = setupRun(existingRunId, prompt, cwd);
112-
commitAll(
113-
`gnhf: overwrite run ${existingRunId}`,
114-
cwd,
115-
);
103+
commitAll(`gnhf: overwrite run ${existingRunId}`, cwd);
116104
} else if (answer === "n") {
117105
ensureCleanWorkingTree(cwd);
118106
const branchName = slugifyPrompt(prompt);

src/core/agents/claude.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -125,7 +125,11 @@ export class ClaudeAgent implements Agent {
125125
const content = (msg as Record<string, unknown>).content;
126126
if (Array.isArray(content)) {
127127
for (const block of content) {
128-
if (block?.type === "text" && typeof block.text === "string" && block.text.trim()) {
128+
if (
129+
block?.type === "text" &&
130+
typeof block.text === "string" &&
131+
block.text.trim()
132+
) {
129133
onMessage(block.text.trim());
130134
}
131135
}

src/mock-orchestrator.ts

Lines changed: 95 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -5,24 +5,96 @@ import type {
55
IterationRecord,
66
} from "./core/orchestrator.js";
77

8-
function mockIter(n: number, success: boolean, summary: string, agoMs: number): IterationRecord {
9-
return { number: n, success, summary, keyChanges: [], keyLearnings: [], timestamp: new Date(Date.now() - agoMs) };
8+
function mockIter(
9+
n: number,
10+
success: boolean,
11+
summary: string,
12+
agoMs: number,
13+
): IterationRecord {
14+
return {
15+
number: n,
16+
success,
17+
summary,
18+
keyChanges: [],
19+
keyLearnings: [],
20+
timestamp: new Date(Date.now() - agoMs),
21+
};
1022
}
1123

1224
const MOCK_ITERATIONS: IterationRecord[] = [
13-
mockIter(1, true, "Profiled cold start — identified 3 major bottlenecks", 25_200_000),
14-
mockIter(2, true, "Lazy-loaded config module, shaved 340ms off init", 24_000_000),
25+
mockIter(
26+
1,
27+
true,
28+
"Profiled cold start — identified 3 major bottlenecks",
29+
25_200_000,
30+
),
31+
mockIter(
32+
2,
33+
true,
34+
"Lazy-loaded config module, shaved 340ms off init",
35+
24_000_000,
36+
),
1537
mockIter(3, true, "Deferred plugin discovery to post-render", 22_800_000),
16-
mockIter(4, false, "Attempted parallel module init — race condition in DI container", 21_000_000),
17-
mockIter(5, true, "Fixed DI ordering, parallelized safe modules only", 19_200_000),
18-
mockIter(6, true, "Replaced synchronous JSON parse with streaming decoder", 17_400_000),
19-
mockIter(7, true, "Cached resolved dependency graph across restarts", 15_000_000),
20-
mockIter(8, false, "Tree-shaking broke runtime dynamic import paths", 12_600_000),
21-
mockIter(9, true, "Restored dynamic imports, added explicit entry chunks", 10_800_000),
22-
mockIter(10, true, "Inlined critical-path CSS, deferred non-essential styles", 8_400_000),
23-
mockIter(11, true, "Switched from full Intl polyfill to locale-on-demand", 6_000_000),
24-
mockIter(12, true, "Pre-compiled handlebars templates at build time", 3_600_000),
25-
mockIter(13, true, "Moved telemetry init behind requestIdleCallback", 1_800_000),
38+
mockIter(
39+
4,
40+
false,
41+
"Attempted parallel module init — race condition in DI container",
42+
21_000_000,
43+
),
44+
mockIter(
45+
5,
46+
true,
47+
"Fixed DI ordering, parallelized safe modules only",
48+
19_200_000,
49+
),
50+
mockIter(
51+
6,
52+
true,
53+
"Replaced synchronous JSON parse with streaming decoder",
54+
17_400_000,
55+
),
56+
mockIter(
57+
7,
58+
true,
59+
"Cached resolved dependency graph across restarts",
60+
15_000_000,
61+
),
62+
mockIter(
63+
8,
64+
false,
65+
"Tree-shaking broke runtime dynamic import paths",
66+
12_600_000,
67+
),
68+
mockIter(
69+
9,
70+
true,
71+
"Restored dynamic imports, added explicit entry chunks",
72+
10_800_000,
73+
),
74+
mockIter(
75+
10,
76+
true,
77+
"Inlined critical-path CSS, deferred non-essential styles",
78+
8_400_000,
79+
),
80+
mockIter(
81+
11,
82+
true,
83+
"Switched from full Intl polyfill to locale-on-demand",
84+
6_000_000,
85+
),
86+
mockIter(
87+
12,
88+
true,
89+
"Pre-compiled handlebars templates at build time",
90+
3_600_000,
91+
),
92+
mockIter(
93+
13,
94+
true,
95+
"Moved telemetry init behind requestIdleCallback",
96+
1_800_000,
97+
),
2698
];
2799

28100
const AGENT_MESSAGES: string[] = [
@@ -105,12 +177,15 @@ export class MockOrchestrator extends EventEmitter<OrchestratorEvents> {
105177
}
106178

107179
private scheduleTokenBump(): void {
108-
this.tokenTimer = setTimeout(() => {
109-
this.state.totalInputTokens += randInt(40_000, 180_000);
110-
this.state.totalOutputTokens += randInt(200, 2_000);
111-
this.emit("state", this.getState());
112-
if (this.state.status === "running") this.scheduleTokenBump();
113-
}, randInt(1500, 7000));
180+
this.tokenTimer = setTimeout(
181+
() => {
182+
this.state.totalInputTokens += randInt(40_000, 180_000);
183+
this.state.totalOutputTokens += randInt(200, 2_000);
184+
this.emit("state", this.getState());
185+
if (this.state.status === "running") this.scheduleTokenBump();
186+
},
187+
randInt(1500, 7000),
188+
);
114189
}
115190

116191
private scheduleNextMessage(): void {

src/renderer.test.ts

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -41,16 +41,12 @@ describe("renderStats", () => {
4141

4242
describe("renderAgentMessage", () => {
4343
it("shows working indicator when no message", () => {
44-
const plain = renderAgentMessage(null, "running")
45-
.map(stripAnsi)
46-
.join("\n");
44+
const plain = renderAgentMessage(null, "running").map(stripAnsi).join("\n");
4745
expect(plain).toContain("working...");
4846
});
4947

5048
it("shows waiting status during backoff", () => {
51-
const plain = renderAgentMessage(null, "waiting")
52-
.map(stripAnsi)
53-
.join("\n");
49+
const plain = renderAgentMessage(null, "waiting").map(stripAnsi).join("\n");
5450
expect(plain).toContain("waiting");
5551
});
5652

@@ -81,9 +77,7 @@ describe("renderMoonStrip", () => {
8177
{ success: true },
8278
{ success: false },
8379
];
84-
const text = renderMoonStrip(iterations, false, Date.now()).join(
85-
"",
86-
);
80+
const text = renderMoonStrip(iterations, false, Date.now()).join("");
8781
expect(text).toContain("\u{1F315}\u{1F315}\u{1F311}");
8882
});
8983

src/renderer.ts

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -198,7 +198,11 @@ function renderSideStarsCells(
198198
if (sideWidth <= 0) return [];
199199
const cells = emptyCells(sideWidth);
200200
for (const star of stars) {
201-
if (star.y !== rowIndex || star.x < xOffset || star.x >= xOffset + sideWidth)
201+
if (
202+
star.y !== rowIndex ||
203+
star.x < xOffset ||
204+
star.x >= xOffset + sideWidth
205+
)
202206
continue;
203207
const localX = star.x - xOffset;
204208
const state = getStarState(star, now);
@@ -227,8 +231,7 @@ export function buildContentCells(
227231
now: number,
228232
): Cell[][] {
229233
const rows: Cell[][] = [];
230-
const isRunning =
231-
state.status === "running" || state.status === "waiting";
234+
const isRunning = state.status === "running" || state.status === "waiting";
232235

233236
rows.push([]);
234237
rows.push(...renderTitleCells());
@@ -413,7 +416,8 @@ export class Renderer {
413416
const topHeight = Math.ceil(remaining / 2) - 1;
414417
const proximityRows = 8;
415418
const shrinkBig = (s: Star, nearContentRow: boolean): Star => {
416-
if (!nearContentRow || s.x < contentStart || s.x >= contentEnd) return s;
419+
if (!nearContentRow || s.x < contentStart || s.x >= contentEnd)
420+
return s;
417421
const star = s.char !== "·" ? { ...s, char: "·" } : s;
418422
return star.rest === "bright" ? { ...star, rest: "dim" } : star;
419423
};
@@ -452,9 +456,7 @@ export class Renderer {
452456
);
453457

454458
if (this.isFirstFrame || resized) {
455-
process.stdout.write(
456-
"\x1b[H" + nextCells.map(rowToString).join("\n"),
457-
);
459+
process.stdout.write("\x1b[H" + nextCells.map(rowToString).join("\n"));
458460
this.isFirstFrame = false;
459461
} else {
460462
const changes = diffFrames(this.prevCells, nextCells);

src/utils/moon.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,12 @@
11
export const MOON_PHASES = [
2-
"🌑", "🌒", "🌓", "🌔", "🌕", "🌖", "🌗", "🌘",
2+
"🌑",
3+
"🌒",
4+
"🌓",
5+
"🌔",
6+
"🌕",
7+
"🌖",
8+
"🌗",
9+
"🌘",
310
] as const;
411

512
export type MoonState = "success" | "fail" | "active";

src/utils/stars.test.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,14 @@ describe("generateStarField", () => {
5252
});
5353

5454
describe("getStarState", () => {
55-
const bright = { x: 0, y: 0, char: "·", phase: 0, period: 10_000, rest: "bright" as const };
55+
const bright = {
56+
x: 0,
57+
y: 0,
58+
char: "·",
59+
phase: 0,
60+
period: 10_000,
61+
rest: "bright" as const,
62+
};
5663

5764
it("returns rest state for most of the cycle", () => {
5865
expect(getStarState(bright, 5000)).toBe("bright");

src/utils/stars.ts

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,17 @@
1-
const STAR_CHARS = ["·", "·", "·", "·", "·", "·", "✧", "⋆", "⋆", "⋆", "°", "°"] as const;
1+
const STAR_CHARS = [
2+
"·",
3+
"·",
4+
"·",
5+
"·",
6+
"·",
7+
"·",
8+
"✧",
9+
"⋆",
10+
"⋆",
11+
"⋆",
12+
"°",
13+
"°",
14+
] as const;
215

316
export interface Star {
417
x: number;
@@ -49,9 +62,7 @@ export function generateStarField(
4962

5063
export function getStarState(star: Star, now: number): StarState {
5164
const t =
52-
((now % star.period) / star.period +
53-
star.phase / (Math.PI * 2)) %
54-
1;
65+
((now % star.period) / star.period + star.phase / (Math.PI * 2)) % 1;
5566
// Outside the blink window → steady state
5667
if (t > 0.05) return star.rest;
5768
// Inside the blink window → shift away from rest

src/utils/wordwrap.ts

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,26 @@
1-
export function wordWrap(text: string, width: number, maxLines?: number): string[] {
1+
export function wordWrap(
2+
text: string,
3+
width: number,
4+
maxLines?: number,
5+
): string[] {
26
if (!text) return [];
37

48
const lines: string[] = [];
59

610
for (const paragraph of text.split("\n")) {
711
const words = paragraph.split(/\s+/).filter(Boolean);
8-
if (words.length === 0) { lines.push(""); continue; }
12+
if (words.length === 0) {
13+
lines.push("");
14+
continue;
15+
}
916
let current = "";
1017

1118
for (const word of words) {
1219
if (word.length > width) {
13-
if (current) { lines.push(current); current = ""; }
20+
if (current) {
21+
lines.push(current);
22+
current = "";
23+
}
1424
for (let i = 0; i < word.length; i += width) {
1525
lines.push(word.slice(i, i + width));
1626
}
@@ -30,9 +40,8 @@ export function wordWrap(text: string, width: number, maxLines?: number): string
3040
if (maxLines && lines.length > maxLines) {
3141
const capped = lines.slice(0, maxLines);
3242
const last = capped[maxLines - 1];
33-
capped[maxLines - 1] = last.length >= width
34-
? last.slice(0, width - 1) + "…"
35-
: last + "…";
43+
capped[maxLines - 1] =
44+
last.length >= width ? last.slice(0, width - 1) + "…" : last + "…";
3645
return capped;
3746
}
3847

0 commit comments

Comments
 (0)