@pushpalsdev/cli 1.0.32 → 1.0.33

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 (46) hide show
  1. package/dist/pushpals-cli.js +4 -4
  2. package/monitor-ui/+not-found.html +1 -1
  3. package/monitor-ui/_expo/static/js/web/{entry-275c5f7972e2d2f4f0422fe2213a7f89.js → entry-5e6db7139bc13703a24f952bd64faf4c.js} +2 -2
  4. package/monitor-ui/_sitemap.html +1 -1
  5. package/monitor-ui/index.html +1 -1
  6. package/monitor-ui/modal.html +1 -1
  7. package/package.json +1 -1
  8. package/runtime/prompts/localbuddy/localbuddy_planner_git_diff_section.md +0 -1
  9. package/runtime/prompts/localbuddy/localbuddy_planner_git_status_section.md +0 -1
  10. package/runtime/prompts/localbuddy/localbuddy_planner_output_contract.md +1 -0
  11. package/runtime/prompts/remotebuddy/autonomy_ideation_system_prompt.md +31 -30
  12. package/runtime/prompts/remotebuddy/autonomy_scoring_system_prompt.md +2 -2
  13. package/runtime/prompts/remotebuddy/context_packer_user_prompt.md +1 -0
  14. package/runtime/prompts/remotebuddy/remotebuddy_system_prompt.md +1 -0
  15. package/runtime/prompts/review_agent/review_prompt_template.md +1 -0
  16. package/runtime/prompts/review_agent/reviewer.md +6 -4
  17. package/runtime/prompts/workerpals/commit_message_prompt.md +3 -0
  18. package/runtime/prompts/workerpals/miniswe_broker_system_prompt.md +10 -9
  19. package/runtime/prompts/workerpals/miniswe_strict_tool_use_guidance.md +1 -0
  20. package/runtime/prompts/workerpals/openai_codex_runtime_policy_appendix.md +1 -0
  21. package/runtime/prompts/workerpals/openai_codex_task_execute_system_prompt.md +2 -0
  22. package/runtime/prompts/workerpals/task_quality_critic_system_prompt.md +3 -2
  23. package/runtime/sandbox/apps/workerpals/src/backends/openai_codex_backend.ts +4 -4
  24. package/runtime/sandbox/apps/workerpals/src/backends/openhands_task_execute.ts +1 -4
  25. package/runtime/sandbox/apps/workerpals/src/common/execution_utils.ts +5 -3
  26. package/runtime/sandbox/apps/workerpals/src/common/generic_python_executor.ts +1 -4
  27. package/runtime/sandbox/apps/workerpals/src/common/worktree_cleanup.ts +3 -2
  28. package/runtime/sandbox/apps/workerpals/src/docker_executor.ts +42 -27
  29. package/runtime/sandbox/apps/workerpals/src/execute_job.ts +7 -7
  30. package/runtime/sandbox/apps/workerpals/src/job_runner.ts +7 -4
  31. package/runtime/sandbox/apps/workerpals/src/workerpals_main.ts +2 -1
  32. package/runtime/sandbox/packages/shared/src/autonomy_policy.ts +8 -3
  33. package/runtime/sandbox/packages/shared/src/communication.ts +19 -8
  34. package/runtime/sandbox/packages/shared/src/config.ts +9 -24
  35. package/runtime/sandbox/packages/shared/src/config_template_parity.ts +5 -6
  36. package/runtime/sandbox/packages/shared/src/git_backend.ts +5 -9
  37. package/runtime/sandbox/packages/shared/src/local_network.ts +3 -1
  38. package/runtime/sandbox/packages/shared/src/localbuddy_runtime.ts +4 -5
  39. package/runtime/sandbox/packages/shared/src/vision.ts +6 -2
  40. package/runtime/sandbox/prompts/workerpals/commit_message_prompt.md +3 -0
  41. package/runtime/sandbox/prompts/workerpals/miniswe_broker_system_prompt.md +10 -9
  42. package/runtime/sandbox/prompts/workerpals/miniswe_strict_tool_use_guidance.md +1 -0
  43. package/runtime/sandbox/prompts/workerpals/openai_codex_runtime_policy_appendix.md +1 -0
  44. package/runtime/sandbox/prompts/workerpals/openai_codex_task_execute_system_prompt.md +2 -0
  45. package/runtime/sandbox/prompts/workerpals/task_quality_critic_system_prompt.md +3 -2
  46. package/runtime/vision.example.md +24 -5
@@ -23,6 +23,7 @@ Intent taxonomy (choose the single best fit):
23
23
  - `other` — do NOT use `other` with `requires_worker=false`. `other` must always have `requires_worker=true`. When in doubt, prefer `code_change`.
24
24
 
25
25
  Classification rules (applied in order):
