@cruxy/cli 0.28.2 → 0.29.0

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.
@@ -187,7 +187,24 @@ export function runCommand() {
187
187
  logger.info(t.muted(`mcp: server "${d.server}" declared in ${d.root} not loaded (primary-root MCP only this release)`));
188
188
  }
189
189
  }
190
- const session = buildAgentSession(config, apiKey, workspace, Boolean(process.stdin.isTTY), planMode, renderer, checkpoints, sandbox, hooksRunner, mcp.tools);
190
+ // One-shot vs REPL turn ceiling. A one-shot run caps at
191
+ // `agent.maxIterationsOneShot` (default 40): a headless run can't be
192
+ // resumed by a human, so hitting the cap abandons the whole task — it
193
+ // needs headroom above the interactive cap to clear legit long work,
194
+ // while still bounding a runaway (and it now exits non-zero, see below).
195
+ // The REPL keeps `agent.maxIterations` (a soft checkpoint). Keyed on the
196
+ // one-shot MODE, never on TTY, so `cruxy run "task"` behaves the same
197
+ // piped into CI or run in a terminal.
198
+ const sessionConfig = interactive
199
+ ? config
200
+ : {
201
+ ...config,
202
+ agent: {
203
+ ...config.agent,
204
+ maxIterations: config.agent.maxIterationsOneShot,
205
+ },
206
+ };
207
+ const session = buildAgentSession(sessionConfig, apiKey, workspace, Boolean(process.stdin.isTTY), planMode, renderer, checkpoints, sandbox, hooksRunner, mcp.tools);
191
208
  if (interactive) {
192
209
  try {
193
210
  await runInteractive(session, undefined, renderer, checkpoints, hookCommands);
@@ -253,7 +270,9 @@ export function runCommand() {
253
270
  stop: result.stop,
254
271
  iterations: result.iterations,
255
272
  reason: result.stopReason,
256
- maxIterations: config.agent.maxIterations,
273
+ // The effective one-shot cap (agent.maxIterationsOneShot), so the
274
+ // message names the limit the run actually hit.
275
+ maxIterations: sessionConfig.agent.maxIterations,
257
276
  });
258
277
  }
259
278
  });
@@ -26,18 +26,33 @@ export declare const CruxyBackendConfigSchema: z.ZodObject<{
26
26
  gatewayUrl?: string | undefined;
27
27
  }>;
28
28
  export declare const AgentConfigSchema: z.ZodObject<{
29
- /** Hard ceiling on agent loop turns. */
29
+ /**
30
+ * Hard ceiling on agent loop turns per user turn — the interactive default.
31
+ * In the REPL this is a soft checkpoint: hitting it ends the turn and the
32
+ * human continues (or Ctrl-C's a runaway), so it can be generous.
33
+ */
30
34
  maxIterations: z.ZodDefault<z.ZodNumber>;
35
+ /**
36
+ * Turn ceiling for a ONE-SHOT `cruxy run "task"` (non-interactive). Set higher
37
+ * than {@link maxIterations} because a headless run has no human to resume it:
38
+ * hitting the cap abandons the whole task (fail-loud, non-zero exit), so it
39
+ * must clear the legit long tail (real "fix the failing tests" work runs
40
+ * ~10-25 turns) while still bounding a runaway. Not clamped to
41
+ * `maxIterations` — a user may deliberately set it lower or higher.
42
+ */
43
+ maxIterationsOneShot: z.ZodDefault<z.ZodNumber>;
31
44
  /** Skip per-action confirmation prompts. */
32
45
  autoApprove: z.ZodDefault<z.ZodBoolean>;
33
46
  /** Plan mode: propose a plan for approval before executing (C.31, opt-in). */
34
47
  planMode: z.ZodDefault<z.ZodBoolean>;
35
48
  }, "strict", z.ZodTypeAny, {
36
49
  maxIterations: number;
50
+ maxIterationsOneShot: number;
37
51
  autoApprove: boolean;
38
52
  planMode: boolean;
39
53
  }, {
40
54
  maxIterations?: number | undefined;
55
+ maxIterationsOneShot?: number | undefined;
41
56
  autoApprove?: boolean | undefined;
42
57
  planMode?: boolean | undefined;
43
58
  }>;
@@ -979,18 +994,33 @@ export declare const CruxyConfigSchema: z.ZodObject<{
979
994
  gatewayUrl?: string | undefined;
980
995
  }>>;
