@pushpalsdev/cli 1.0.32 → 1.0.34
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.
- package/dist/pushpals-cli.js +4 -4
- package/monitor-ui/+not-found.html +1 -1
- package/monitor-ui/_expo/static/js/web/{entry-275c5f7972e2d2f4f0422fe2213a7f89.js → entry-5e6db7139bc13703a24f952bd64faf4c.js} +2 -2
- package/monitor-ui/_sitemap.html +1 -1
- package/monitor-ui/index.html +1 -1
- package/monitor-ui/modal.html +1 -1
- package/package.json +1 -1
- package/runtime/prompts/localbuddy/localbuddy_planner_git_diff_section.md +0 -1
- package/runtime/prompts/localbuddy/localbuddy_planner_git_status_section.md +0 -1
- package/runtime/prompts/localbuddy/localbuddy_planner_output_contract.md +1 -0
- package/runtime/prompts/remotebuddy/autonomy_ideation_system_prompt.md +31 -30
- package/runtime/prompts/remotebuddy/autonomy_scoring_system_prompt.md +2 -2
- package/runtime/prompts/remotebuddy/context_packer_user_prompt.md +1 -0
- package/runtime/prompts/remotebuddy/remotebuddy_system_prompt.md +1 -0
- package/runtime/prompts/review_agent/review_prompt_template.md +1 -0
- package/runtime/prompts/review_agent/reviewer.md +6 -4
- package/runtime/prompts/workerpals/commit_message_prompt.md +3 -0
- package/runtime/prompts/workerpals/miniswe_broker_system_prompt.md +10 -9
- package/runtime/prompts/workerpals/miniswe_strict_tool_use_guidance.md +1 -0
- package/runtime/prompts/workerpals/openai_codex_runtime_policy_appendix.md +1 -0
- package/runtime/prompts/workerpals/openai_codex_task_execute_system_prompt.md +2 -0
- package/runtime/prompts/workerpals/task_quality_critic_system_prompt.md +3 -2
- package/runtime/sandbox/apps/workerpals/src/backends/openai_codex_backend.ts +4 -4
- package/runtime/sandbox/apps/workerpals/src/backends/openhands_task_execute.ts +1 -4
- package/runtime/sandbox/apps/workerpals/src/common/execution_utils.ts +5 -3
- package/runtime/sandbox/apps/workerpals/src/common/generic_python_executor.ts +1 -4
- package/runtime/sandbox/apps/workerpals/src/common/worktree_cleanup.ts +3 -2
- package/runtime/sandbox/apps/workerpals/src/docker_executor.ts +132 -32
- package/runtime/sandbox/apps/workerpals/src/execute_job.ts +7 -7
- package/runtime/sandbox/apps/workerpals/src/job_runner.ts +7 -4
- package/runtime/sandbox/apps/workerpals/src/workerpals_main.ts +46 -27
- package/runtime/sandbox/packages/shared/src/autonomy_policy.ts +8 -3
- package/runtime/sandbox/packages/shared/src/communication.ts +19 -8
- package/runtime/sandbox/packages/shared/src/config.ts +9 -24
- package/runtime/sandbox/packages/shared/src/config_template_parity.ts +5 -6
- package/runtime/sandbox/packages/shared/src/git_backend.ts +5 -9
- package/runtime/sandbox/packages/shared/src/local_network.ts +3 -1
- package/runtime/sandbox/packages/shared/src/localbuddy_runtime.ts +4 -5
- package/runtime/sandbox/packages/shared/src/vision.ts +6 -2
- package/runtime/sandbox/prompts/workerpals/commit_message_prompt.md +3 -0
- package/runtime/sandbox/prompts/workerpals/miniswe_broker_system_prompt.md +10 -9
- package/runtime/sandbox/prompts/workerpals/miniswe_strict_tool_use_guidance.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_runtime_policy_appendix.md +1 -0
- package/runtime/sandbox/prompts/workerpals/openai_codex_task_execute_system_prompt.md +2 -0
- package/runtime/sandbox/prompts/workerpals/task_quality_critic_system_prompt.md +3 -2
- package/runtime/vision.example.md +24 -5
|
@@ -123,7 +123,8 @@ function buildWorkerLlmUsageEvent(
|
|
|
123
123
|
return {
|
|
124
124
|
service: "workerpals",
|
|
125
125
|
sessionId,
|
|
126
|
-
backend:
|
|
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,
|
|
@@ -214,6 +215,14 @@ function isNoisyProgressLine(line: string): boolean {
|
|
|
214
215
|
return /^(📦 Installing \[\d+\/\d+\]|🔍 Resolving\.\.\.|🔒 Saving lockfile\.\.\.)$/.test(line);
|
|
215
216
|
}
|
|
216
217
|
|
|
218
|
+
export function shouldEmitDirectSessionJobEvent(options: {
|
|
219
|
+
ok: boolean;
|
|
220
|
+
statusPersistedToServer: boolean;
|
|
221
|
+
}): boolean {
|
|
222
|
+
if (options.ok) return true;
|
|
223
|
+
return !options.statusPersistedToServer;
|
|
224
|
+
}
|
|
225
|
+
|
|
217
226
|
function shouldRecycleWorkerForCodexUnavailableFailure(
|
|
218
227
|
summary: string,
|
|
219
228
|
stderr?: string | null,
|
|
@@ -949,13 +958,15 @@ async function failActiveJobOnShutdown(
|
|
|
949
958
|
|
|
950
959
|
const message = "Worker process shutting down during claimed job";
|
|
951
960
|
const detail = `worker=${opts.workerId}; signal=${signalName}; action=fail-claimed-job-on-shutdown`;
|
|
961
|
+
let statusPersistedToServer = false;
|
|
952
962
|
|
|
953
963
|
try {
|
|
954
|
-
await fetch(`${opts.server}/jobs/${activeJobId}/fail`, {
|
|
964
|
+
const response = await fetch(`${opts.server}/jobs/${activeJobId}/fail`, {
|
|
955
965
|
method: "POST",
|
|
956
966
|
headers,
|
|
957
967
|
body: JSON.stringify({ message, detail }),
|
|
958
968
|
});
|
|
969
|
+
statusPersistedToServer = response.ok;
|
|
959
970
|
} catch (err) {
|
|
960
971
|
console.error(
|
|
961
972
|
`[WorkerPals] Failed to mark active job ${activeJobId} as failed during shutdown:`,
|
|
@@ -963,7 +974,10 @@ async function failActiveJobOnShutdown(
|
|
|
963
974
|
);
|
|
964
975
|
}
|
|
965
976
|
|
|
966
|
-
if (
|
|
977
|
+
if (
|
|
978
|
+
runtimeState.currentSessionId &&
|
|
979
|
+
shouldEmitDirectSessionJobEvent({ ok: false, statusPersistedToServer })
|
|
980
|
+
) {
|
|
967
981
|
await sendCommand(opts.server, runtimeState.currentSessionId, headers, {
|
|
968
982
|
type: "job_failed",
|
|
969
983
|
payload: {
|
|
@@ -1241,6 +1255,7 @@ async function workerLoop(
|
|
|
1241
1255
|
}
|
|
1242
1256
|
}
|
|
1243
1257
|
|
|
1258
|
+
let statusPersistedToServer = false;
|
|
1244
1259
|
if (result.ok) {
|
|
1245
1260
|
const reviewAgent =
|
|
1246
1261
|
parsedParams.reviewAgent && typeof parsedParams.reviewAgent === "object"
|
|
@@ -1252,7 +1267,7 @@ async function workerLoop(
|
|
|
1252
1267
|
reviewAgent.prUrl.trim().length > 0
|
|
1253
1268
|
? reviewAgent.prUrl.trim()
|
|
1254
1269
|
: null;
|
|
1255
|
-
await fetch(`${opts.server}/jobs/${job.id}/complete`, {
|
|
1270
|
+
const response = await fetch(`${opts.server}/jobs/${job.id}/complete`, {
|
|
1256
1271
|
method: "POST",
|
|
1257
1272
|
headers,
|
|
1258
1273
|
body: JSON.stringify({
|
|
@@ -1265,11 +1280,12 @@ async function workerLoop(
|
|
|
1265
1280
|
],
|
|
1266
1281
|
}),
|
|
1267
1282
|
});
|
|
1283
|
+
statusPersistedToServer = response.ok;
|
|
1268
1284
|
console.log(
|
|
1269
1285
|
`[WorkerPals] Job ${job.id} completed in ${formatDurationMs(jobDurationMs)}: ${result.summary}`,
|
|
1270
1286
|
);
|
|
1271
1287
|
} else {
|
|
1272
|
-
await fetch(`${opts.server}/jobs/${job.id}/fail`, {
|
|
1288
|
+
const response = await fetch(`${opts.server}/jobs/${job.id}/fail`, {
|
|
1273
1289
|
method: "POST",
|
|
1274
1290
|
headers,
|
|
1275
1291
|
body: JSON.stringify({
|
|
@@ -1278,6 +1294,7 @@ async function workerLoop(
|
|
|
1278
1294
|
durationMs: jobDurationMs,
|
|
1279
1295
|
}),
|
|
1280
1296
|
});
|
|
1297
|
+
statusPersistedToServer = response.ok;
|
|
1281
1298
|
console.log(
|
|
1282
1299
|
`[WorkerPals] Job ${job.id} failed in ${formatDurationMs(jobDurationMs)}: ${result.summary}`,
|
|
1283
1300
|
);
|
|
@@ -1318,29 +1335,31 @@ async function workerLoop(
|
|
|
1318
1335
|
}
|
|
1319
1336
|
}
|
|
1320
1337
|
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1338
|
+
if (shouldEmitDirectSessionJobEvent({ ok: result.ok, statusPersistedToServer })) {
|
|
1339
|
+
const eventCmd = result.ok
|
|
1340
|
+
? {
|
|
1341
|
+
type: "job_completed" as const,
|
|
1342
|
+
payload: {
|
|
1343
|
+
jobId: job.id,
|
|
1344
|
+
summary: result.summary,
|
|
1345
|
+
artifacts: result.stdout
|
|
1346
|
+
? [{ kind: "log" as const, text: result.stdout }]
|
|
1347
|
+
: undefined,
|
|
1348
|
+
},
|
|
1349
|
+
from: `worker:${opts.workerId}`,
|
|
1350
|
+
}
|
|
1351
|
+
: {
|
|
1352
|
+
type: "job_failed" as const,
|
|
1353
|
+
payload: {
|
|
1354
|
+
jobId: job.id,
|
|
1355
|
+
message: result.summary,
|
|
1356
|
+
detail: redactSensitiveText(result.stderr ?? ""),
|
|
1357
|
+
},
|
|
1358
|
+
from: `worker:${opts.workerId}`,
|
|
1359
|
+
};
|
|
1342
1360
|
|
|
1343
|
-
|
|
1361
|
+
await sendCommand(opts.server, job.sessionId, headers, eventCmd);
|
|
1362
|
+
}
|
|
1344
1363
|
}
|
|
1345
1364
|
} finally {
|
|
1346
1365
|
clearInterval(busyHeartbeat);
|
|
@@ -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(
|
|
267
|
-
|
|
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 (
|
|
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
|
|
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)
|
|
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(
|
|
95
|
-
.
|
|
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(
|
|
155
|
-
|
|
156
|
-
|
|
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") ??
|
|
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(
|
|
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
|
|
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 ?? "")
|
|
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 {
|
|
@@ -50,10 +50,7 @@ export function parseLocalBuddyRuntimeSnapshot(raw: string): LocalBuddyRuntimeSn
|
|
|
50
50
|
return {
|
|
51
51
|
localbuddy: {
|
|
52
52
|
enabled,
|
|
53
|
-
port:
|
|
54
|
-
Number.isFinite(port) && port >= 1 && port <= 65_535
|
|
55
|
-
? port
|
|
56
|
-
: DEFAULT_LOCALBUDDY_PORT,
|
|
53
|
+
port: Number.isFinite(port) && port >= 1 && port <= 65_535 ? port : DEFAULT_LOCALBUDDY_PORT,
|
|
57
54
|
},
|
|
58
55
|
};
|
|
59
56
|
}
|
|
@@ -155,7 +152,9 @@ function resolveRuntimeConfigDir(workspaceRoot: string, configuredDir?: string):
|
|
|
155
152
|
}
|
|
156
153
|
|
|
157
154
|
function parseBoolEnv(value: string | undefined): boolean | undefined {
|
|
158
|
-
const text = String(value ?? "")
|
|
155
|
+
const text = String(value ?? "")
|
|
156
|
+
.trim()
|
|
157
|
+
.toLowerCase();
|
|
159
158
|
if (!text) return undefined;
|
|
160
159
|
if (TRUTHY.has(text)) return true;
|
|
161
160
|
if (FALSY.has(text)) return false;
|
|
@@ -40,7 +40,9 @@ export type VisionDocValidation = {
|
|
|
40
40
|
const MAX_KEY_ITEMS_PER_BUCKET = 8;
|
|
41
41
|
|
|
42
42
|
function toLines(markdown: string): string[] {
|
|
43
|
-
return String(markdown ?? "")
|
|
43
|
+
return String(markdown ?? "")
|
|
44
|
+
.replace(/\r\n/g, "\n")
|
|
45
|
+
.split("\n");
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
function extractOneSentence(lines: string[]): string {
|
|
@@ -75,7 +77,9 @@ function extractOneSentence(lines: string[]): string {
|
|
|
75
77
|
}
|
|
76
78
|
|
|
77
79
|
function normalizeItem(value: string): string {
|
|
78
|
-
return String(value ?? "")
|
|
80
|
+
return String(value ?? "")
|
|
81
|
+
.replace(/\s+/g, " ")
|
|
82
|
+
.trim();
|
|
79
83
|
}
|
|
80
84
|
|
|
81
85
|
function dedupeAndClamp(values: string[]): string[] {
|
|
@@ -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
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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,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.
|
|
@@ -9,18 +9,21 @@
|
|
|
9
9
|
## 1) Who this is for
|
|
10
10
|
|
|
11
11
|
### Primary users
|
|
12
|
+
|
|
12
13
|
- **User type A:** (e.g., app developers, SREs, analysts, end-users)
|
|
13
14
|
- Jobs-to-be-done: …
|
|
14
15
|
- Pain today: …
|
|
15
16
|
- Success looks like: …
|
|
16
17
|
|
|
17
18
|
### Secondary users
|
|
19
|
+
|
|
18
20
|
- **User type B:** …
|
|
19
21
|
- Jobs-to-be-done: …
|
|
20
22
|
- Pain today: …
|
|
21
23
|
- Success looks like: …
|
|
22
24
|
|
|
23
|
-
### Non-users (explicitly
|
|
25
|
+
### Non-users (explicitly _not_ optimizing for)
|
|
26
|
+
|
|
24
27
|
- **Not for:** …
|
|
25
28
|
- Why: …
|
|
26
29
|
|
|
@@ -31,13 +34,15 @@
|
|
|
31
34
|
## 2) The problem we solve
|
|
32
35
|
|
|
33
36
|
### Today’s reality
|
|
37
|
+
|
|
34
38
|
- What is hard / slow / risky today?
|
|
35
39
|
- What failures happen repeatedly? (bugs, incidents, misconfig, confusion)
|
|
36
40
|
- What is expensive? (time, money, cognitive load, coordination)
|
|
37
41
|
|
|
38
42
|
### The change we want
|
|
39
|
-
|
|
40
|
-
- In
|
|
43
|
+
|
|
44
|
+
- In 6–12 months, what should feel _meaningfully easier_?
|
|
45
|
+
- In 2–3 years, what should be _obviously different_?
|
|
41
46
|
|
|
42
47
|
> **Optional:** Add a 3–5 line “story” of a user before vs after.
|
|
43
48
|
|
|
@@ -66,11 +71,13 @@ These are **tie-breakers** when tradeoffs happen. Put them in priority order.
|
|
|
66
71
|
Pick a small set of metrics you can actually track.
|
|
67
72
|
|
|
68
73
|
### User-facing outcomes
|
|
74
|
+
|
|
69
75
|
- **Time-to-success:** e.g., median time from install → first successful use
|
|
70
76
|
- **Quality:** e.g., bug rate / support tickets per active user
|
|
71
77
|
- **Trust:** e.g., SLO compliance, error rate, crash-free sessions
|
|
72
78
|
|
|
73
79
|
### Developer / maintainer outcomes
|
|
80
|
+
|
|
74
81
|
- **Change velocity:** PR cycle time, lead time to release
|
|
75
82
|
- **Operational burden:** pages/alerts per week, toil hours
|
|
76
83
|
- **Maintainability:** test coverage for critical paths, build time, flake rate
|
|
@@ -81,17 +88,20 @@ Pick a small set of metrics you can actually track.
|
|
|
81
88
|
|
|
82
89
|
## 5) Scope and boundaries
|
|
83
90
|
|
|
84
|
-
### In scope (what we
|
|
91
|
+
### In scope (what we _are_)
|
|
92
|
+
|
|
85
93
|
- Core capability A: …
|
|
86
94
|
- Core capability B: …
|
|
87
95
|
- Core capability C: …
|
|
88
96
|
|
|
89
|
-
### Out of scope / non-goals (what we are
|
|
97
|
+
### Out of scope / non-goals (what we are _not_)
|
|
98
|
+
|
|
90
99
|
- Not a replacement for: …
|
|
91
100
|
- Not trying to support: …
|
|
92
101
|
- Not optimizing for: …
|
|
93
102
|
|
|
94
103
|
### Compatibility & support policy (optional)
|
|
104
|
+
|
|
95
105
|
- Supported platforms / versions: …
|
|
96
106
|
- Breaking changes policy: …
|
|
97
107
|
- Deprecation timeline: …
|
|
@@ -120,6 +130,7 @@ Pick 3–5 items max. Each should be **outcome-oriented**.
|
|
|
120
130
|
These are “bets” with explicit results.
|
|
121
131
|
|
|
122
132
|
### Objective A: <name>
|
|
133
|
+
|
|
123
134
|
- **Problem:** …
|
|
124
135
|
- **Approach:** …
|
|
125
136
|
- **Deliverables:** …
|
|
@@ -127,6 +138,7 @@ These are “bets” with explicit results.
|
|
|
127
138
|
- **Exit criteria:** How we’ll know it worked (measurable)
|
|
128
139
|
|
|
129
140
|
### Objective B: <name>
|
|
141
|
+
|
|
130
142
|
- …
|
|
131
143
|
|
|
132
144
|
---
|
|
@@ -136,6 +148,7 @@ These are “bets” with explicit results.
|
|
|
136
148
|
Describe where this repo is going, without over-promising.
|
|
137
149
|
|
|
138
150
|
### Strategic bets
|
|
151
|
+
|
|
139
152
|
- **Bet 1:** …
|
|
140
153
|
- Why it matters: …
|
|
141
154
|
- What we’ll likely build: …
|
|
@@ -143,6 +156,7 @@ Describe where this repo is going, without over-promising.
|
|
|
143
156
|
- **Bet 2:** …
|
|
144
157
|
|
|
145
158
|
### “If we’re right, then…”
|
|
159
|
+
|
|
146
160
|
- Users will be able to: …
|
|
147
161
|
- Maintainers will spend less time on: …
|
|
148
162
|
- The ecosystem will have: …
|
|
@@ -152,6 +166,7 @@ Describe where this repo is going, without over-promising.
|
|
|
152
166
|
## 9) Guardrails and constraints
|
|
153
167
|
|
|
154
168
|
### Guardrails (how we avoid harm / churn)
|
|
169
|
+
|
|
155
170
|
- Prefer changes that are **reversible** or behind flags.
|
|
156
171
|
- Default to **secure / safe** settings.
|
|
157
172
|
- Optimize for the **common path**; support escape hatches for experts.
|
|
@@ -159,6 +174,7 @@ Describe where this repo is going, without over-promising.
|
|
|
159
174
|
- Pay down operational toil before adding big surface area.
|
|
160
175
|
|
|
161
176
|
### Constraints (reality checks)
|
|
177
|
+
|
|
162
178
|
- Staffing level / maintainer bandwidth: …
|
|
163
179
|
- Hard requirements (privacy, compliance, perf, cost): …
|
|
164
180
|
- External dependencies: …
|
|
@@ -181,11 +197,14 @@ Describe where this repo is going, without over-promising.
|
|
|
181
197
|
## Appendix (optional but powerful)
|
|
182
198
|
|
|
183
199
|
### A) Glossary
|
|
200
|
+
|
|
184
201
|
- Term: definition…
|
|
185
202
|
|
|
186
203
|
### B) Personas (one-page each)
|
|
204
|
+
|
|
187
205
|
- Persona, environment, constraints, success criteria…
|
|
188
206
|
|
|
189
207
|
### C) Example “no” responses (template)
|
|
208
|
+
|
|
190
209
|
- “Thanks — this is valuable, but it conflicts with our non-goal X…”
|
|
191
210
|
- “We’d reconsider if metric Y becomes a problem…”
|