26
+
26
27
  1. Action verb present (add, fix, update, implement, create, remove, test, run, build, configure, refactor, improve, etc.) → `code_change` + `requires_worker=true`
27
28
  2. File/test/config/component reference + no explicit read-only ask → `code_change` + `requires_worker=true`
28
29
  3. Read-only analysis explicitly requested → `analysis` + `requires_worker=false`
@@ -11,6 +11,7 @@ Pull Request: #{{pr_number}} - {{pr_title}}
11
11
  Branch: {{head_ref}} -> {{base_ref}}
12
12
 
13
13
  Diff:
14
+
14
15
  ```diff
15
16
  {{diff}}
16
17
  ```
@@ -7,6 +7,7 @@ Return an objective quality score and specific, actionable feedback. The ReviewA
7
7
  ## Rating Criteria
8
8
 
9
9
  ### 9.0-10.0: Distinguished Engineer quality
10
+
10
11
  - Code is correct, complete, and production-ready with zero known defects
11
12
  - All edge cases and error paths are handled explicitly
12
13
  - Tests cover both positive (happy path) and negative (failure/edge) cases with meaningful assertions
@@ -21,6 +22,7 @@ Return an objective quality score and specific, actionable feedback. The ReviewA
21
22
  ### 1.0-6.9: Not production-ready - list specific issues and remediation steps
22
23
 
23
24
  Common rejection reasons:
25
+
24
26
  - Missing negative test assertions
25
27
  - Incomplete error handling
26
28
  - Tests that pass trivially (no real assertions)
@@ -32,8 +34,8 @@ Common rejection reasons:
32
34
 
33
35
  Respond with a JSON object only (no markdown wrapper):
34
36
  {
35
- "score": <number 1.0-10.0>,
36
- "summary": "<one sentence verdict>",
37
- "issues": ["<issue 1>", "<issue 2>", ...],
38
- "fix_instruction": "<precise instruction for the worker to fix all issues - this will be sent directly to the WorkerPal as its task>"
37
+ "score": <number 1.0-10.0>,
38
+ "summary": "<one sentence verdict>",
39
+ "issues": ["<issue 1>", "<issue 2>", ...],
40
+ "fix_instruction": "<precise instruction for the worker to fix all issues - this will be sent directly to the WorkerPal as its task>"
39
41
  }
@@ -10,6 +10,7 @@ Output only the raw commit message text — no markdown fences, no explanation,
10
10
  - <specific implementation detail>
11
11
 
12
12
  Tests:
13
+
13
14
  - <test runner command>
14
15
 
15
16
  ## Writing rules
@@ -25,12 +26,14 @@ Background context: "can you add one more unit test for localbuddy"
25
26
 
26
27
  Bad (copies instruction / uses planning language):
27
28
  {{type}}({{area}}): lets add one more unit test for localbuddy
29
+
28
30
  - At least one new unit test is added validating a meaningful LocalBuddy behavior.
29
31
  - All existing and new tests pass.
30
32
  - No unrelated files are modified.
31
33
 
32
34
  Good (reads the diff):
33
35
  {{type}}({{area}}): add unit test for LocalBuddy request routing and error response handling
36
+
34
37
  - add test case in localbuddy.test.ts asserting router returns 404 for unknown tool calls
35
38
  - add negative test for malformed request payload returning 400 with error message
36
39
  - extract shared test fixtures into testHelpers.ts to reduce duplication
@@ -5,18 +5,19 @@ Repository root: {{repo}}
5
5
 
6
6
  Output format (STRICT JSON, no markdown, no extra keys unless specified):
7
7
  {
8
- "actions": [
9
- {"type":"read_file","path":"README.md"},
10
- {"type":"append_line","path":"README.md","line":"..."},
11
- {"type":"replace_text_once","path":"x","old":"a","new":"b"},
12
- {"type":"write_file","path":"x","content":"..."},
13
- {"type":"run_shell","command":"git status --porcelain"}
14
- ],
15
- "done": false,
16
- "note": "short explanation"
8
+ "actions": [
9
+ {"type":"read_file","path":"README.md"},
10
+ {"type":"append_line","path":"README.md","line":"..."},
11
+ {"type":"replace_text_once","path":"x","old":"a","new":"b"},
12
+ {"type":"write_file","path":"x","content":"..."},
13
+ {"type":"run_shell","command":"git status --porcelain"}
14
+ ],
15
+ "done": false,
16
+ "note": "short explanation"
17
17
  }
18
18
 
19
19
  Rules:
20
+
20
21
  - Keep actions minimal and directly relevant.
21
22
  - JSON syntax must be exact: use ":" between keys and values, never ",".
22
23
  - Use double quotes for all keys and string values.
@@ -1,4 +1,5 @@
1
1
  CRITICAL: You must use tools to make progress.
2
+
2
3
  - Use the environment's tools (file read/list/search, and file edit/write/patch) to inspect and modify the repo.
