@pushpalsdev/cli 1.0.84 → 1.0.86

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.
@@ -450,7 +450,7 @@ var DEFAULT_CONFIG_DIR = "configs";
450
450
  var TRUTHY2 = new Set(["1", "true", "yes", "on"]);
451
451
  var FALSY2 = new Set(["0", "false", "no", "off"]);
452
452
  var DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE = 8;
453
- var DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS = 1;
453
+ var DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS = 3;
454
454
  var DEFAULT_WORKERPALS_FILE_MODIFYING_JOBS = ["task.execute"];
455
455
  var DEFAULT_WORKERPALS_OUTPUT_MAX_CHARS = 192 * 1024;
456
456
  var DEFAULT_WORKERPALS_OUTPUT_MAX_LINES = 600;
@@ -817,6 +817,7 @@ function loadPushPalsConfig(options = {}) {
817
817
  const workerOpenAICodexPython = firstNonEmpty(process.env.PUSHPALS_OPENAI_CODEX_PYTHON, asString(workerNode.openai_codex_python, "python"), "python");
818
818
  const workerOpenAICodexTimeoutMs = Math.max(1e4, asInt(workerNode.openai_codex_timeout_ms, 7200000));
819
819
  const workerQualityMaxAutoRevisions = Math.max(0, Math.min(10, asInt(parseIntEnv("WORKERPALS_QUALITY_MAX_AUTO_REVISIONS") ?? workerNode.quality_max_auto_revisions, DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS)));
820
+ const workerQualityValidationMaxAutoRevisions = Math.max(0, Math.min(10, asInt(parseIntEnv("WORKERPALS_QUALITY_VALIDATION_MAX_AUTO_REVISIONS") ?? workerNode.quality_validation_max_auto_revisions, DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS)));
820
821
  const workerFileModifyingJobs = (() => {
821
822
  const envRaw = firstNonEmpty(process.env.WORKERPALS_FILE_MODIFYING_JOBS);
822
823
  const parsed = envRaw ? envRaw.split(",").map((entry) => entry.trim()).filter(Boolean) : asStringArray(workerNode.file_modifying_jobs);
@@ -829,6 +830,10 @@ function loadPushPalsConfig(options = {}) {
829
830
  const workerQualityValidationStepTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS") ?? workerNode.quality_validation_step_timeout_ms, DEFAULT_WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS));
830
831
  const workerQualityCriticTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS") ?? workerNode.quality_critic_timeout_ms, DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS));
831
832
  const workerQualitySoftPassOnExhausted = parseBoolEnv("WORKERPALS_QUALITY_SOFT_PASS_ON_EXHAUSTED") ?? asBoolean(workerNode.quality_soft_pass_on_exhausted, true);
833
+ const workerQualityScopeGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_SCOPE_GATE_ENABLED") ?? asBoolean(workerNode.quality_scope_gate_enabled, true);
834
+ const workerQualityValidationGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_VALIDATION_GATE_ENABLED") ?? asBoolean(workerNode.quality_validation_gate_enabled, true);
835
+ const workerQualityCriticGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_CRITIC_GATE_ENABLED") ?? asBoolean(workerNode.quality_critic_gate_enabled, true);
836
+ const workerQualityPublishGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_PUBLISH_GATE_ENABLED") ?? asBoolean(workerNode.quality_publish_gate_enabled, true);
832
837
  const workerQualityCriticMinScore = (() => {
833
838
  const configThresholdRaw = workerNode.quality_critic_min_score == null ? "" : String(workerNode.quality_critic_min_score);
834
839
  const raw = firstNonEmpty(process.env.WORKERPALS_QUALITY_CRITIC_MIN_SCORE, configThresholdRaw, String(DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE));
@@ -1119,6 +1124,11 @@ function loadPushPalsConfig(options = {}) {
1119
1124
  outputMaxLines: workerOutputMaxLines,
1120
1125
  outputMaxHeadLines: workerOutputMaxHeadLines,
1121
1126
  qualityMaxAutoRevisions: workerQualityMaxAutoRevisions,
1127
+ qualityValidationMaxAutoRevisions: workerQualityValidationMaxAutoRevisions,
1128
+ qualityScopeGateEnabled: workerQualityScopeGateEnabled,
1129
+ qualityValidationGateEnabled: workerQualityValidationGateEnabled,
1130
+ qualityCriticGateEnabled: workerQualityCriticGateEnabled,
1131
+ qualityPublishGateEnabled: workerQualityPublishGateEnabled,
1122
1132
  qualityValidationStepTimeoutMs: workerQualityValidationStepTimeoutMs,
1123
1133
  qualityCriticTimeoutMs: workerQualityCriticTimeoutMs,
1124
1134
  qualitySoftPassOnExhausted: workerQualitySoftPassOnExhausted,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pushpalsdev/cli",
3
- "version": "1.0.84",
3
+ "version": "1.0.86",
4
4
  "description": "PushPals terminal CLI for LocalBuddy -> RemoteBuddy orchestration",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -150,7 +150,14 @@ file_modifying_jobs = ["task.execute"]
150
150
  output_max_chars = 196608
151
151
  output_max_lines = 600
152
152
  output_max_head_lines = 120
153
- quality_max_auto_revisions = 1
153
+ quality_max_auto_revisions = 3
154
+ quality_validation_max_auto_revisions = 3
155
+ quality_scope_gate_enabled = true
156
+ quality_validation_gate_enabled = true
157
+ quality_critic_gate_enabled = true
158
+ quality_publish_gate_enabled = true
159
+ # Browser/e2e validation commands get a longer built-in floor (10m) because they
160
+ # may need to start a dev server and run browser automation.
154
161
  quality_validation_step_timeout_ms = 180000
155
162
  quality_critic_timeout_ms = 45000
156
163
  quality_soft_pass_on_exhausted = true
@@ -66,7 +66,14 @@ file_modifying_jobs = ["task.execute"]
66
66
  output_max_chars = 196608
67
67
  output_max_lines = 600
68
68
  output_max_head_lines = 120
69
- quality_max_auto_revisions = 1
69
+ quality_max_auto_revisions = 3
70
+ quality_validation_max_auto_revisions = 3
71
+ quality_scope_gate_enabled = true
72
+ quality_validation_gate_enabled = true
73
+ quality_critic_gate_enabled = true
74
+ quality_publish_gate_enabled = true
75
+ # Browser/e2e validation commands get a longer built-in floor (10m) because they
76
+ # may need to start a dev server and run browser automation.
70
77
  quality_validation_step_timeout_ms = 180000
71
78
  quality_critic_timeout_ms = 45000
72
79
  quality_soft_pass_on_exhausted = true
@@ -57,6 +57,8 @@ Constraints:
57
57
  - `feature_hypotheses` may contain any suitable product/engineering features; keep each item concise and actionable.
58
58
  - target_paths must be literal repo-relative paths.
59
59
  - write_globs must be repo-relative globs.
60
+ - Choose target_paths that own the behavior being improved, not thin route wrappers, re-export files, or shell components, unless the requested change is explicitly at that wrapper boundary.
61
+ - For UI/game/product-surface objectives, prefer files that render or compute the relevant state directly; use wrapper files only for navigation, mounting, or screen-level chrome work.
60
62
  - do not invent evidence ids.
61
63
  - If all signals are low/noisy, it is valid to return zero candidates.
62
64
  - Treat a low `sig_queue_health` value as maintenance-window evidence for safe proactive work, not only incident response.
@@ -734,7 +734,7 @@ var DEFAULT_CONFIG_DIR = "configs";
734
734
  var TRUTHY = new Set(["1", "true", "yes", "on"]);
735
735
  var FALSY = new Set(["0", "false", "no", "off"]);
736
736
  var DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE = 8;
737
- var DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS = 1;
737
+ var DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS = 3;
738
738
  var DEFAULT_WORKERPALS_FILE_MODIFYING_JOBS = ["task.execute"];
739
739
  var DEFAULT_WORKERPALS_OUTPUT_MAX_CHARS = 192 * 1024;
740
740
  var DEFAULT_WORKERPALS_OUTPUT_MAX_LINES = 600;
@@ -1103,6 +1103,7 @@ function loadPushPalsConfig(options = {}) {
1103
1103
  const workerOpenAICodexPython = firstNonEmpty(process.env.PUSHPALS_OPENAI_CODEX_PYTHON, asString(workerNode.openai_codex_python, "python"), "python");
1104
1104
  const workerOpenAICodexTimeoutMs = Math.max(1e4, asInt(workerNode.openai_codex_timeout_ms, 7200000));
1105
1105
  const workerQualityMaxAutoRevisions = Math.max(0, Math.min(10, asInt(parseIntEnv("WORKERPALS_QUALITY_MAX_AUTO_REVISIONS") ?? workerNode.quality_max_auto_revisions, DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS)));
1106
+ const workerQualityValidationMaxAutoRevisions = Math.max(0, Math.min(10, asInt(parseIntEnv("WORKERPALS_QUALITY_VALIDATION_MAX_AUTO_REVISIONS") ?? workerNode.quality_validation_max_auto_revisions, DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS)));
1106
1107
  const workerFileModifyingJobs = (() => {
1107
1108
  const envRaw = firstNonEmpty(process.env.WORKERPALS_FILE_MODIFYING_JOBS);
1108
1109
  const parsed = envRaw ? envRaw.split(",").map((entry) => entry.trim()).filter(Boolean) : asStringArray(workerNode.file_modifying_jobs);
@@ -1115,6 +1116,10 @@ function loadPushPalsConfig(options = {}) {
1115
1116
  const workerQualityValidationStepTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS") ?? workerNode.quality_validation_step_timeout_ms, DEFAULT_WORKERPALS_QUALITY_VALIDATION_STEP_TIMEOUT_MS));
1116
1117
  const workerQualityCriticTimeoutMs = Math.max(1000, asInt(parseIntEnv("WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS") ?? workerNode.quality_critic_timeout_ms, DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS));
1117
1118
  const workerQualitySoftPassOnExhausted = parseBoolEnv("WORKERPALS_QUALITY_SOFT_PASS_ON_EXHAUSTED") ?? asBoolean(workerNode.quality_soft_pass_on_exhausted, true);
1119
+ const workerQualityScopeGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_SCOPE_GATE_ENABLED") ?? asBoolean(workerNode.quality_scope_gate_enabled, true);
1120
+ const workerQualityValidationGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_VALIDATION_GATE_ENABLED") ?? asBoolean(workerNode.quality_validation_gate_enabled, true);
1121
+ const workerQualityCriticGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_CRITIC_GATE_ENABLED") ?? asBoolean(workerNode.quality_critic_gate_enabled, true);
1122
+ const workerQualityPublishGateEnabled = parseBoolEnv("WORKERPALS_QUALITY_PUBLISH_GATE_ENABLED") ?? asBoolean(workerNode.quality_publish_gate_enabled, true);
1118
1123
  const workerQualityCriticMinScore = (() => {
1119
1124
  const configThresholdRaw = workerNode.quality_critic_min_score == null ? "" : String(workerNode.quality_critic_min_score);
1120
1125
  const raw = firstNonEmpty(process.env.WORKERPALS_QUALITY_CRITIC_MIN_SCORE, configThresholdRaw, String(DEFAULT_WORKERPALS_QUALITY_CRITIC_MIN_SCORE));
@@ -1405,6 +1410,11 @@ function loadPushPalsConfig(options = {}) {
1405
1410
  outputMaxLines: workerOutputMaxLines,
1406
1411
  outputMaxHeadLines: workerOutputMaxHeadLines,
1407
1412
  qualityMaxAutoRevisions: workerQualityMaxAutoRevisions,
1413
+ qualityValidationMaxAutoRevisions: workerQualityValidationMaxAutoRevisions,
1414
+ qualityScopeGateEnabled: workerQualityScopeGateEnabled,
1415
+ qualityValidationGateEnabled: workerQualityValidationGateEnabled,
1416
+ qualityCriticGateEnabled: workerQualityCriticGateEnabled,
1417
+ qualityPublishGateEnabled: workerQualityPublishGateEnabled,
1408
1418
  qualityValidationStepTimeoutMs: workerQualityValidationStepTimeoutMs,
1409
1419
  qualityCriticTimeoutMs: workerQualityCriticTimeoutMs,
1410
1420
  qualitySoftPassOnExhausted: workerQualitySoftPassOnExhausted,
@@ -16,6 +16,7 @@ import {
16
16
  parseStructuredResult,
17
17
  filterResultLines,
18
18
  } from "../common/execution_utils.js";
19
+ import { buildWorkerSandboxWritableEnv } from "../common/sandbox_env.js";
19
20
  import { computeTimeoutWarningWindow } from "../timeout_policy.js";
20
21
 
21
22
  // ---- Script path (resolved relative to this file) ----------------------------
@@ -286,7 +287,7 @@ export async function executeWithOpenHands(
286
287
  stdout: "pipe",
287
288
  stderr: "pipe",
288
289
  env: {
289
- ...process.env,
290
+ ...buildWorkerSandboxWritableEnv(repo),
290
291
  PUSHPALS_REPO_PATH: repo,
291
292
  PUSHPALS_ASSIGNED_REPO_ROOT: repo,
292
293
  PYTHONIOENCODING: "utf-8",
@@ -18,6 +18,7 @@ import {
18
18
  filterResultLines,
19
19
  streamLines,
20
20
  } from "./execution_utils.js";
21
+ import { buildWorkerSandboxWritableEnv } from "./sandbox_env.js";
21
22
 
22
23
  interface GenericPythonExecutorConfig {
23
24
  backendName: string;
@@ -180,7 +181,7 @@ export function createGenericPythonExecutor(
180
181
  stdout: "pipe",
181
182
  stderr: "pipe",
182
183
  env: {
183
- ...process.env,
184
+ ...buildWorkerSandboxWritableEnv(repo),
184
185
  PUSHPALS_REPO_PATH: repo,
185
186
  PUSHPALS_ASSIGNED_REPO_ROOT: repo,
186
187
  PYTHONIOENCODING: "utf-8",
@@ -0,0 +1,76 @@
1
+ import { createHash } from "crypto";
2
+ import { existsSync, mkdirSync } from "fs";
3
+ import { homedir, tmpdir } from "os";
4
+ import { basename, resolve } from "path";
5
+
6
+ function stringEnv(source: NodeJS.ProcessEnv = process.env): Record<string, string> {
7
+ const env: Record<string, string> = {};
8
+ for (const [key, value] of Object.entries(source)) {
9
+ if (typeof value === "string") env[key] = value;
10
+ }
11
+ return env;
12
+ }
13
+
14
+ function safeRepoSlug(repo: string): string {
15
+ const leaf = basename(resolve(repo)).replace(/[^A-Za-z0-9_.-]+/g, "-") || "repo";
16
+ const hash = createHash("sha256").update(resolve(repo)).digest("hex").slice(0, 12);
17
+ return `${leaf}-${hash}`;
18
+ }
19
+
20
+ function defaultExpoPortForRepo(repo: string): string {
21
+ const hashPrefix = createHash("sha256").update(resolve(repo)).digest("hex").slice(0, 8);
22
+ const offset = Number.parseInt(hashPrefix, 16) % 1_000;
23
+ return String(19_006 + offset);
24
+ }
25
+
26
+ function ensureDirs(paths: string[]): void {
27
+ for (const path of paths) {
28
+ try {
29
+ mkdirSync(path, { recursive: true });
30
+ } catch {
31
+ // Best effort: command output will expose any remaining filesystem blocker.
32
+ }
33
+ }
34
+ }
35
+
36
+ function resolveOriginalHome(env: Record<string, string>): string {
37
+ return env.HOME || env.USERPROFILE || homedir();
38
+ }
39
+
40
+ function resolveCodexHome(env: Record<string, string>, originalHome: string): string | undefined {
41
+ if (env.CODEX_HOME) return env.CODEX_HOME;
42
+ const defaultCodexHome = resolve(originalHome, ".codex");
43
+ return existsSync(defaultCodexHome) ? defaultCodexHome : undefined;
44
+ }
45
+
46
+ export function buildWorkerSandboxWritableEnv(
47
+ repo: string,
48
+ sourceEnv: NodeJS.ProcessEnv = process.env,
49
+ ): Record<string, string> {
50
+ const env = stringEnv(sourceEnv);
51
+ const originalHome = resolveOriginalHome(env);
52
+ const codexHome = resolveCodexHome(env, originalHome);
53
+ const baseDir = resolve(tmpdir(), "pushpals-worker-env", safeRepoSlug(repo));
54
+ const homeDir = resolve(baseDir, "home");
55
+ const cacheDir = resolve(baseDir, "cache");
56
+ const expoDir = resolve(baseDir, "expo");
57
+ const defaultExpoPort = defaultExpoPortForRepo(repo);
58
+ ensureDirs([homeDir, cacheDir, expoDir, resolve(cacheDir, "npm")]);
59
+
60
+ return {
61
+ ...env,
62
+ ...(codexHome ? { CODEX_HOME: codexHome } : {}),
63
+ HOME: homeDir,
64
+ USERPROFILE: homeDir,
65
+ XDG_CACHE_HOME: cacheDir,
66
+ npm_config_cache: resolve(cacheDir, "npm"),
67
+ EXPO_HOME: expoDir,
68
+ EXPO_NO_TELEMETRY: env.EXPO_NO_TELEMETRY ?? "1",
69
+ EXPO_NO_INTERACTIVE: env.EXPO_NO_INTERACTIVE ?? "1",
70
+ CI: env.CI ?? "1",
71
+ BROWSER: env.BROWSER ?? "none",
72
+ EXPO_DEV_SERVER_PORT: env.EXPO_DEV_SERVER_PORT ?? defaultExpoPort,
73
+ RCT_METRO_PORT: env.RCT_METRO_PORT ?? defaultExpoPort,
74
+ PUSHPALS_VALIDATION_REPO: repo,
75
+ };
76
+ }