gnhf 0.1.15 → 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 +174 -79
  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
  }
@@ -964,29 +1020,37 @@ function terminateClaudeProcess(child, platform) {
964
1020
  }
965
1021
  child.kill("SIGTERM");
966
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
+ }
967
1038
  var ClaudeAgent = class {
968
1039
  name = "claude";
969
1040
  bin;
1041
+ extraArgs;
970
1042
  platform;
971
1043
  constructor(binOrDeps = {}) {
972
1044
  const deps = typeof binOrDeps === "string" ? { bin: binOrDeps } : binOrDeps;
973
1045
  this.bin = deps.bin ?? "claude";
1046
+ this.extraArgs = deps.extraArgs;
974
1047
  this.platform = deps.platform ?? process.platform;
975
1048
  }
976
1049
  run(prompt, cwd, options) {
977
1050
  const { onUsage, onMessage, signal, logPath } = options ?? {};
978
1051
  return new Promise((resolve, reject) => {
979
1052
  const logStream = logPath ? createWriteStream(logPath) : null;
980
- const child = spawn(this.bin, [
981
- "-p",
982
- prompt,
983
- "--verbose",
984
- "--output-format",
985
- "stream-json",
986
- "--json-schema",
987
- JSON.stringify(AGENT_OUTPUT_SCHEMA),
988
- "--dangerously-skip-permissions"
989
- ], {
1053
+ const child = spawn(this.bin, buildClaudeArgs(prompt, this.extraArgs), {
990
1054
  cwd,
991
1055
  shell: shouldUseWindowsShell$2(this.bin, this.platform),
992
1056
  stdio: [
@@ -1084,14 +1148,31 @@ function terminateCodexProcess(child, platform) {
1084
1148
  }
1085
1149
  child.kill("SIGTERM");
1086
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
+ }
1087
1166
  var CodexAgent = class {
1088
1167
  name = "codex";
1089
1168
  bin;
1169
+ extraArgs;
1090
1170
  platform;
1091
1171
  schemaPath;
1092
1172
  constructor(schemaPath, binOrDeps = {}) {
1093
1173
  const deps = typeof binOrDeps === "string" ? { bin: binOrDeps } : binOrDeps;
1094
1174
  this.bin = deps.bin ?? "codex";
1175
+ this.extraArgs = deps.extraArgs;
1095
1176
  this.platform = deps.platform ?? process.platform;
1096
1177
  this.schemaPath = schemaPath;
1097
1178
  }
@@ -1099,16 +1180,7 @@ var CodexAgent = class {
1099
1180
  const { onUsage, onMessage, signal, logPath } = options ?? {};
1100
1181
  return new Promise((resolve, reject) => {
1101
1182
  const logStream = logPath ? createWriteStream(logPath) : null;
1102
- const child = spawn(this.bin, [
1103
- "exec",
1104
- prompt,
1105
- "--json",
1106
- "--output-schema",
1107
- this.schemaPath,
1108
- "--dangerously-bypass-approvals-and-sandbox",
1109
- "--color",
1110
- "never"
1111
- ], {
1183
+ const child = spawn(this.bin, buildCodexArgs(prompt, this.schemaPath, this.extraArgs), {
1112
1184
  cwd,
1113
1185
  shell: shouldUseWindowsShell$1(this.bin, this.platform),
1114
1186
  stdio: [
@@ -1208,6 +1280,9 @@ function isAgentAbortError(error) {
1208
1280
  function isAbortError$1(error) {
1209
1281
  return error instanceof Error && error.name === "AbortError";
1210
1282
  }
1283
+ function toNonEmptyString(value) {
1284
+ return typeof value === "string" && value.length > 0 ? value : null;
1285
+ }
1211
1286
  function getAvailablePort$1() {
1212
1287
  return new Promise((resolve, reject) => {
1213
1288
  const server = createServer();
@@ -1268,6 +1343,7 @@ function withTimeoutSignal$1(signal, timeoutMs) {
1268
1343
  var OpenCodeAgent = class {
1269
1344
  name = "opencode";
1270
1345
  bin;
1346
+ extraArgs;
1271
1347
  fetchFn;
1272
1348
  getPortFn;
1273
1349
  killProcessFn;
@@ -1277,6 +1353,7 @@ var OpenCodeAgent = class {
1277
1353
  closingPromise = null;
1278
1354
  constructor(deps = {}) {
1279
1355
  this.bin = deps.bin ?? "opencode";
1356
+ this.extraArgs = deps.extraArgs;
1280
1357
  this.fetchFn = deps.fetch ?? fetch;
1281
1358
  this.getPortFn = deps.getPort ?? getAvailablePort$1;
1282
1359
  this.killProcessFn = deps.killProcess ?? process.kill.bind(process);
@@ -1358,6 +1435,7 @@ var OpenCodeAgent = class {
1358
1435
  const detached = !isWindows;
1359
1436
  const child = this.spawnFn(this.bin, [
1360
1437
  "serve",
1438
+ ...this.extraArgs ?? [],
1361
1439
  "--hostname",
1362
1440
  "127.0.0.1",
1363
1441
  "--port",
@@ -1519,51 +1597,6 @@ var OpenCodeAgent = class {
1519
1597
  appendDebugLog("opencode:stream:no-body", { sessionId });
1520
1598
  throw new Error("opencode returned no event stream body");
1521
1599
  }
1522
- const messagePostStartedAt = Date.now();
1523
- appendDebugLog("opencode:message-post:start", {
1524
- sessionId,
1525
- promptLength: prompt.length
1526
- });
1527
- let messageRequestError = null;
1528
- const messageRequest = (async () => {
1529
- try {
1530
- await this.request(server, `/session/${sessionId}/prompt_async`, {
1531
- method: "POST",
1532
- body: {
1533
- role: "user",
1534
- parts: [{
1535
- type: "text",
1536
- text: prompt
1537
- }],
1538
- format: STRUCTURED_OUTPUT_FORMAT
1539
- },
1540
- signal
1541
- });
1542
- appendDebugLog("opencode:message-post:end", {
1543
- sessionId,
1544
- elapsedMs: Date.now() - messagePostStartedAt
1545
- });
1546
- return {
1547
- ok: true,
1548
- body: ""
1549
- };
1550
- } catch (error) {
1551
- messageRequestError = error;
1552
- appendDebugLog("opencode:message-post:error", {
1553
- sessionId,
1554
- elapsedMs: Date.now() - messagePostStartedAt,
1555
- error: serializeError(error),
1556
- serverClosed: server.closed,
1557
- serverStderr: server.stderr.slice(-2048),
1558
- streamTelemetry: buildTelemetry()
1559
- });
1560
- streamAbortController.abort();
1561
- return {
1562
- ok: false,
1563
- error
1564
- };
1565
- }
1566
- })();
1567
1600
  const usage = {
1568
1601
  inputTokens: 0,
1569
1602
  outputTokens: 0,
@@ -1611,6 +1644,51 @@ var OpenCodeAgent = class {
1611
1644
  currentPhase,
1612
1645
  sawSessionIdle: (eventCounts["session.idle"] ?? 0) > 0
1613
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
+ })();
1614
1692
  const STALL_THRESHOLDS_MS = [
1615
1693
  6e4,
1616
1694
  12e4,
@@ -1828,20 +1906,21 @@ var OpenCodeAgent = class {
1828
1906
  usage
1829
1907
  };
1830
1908
  }
1831
- const outputText = lastFinalAnswerText ?? lastText;
1832
- if (!outputText) {
1909
+ const outputText = toNonEmptyString(lastFinalAnswerText) ?? toNonEmptyString(lastText);
1910
+ if (outputText === null) {
1833
1911
  appendDebugLog("opencode:output:missing", {
1834
1912
  sessionId,
1835
1913
  hasStructuredOutput: structuredOutputFromSSE !== null
1836
1914
  });
1837
1915
  throw new Error("opencode returned no text output");
1838
1916
  }
1917
+ const finalOutputText = outputText;
1839
1918
  try {
1840
- const output = JSON.parse(outputText);
1919
+ const output = JSON.parse(finalOutputText);
1841
1920
  appendDebugLog("opencode:output:structured", {
1842
1921
  sessionId,
1843
1922
  source: lastFinalAnswerText ? "final_answer" : "last_text",
1844
- outputTextLength: outputText.length
1923
+ outputTextLength: finalOutputText.length
1845
1924
  });
1846
1925
  return {
1847
1926
  output,
@@ -1850,8 +1929,8 @@ var OpenCodeAgent = class {
1850
1929
  } catch (error) {
1851
1930
  appendDebugLog("opencode:output:parse-error", {
1852
1931
  sessionId,
1853
- outputTextLength: outputText.length,
1854
- outputTextSample: outputText.slice(0, 512),
1932
+ outputTextLength: finalOutputText.length,
1933
+ outputTextSample: finalOutputText.slice(0, 512),
1855
1934
  error: serializeError(error)
1856
1935
  });
1857
1936
  throw new Error(`Failed to parse opencode output: ${error instanceof Error ? error.message : String(error)}`);
@@ -2057,6 +2136,7 @@ async function delay(ms, signal) {
2057
2136
  var RovoDevAgent = class {
2058
2137
  name = "rovodev";
2059
2138
  bin;
2139
+ extraArgs;
2060
2140
  schemaPath;
2061
2141
  fetchFn;
2062
2142
  getPortFn;
@@ -2067,6 +2147,7 @@ var RovoDevAgent = class {
2067
2147
  closingPromise = null;
2068
2148
  constructor(schemaPath, deps = {}) {
2069
2149
  this.bin = deps.bin ?? "acli";
2150
+ this.extraArgs = deps.extraArgs;
2070
2151
  this.schemaPath = schemaPath;
2071
2152
  this.fetchFn = deps.fetch ?? fetch;
2072
2153
  this.getPortFn = deps.getPort ?? getAvailablePort;
@@ -2147,6 +2228,7 @@ var RovoDevAgent = class {
2147
2228
  const child = this.spawnFn(this.bin, [
2148
2229
  "rovodev",
2149
2230
  "serve",
2231
+ ...this.extraArgs ?? [],
2150
2232
  "--disable-session-token",
2151
2233
  String(port)
2152
2234
  ], {
@@ -2571,12 +2653,24 @@ function withTimeoutSignal(signal, timeoutMs) {
2571
2653
  }
2572
2654
  //#endregion
2573
2655
  //#region src/core/agents/factory.ts
2574
- function createAgent(name, runInfo, pathOverride) {
2656
+ function createAgent(name, runInfo, pathOverride, agentArgsOverride) {
2575
2657
  switch (name) {
2576
- case "claude": return new ClaudeAgent(pathOverride);
2577
- case "codex": return new CodexAgent(runInfo.schemaPath, pathOverride);
2578
- case "opencode": return new OpenCodeAgent({ bin: pathOverride });
2579
- 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
+ });
2580
2674
  }
2581
2675
  }
2582
2676
  //#endregion
@@ -2856,7 +2950,7 @@ var Orchestrator = class extends EventEmitter {
2856
2950
  };
2857
2951
  return {
2858
2952
  type: "completed",
2859
- 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))
2860
2954
  };
2861
2955
  } catch (err) {
2862
2956
  const elapsedMs = Date.now() - agentStartedAt;
@@ -3912,11 +4006,12 @@ program.name("gnhf").description("Before I go to bed, I tell my agents: good nig
3912
4006
  maxIterations: options.maxIterations,
3913
4007
  maxTokens: options.maxTokens,
3914
4008
  preventSleep: config.preventSleep,
4009
+ agentArgsOverride: config.agentArgsOverride?.[config.agent],
3915
4010
  platform: process$1.platform,
3916
4011
  nodeVersion: process$1.version,
3917
4012
  gnhfVersion: packageVersion
3918
4013
  });
3919
- 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, {
3920
4015
  maxIterations: options.maxIterations,
3921
4016
  maxTokens: options.maxTokens
3922
4017
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "gnhf",
3
- "version": "0.1.15",
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"