3
4
  - Do NOT only describe what you would do; actually do it.
4
5
  - Avoid broad scans; choose one target file quickly.
@@ -1,4 +1,5 @@
1
1
  Runtime policy guardrails (mandatory):
2
+
2
3
  - Codex CLI is required infrastructure in this environment.
3
4
  - Never bypass Codex usage by changing tests/code expectations.
4
5
  - If Codex CLI auth/execution is unavailable, hard-fail and stop.
@@ -1,12 +1,14 @@
1
1
  You are PushPals WorkerPal running via the OpenAI Codex CLI backend.
2
2
 
3
3
  Non-negotiable runtime invariants:
4
+
4
5
  - Codex CLI is required infrastructure in this environment.
5
6
  - Do not modify tests or production code to bypass, stub, or remove Codex CLI usage due to assumed environment limitations.
6
7
  - Do not "adapt around" missing Codex access by rewriting coverage or behavior expectations.
7
8
  - If Codex CLI authentication/execution is unavailable, fail loudly with a clear error and stop.
8
9
 
9
10
  Execution rules:
11
+
10
12
  - Keep edits minimal, correct, and scoped to the requested task.
11
13
  - Read relevant files before editing, then run focused validation.
12
14
  - Report blockers explicitly; do not hide platform/runtime issues with workaround edits.
@@ -2,8 +2,9 @@ You are a strict code-review critic for worker-generated patches.
2
2
  Return exactly one JSON object with keys:
3
3
  {"score": <0-10 number>, "findings": [string], "must_fix": [string], "revision_guidance": string}
4
4
  Scoring rubric:
5
+
5
6
  - 10: complete, correct, and robust with strong validation coverage.
6
7
  - 8-9: good quality with minor non-blocking issues.
7
8
  - <=7: requires revision before commit.
8
- must_fix must list blocking issues only.
9
- Do not include markdown or prose outside JSON.
9
+ must_fix must list blocking issues only.
10
+ Do not include markdown or prose outside JSON.
@@ -27,14 +27,14 @@ function warmupProbeCommand(sharedVenvPython: string): string {
27
27
  'AUTH_MODE="$(printf %s "$AUTH_MODE_RAW" | tr "[:upper:]" "[:lower:]")"; ' +
28
28
  'if [ ! -x "$PY" ]; then PY="$(command -v python3 || command -v python || true)"; fi; ' +
29
29
  '[ -n "$PY" ] || { echo "python runtime not found" >&2; exit 1; }; ' +
30
- 'if command -v bunx >/dev/null 2>&1; then ' +
30
+ "if command -v bunx >/dev/null 2>&1; then " +
31
31
  ' CODEX_CMD="bunx --yes @openai/codex"; ' +
32
- 'elif command -v codex >/dev/null 2>&1; then ' +
32
+ "elif command -v codex >/dev/null 2>&1; then " +
33
33
  ' CODEX_CMD="codex"; ' +
34
- 'else ' +
34
+ "else " +
35
35
  ' echo "Neither bunx nor codex was found in PATH" >&2; ' +
36
36
  " exit 1; " +
37
- 'fi; ' +
37
+ "fi; " +
38
38
  'sh -lc "$CODEX_CMD --version"; ' +
39
39
  'NEED_LOGIN="0"; ' +
40
40
  'if [ "$AUTH_MODE" = "chatgpt" ] || [ "$AUTH_MODE" = "chatgpt_login" ] || [ "$AUTH_MODE" = "subscription" ]; then NEED_LOGIN="1"; fi; ' +
@@ -55,10 +55,7 @@ function estimateJobTokenUsage(
55
55
  };
56
56
  }
57
57
 
