gnhf 0.1.14 → 0.1.16

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 +15 -0
  2. package/dist/cli.mjs +190 -84
  3. package/package.json +2 -1
package/README.md CHANGED
@@ -165,6 +165,15 @@ agent: claude
165
165
  # claude: /path/to/custom-claude
166
166
  # codex: /path/to/custom-codex
167
167
 
168
+ # Per-agent CLI arg overrides (optional)
169
+ # agentArgsOverride:
170
+ # codex:
171
+ # - -m
172
+ # - gpt-5.4
173
+ # - -c
174
+ # - model_reasoning_effort="high"
175
+ # - --full-auto
176
+
168
177
  # Abort after this many consecutive failures
169
178
  maxConsecutiveFailures: 3
170
179
 
@@ -177,6 +186,12 @@ If the file does not exist yet, `gnhf` creates it on first run using the resolve
177
186
  CLI flags override config file values. `--prevent-sleep` accepts `on`/`off` as well as `true`/`false`; the config file always uses a boolean.
178
187
  The iteration and token caps are runtime-only flags and are not persisted in `config.yml`.
179
188
 
189
+ `agentArgsOverride.<name>` lets you pass through extra CLI flags for any supported agent.
190
+
191
+ - Use it for agent-specific options like models, profiles, or reasoning settings without adding a dedicated `gnhf` config field for each one.
192
+ - For `codex` and `claude`, `gnhf` adds its usual non-interactive permission default only when you do not provide your own permission or execution-mode flag. If you set one explicitly, `gnhf` treats that as user-managed and does not add its default on top.
193
+ - Flags that `gnhf` manages itself for a given agent, such as output-shaping or local-server startup flags, are rejected during config loading so you get a clear error instead of duplicate-argument ambiguity.
194
+
180
195
  ### Custom Agent Paths
181
196
 
182
197
  Use `agentPathOverride` to point any agent at a custom binary — useful for wrappers like Claude Code Switch or custom Codex builds that accept the same flags and arguments as the original:
package/dist/cli.mjs CHANGED
@@ -20,6 +20,7 @@ const AGENT_NAMES = [
20
20
  const DEFAULT_CONFIG = {
21
21
  agent: "claude",
22
22
  agentPathOverride: {},
23
+ agentArgsOverride: {},
23
24
  maxConsecutiveFailures: 3,
24
25
  preventSleep: true
25
26
  };
@@ -32,6 +33,14 @@ function normalizePreventSleep(value) {
32
33
  if (value === "on") return true;
33
34
  if (value === "off") return false;
34
35
  }
36
+ function isReservedAgentArg(agent, arg) {
37
+ switch (agent) {
38
+ case "claude": return arg === "-p" || arg === "--print" || arg === "--verbose" || arg === "--output-format" || arg.startsWith("--output-format=") || arg === "--json-schema" || arg.startsWith("--json-schema=");
39
+ case "codex": return arg === "exec" || arg === "--json" || arg === "--output-schema" || arg.startsWith("--output-schema=") || arg === "--color" || arg.startsWith("--color=");
40
+ case "opencode": return arg === "serve" || arg === "--hostname" || arg.startsWith("--hostname=") || arg === "--port" || arg.startsWith("--port=") || arg === "--print-logs";
41
+ case "rovodev": return arg === "rovodev" || arg === "serve" || arg === "--disable-session-token";
42
+ }
43
+ }
35
44
  /**
36
45
  * Resolve a user-supplied path against the config directory (~/.gnhf).
37
46
  * Expands leading `~` or `~/` to the home directory, then resolves relative
@@ -60,6 +69,29 @@ function normalizeAgentPathOverride(value, configDir) {
60
69
  }
61
70
  return result;
62
71
  }
72
+ function normalizeAgentExtraArgs(value, label, agent) {
73
+ if (value === void 0 || value === null) return void 0;
74
+ if (!Array.isArray(value)) throw new InvalidConfigError(`Invalid config value for ${label}: expected an array of strings`);
75
+ return value.map((entry, index) => {
76
+ if (typeof entry !== "string") throw new InvalidConfigError(`Invalid config value for ${label}[${index}]: expected a string`);
77
+ const trimmed = entry.trim();
78
+ if (trimmed === "") throw new InvalidConfigError(`Invalid config value for ${label}[${index}]: expected a non-empty string`);
79
+ if (isReservedAgentArg(agent, trimmed)) throw new InvalidConfigError(`Invalid config value for ${label}[${index}]: "${trimmed}" is managed by gnhf and cannot be overridden`);
80
+ return trimmed;
81
+ });
82
+ }
83
+ function normalizeAgentArgsOverride(value) {
84
+ if (value === void 0 || value === null) return void 0;
85
+ if (typeof value !== "object" || Array.isArray(value)) throw new InvalidConfigError(`Invalid config value for agentArgsOverride: expected an object`);
86
+ const validNames = new Set(AGENT_NAMES);
87
+ const result = {};
88
+ for (const [key, rawConfig] of Object.entries(value)) {
89
+ if (!validNames.has(key)) throw new InvalidConfigError(`Invalid agent name in agentArgsOverride: "${key}". Use "claude", "codex", "rovodev", or "opencode".`);
90
+ const args = normalizeAgentExtraArgs(rawConfig, `agentArgsOverride.${key}`, key);
91
+ if (args !== void 0) result[key] = args;
92
+ }
93
+ return Object.keys(result).length === 0 ? void 0 : result;
94
+ }
63
95
  function normalizeConfig(config, configDir) {
64
96
  const normalized = { ...config };
65
97
  const hasPreventSleep = Object.prototype.hasOwnProperty.call(config, "preventSleep");
@@ -74,6 +106,11 @@ function normalizeConfig(config, configDir) {
74
106
  if (agentPathOverride === void 0) delete normalized.agentPathOverride;
75
107
  else normalized.agentPathOverride = agentPathOverride;
76
108
  } else delete normalized.agentPathOverride;
109
+ if (Object.prototype.hasOwnProperty.call(config, "agentArgsOverride")) {
110
+ const agentArgsOverride = normalizeAgentArgsOverride(config.agentArgsOverride);
111
+ if (agentArgsOverride === void 0) delete normalized.agentArgsOverride;
112
+ else normalized.agentArgsOverride = agentArgsOverride;
113
+ } else delete normalized.agentArgsOverride;
77
114
  return normalized;
78
115
  }
79
116
  function isMissingConfigError(error) {
@@ -92,8 +129,17 @@ function serializeAgentPathOverride(agentPathOverride) {
92
129
  sortKeys: false
93
130
  }).trimEnd();
94
131
  }
132
+ function serializeAgentArgsOverride(agentArgsOverride) {
133
+ if (Object.keys(agentArgsOverride).length === 0) return "";
134
+ return yaml.dump({ agentArgsOverride }, {
135
+ lineWidth: -1,
136
+ noRefs: true,
137
+ sortKeys: false
138
+ }).trimEnd();
139
+ }
95
140
  function serializeConfig(config) {
96
141
  const agentPathOverrideSection = serializeAgentPathOverride(config.agentPathOverride);
142
+ const agentArgsOverrideSection = serializeAgentArgsOverride(config.agentArgsOverride);
97
143
  const lines = [
98
144
  "# Agent to use by default",
99
145
  `agent: ${config.agent}`,
@@ -104,9 +150,19 @@ function serializeConfig(config) {
104
150
  "# Note: rovodev overrides must point to an acli-compatible binary.",
105
151
  "# agentPathOverride:",
106
152
  "# claude: /path/to/custom-claude",
107
- "# codex: /path/to/custom-codex"
153
+ "# codex: /path/to/custom-codex",
154
+ "",
155
+ "# Per-agent CLI arg overrides (optional)",
156
+ "# agentArgsOverride:",
157
+ "# codex:",
158
+ "# - -m",
159
+ "# - gpt-5.4",
160
+ "# - -c",
161
+ "# - model_reasoning_effort=\"high\"",
162
+ "# - --full-auto"
108
163
  ];
109
164
  if (agentPathOverrideSection) lines.push(...agentPathOverrideSection.split("\n"));
165
+ if (agentArgsOverrideSection) lines.push(...agentArgsOverrideSection.split("\n"));
110
166
  lines.push("", "# Abort after this many consecutive failures", `maxConsecutiveFailures: ${config.maxConsecutiveFailures}`, "", "# Prevent the machine from sleeping during a run", `preventSleep: ${config.preventSleep}`, "");
111
167
  return lines.join("\n");
112
168
  }
@@ -433,6 +489,17 @@ function getLastIterationNumber(runInfo) {
433
489
  }
434
490
  return max;
435
491
  }
492
+ function toStringArray(value) {
493
+ if (Array.isArray(value)) return value.filter((v) => typeof v === "string");
494
+ if (typeof value === "string") {
495
+ try {
496
+ const parsed = JSON.parse(value);
497
+ if (Array.isArray(parsed)) return parsed.filter((v) => typeof v === "string");
498
+ } catch {}
499
+ return [value];
500
+ }
501
+ return [];
502
+ }
436
503
  function formatListSection(title, items) {
437
504
  if (items.length === 0) return "";
438
505
  return `**${title}:**\n${items.map((item) => `- ${item}`).join("\n")}\n`;
@@ -953,29 +1020,37 @@ function terminateClaudeProcess(child, platform) {
953
1020
  }
954
1021
  child.kill("SIGTERM");
955
1022
  }
1023
+ function buildClaudeArgs(prompt, extraArgs) {
1024
+ const userArgs = extraArgs ?? [];
1025
+ const userSpecifiedPermissionMode = userArgs.some((arg) => arg === "--dangerously-skip-permissions" || arg === "--permission-mode" || arg.startsWith("--permission-mode=") || arg === "--permission-prompt-tool" || arg.startsWith("--permission-prompt-tool="));
1026
+ return [
1027
+ ...userArgs,
1028
+ "-p",
1029
+ prompt,
1030
+ "--verbose",
1031
+ "--output-format",
1032
+ "stream-json",
1033
+ "--json-schema",
1034
+ JSON.stringify(AGENT_OUTPUT_SCHEMA),
1035
+ ...userSpecifiedPermissionMode ? [] : ["--dangerously-skip-permissions"]
1036
+ ];
1037
+ }
956
1038
  var ClaudeAgent = class {
957
1039
  name = "claude";
958
1040
  bin;
1041
+ extraArgs;
959
1042
  platform;
960
1043
  constructor(binOrDeps = {}) {
961
1044
  const deps = typeof binOrDeps === "string" ? { bin: binOrDeps } : binOrDeps;
962
1045
  this.bin = deps.bin ?? "claude";
1046
+ this.extraArgs = deps.extraArgs;
963
1047
  this.platform = deps.platform ?? process.platform;
964
1048
  }
965
1049
  run(prompt, cwd, options) {
966
1050
  const { onUsage, onMessage, signal, logPath } = options ?? {};
967
1051
  return new Promise((resolve, reject) => {
968
1052
  const logStream = logPath ? createWriteStream(logPath) : null;
969
- const child = spawn(this.bin, [
970
- "-p",
971
- prompt,
972
- "--verbose",
973
- "--output-format",
974
- "stream-json",
975
- "--json-schema",
976
- JSON.stringify(AGENT_OUTPUT_SCHEMA),
977
- "--dangerously-skip-permissions"
978
- ], {
1053
+ const child = spawn(this.bin, buildClaudeArgs(prompt, this.extraArgs), {
979
1054
  cwd,
980
1055
  shell: shouldUseWindowsShell$2(this.bin, this.platform),
981
1056
  stdio: [
@@ -1073,14 +1148,31 @@ function terminateCodexProcess(child, platform) {
1073
1148
  }
1074
1149
  child.kill("SIGTERM");
1075
1150
  }
1151
+ function buildCodexArgs(prompt, schemaPath, extraArgs) {
1152
+ const userArgs = extraArgs ?? [];
1153
+ const userSpecifiedExecutionMode = userArgs.some((arg) => arg === "--full-auto" || arg === "--dangerously-bypass-approvals-and-sandbox" || arg === "--sandbox" || arg.startsWith("--sandbox=") || arg === "-s" || arg === "--ask-for-approval" || arg.startsWith("--ask-for-approval=") || arg === "-a");
1154
+ return [
1155
+ "exec",
1156
+ ...userArgs,
1157
+ prompt,
1158
+ "--json",
1159
+ "--output-schema",
1160
+ schemaPath,
1161
+ ...userSpecifiedExecutionMode ? [] : ["--dangerously-bypass-approvals-and-sandbox"],
1162
+ "--color",
1163
+ "never"
1164
+ ];
1165
+ }
1076
1166
  var CodexAgent = class {
1077
1167
  name = "codex";
1078
1168
  bin;
1169
+ extraArgs;
1079
1170
  platform;
1080
1171
  schemaPath;
1081
1172
  constructor(schemaPath, binOrDeps = {}) {
1082
1173
  const deps = typeof binOrDeps === "string" ? { bin: binOrDeps } : binOrDeps;
1083
1174
  this.bin = deps.bin ?? "codex";
1175
+ this.extraArgs = deps.extraArgs;
1084
1176
  this.platform = deps.platform ?? process.platform;
1085
1177
  this.schemaPath = schemaPath;
1086
1178
  }
@@ -1088,16 +1180,7 @@ var CodexAgent = class {
1088
1180
  const { onUsage, onMessage, signal, logPath } = options ?? {};
1089
1181
  return new Promise((resolve, reject) => {
1090
1182
  const logStream = logPath ? createWriteStream(logPath) : null;
1091
- const child = spawn(this.bin, [
1092
- "exec",
1093
- prompt,
1094
- "--json",
1095
- "--output-schema",
1096
- this.schemaPath,
1097
- "--dangerously-bypass-approvals-and-sandbox",
1098
- "--color",
1099
- "never"
1100
- ], {
1183
+ const child = spawn(this.bin, buildCodexArgs(prompt, this.schemaPath, this.extraArgs), {
1101
1184
  cwd,
1102
1185
  shell: shouldUseWindowsShell$1(this.bin, this.platform),
1103
1186
  stdio: [
@@ -1197,6 +1280,9 @@ function isAgentAbortError(error) {
1197
1280
  function isAbortError$1(error) {
1198
1281
  return error instanceof Error && error.name === "AbortError";
1199
1282
  }
1283
+ function toNonEmptyString(value) {
1284
+ return typeof value === "string" && value.length > 0 ? value : null;
1285
+ }
1200
1286
  function getAvailablePort$1() {
1201
1287
  return new Promise((resolve, reject) => {
1202
1288
  const server = createServer();
@@ -1257,6 +1343,7 @@ function withTimeoutSignal$1(signal, timeoutMs) {
1257
1343
  var OpenCodeAgent = class {
1258
1344
  name = "opencode";
1259
1345
  bin;
1346
+ extraArgs;
1260
1347
  fetchFn;
1261
1348
  getPortFn;
1262
1349
  killProcessFn;
@@ -1266,6 +1353,7 @@ var OpenCodeAgent = class {
1266
1353
  closingPromise = null;
1267
1354
  constructor(deps = {}) {
1268
1355
  this.bin = deps.bin ?? "opencode";
1356
+ this.extraArgs = deps.extraArgs;
1269
1357
  this.fetchFn = deps.fetch ?? fetch;
1270
1358
  this.getPortFn = deps.getPort ?? getAvailablePort$1;
1271
1359
  this.killProcessFn = deps.killProcess ?? process.kill.bind(process);
@@ -1347,6 +1435,7 @@ var OpenCodeAgent = class {
1347
1435
  const detached = !isWindows;
1348
1436
  const child = this.spawnFn(this.bin, [
1349
1437
  "serve",
1438
+ ...this.extraArgs ?? [],
1350
1439
  "--hostname",
1351
1440
  "127.0.0.1",
1352
1441
  "--port",
@@ -1508,51 +1597,6 @@ var OpenCodeAgent = class {
1508
1597
  appendDebugLog("opencode:stream:no-body", { sessionId });
1509
1598
  throw new Error("opencode returned no event stream body");
1510
1599
  }
1511
- const messagePostStartedAt = Date.now();
1512
- appendDebugLog("opencode:message-post:start", {
1513
- sessionId,
1514
- promptLength: prompt.length
1515
- });
1516
- let messageRequestError = null;
1517
- const messageRequest = (async () => {
1518
- try {
1519
- await this.request(server, `/session/${sessionId}/prompt_async`, {
1520
- method: "POST",
1521
- body: {
1522
- role: "user",
1523
- parts: [{
1524
- type: "text",
1525
- text: prompt
1526
- }],
1527
- format: STRUCTURED_OUTPUT_FORMAT
1528
- },
1529
- signal
1530
- });
1531
- appendDebugLog("opencode:message-post:end", {
1532
- sessionId,
1533
- elapsedMs: Date.now() - messagePostStartedAt
1534
- });
1535
- return {
1536
- ok: true,
1537
- body: ""
1538
- };
1539
- } catch (error) {
1540
- messageRequestError = error;
1541
- appendDebugLog("opencode:message-post:error", {
1542
- sessionId,
1543
- elapsedMs: Date.now() - messagePostStartedAt,
1544
- error: serializeError(error),
1545
- serverClosed: server.closed,
1546
- serverStderr: server.stderr.slice(-2048),
1547
- streamTelemetry: buildTelemetry()
1548
- });
1549
- streamAbortController.abort();
1550
- return {
1551
- ok: false,
1552
- error
1553
- };
1554
- }
1555
- })();
1556
1600
  const usage = {
1557
1601
  inputTokens: 0,
1558
1602
  outputTokens: 0,
@@ -1600,6 +1644,51 @@ var OpenCodeAgent = class {
1600
1644
  currentPhase,
1601
1645
  sawSessionIdle: (eventCounts["session.idle"] ?? 0) > 0
1602
1646
  });
1647
+ const messagePostStartedAt = Date.now();
1648
+ appendDebugLog("opencode:message-post:start", {
1649
+ sessionId,
1650
+ promptLength: prompt.length
1651
+ });
1652
+ let messageRequestError = null;
1653
+ const messageRequest = (async () => {
1654
+ try {
1655
+ await this.request(server, `/session/${sessionId}/prompt_async`, {
1656
+ method: "POST",
1657
+ body: {
1658
+ role: "user",
1659
+ parts: [{
1660
+ type: "text",
1661
+ text: prompt
1662
+ }],
1663
+ format: STRUCTURED_OUTPUT_FORMAT
1664
+ },
1665
+ signal
1666
+ });
1667
+ appendDebugLog("opencode:message-post:end", {
1668
+ sessionId,
1669
+ elapsedMs: Date.now() - messagePostStartedAt
1670
+ });
1671
+ return {
1672
+ ok: true,
1673
+ body: ""
1674
+ };
1675
+ } catch (error) {
1676
+ messageRequestError = error;
1677
+ appendDebugLog("opencode:message-post:error", {
1678
+ sessionId,
1679
+ elapsedMs: Date.now() - messagePostStartedAt,
1680
+ error: serializeError(error),
1681
+ serverClosed: server.closed,
1682
+ serverStderr: server.stderr.slice(-2048),
1683
+ streamTelemetry: buildTelemetry()
1684
+ });
1685
+ streamAbortController.abort();
1686
+ return {
1687
+ ok: false,
1688
+ error
1689
+ };
1690
+ }
1691
+ })();
1603
1692
  const STALL_THRESHOLDS_MS = [
1604
1693
  6e4,
1605
1694
  12e4,
@@ -1817,20 +1906,21 @@ var OpenCodeAgent = class {
1817
1906
  usage
1818
1907
  };
1819
1908
  }
1820
- const outputText = lastFinalAnswerText ?? lastText;
1821
- if (!outputText) {
1909
+ const outputText = toNonEmptyString(lastFinalAnswerText) ?? toNonEmptyString(lastText);
1910
+ if (outputText === null) {
1822
1911
  appendDebugLog("opencode:output:missing", {
1823
1912
  sessionId,
1824
1913
  hasStructuredOutput: structuredOutputFromSSE !== null
1825
1914
  });
1826
1915
  throw new Error("opencode returned no text output");
1827
1916
  }
1917
+ const finalOutputText = outputText;
1828
1918
  try {
1829
- const output = JSON.parse(outputText);
1919
+ const output = JSON.parse(finalOutputText);
1830
1920
  appendDebugLog("opencode:output:structured", {
1831
1921
  sessionId,
1832
1922
  source: lastFinalAnswerText ? "final_answer" : "last_text",
1833
- outputTextLength: outputText.length
1923
+ outputTextLength: finalOutputText.length
1834
1924
  });
1835
1925
  return {
1836
1926
  output,
@@ -1839,8 +1929,8 @@ var OpenCodeAgent = class {
1839
1929
  } catch (error) {
1840
1930
  appendDebugLog("opencode:output:parse-error", {
1841
1931
  sessionId,
1842
- outputTextLength: outputText.length,
1843
- outputTextSample: outputText.slice(0, 512),
1932
+ outputTextLength: finalOutputText.length,
1933
+ outputTextSample: finalOutputText.slice(0, 512),
1844
1934
  error: serializeError(error)
1845
1935
  });
1846
1936
  throw new Error(`Failed to parse opencode output: ${error instanceof Error ? error.message : String(error)}`);
@@ -2046,6 +2136,7 @@ async function delay(ms, signal) {
2046
2136
  var RovoDevAgent = class {
2047
2137
  name = "rovodev";
2048
2138
  bin;
2139
+ extraArgs;
2049
2140
  schemaPath;
2050
2141
  fetchFn;
2051
2142
  getPortFn;
@@ -2056,6 +2147,7 @@ var RovoDevAgent = class {
2056
2147
  closingPromise = null;
2057
2148
  constructor(schemaPath, deps = {}) {
2058
2149
  this.bin = deps.bin ?? "acli";
2150
+ this.extraArgs = deps.extraArgs;
2059
2151
  this.schemaPath = schemaPath;
2060
2152
  this.fetchFn = deps.fetch ?? fetch;
2061
2153
  this.getPortFn = deps.getPort ?? getAvailablePort;
@@ -2136,6 +2228,7 @@ var RovoDevAgent = class {
2136
2228
  const child = this.spawnFn(this.bin, [
2137
2229
  "rovodev",
2138
2230
  "serve",
2231
+ ...this.extraArgs ?? [],
2139
2232
  "--disable-session-token",
2140
2233
  String(port)
2141
2234
  ], {
@@ -2560,12 +2653,24 @@ function withTimeoutSignal(signal, timeoutMs) {
2560
2653
  }
2561
2654
  //#endregion
2562
2655
  //#region src/core/agents/factory.ts
2563
- function createAgent(name, runInfo, pathOverride) {
2656
+ function createAgent(name, runInfo, pathOverride, agentArgsOverride) {
2564
2657
  switch (name) {
2565
- case "claude": return new ClaudeAgent(pathOverride);
2566
- case "codex": return new CodexAgent(runInfo.schemaPath, pathOverride);
2567
- case "opencode": return new OpenCodeAgent({ bin: pathOverride });
2568
- case "rovodev": return new RovoDevAgent(runInfo.schemaPath, { bin: pathOverride });
2658
+ case "claude": return new ClaudeAgent({
2659
+ bin: pathOverride,
2660
+ extraArgs: agentArgsOverride
2661
+ });
2662
+ case "codex": return new CodexAgent(runInfo.schemaPath, {
2663
+ bin: pathOverride,
2664
+ extraArgs: agentArgsOverride
2665
+ });
2666
+ case "opencode": return new OpenCodeAgent({
2667
+ bin: pathOverride,
2668
+ extraArgs: agentArgsOverride
2669
+ });
2670
+ case "rovodev": return new RovoDevAgent(runInfo.schemaPath, {
2671
+ bin: pathOverride,
2672
+ extraArgs: agentArgsOverride
2673
+ });
2569
2674
  }
2570
2675
  }
2571
2676
  //#endregion
@@ -2845,7 +2950,7 @@ var Orchestrator = class extends EventEmitter {
2845
2950
  };
2846
2951
  return {
2847
2952
  type: "completed",
2848
- record: this.recordFailure(`[FAIL] ${result.output.summary}`, result.output.summary, result.output.key_learnings)
2953
+ record: this.recordFailure(`[FAIL] ${result.output.summary}`, result.output.summary, toStringArray(result.output.key_learnings))
2849
2954
  };
2850
2955
  } catch (err) {
2851
2956
  const elapsedMs = Date.now() - agentStartedAt;
@@ -2884,7 +2989,7 @@ var Orchestrator = class extends EventEmitter {
2884
2989
  }
2885
2990
  }
2886
2991
  recordSuccess(output) {
2887
- appendNotes(this.runInfo.notesPath, this.state.currentIteration, output.summary, output.key_changes_made, output.key_learnings);
2992
+ appendNotes(this.runInfo.notesPath, this.state.currentIteration, output.summary, toStringArray(output.key_changes_made), toStringArray(output.key_learnings));
2888
2993
  commitAll(`gnhf #${this.state.currentIteration}: ${output.summary}`, this.cwd);
2889
2994
  this.state.commitCount = getBranchCommitCount(this.runInfo.baseCommit, this.cwd);
2890
2995
  this.state.successCount++;
@@ -2893,13 +2998,13 @@ var Orchestrator = class extends EventEmitter {
2893
2998
  number: this.state.currentIteration,
2894
2999
  success: true,
2895
3000
  summary: output.summary,
2896
- keyChanges: output.key_changes_made,
2897
- keyLearnings: output.key_learnings,
3001
+ keyChanges: toStringArray(output.key_changes_made),
3002
+ keyLearnings: toStringArray(output.key_learnings),
2898
3003
  timestamp: /* @__PURE__ */ new Date()
2899
3004
  };