981
996
  agent: z.ZodDefault<z.ZodObject<{
982
- /** Hard ceiling on agent loop turns. */
997
+ /**
998
+ * Hard ceiling on agent loop turns per user turn — the interactive default.
999
+ * In the REPL this is a soft checkpoint: hitting it ends the turn and the
1000
+ * human continues (or Ctrl-C's a runaway), so it can be generous.
1001
+ */
983
1002
  maxIterations: z.ZodDefault<z.ZodNumber>;
1003
+ /**
1004
+ * Turn ceiling for a ONE-SHOT `cruxy run "task"` (non-interactive). Set higher
1005
+ * than {@link maxIterations} because a headless run has no human to resume it:
1006
+ * hitting the cap abandons the whole task (fail-loud, non-zero exit), so it
1007
+ * must clear the legit long tail (real "fix the failing tests" work runs
1008
+ * ~10-25 turns) while still bounding a runaway. Not clamped to
1009
+ * `maxIterations` — a user may deliberately set it lower or higher.
1010
+ */
1011
+ maxIterationsOneShot: z.ZodDefault<z.ZodNumber>;
984
1012
  /** Skip per-action confirmation prompts. */
985
1013
  autoApprove: z.ZodDefault<z.ZodBoolean>;
986
1014
  /** Plan mode: propose a plan for approval before executing (C.31, opt-in). */
987
1015
  planMode: z.ZodDefault<z.ZodBoolean>;
988
1016
  }, "strict", z.ZodTypeAny, {
989
1017
  maxIterations: number;
1018
+ maxIterationsOneShot: number;
990
1019
  autoApprove: boolean;
991
1020
  planMode: boolean;
992
1021
  }, {
993
1022
  maxIterations?: number | undefined;
1023
+ maxIterationsOneShot?: number | undefined;
994
1024
  autoApprove?: boolean | undefined;
995
1025
  planMode?: boolean | undefined;
996
1026
  }>>;
@@ -1737,6 +1767,7 @@ export declare const CruxyConfigSchema: z.ZodObject<{
1737
1767
  };
1738
1768
  agent: {
1739
1769
  maxIterations: number;
1770
+ maxIterationsOneShot: number;
1740
1771
  autoApprove: boolean;
1741
1772
  planMode: boolean;
1742
1773
  };
@@ -1889,6 +1920,7 @@ export declare const CruxyConfigSchema: z.ZodObject<{
1889
1920
  } | undefined;
1890
1921
  agent?: {
1891
1922
  maxIterations?: number | undefined;
1923
+ maxIterationsOneShot?: number | undefined;
1892
1924
  autoApprove?: boolean | undefined;
1893
1925
  planMode?: boolean | undefined;
1894
1926
  } | undefined;
@@ -26,8 +26,21 @@ export const CruxyBackendConfigSchema = z
26
26
  .strict();
27
27
  export const AgentConfigSchema = z
28
28
  .object({
29
- /** Hard ceiling on agent loop turns. */
29
+ /**
30
+ * Hard ceiling on agent loop turns per user turn — the interactive default.
31
+ * In the REPL this is a soft checkpoint: hitting it ends the turn and the
32
+ * human continues (or Ctrl-C's a runaway), so it can be generous.
33
+ */
30
34
  maxIterations: z.number().int().positive().default(25),
35
+ /**
36
+ * Turn ceiling for a ONE-SHOT `cruxy run "task"` (non-interactive). Set higher
37
+ * than {@link maxIterations} because a headless run has no human to resume it:
38
+ * hitting the cap abandons the whole task (fail-loud, non-zero exit), so it
39
+ * must clear the legit long tail (real "fix the failing tests" work runs
40
+ * ~10-25 turns) while still bounding a runaway. Not clamped to
41
+ * `maxIterations` — a user may deliberately set it lower or higher.
42
+ */
43
+ maxIterationsOneShot: z.number().int().positive().default(40),
31
44
  /** Skip per-action confirmation prompts. */
32
45
  autoApprove: z.boolean().default(false),
33
46
  /** Plan mode: propose a plan for approval before executing (C.31, opt-in). */
@@ -1235,7 +1235,7 @@ export function agentIncomplete(info) {
1235
1235
  cause: `reached the turn limit${cap} after ${turns} without completing. The work so far is shown above.`,
1236
1236
  nextSteps: [
1237
1237
  "review the partial output above, then re-run with a narrower prompt",
1238
- "raise `agent.maxIterations` if the task legitimately needs more turns",
1238
+ "raise `agent.maxIterationsOneShot` (one-shot) or `agent.maxIterations` if the task legitimately needs more turns",
1239
1239
  ],
1240
1240
  meta,
1241
1241
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cruxy/cli",
3
- "version": "0.28.2",
3
+ "version": "0.29.0",
4
4
  "description": "an agentic coding CLI",
5
5
  "type": "module",
6
6
  "bin": {