58
- function coerceJobTokenUsage(
59
- value: unknown,
60
- fallback: JobTokenUsage,
61
- ): JobTokenUsage {
58
+ function coerceJobTokenUsage(value: unknown, fallback: JobTokenUsage): JobTokenUsage {
62
59
  if (!value || typeof value !== "object" || Array.isArray(value)) {
63
60
  return fallback;
64
61
  }
@@ -16,8 +16,7 @@ export function resolveOutputCompactionPolicy(
16
16
  const maxOutputChars = Number(overrides.maxOutputChars ?? worker.outputMaxChars);
17
17
  const maxOutputLines = Number(overrides.maxOutputLines ?? worker.outputMaxLines);
18
18
  const maxOutputHeadLines = Number(overrides.maxOutputHeadLines ?? worker.outputMaxHeadLines);
19
- const executorResultPrefixRaw =
20
- overrides.executorResultPrefix ?? worker.executorResultPrefix;
19
+ const executorResultPrefixRaw = overrides.executorResultPrefix ?? worker.executorResultPrefix;
21
20
  const executorResultPrefix =
22
21
  typeof executorResultPrefixRaw === "string" && executorResultPrefixRaw.length > 0
23
22
  ? executorResultPrefixRaw
@@ -42,7 +41,10 @@ export function resolveOutputCompactionPolicy(
42
41
 
43
42
  // ---- Output truncation -------------------------------------------------------
44
43
 
45
- export function compactJobOutput(text: string, policyOverrides: Partial<OutputCompactionPolicy> = {}): string {
44
+ export function compactJobOutput(
45
+ text: string,
46
+ policyOverrides: Partial<OutputCompactionPolicy> = {},
47
+ ): string {
46
48
  if (!text) return "";
47
49
  const policy = resolveOutputCompactionPolicy(policyOverrides);
48
50
  const maxOutputChars = policy.maxOutputChars;
@@ -58,10 +58,7 @@ function estimateJobTokenUsage(
58
58
  };
59
59
  }
60
60
 
61
- function coerceJobTokenUsage(
62
- value: unknown,
63
- fallback: JobTokenUsage,
64
- ): JobTokenUsage {
61
+ function coerceJobTokenUsage(value: unknown, fallback: JobTokenUsage): JobTokenUsage {
65
62
  if (!value || typeof value !== "object" || Array.isArray(value)) {
66
63
  return fallback;
67
64
  }
@@ -39,7 +39,9 @@ export async function forceDeleteWorktreePath(
39
39
  const retries = Math.max(1, Math.floor(options.retries ?? 5));
40
40
  const delayMs = Math.max(0, Math.floor(options.delayMs ?? 120));
41
41
  const sleep = options.sleepFn ?? defaultSleep;
42
- const removePath = options.removeFn ?? ((targetPath: string) => rmSync(targetPath, { recursive: true, force: true }));
42
+ const removePath =
43
+ options.removeFn ??
44
+ ((targetPath: string) => rmSync(targetPath, { recursive: true, force: true }));
43
45
  const pathExists = options.existsFn ?? ((targetPath: string) => existsSync(targetPath));
44
46
  let lastError = "";
45
47
 
@@ -63,4 +65,3 @@ export async function forceDeleteWorktreePath(
63
65
  ...(lastError ? { lastError } : {}),
64
66
  };
65
67
  }
66
-
@@ -75,7 +75,10 @@ function resolveDockerExecutable(): string {
75
75
  return process.platform === "win32" ? "docker.exe" : "docker";
76
76
  }
77
77
 
78
- function resolveWorkerpalSandboxBuildContext(repoRoot: string): { root: string; dockerfilePath: string } {
78
+ function resolveWorkerpalSandboxBuildContext(repoRoot: string): {
79
+ root: string;
80
+ dockerfilePath: string;
81
+ } {
79
82
  const configuredRoot = String(process.env.PUSHPALS_WORKERPALS_SANDBOX_ROOT ?? "").trim();
80
83
  const sandboxRoot = configuredRoot || repoRoot;
81
84
  const dockerfilePath = configuredRoot
@@ -446,14 +449,11 @@ export class DockerExecutor {
446
449
  });
447
450
  await prune.exited;
448
451
 
449
- proc = Bun.spawn(
450
- ["git", "worktree", "add", "--force", "--detach", worktreePath, baseRef],
451
- {
452
- cwd: this.options.repo,
453
- stdout: "pipe",
454
- stderr: "pipe",
455
- },
456
- );
452
+ proc = Bun.spawn(["git", "worktree", "add", "--force", "--detach", worktreePath, baseRef], {
453
+ cwd: this.options.repo,
454
+ stdout: "pipe",
455
+ stderr: "pipe",
456
+ });
457
457
  exitCode = await proc.exited;
458
458
  stdout = await new Response(proc.stdout).text();
459
459
  stderr = await new Response(proc.stderr).text();
@@ -713,7 +713,10 @@ export class DockerExecutor {
713
713
 
714
714
  args.push("--entrypoint", "/bin/sh", this.options.imageName, "-lc", startupCmd);
715
715
 
716
- const proc = Bun.spawn([resolveDockerExecutable(), ...args], { stdout: "pipe", stderr: "pipe" });
716
+ const proc = Bun.spawn([resolveDockerExecutable(), ...args], {
717
+ stdout: "pipe",
718
+ stderr: "pipe",
719
+ });
717
720
  const [exitCode, stdout, stderr] = await Promise.all([
718
721
  proc.exited,
719
722
  new Response(proc.stdout).text(),
@@ -814,10 +817,13 @@ export class DockerExecutor {
814
817
  stderr: string;
815
818
  exitCode: number;
816
819
  }> {
817
- const proc = Bun.spawn([resolveDockerExecutable(), "exec", this.warmContainerName, "/bin/sh", "-lc", command], {
818
- stdout: "pipe",
819
- stderr: "pipe",
820
- });
820
+ const proc = Bun.spawn(
821
+ [resolveDockerExecutable(), "exec", this.warmContainerName, "/bin/sh", "-lc", command],
822
+ {
823
+ stdout: "pipe",
824
+ stderr: "pipe",
825
+ },
826
+ );
821
827
  const [stdout, stderr, exitCode] = await Promise.all([
822
828
  new Response(proc.stdout).text(),
823
829
  new Response(proc.stderr).text(),
@@ -854,10 +860,13 @@ export class DockerExecutor {
854
860
  }
855
861
 
856
862
  private async readWarmContainerLogs(tail = 160): Promise<string> {
857
- const proc = Bun.spawn([resolveDockerExecutable(), "logs", "--tail", String(tail), this.warmContainerName], {
858
- stdout: "pipe",
859
- stderr: "pipe",
860
- });
863
+ const proc = Bun.spawn(
864
+ [resolveDockerExecutable(), "logs", "--tail", String(tail), this.warmContainerName],
865
+ {
866
+ stdout: "pipe",
867
+ stderr: "pipe",
868
+ },
869
+ );
861
870
  const [stdout, stderr, exitCode] = await Promise.all([
862
871
  new Response(proc.stdout).text(),
863
872
  new Response(proc.stderr).text(),
@@ -1555,11 +1564,14 @@ export class DockerExecutor {
1555
1564
  `[DockerExecutor] Worktree path already exists; forcing cleanup before create: ${worktreePath}`,
1556
1565
  );
1557
1566
 
1558
- const unregister = Bun.spawn(["git", "worktree", "remove", "--force", "--force", worktreePath], {
1559
- cwd: this.options.repo,
1560
- stdout: "pipe",
1561
- stderr: "pipe",
1562
- });
1567
+ const unregister = Bun.spawn(
1568
+ ["git", "worktree", "remove", "--force", "--force", worktreePath],
1569
+ {
1570
+ cwd: this.options.repo,
1571
+ stdout: "pipe",
1572
+ stderr: "pipe",
1573
+ },
1574
+ );
1563
1575
  await unregister.exited;
1564
1576
 
1565
1577
  const prune = Bun.spawn(["git", "worktree", "prune"], {
@@ -1777,10 +1789,13 @@ export class DockerExecutor {
1777
1789
  * Check if the Docker image exists locally
1778
1790
  */
1779
1791
  private async imageExists(): Promise<boolean> {
1780
- const proc = Bun.spawn([resolveDockerExecutable(), "image", "inspect", this.options.imageName], {
1781
- stdout: "pipe",
1782
- stderr: "pipe",
1783
- });
1792
+ const proc = Bun.spawn(
1793
+ [resolveDockerExecutable(), "image", "inspect", this.options.imageName],
1794
+ {
1795
+ stdout: "pipe",
1796
+ stderr: "pipe",
1797
+ },
1798
+ );
1784
1799
  const exitCode = await proc.exited;
1785
1800
  return exitCode === 0;
1786
1801
  }
@@ -173,7 +173,10 @@ export function buildQualityGateRevisionIssues(
173
173
  if (!critic || critic.score >= qualityCriticMinScore) {
174
174
  return [...normalizedQualityIssues];
175
175
  }
176
- const merged = [...normalizedQualityIssues, ...buildCriticRevisionIssues(critic, qualityCriticMinScore)];
176
+ const merged = [
177
+ ...normalizedQualityIssues,
178
+ ...buildCriticRevisionIssues(critic, qualityCriticMinScore),
179
+ ];
177
180
  return [...new Set(merged)];
178
181
  }
179
182
 
@@ -2569,7 +2572,8 @@ function validateTaskExecutePlanning(
2569
2572
  if (!componentArea) {
2570
2573
  return {
2571
2574
  ok: false,
2572
- message: "task.execute planning.targetPaths must resolve to a repo-relative componentArea",
2575
+ message:
2576
+ "task.execute planning.targetPaths must resolve to a repo-relative componentArea",
2573
2577
  };
2574
2578
  }
2575
2579
  if (
@@ -3056,11 +3060,7 @@ export async function executeJob(
3056
3060
  return result;
3057
3061
  }
3058
3062
 
3059
- const issues = buildQualityGateRevisionIssues(
3060
- quality.issues,
3061
- critic,
3062
- qualityCriticMinScore,
3063
- );
3063
+ const issues = buildQualityGateRevisionIssues(quality.issues, critic, qualityCriticMinScore);
3064
3064
  const issueSummary = issues.map((entry) => toSingleLine(entry, 180)).join(" | ");
3065
3065
  if (revisionAttempt >= qualityMaxAutoRevisions) {
3066
3066
  if (qualitySoftPassOnExhausted) {
@@ -67,10 +67,13 @@ echo "password=${token}"
67
67
  writeFileSync(helperPath, helperScript, { mode: 0o755 });
68
68
 
69
69
  // Remove any legacy URL rewrite rules that may have embedded token credentials.
70
- const urlRules = Bun.spawnSync(["git", "config", "--global", "--get-regexp", "^url\\..*\\.insteadOf$"], {
71
- stdout: "pipe",
72
- stderr: "pipe",
73
- });
70
+ const urlRules = Bun.spawnSync(
71
+ ["git", "config", "--global", "--get-regexp", "^url\\..*\\.insteadOf$"],
72
+ {
73
+ stdout: "pipe",
74
+ stderr: "pipe",
75
+ },
76
+ );
74
77
  if (urlRules.exitCode === 0) {
75
78
  const lines = String(urlRules.stdout ?? "")
76
79
  .split(/\r?\n/)
@@ -123,7 +123,8 @@ function buildWorkerLlmUsageEvent(
123
123
  return {
124
124
  service: "workerpals",
125
125
  sessionId,
126
- backend: String(explicitUsage.backend ?? resolveExecutor(CONFIG)).trim() || resolveExecutor(CONFIG),
126
+ backend:
127
+ String(explicitUsage.backend ?? resolveExecutor(CONFIG)).trim() || resolveExecutor(CONFIG),
127
128
  modelId: String(explicitUsage.modelId ?? llmConfig.model).trim() || llmConfig.model,
128
129
  promptTokens,
129
130
  completionTokens,
@@ -263,8 +263,9 @@ export function globBreadthScore(glob: string): number {
263
263
  const rootWide = /^[\*]/.test(glob) || glob.startsWith("**/") ? 1 : 0;
264
264
  const literalSegments = glob
265
265
  .split("/")
266
- .filter((segment) => segment.length > 0 && !segment.includes("*") && !segment.includes("?"))
267
- .length;
266
+ .filter(
267
+ (segment) => segment.length > 0 && !segment.includes("*") && !segment.includes("?"),
268
+ ).length;
268
269
  const shallowPenalty = Math.max(0, 2 - Math.min(literalSegments, 2));
269
270
  return 4 * hasGlobStar + 2 * rootWide + Math.min(4, wildcardCount) + shallowPenalty;
270
271
  }
@@ -343,7 +344,11 @@ export function validateScopeInvariants(
343
344
  errors.push(`write_glob outside component root: ${normalized}`);
344
345
  continue;
345
346
  }
346
- if (!normalizedTargetPaths.some((targetPath) => targetPath === prefix || targetPath.startsWith(`${prefix}/`))) {
347
+ if (
348
+ !normalizedTargetPaths.some(
349
+ (targetPath) => targetPath === prefix || targetPath.startsWith(`${prefix}/`),
350
+ )
351
+ ) {
347
352
  errors.push(`write_glob prefix does not align with target_paths: ${normalized}`);
348
353
  continue;
349
354
  }
@@ -21,7 +21,10 @@ function stripPresenceSourcePrefix(value: string): string {
21
21
 
22
22
  export function normalizePresenceClientId(value: unknown): string {
23
23
  const raw = stripPresenceSourcePrefix(String(value ?? "").trim());
24
- return raw.replace(/[^a-zA-Z0-9._-]+/g, "_").replace(/^_+|_+$/g, "").trim();
24
+ return raw
25
+ .replace(/[^a-zA-Z0-9._-]+/g, "_")
26
+ .replace(/^_+|_+$/g, "")
27
+ .trim();
25
28
  }
26
29
 
27
30
  export function normalizePresenceClientLabel(value: unknown): string {
@@ -31,7 +34,9 @@ export function normalizePresenceClientLabel(value: unknown): string {
31
34
  }
32
35
 
33
36
  export function normalizePresenceLookupToken(value: unknown): string {
34
- return normalizePresenceClientLabel(value).toLowerCase().replace(/[^a-z0-9]+/g, "");
37
+ return normalizePresenceClientLabel(value)
38
+ .toLowerCase()
39
+ .replace(/[^a-z0-9]+/g, "");
35
40
  }
36
41
 
37
42
  type SessionTransportPresence = {
@@ -91,8 +96,9 @@ export class CommunicationManager {
91
96
  : `${normalizedFrom || "agent"}__${normalizedSessionId || "session"}`,
92
97
  kind: "agent",
93
98
  label: labelFrom || normalizedFrom || "Agent",
94
- version: String(process.env.PUSHPALS_RUNTIME_TAG ?? process.env.npm_package_version ?? "")
95
- .trim(),
99
+ version: String(
100
+ process.env.PUSHPALS_RUNTIME_TAG ?? process.env.npm_package_version ?? "",
101
+ ).trim(),
96
102
  platform: `${process.platform}/${process.arch}`,
97
103
  repoRoot,
98
104
  };
@@ -151,10 +157,15 @@ export class CommunicationManager {
151
157
  text: string,
152
158
  meta: EventMeta = {},
153
159
  ): Promise<boolean> {
154
- return this.emitToSession(sessionId, "message", { text }, {
155
- ...meta,
156
- from: meta.from ?? "client",
157
- });
160
+ return this.emitToSession(
161
+ sessionId,
162
+ "message",
163
+ { text },
164
+ {
165
+ ...meta,
166
+ from: meta.from ?? "client",
167
+ },
168
+ );
158
169
  }
159
170
 
160
171
  async userMessage(text: string, meta: EventMeta = {}): Promise<boolean> {
@@ -457,11 +457,7 @@ function normalizeWorkerImageRebuildMode(value: string): "auto" | "always" | "ne
457
457
 
458
458
  function normalizeStartupPortConflictPolicy(value: string): "fail" | "terminate_pushpals" {
459
459
  const text = value.trim().toLowerCase().replace(/-/g, "_");
460
- if (
461
- text === "terminate_pushpals" ||
462
- text === "kill_pushpals" ||
463
- text === "auto_kill_pushpals"
464
- ) {
460
+ if (text === "terminate_pushpals" || text === "kill_pushpals" || text === "auto_kill_pushpals") {
465
461
  return "terminate_pushpals";
466
462
  }
467
463
  return "fail";
@@ -535,10 +531,7 @@ function resolveLlmConfig(
535
531
  );
536
532
  const codexTimeoutMs = Math.max(
537
533
  10_000,
538
- asInt(
539
- parseIntEnv(`${envPrefix}_LLM_CODEX_TIMEOUT_MS`) ?? llmNode.codex_timeout_ms,
540
- 120_000,
541
- ),
534
+ asInt(parseIntEnv(`${envPrefix}_LLM_CODEX_TIMEOUT_MS`) ?? llmNode.codex_timeout_ms, 120_000),
542
535
  );
543
536
  return {
544
537
  backend,
@@ -916,7 +909,8 @@ export function loadPushPalsConfig(options: LoadOptions = {}): PushPalsConfig {
916
909
  Math.min(
917
910
  10,
918
911
  asInt(
919
- parseIntEnv("WORKERPALS_QUALITY_MAX_AUTO_REVISIONS") ?? workerNode.quality_max_auto_revisions,
912
+ parseIntEnv("WORKERPALS_QUALITY_MAX_AUTO_REVISIONS") ??
913
+ workerNode.quality_max_auto_revisions,
920
914
  DEFAULT_WORKERPALS_QUALITY_MAX_AUTO_REVISIONS,
921
915
  ),
922
916
  ),
@@ -973,8 +967,7 @@ export function loadPushPalsConfig(options: LoadOptions = {}): PushPalsConfig {
973
967
  const workerQualityCriticTimeoutMs = Math.max(
974
968
  1_000,
975
969
  asInt(
976
- parseIntEnv("WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS") ??
977
- workerNode.quality_critic_timeout_ms,
970
+ parseIntEnv("WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS") ?? workerNode.quality_critic_timeout_ms,
978
971
  DEFAULT_WORKERPALS_QUALITY_CRITIC_TIMEOUT_MS,
979
972
  ),
980
973
  );
@@ -1431,8 +1424,7 @@ export function loadPushPalsConfig(options: LoadOptions = {}): PushPalsConfig {
1431
1424
  parseBoolEnv("PUSHPALS_ALLOW_EXTERNAL_CLEAN") ??
1432
1425
  asBoolean(startupNode.allow_external_clean, false);
1433
1426
  const startupPortPreflight =
1434
- parseBoolEnv("PUSHPALS_STARTUP_PORT_PREFLIGHT") ??
1435
- asBoolean(startupNode.port_preflight, true);
1427
+ parseBoolEnv("PUSHPALS_STARTUP_PORT_PREFLIGHT") ?? asBoolean(startupNode.port_preflight, true);
1436
1428
  const startupPortConflictPolicy = normalizeStartupPortConflictPolicy(
1437
1429
  firstNonEmpty(
1438
1430
  process.env.PUSHPALS_STARTUP_PORT_CONFLICT_POLICY,
@@ -1508,10 +1500,7 @@ export function loadPushPalsConfig(options: LoadOptions = {}): PushPalsConfig {
1508
1500
  asBoolean(remoteNode.auto_spawn_workerpals, true),
1509
1501
  maxWorkerpals: Math.max(
1510
1502
  1,
1511
- asInt(
1512
- parseIntEnv("REMOTEBUDDY_MAX_WORKERPALS") ?? remoteNode.max_workerpals,
1513
- 20,
1514
- ),
1503
+ asInt(parseIntEnv("REMOTEBUDDY_MAX_WORKERPALS") ?? remoteNode.max_workerpals, 20),
1515
1504
  ),
1516
1505
  workerpalStartupTimeoutMs: Math.max(
1517
1506
  1_000,
@@ -1647,8 +1636,7 @@ export function loadPushPalsConfig(options: LoadOptions = {}): PushPalsConfig {
1647
1636
  llmTimeoutMs: Math.max(
1648
1637
  1_000,
1649
1638
  asInt(
1650
- parseIntEnv("REMOTEBUDDY_AUTONOMY_LLM_TIMEOUT_MS") ??
1651
- remoteAutonomyNode.llm_timeout_ms,
1639
+ parseIntEnv("REMOTEBUDDY_AUTONOMY_LLM_TIMEOUT_MS") ?? remoteAutonomyNode.llm_timeout_ms,
1652
1640
  12_000,
1653
1641
  ),
1654
1642
  ),
@@ -2156,10 +2144,7 @@ function sanitizeConfigString(value: string): string {
2156
2144
  return out;
2157
2145
  }
2158
2146
 
2159
- function sanitizeConfigValueForLogging(
2160
- value: unknown,
2161
- parentKey = "",
2162
- ): unknown {
2147
+ function sanitizeConfigValueForLogging(value: unknown, parentKey = ""): unknown {
2163
2148
  if (
2164
2149
  typeof value === "string" ||
2165
2150
  typeof value === "number" ||
@@ -18,11 +18,7 @@ export function collectDotEnvKeys(raw: string): Set<string> {
18
18
  return keys;
19
19
  }
20
20
 
21
- function collectTomlLeafKeysFromNode(
22
- node: unknown,
23
- prefix: string,
24
- out: Set<string>,
25
- ): void {
21
+ function collectTomlLeafKeysFromNode(node: unknown, prefix: string, out: Set<string>): void {
26
22
  if (!isObject(node)) return;
27
23
  for (const [rawKey, value] of Object.entries(node)) {
28
24
  const key = rawKey.trim();
@@ -43,7 +39,10 @@ export function collectTomlLeafKeys(raw: string): Set<string> {
43
39
  return keys;
44
40
  }
45
41
 
46
- export function missingTemplateKeys(templateKeys: Iterable<string>, localKeys: Set<string>): string[] {
42
+ export function missingTemplateKeys(
43
+ templateKeys: Iterable<string>,
44
+ localKeys: Set<string>,
45
+ ): string[] {
47
46
  const templateSet = new Set(templateKeys);
48
47
  const missing: string[] = [];
49
48
  for (const key of templateSet) {
@@ -42,10 +42,7 @@ export function sanitizeGitRemoteUrl(remoteUrl: string): string {
42
42
  return raw.replace(/^(https?:\/\/)[^@/]+@/i, "$1");
43
43
  }
44
44
 
45
- function firstNonEmpty(
46
- env: Record<string, string | undefined>,
47
- keys: readonly string[],
48
- ): string {
45
+ function firstNonEmpty(env: Record<string, string | undefined>, keys: readonly string[]): string {
49
46
  for (const key of keys) {
50
47
  const value = trimToken(env[key]);
51
48
  if (value) return value;
@@ -110,10 +107,7 @@ export function toGitHubRepoWebUrl(remoteUrl: string): string | null {
110
107
  return `https://github.com/${repo.owner}/${repo.repo}`;
111
108
  }
112
109
 
113
- async function defaultRunCommand(
114
- command: string[],
115
- cwd?: string,
116
- ): Promise<CommandCaptureResult> {
110
+ async function defaultRunCommand(command: string[], cwd?: string): Promise<CommandCaptureResult> {
117
111
  try {
118
112
  const proc = Bun.spawn(command, {
119
113
  cwd,
@@ -147,7 +141,9 @@ async function resolveGitHubCliToken(
147
141
  cwd?: string,
148
142
  ): Promise<string> {
149
143
  const useHostname = host && host !== "github.com";
150
- const command = useHostname ? ["gh", "auth", "token", "--hostname", host] : ["gh", "auth", "token"];
144
+ const command = useHostname
145
+ ? ["gh", "auth", "token", "--hostname", host]
146
+ : ["gh", "auth", "token"];
151
147
  const result = await runCommand(command, cwd);
152
148
  return result.ok ? trimToken(result.stdout) : "";
153
149
  }
@@ -89,7 +89,9 @@ export function resolveLocalServerConnection(options: {
89
89
  serverWasNormalized: boolean;
90
90
  authTokenWasIgnored: boolean;
91
91
  } {
92
- const rawServer = String(options.serverUrl ?? "").trim().replace(/\/+$/, "");
92
+ const rawServer = String(options.serverUrl ?? "")
93
+ .trim()
94
+ .replace(/\/+$/, "");
93
95
  const normalizedServer = normalizeLoopbackHttpUrl(rawServer, options.fallbackPort);
94
96
  const authToken = String(options.authToken ?? "").trim();
95
97
  return {