2900
3005
  }
2901
3006
  recordFailure(notesSummary, recordSummary, learnings) {
2902
- appendNotes(this.runInfo.notesPath, this.state.currentIteration, notesSummary, [], learnings);
3007
+ appendNotes(this.runInfo.notesPath, this.state.currentIteration, notesSummary, [], toStringArray(learnings));
2903
3008
  resetHard(this.cwd);
2904
3009
  this.state.failCount++;
2905
3010
  this.state.consecutiveFailures++;
@@ -2908,7 +3013,7 @@ var Orchestrator = class extends EventEmitter {
2908
3013
  success: false,
2909
3014
  summary: recordSummary,
2910
3015
  keyChanges: [],
2911
- keyLearnings: learnings,
3016
+ keyLearnings: toStringArray(learnings),
2912
3017
  timestamp: /* @__PURE__ */ new Date()
2913
3018
  };
2914
3019
  }
@@ -3901,11 +4006,12 @@ program.name("gnhf").description("Before I go to bed, I tell my agents: good nig
3901
4006
  maxIterations: options.maxIterations,
3902
4007
  maxTokens: options.maxTokens,
3903
4008
  preventSleep: config.preventSleep,
4009
+ agentArgsOverride: config.agentArgsOverride?.[config.agent],
3904
4010
  platform: process$1.platform,
3905
4011
  nodeVersion: process$1.version,
3906
4012
  gnhfVersion: packageVersion
3907
4013
  });
3908
- const orchestrator = new Orchestrator(config, createAgent(config.agent, runInfo, config.agentPathOverride[config.agent]), runInfo, prompt, cwd, startIteration, {
4014
+ const orchestrator = new Orchestrator(config, createAgent(config.agent, runInfo, config.agentPathOverride[config.agent], config.agentArgsOverride?.[config.agent]), runInfo, prompt, cwd, startIteration, {
3909
4015
  maxIterations: options.maxIterations,
3910
4016
  maxTokens: options.maxTokens
3911
4017
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnhf",
3
- "version": "0.1.14",
3
+ "version": "0.1.16",
4
4
  "description": "Before I go to bed, I tell my agents: good night, have fun",
5
5
  "type": "module",
6
6
  "bin": {
@@ -13,6 +13,7 @@
13
13
  "lint": "eslint src",
14
14
  "format": "prettier --write src",
15
15
  "format:check": "prettier --check src",
16
+ "typecheck": "tsc -p tsconfig.typecheck.json --noEmit",
16
17
  "test": "npm run build && vitest run",
17
18
  "test:e2e": "npm run build && vitest run test/e2e.test.ts",
18
19
  "test:coverage": "vitest run --coverage --exclude test/e2e.test.ts"