@pushpalsdev/cli 1.1.5 → 1.1.7

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.
@@ -125,11 +125,31 @@ function isTestPath(path: string): boolean {
125
125
  return /(^tests\/|__tests__\/|\.test\.[cm]?[jt]sx?$|\.spec\.[cm]?[jt]sx?$)/i.test(path);
126
126
  }
127
127
 
128
+ function formatBunTestPathArg(path: string): string {
129
+ const normalized = String(path ?? "").replace(/\\/g, "/").trim();
130
+ if (!normalized) return normalized;
131
+ const pathArg =
132
+ normalized.startsWith("./") ||
133
+ normalized.startsWith("../") ||
134
+ normalized.startsWith("/") ||
135
+ /^[A-Za-z]:\//.test(normalized)
136
+ ? normalized
137
+ : `./${normalized}`;
138
+ return quoteValidationCommandArg(pathArg);
139
+ }
140
+
141
+ function quoteValidationCommandArg(arg: string): string {
142
+ if (!/[\s"\\]/.test(arg)) return arg;
143
+ return `"${arg.replace(/\\/g, "\\\\").replace(/"/g, '\\"')}"`;
144
+ }
145
+
128
146
  function deriveValidationSteps(existing: unknown, conflictPaths: string[]): string[] {
129
147
  const preserved = Array.isArray(existing)
130
148
  ? existing.map((entry) => String(entry ?? "").trim()).filter(Boolean)
131
149
  : [];
132
- const targeted = conflictPaths.filter(isTestPath).map((entry) => `bun test ${entry}`);
150
+ const targeted = conflictPaths
151
+ .filter(isTestPath)
152
+ .map((entry) => `bun test ${formatBunTestPathArg(entry)}`);
133
153
  const merged = dedupeStrings([...targeted, ...preserved], 8);
134
154
  return merged.length > 0 ? merged : ["bun test"];
135
155
  }
@@ -987,7 +987,11 @@ function failNoChangeReviewFixJob(jobId: string, result: WorkerJobResult): Worke
987
987
  ok: false,
988
988
  summary:
989
989
  `Rejected review-fix job ${jobId} produced no code changes; refusing unchanged branch re-review.`,
990
- stderr: [result.stderr, "Apply at least one concrete fix before requesting another review."]
990
+ stderr: [
991
+ result.stderr,
992
+ "Review-fix jobs must make at least one concrete code/test/docs change before requesting another review.",
993
+ "If the reviewer feedback is invalid, commit a narrow explanatory change that documents the decision; unchanged branch re-review is refused.",
994
+ ]
991
995
  .filter(Boolean)
992
996
  .join("\n"),
993
997
  exitCode: typeof result.exitCode === "number" ? result.exitCode : 4,
@@ -159,9 +159,11 @@ quality_publish_gate_enabled = true
159
159
  # Browser/e2e validation commands get a longer built-in floor (10m) because they
160
160
  # may need to start a dev server and run browser automation.
161
161
  quality_validation_step_timeout_ms = 180000
162
- quality_critic_timeout_ms = 45000
162
+ quality_critic_timeout_ms = 90000
163
+ quality_critic_timeout_behavior = "retry_once" # skip | retry_once | block
163
164
  quality_soft_pass_on_exhausted = true
164
165
  quality_critic_min_score = 8.0
166
+ quality_critic_model = "" # Optional faster/smaller model override for the critic.
165
167
  quality_critic_max_diff_chars = 16000
166
168
  quality_critic_max_validation_output_chars = 8000
167
169
  executor_result_prefix = "__PUSHPALS_OH_RESULT__ "
@@ -75,9 +75,11 @@ quality_publish_gate_enabled = true
75
75
  # Browser/e2e validation commands get a longer built-in floor (10m) because they
76
76
  # may need to start a dev server and run browser automation.
77
77
  quality_validation_step_timeout_ms = 180000
78
- quality_critic_timeout_ms = 45000
78
+ quality_critic_timeout_ms = 90000
79
+ quality_critic_timeout_behavior = "retry_once" # skip | retry_once | block
79
80
  quality_soft_pass_on_exhausted = true
80
81
  quality_critic_min_score = 8.0
82
+ quality_critic_model = "" # Optional faster/smaller model override for the critic.
81
83
  quality_critic_max_diff_chars = 16000
82
84
  quality_critic_max_validation_output_chars = 8000
83
85
  executor_result_prefix = "__PUSHPALS_OH_RESULT__ "
@@ -20,7 +20,8 @@ const DEFAULT_WORKERPALS_OUTPUT_MAX_CHARS = 192 * 1024;
20
20
  const DEFAULT_WORKERPALS_OUTPUT_MAX_LINES = 600;
21
21
  const DEFAULT_WORKERPALS_OUTPUT_MAX_HEAD_LINES = 120;
22
22
  const DEFAULT_WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS = 180_000;
23
- const DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS = 45_000;
23
+ const DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS = 90_000;
24
+ const DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_BEHAVIOR = "retry_once";
24
25
  const DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_DIFF_CHARS = 16_000;
25
26
  const DEFAULT_WORKERPALS_QUALITY_CRITIC_MAX_VALIDATION_OUTPUT_CHARS = 8_000;
26
27
  export const DEFAULT_WORKERPALS_EXECUTOR = "openai_codex";
@@ -217,8 +218,10 @@ export interface PushPalsConfig {
217
218
  qualityPublishGateEnabled: boolean;
218
219
  qualityValidationStepTimeoutMs: number;
219
220
  qualityCriticTimeoutMs: number;
221
+ qualityCriticTimeoutBehavior: "skip" | "retry_once" | "block";
220
222
  qualitySoftPassOnExhausted: boolean;
221
223
  qualityCriticMinScore: number;
224
+ qualityCriticModel: string;
222
225
  qualityCriticMaxDiffChars: number;
223
226
  qualityCriticMaxValidationOutputChars: number;
224
227
  executorResultPrefix: string;
@@ -365,6 +368,17 @@ function asString(value: unknown, fallback: string): string {
365
368
  return fallback;
366
369
  }
367
370
 
371
+ function asQualityCriticTimeoutBehavior(value: unknown): "skip" | "retry_once" | "block" {
372
+ const normalized = String(value ?? "")
373
+ .trim()
374
+ .toLowerCase()
375
+ .replace(/-/g, "_");
376
+ if (normalized === "skip" || normalized === "retry_once" || normalized === "block") {
377
+ return normalized;
378
+ }
379
+ return DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_BEHAVIOR as "skip" | "retry_once" | "block";
380
+ }
381
+
368
382
  function asBoolean(value: unknown, fallback: boolean): boolean {
369
383
  if (typeof value === "boolean") return value;
370
384
  if (typeof value === "string") {
@@ -1003,6 +1017,10 @@ export function loadPushPalsConfig(options: LoadOptions = {}): PushPalsConfig {
1003
1017
  DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS,
1004
1018
  ),
1005
1019
  );
1020
+ const workerQualityCriticTimeoutBehavior = asQualityCriticTimeoutBehavior(
1021
+ process.env.WORKERPALS_QUALITY_CRITIC_TIMEOUT_BEHAVIOR ??
1022
+ workerNode.quality_critic_timeout_behavior,
1023
+ );
1006
1024
  const workerQualitySoftPassOnExhausted =
1007
1025
  parseBoolEnv("WORKERPALS_QUALITY_SOFT_PASS_ON_EXHAUSTED") ??
1008
1026
  asBoolean(workerNode.quality_soft_pass_on_exhausted, true);
@@ -1032,6 +1050,11 @@ export function loadPushPalsConfig(options: LoadOptions = {}): PushPalsConfig {
1032
1050
  if (!Number.isFinite(parsed)) return DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE;
1033
1051
  return Math.max(0, Math.min(10, parsed));
1034
1052
  })();
1053
+ const workerQualityCriticModel = firstNonEmpty(
1054
+ process.env.WORKERPALS_QUALITY_CRITIC_MODEL,
1055
+ asString(workerNode.quality_critic_model, ""),
1056
+ "",
1057
+ );
1035
1058
  const workerQualityCriticMaxDiffChars = Math.max(
1036
1059
  256,
1037
1060
  Math.min(
@@ -2067,8 +2090,10 @@ export function loadPushPalsConfig(options: LoadOptions = {}): PushPalsConfig {
2067
2090
  qualityPublishGateEnabled: workerQualityPublishGateEnabled,
2068
2091
  qualityValidationStepTimeoutMs: workerQualityValidationStepTimeoutMs,
2069
2092
  qualityCriticTimeoutMs: workerQualityCriticTimeoutMs,
2093
+ qualityCriticTimeoutBehavior: workerQualityCriticTimeoutBehavior,
2070
2094
  qualitySoftPassOnExhausted: workerQualitySoftPassOnExhausted,
2071
2095
  qualityCriticMinScore: workerQualityCriticMinScore,
2096
+ qualityCriticModel: workerQualityCriticModel,
2072
2097
  qualityCriticMaxDiffChars: workerQualityCriticMaxDiffChars,
2073
2098
  qualityCriticMaxValidationOutputChars: workerQualityCriticMaxValidationOutputChars,
2074
2099
  executorResultPrefix: workerExecutorResultPrefix,