@brainervirus/workit-pi 2.5.0 → 2.7.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.
- package/dist/worker.js +38 -3
- package/dist/workit.js +774 -262
- package/package.json +1 -1
- package/skills/workit-behavioral-tdd/SKILL.md +15 -9
- package/skills/workit-debug/SKILL.md +2 -1
package/dist/workit.js
CHANGED
|
@@ -2,9 +2,9 @@
|
|
|
2
2
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
3
3
|
|
|
4
4
|
// packages/workit-core/src/core.ts
|
|
5
|
-
import { spawnSync as
|
|
5
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
6
6
|
import { existsSync as existsSync6, realpathSync as realpathSync8 } from "node:fs";
|
|
7
|
-
import
|
|
7
|
+
import path15 from "node:path";
|
|
8
8
|
|
|
9
9
|
// packages/workit-core/src/core/task-contract.ts
|
|
10
10
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -8121,7 +8121,37 @@ var candidateSchema = object({
|
|
|
8121
8121
|
path: ["id"]
|
|
8122
8122
|
});
|
|
8123
8123
|
});
|
|
8124
|
-
var
|
|
8124
|
+
var checkObservationSchema = object({
|
|
8125
|
+
observer: literal("workit_cli"),
|
|
8126
|
+
name: nonEmpty.nullable(),
|
|
8127
|
+
configured: boolean2(),
|
|
8128
|
+
argv: array(text).min(1),
|
|
8129
|
+
shell: boolean2(),
|
|
8130
|
+
cwd: nonEmpty,
|
|
8131
|
+
exitCode: safeInteger,
|
|
8132
|
+
durationMs: safeInteger.min(0),
|
|
8133
|
+
timedOut: boolean2(),
|
|
8134
|
+
head: text.nullable(),
|
|
8135
|
+
tree: text.nullable(),
|
|
8136
|
+
dirty: boolean2().nullable(),
|
|
8137
|
+
signal: text.nullable(),
|
|
8138
|
+
treeAfter: text.nullable(),
|
|
8139
|
+
modifiedWorktree: boolean2(),
|
|
8140
|
+
base: text.nullable(),
|
|
8141
|
+
patchId: text.nullable(),
|
|
8142
|
+
environment: object({
|
|
8143
|
+
platform: nonEmpty,
|
|
8144
|
+
arch: nonEmpty,
|
|
8145
|
+
vars: record(nonEmpty, text.nullable())
|
|
8146
|
+
}).strict(),
|
|
8147
|
+
logDigest: text.nullable(),
|
|
8148
|
+
logRef: text.nullable(),
|
|
8149
|
+
logTail: array(text).max(40),
|
|
8150
|
+
ledgerRowId: text.nullable(),
|
|
8151
|
+
attestation: object({ host: nonEmpty, session: text.nullable(), agentId: text.nullable() }).strict().nullable()
|
|
8152
|
+
}).strict();
|
|
8153
|
+
var CHECK_OBSERVATION_PATH = "evidence.*.data.observation";
|
|
8154
|
+
var evidenceFields = {
|
|
8125
8155
|
kind: _enum(["check", "review", "investigation", "artifact"]),
|
|
8126
8156
|
claim: text,
|
|
8127
8157
|
requirementIds: array(digest),
|
|
@@ -8132,7 +8162,9 @@ var evidenceSchema = object({
|
|
|
8132
8162
|
refs: array(refSchema),
|
|
8133
8163
|
exitCode: safeInteger.nullable(),
|
|
8134
8164
|
reviewContext: refSchema.nullable()
|
|
8135
|
-
}
|
|
8165
|
+
};
|
|
8166
|
+
var evidenceSchema = object({ ...evidenceFields, observation: checkObservationSchema.optional() }).strict();
|
|
8167
|
+
var reportedEvidenceSchema = object(evidenceFields).strict();
|
|
8136
8168
|
var evidenceEvaluationSchema = object({
|
|
8137
8169
|
evidenceId: id,
|
|
8138
8170
|
status: _enum(["passed", "failed", "missing", "skipped", "stale"]),
|
|
@@ -8464,7 +8496,7 @@ var evidenceOperations = {
|
|
|
8464
8496
|
action: literal("record"),
|
|
8465
8497
|
...taskId,
|
|
8466
8498
|
expectedRevision: revision.optional(),
|
|
8467
|
-
evidence:
|
|
8499
|
+
evidence: reportedEvidenceSchema
|
|
8468
8500
|
})
|
|
8469
8501
|
};
|
|
8470
8502
|
var findingOperations = {
|
|
@@ -10964,10 +10996,10 @@ function assertProductWriteAllowed(input) {
|
|
|
10964
10996
|
}
|
|
10965
10997
|
|
|
10966
10998
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
10967
|
-
import { createHash as
|
|
10968
|
-
import * as
|
|
10969
|
-
import { spawnSync as
|
|
10970
|
-
import
|
|
10999
|
+
import { createHash as createHash8 } from "node:crypto";
|
|
11000
|
+
import * as fs10 from "node:fs";
|
|
11001
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
11002
|
+
import path13 from "node:path";
|
|
10971
11003
|
|
|
10972
11004
|
// packages/workit-core/src/core/authority.ts
|
|
10973
11005
|
import { createHash as createHash6 } from "node:crypto";
|
|
@@ -13174,16 +13206,407 @@ function reconcileAction(input) {
|
|
|
13174
13206
|
var bindingCovers = scopeCovers;
|
|
13175
13207
|
var verifyDecisionContent = verifyContentRefs;
|
|
13176
13208
|
|
|
13209
|
+
// packages/workit-core/src/check-config.ts
|
|
13210
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
13211
|
+
import fs8 from "node:fs";
|
|
13212
|
+
import path11 from "node:path";
|
|
13213
|
+
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
13214
|
+
var CHECKS_FILE = "workit.checks.json";
|
|
13215
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
13216
|
+
var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
|
|
13217
|
+
function splitCommand(command) {
|
|
13218
|
+
const argv = [];
|
|
13219
|
+
let current = "";
|
|
13220
|
+
let quote = null;
|
|
13221
|
+
let started = false;
|
|
13222
|
+
for (const char of command) {
|
|
13223
|
+
if (quote) {
|
|
13224
|
+
if (char === quote)
|
|
13225
|
+
quote = null;
|
|
13226
|
+
else
|
|
13227
|
+
current += char;
|
|
13228
|
+
continue;
|
|
13229
|
+
}
|
|
13230
|
+
if (char === '"' || char === "'") {
|
|
13231
|
+
quote = char;
|
|
13232
|
+
started = true;
|
|
13233
|
+
continue;
|
|
13234
|
+
}
|
|
13235
|
+
if (/\s/u.test(char)) {
|
|
13236
|
+
if (started)
|
|
13237
|
+
argv.push(current);
|
|
13238
|
+
current = "";
|
|
13239
|
+
started = false;
|
|
13240
|
+
continue;
|
|
13241
|
+
}
|
|
13242
|
+
if (SHELL_META.test(char))
|
|
13243
|
+
return new Error(`"${command}" uses shell syntax (${char}); configure an argv array instead`);
|
|
13244
|
+
current += char;
|
|
13245
|
+
started = true;
|
|
13246
|
+
}
|
|
13247
|
+
if (quote)
|
|
13248
|
+
return new Error(`"${command}" has an unterminated quote`);
|
|
13249
|
+
if (started)
|
|
13250
|
+
argv.push(current);
|
|
13251
|
+
return argv.length ? argv : new Error("empty command");
|
|
13252
|
+
}
|
|
13253
|
+
var git = (cwd, args) => {
|
|
13254
|
+
const run = spawnSync3("git", args, {
|
|
13255
|
+
cwd,
|
|
13256
|
+
encoding: "utf8",
|
|
13257
|
+
timeout: 20000,
|
|
13258
|
+
killSignal: "SIGKILL",
|
|
13259
|
+
windowsHide: true
|
|
13260
|
+
});
|
|
13261
|
+
const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
|
|
13262
|
+
return value || null;
|
|
13263
|
+
};
|
|
13264
|
+
function checkRoot(cwd) {
|
|
13265
|
+
return path11.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
13266
|
+
}
|
|
13267
|
+
var packageManager = (root) => {
|
|
13268
|
+
const has = (file) => fs8.existsSync(path11.join(root, file));
|
|
13269
|
+
if (has("bun.lock") || has("bun.lockb"))
|
|
13270
|
+
return "bun";
|
|
13271
|
+
if (has("pnpm-lock.yaml"))
|
|
13272
|
+
return "pnpm";
|
|
13273
|
+
if (has("yarn.lock"))
|
|
13274
|
+
return "yarn";
|
|
13275
|
+
return "npm";
|
|
13276
|
+
};
|
|
13277
|
+
var scriptCheck = (pm, name) => {
|
|
13278
|
+
const argv = [pm, "run", name];
|
|
13279
|
+
const accepts = [argv];
|
|
13280
|
+
if (pm === "pnpm" || pm === "yarn" || pm === "npm" && name === "test")
|
|
13281
|
+
accepts.push([pm, name]);
|
|
13282
|
+
return { name, argv, accepts };
|
|
13283
|
+
};
|
|
13284
|
+
var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
13285
|
+
function detectedChecks(root) {
|
|
13286
|
+
const read = (file) => {
|
|
13287
|
+
try {
|
|
13288
|
+
return fs8.readFileSync(path11.join(root, file), "utf8");
|
|
13289
|
+
} catch {
|
|
13290
|
+
return null;
|
|
13291
|
+
}
|
|
13292
|
+
};
|
|
13293
|
+
const checks = [];
|
|
13294
|
+
const add = (check) => {
|
|
13295
|
+
if (!checks.some((item) => item.name === check.name))
|
|
13296
|
+
checks.push(check);
|
|
13297
|
+
};
|
|
13298
|
+
const manifestText = read("package.json");
|
|
13299
|
+
if (manifestText !== null) {
|
|
13300
|
+
let manifest = null;
|
|
13301
|
+
try {
|
|
13302
|
+
manifest = JSON.parse(manifestText);
|
|
13303
|
+
} catch {}
|
|
13304
|
+
const scripts = isRecord2(manifest) && isRecord2(manifest.scripts) ? manifest.scripts : {};
|
|
13305
|
+
const pm = packageManager(root);
|
|
13306
|
+
for (const name of SCRIPT_CHECKS) {
|
|
13307
|
+
const script = scripts[name];
|
|
13308
|
+
if (typeof script === "string" && script.trim() !== "")
|
|
13309
|
+
add(scriptCheck(pm, name));
|
|
13310
|
+
}
|
|
13311
|
+
}
|
|
13312
|
+
const plain = (name, argv) => ({ name, argv, accepts: [argv] });
|
|
13313
|
+
if (read("go.mod") !== null)
|
|
13314
|
+
add(plain("test", ["go", "test", "./..."]));
|
|
13315
|
+
if (read("Cargo.toml") !== null)
|
|
13316
|
+
add(plain("test", ["cargo", "test"]));
|
|
13317
|
+
if (read("pytest.ini") !== null || /\bpytest\b/u.test(read("pyproject.toml") ?? ""))
|
|
13318
|
+
add(plain("test", ["pytest"]));
|
|
13319
|
+
const makefile = read("Makefile") ?? read("makefile") ?? read("GNUmakefile");
|
|
13320
|
+
if (makefile !== null && /^test\s*:(?!=)/mu.test(makefile))
|
|
13321
|
+
add(plain("test", ["make", "test"]));
|
|
13322
|
+
return checks;
|
|
13323
|
+
}
|
|
13324
|
+
var GATES = ["testing", "verification"];
|
|
13325
|
+
function loadCheckConfig(cwd) {
|
|
13326
|
+
const root = checkRoot(cwd);
|
|
13327
|
+
const file = path11.join(root, CHECKS_FILE);
|
|
13328
|
+
const none = (error = null) => ({
|
|
13329
|
+
source: error ? CHECKS_FILE : "none",
|
|
13330
|
+
root,
|
|
13331
|
+
checks: [],
|
|
13332
|
+
gates: {},
|
|
13333
|
+
error
|
|
13334
|
+
});
|
|
13335
|
+
let text = null;
|
|
13336
|
+
try {
|
|
13337
|
+
text = fs8.readFileSync(file, "utf8");
|
|
13338
|
+
} catch (error) {
|
|
13339
|
+
if (error.code !== "ENOENT")
|
|
13340
|
+
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
13341
|
+
}
|
|
13342
|
+
if (text === null) {
|
|
13343
|
+
const checks = detectedChecks(root);
|
|
13344
|
+
return checks.length ? { source: "defaults", root, checks, gates: {}, error: null } : none();
|
|
13345
|
+
}
|
|
13346
|
+
let parsed;
|
|
13347
|
+
try {
|
|
13348
|
+
parsed = JSON.parse(text);
|
|
13349
|
+
} catch {
|
|
13350
|
+
return none(`${CHECKS_FILE} is not valid JSON`);
|
|
13351
|
+
}
|
|
13352
|
+
if (!isRecord2(parsed) || !isRecord2(parsed.checks))
|
|
13353
|
+
return none(`${CHECKS_FILE} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
|
|
13354
|
+
const checks = [];
|
|
13355
|
+
for (const [name, value] of Object.entries(parsed.checks)) {
|
|
13356
|
+
if (!CHECK_NAME.test(name))
|
|
13357
|
+
return none(`${CHECKS_FILE}: check name "${name}" must match ${CHECK_NAME.source}`);
|
|
13358
|
+
const argv = typeof value === "string" ? splitCommand(value) : Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && item.length > 0) ? value : new Error("expected a command string or a non-empty argv array");
|
|
13359
|
+
if (argv instanceof Error)
|
|
13360
|
+
return none(`${CHECKS_FILE}: check "${name}": ${argv.message}`);
|
|
13361
|
+
checks.push({ name, argv, accepts: [argv] });
|
|
13362
|
+
}
|
|
13363
|
+
const gates = {};
|
|
13364
|
+
if (parsed.gates !== undefined) {
|
|
13365
|
+
if (!isRecord2(parsed.gates))
|
|
13366
|
+
return none(`${CHECKS_FILE}: gates must be {"testing":"<name>"|["<name>",…]}`);
|
|
13367
|
+
for (const [gate, value] of Object.entries(parsed.gates)) {
|
|
13368
|
+
if (!GATES.includes(gate))
|
|
13369
|
+
return none(`${CHECKS_FILE}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
|
|
13370
|
+
const names = typeof value === "string" ? [value] : value;
|
|
13371
|
+
if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
|
|
13372
|
+
return none(`${CHECKS_FILE}: gates.${gate} must name configured checks`);
|
|
13373
|
+
gates[gate] = names;
|
|
13374
|
+
}
|
|
13375
|
+
}
|
|
13376
|
+
return { source: CHECKS_FILE, root, checks, gates, error: null };
|
|
13377
|
+
}
|
|
13378
|
+
var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
|
|
13379
|
+
function matchesNamedCheck(config, name, argv, cwd) {
|
|
13380
|
+
if (config.error || name === null || cwd !== ".")
|
|
13381
|
+
return false;
|
|
13382
|
+
const check = config.checks.find((item) => item.name === name);
|
|
13383
|
+
return Boolean(check?.accepts.some((accepted) => sameArgv(accepted, argv)));
|
|
13384
|
+
}
|
|
13385
|
+
function gateCheckNames(config, dimension) {
|
|
13386
|
+
const explicit = config.gates[dimension];
|
|
13387
|
+
if (explicit)
|
|
13388
|
+
return explicit;
|
|
13389
|
+
const names = config.checks.map((check) => check.name);
|
|
13390
|
+
if (dimension === "testing")
|
|
13391
|
+
return names.includes("test") ? ["test"] : [];
|
|
13392
|
+
return names;
|
|
13393
|
+
}
|
|
13394
|
+
var workitOnPath;
|
|
13395
|
+
function workitAvailable(env = process.env) {
|
|
13396
|
+
if (workitOnPath !== undefined)
|
|
13397
|
+
return workitOnPath;
|
|
13398
|
+
const windows = process.platform === "win32";
|
|
13399
|
+
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
13400
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path11.delimiter).filter(Boolean);
|
|
13401
|
+
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
13402
|
+
try {
|
|
13403
|
+
return fs8.statSync(path11.join(dir, `workit${ext}`)).isFile();
|
|
13404
|
+
} catch {
|
|
13405
|
+
return false;
|
|
13406
|
+
}
|
|
13407
|
+
}));
|
|
13408
|
+
return workitOnPath;
|
|
13409
|
+
}
|
|
13410
|
+
function checkCommand(args, version, onPath = workitAvailable()) {
|
|
13411
|
+
return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
|
|
13412
|
+
}
|
|
13413
|
+
|
|
13414
|
+
// packages/workit-core/src/git/rev.ts
|
|
13415
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
13416
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
13417
|
+
import fs9 from "node:fs";
|
|
13418
|
+
import os2 from "node:os";
|
|
13419
|
+
import path12 from "node:path";
|
|
13420
|
+
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
13421
|
+
var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
13422
|
+
var git2 = (cwd, args, options = {}) => {
|
|
13423
|
+
const result = spawnSync4("git", args, {
|
|
13424
|
+
cwd,
|
|
13425
|
+
encoding: "utf8",
|
|
13426
|
+
env: options.env ?? process.env,
|
|
13427
|
+
input: options.input,
|
|
13428
|
+
maxBuffer: MAX_BUFFER,
|
|
13429
|
+
timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
|
|
13430
|
+
killSignal: "SIGKILL",
|
|
13431
|
+
windowsHide: true
|
|
13432
|
+
});
|
|
13433
|
+
const error = result.error;
|
|
13434
|
+
return {
|
|
13435
|
+
ok: result.status === 0,
|
|
13436
|
+
stdout: result.stdout ?? "",
|
|
13437
|
+
stderr: result.stderr ?? "",
|
|
13438
|
+
timedOut: error?.code === "ETIMEDOUT"
|
|
13439
|
+
};
|
|
13440
|
+
};
|
|
13441
|
+
var line = (run) => {
|
|
13442
|
+
if (!run.ok)
|
|
13443
|
+
return null;
|
|
13444
|
+
const value = run.stdout.trim();
|
|
13445
|
+
return value ? value : null;
|
|
13446
|
+
};
|
|
13447
|
+
function networkGitInvocation(cwd, args, timeoutMs) {
|
|
13448
|
+
const seconds = Math.max(1, Math.ceil(timeoutMs / 1000));
|
|
13449
|
+
const env = {
|
|
13450
|
+
...process.env,
|
|
13451
|
+
GIT_TERMINAL_PROMPT: "0",
|
|
13452
|
+
GIT_ASKPASS: "",
|
|
13453
|
+
SSH_ASKPASS: "",
|
|
13454
|
+
SSH_ASKPASS_REQUIRE: "never",
|
|
13455
|
+
GCM_INTERACTIVE: "never"
|
|
13456
|
+
};
|
|
13457
|
+
const base = process.env.GIT_SSH_COMMAND || line(git2(cwd, ["config", "--get", "core.sshCommand"])) || (process.env.GIT_SSH ? null : "ssh");
|
|
13458
|
+
if (base)
|
|
13459
|
+
env.GIT_SSH_COMMAND = `${base} -o BatchMode=yes -o ConnectTimeout=${Math.min(10, seconds)}`;
|
|
13460
|
+
return {
|
|
13461
|
+
args: ["-c", "http.lowSpeedLimit=1", "-c", `http.lowSpeedTime=${seconds}`, ...args],
|
|
13462
|
+
env
|
|
13463
|
+
};
|
|
13464
|
+
}
|
|
13465
|
+
var WATCHDOG = `
|
|
13466
|
+
const cp = require("node:child_process");
|
|
13467
|
+
const { ms, args } = JSON.parse(process.env.WORKIT_GIT_WATCHDOG);
|
|
13468
|
+
delete process.env.WORKIT_GIT_WATCHDOG;
|
|
13469
|
+
const win = process.platform === "win32";
|
|
13470
|
+
const child = cp.spawn("git", args, { stdio: "inherit", detached: !win, windowsHide: true });
|
|
13471
|
+
const killTree = () => {
|
|
13472
|
+
if (!child.pid) return;
|
|
13473
|
+
if (win) cp.spawnSync("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
|
|
13474
|
+
else try { process.kill(-child.pid, "SIGKILL"); } catch {}
|
|
13475
|
+
};
|
|
13476
|
+
let timedOut = false;
|
|
13477
|
+
const timer = setTimeout(() => { timedOut = true; killTree(); }, ms);
|
|
13478
|
+
child.on("error", () => { clearTimeout(timer); process.exit(127); });
|
|
13479
|
+
child.on("exit", (code) => {
|
|
13480
|
+
clearTimeout(timer);
|
|
13481
|
+
if (!win) killTree();
|
|
13482
|
+
process.exit(timedOut ? 124 : code ?? 1);
|
|
13483
|
+
});
|
|
13484
|
+
`;
|
|
13485
|
+
var WATCHDOG_TIMEOUT_EXIT = 124;
|
|
13486
|
+
var gitNetwork = (cwd, args, timeoutMs) => {
|
|
13487
|
+
const invocation = networkGitInvocation(cwd, args, timeoutMs);
|
|
13488
|
+
const result = spawnSync4(process.execPath, ["-e", WATCHDOG], {
|
|
13489
|
+
cwd,
|
|
13490
|
+
encoding: "utf8",
|
|
13491
|
+
env: {
|
|
13492
|
+
...invocation.env,
|
|
13493
|
+
WORKIT_GIT_WATCHDOG: JSON.stringify({ ms: timeoutMs, args: invocation.args })
|
|
13494
|
+
},
|
|
13495
|
+
maxBuffer: MAX_BUFFER,
|
|
13496
|
+
timeout: timeoutMs + 1e4,
|
|
13497
|
+
killSignal: "SIGKILL",
|
|
13498
|
+
windowsHide: true
|
|
13499
|
+
});
|
|
13500
|
+
const error = result.error;
|
|
13501
|
+
return {
|
|
13502
|
+
ok: result.status === 0,
|
|
13503
|
+
stdout: result.stdout ?? "",
|
|
13504
|
+
stderr: result.stderr ?? "",
|
|
13505
|
+
timedOut: result.status === WATCHDOG_TIMEOUT_EXIT || error?.code === "ETIMEDOUT"
|
|
13506
|
+
};
|
|
13507
|
+
};
|
|
13508
|
+
function headSha(cwd) {
|
|
13509
|
+
return line(git2(cwd, ["rev-parse", "--verify", "-q", "HEAD^{commit}"]));
|
|
13510
|
+
}
|
|
13511
|
+
var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
|
|
13512
|
+
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
13513
|
+
var seedIndex = (from, to) => {
|
|
13514
|
+
fs9.copyFileSync(from, to);
|
|
13515
|
+
const { atimeNs, mtimeNs } = fs9.statSync(from, { bigint: true });
|
|
13516
|
+
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
13517
|
+
fs9.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
13518
|
+
};
|
|
13519
|
+
function worktreeTree(cwd, options = {}) {
|
|
13520
|
+
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
13521
|
+
const indexPath = line(git2(cwd, ["rev-parse", "--path-format=absolute", "--git-path", "index"]));
|
|
13522
|
+
if (!top || !indexPath)
|
|
13523
|
+
return null;
|
|
13524
|
+
const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
|
|
13525
|
+
const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
|
|
13526
|
+
const scratch = fs9.mkdtempSync(path12.join(os2.tmpdir(), "workit-tree-"));
|
|
13527
|
+
try {
|
|
13528
|
+
const tempIndex = path12.join(scratch, "index");
|
|
13529
|
+
if (fs9.existsSync(indexPath))
|
|
13530
|
+
seedIndex(indexPath, tempIndex);
|
|
13531
|
+
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
13532
|
+
const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
13533
|
+
env,
|
|
13534
|
+
timeoutMs
|
|
13535
|
+
});
|
|
13536
|
+
if (!untracked.ok)
|
|
13537
|
+
return null;
|
|
13538
|
+
const skipped = [];
|
|
13539
|
+
for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
|
|
13540
|
+
try {
|
|
13541
|
+
const stat = fs9.statSync(path12.join(top, file));
|
|
13542
|
+
if (stat.isFile() && stat.size > cap)
|
|
13543
|
+
skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
|
|
13544
|
+
} catch {}
|
|
13545
|
+
}
|
|
13546
|
+
skipped.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
13547
|
+
const added = skipped.length ? git2(top, ["add", "-A", "--pathspec-from-file=-", "--pathspec-file-nul"], {
|
|
13548
|
+
env,
|
|
13549
|
+
timeoutMs,
|
|
13550
|
+
input: [".", ...skipped.map((file) => `:(exclude,literal)${file.path}`)].join("\x00")
|
|
13551
|
+
}) : git2(top, ["add", "-A"], { env, timeoutMs });
|
|
13552
|
+
if (!added.ok)
|
|
13553
|
+
return null;
|
|
13554
|
+
const tree = line(git2(top, ["write-tree"], { env, timeoutMs }));
|
|
13555
|
+
if (!tree)
|
|
13556
|
+
return null;
|
|
13557
|
+
const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
|
|
13558
|
+
const key = skipped.length ? `sha256:${createHash7("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
|
|
13559
|
+
return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
|
|
13560
|
+
} finally {
|
|
13561
|
+
fs9.rmSync(scratch, { recursive: true, force: true });
|
|
13562
|
+
}
|
|
13563
|
+
}
|
|
13564
|
+
function worktreeSignal(cwd, options = {}) {
|
|
13565
|
+
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
13566
|
+
if (!top)
|
|
13567
|
+
return null;
|
|
13568
|
+
const status = git2(top, [
|
|
13569
|
+
"status",
|
|
13570
|
+
"--porcelain=v1",
|
|
13571
|
+
"-z",
|
|
13572
|
+
"--untracked-files=all",
|
|
13573
|
+
"--no-renames",
|
|
13574
|
+
"--ignore-submodules=dirty"
|
|
13575
|
+
], {
|
|
13576
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
|
|
13577
|
+
timeoutMs: options.timeoutMs ?? GIT_TIMEOUTS.worktree
|
|
13578
|
+
});
|
|
13579
|
+
if (!status.ok)
|
|
13580
|
+
return null;
|
|
13581
|
+
const hash = createHash7("sha256");
|
|
13582
|
+
hash.update(`${headSha(top) ?? "unborn"}\x00`);
|
|
13583
|
+
for (const entry of status.stdout.split("\x00")) {
|
|
13584
|
+
if (entry.length < 4)
|
|
13585
|
+
continue;
|
|
13586
|
+
const file = entry.slice(3);
|
|
13587
|
+
const absolute = path12.join(top, file);
|
|
13588
|
+
let stat = "-";
|
|
13589
|
+
try {
|
|
13590
|
+
const info = fs9.lstatSync(absolute, { bigint: true });
|
|
13591
|
+
stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
|
|
13592
|
+
if (info.isDirectory())
|
|
13593
|
+
stat += `:${headSha(absolute) ?? "-"}`;
|
|
13594
|
+
} catch {}
|
|
13595
|
+
hash.update(`${entry.slice(0, 2)}\x00${file}\x00${stat}\x00`);
|
|
13596
|
+
}
|
|
13597
|
+
return `sig:${hash.digest("hex")}`;
|
|
13598
|
+
}
|
|
13599
|
+
|
|
13177
13600
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
13178
|
-
var digestBytes3 = (value) =>
|
|
13179
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
13180
|
-
var relative = (root, candidate) =>
|
|
13601
|
+
var digestBytes3 = (value) => createHash8("sha256").update(value).digest("hex");
|
|
13602
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path13.sep}`);
|
|
13603
|
+
var relative = (root, candidate) => path13.relative(root, candidate).split(path13.sep).join("/") || ".";
|
|
13181
13604
|
var canonicalPath = (value) => {
|
|
13182
13605
|
if (typeof value !== "string" || !value)
|
|
13183
13606
|
return null;
|
|
13184
13607
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
13185
13608
|
return null;
|
|
13186
|
-
const normalized =
|
|
13609
|
+
const normalized = path13.posix.normalize(value);
|
|
13187
13610
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
13188
13611
|
return null;
|
|
13189
13612
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -13219,7 +13642,7 @@ var compareCodeUnits2 = (left, right) => {
|
|
|
13219
13642
|
return left.length - right.length;
|
|
13220
13643
|
};
|
|
13221
13644
|
var gitPaths = (root) => {
|
|
13222
|
-
const result =
|
|
13645
|
+
const result = spawnSync5("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
13223
13646
|
cwd: root,
|
|
13224
13647
|
encoding: "buffer"
|
|
13225
13648
|
});
|
|
@@ -13231,21 +13654,21 @@ var gitPaths = (root) => {
|
|
|
13231
13654
|
git: !/not a git repository/i.test(stderr)
|
|
13232
13655
|
};
|
|
13233
13656
|
}
|
|
13234
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
13235
|
-
const stagedDeleted =
|
|
13657
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path13.sep).join("/")) : [];
|
|
13658
|
+
const stagedDeleted = spawnSync5("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
13236
13659
|
if (stagedDeleted.status !== 0) {
|
|
13237
13660
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
13238
13661
|
if (!/not a git repository/i.test(stderr))
|
|
13239
13662
|
return { paths, uncertain: true, git: true };
|
|
13240
13663
|
} else if (stagedDeleted.stdout) {
|
|
13241
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
13664
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path13.sep).join("/")));
|
|
13242
13665
|
}
|
|
13243
13666
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
13244
13667
|
};
|
|
13245
13668
|
var walk = (root, directory, output) => {
|
|
13246
13669
|
let names;
|
|
13247
13670
|
try {
|
|
13248
|
-
names =
|
|
13671
|
+
names = fs10.readdirSync(directory);
|
|
13249
13672
|
} catch {
|
|
13250
13673
|
return true;
|
|
13251
13674
|
}
|
|
@@ -13253,10 +13676,10 @@ var walk = (root, directory, output) => {
|
|
|
13253
13676
|
for (const name of names) {
|
|
13254
13677
|
if (name === ".git" || name === ".workit")
|
|
13255
13678
|
continue;
|
|
13256
|
-
const target =
|
|
13679
|
+
const target = path13.join(directory, name);
|
|
13257
13680
|
let stat;
|
|
13258
13681
|
try {
|
|
13259
|
-
stat =
|
|
13682
|
+
stat = fs10.lstatSync(target);
|
|
13260
13683
|
} catch {
|
|
13261
13684
|
uncertain = true;
|
|
13262
13685
|
continue;
|
|
@@ -13271,14 +13694,14 @@ var walk = (root, directory, output) => {
|
|
|
13271
13694
|
var scopeRoots = (root, scope) => {
|
|
13272
13695
|
const roots = [];
|
|
13273
13696
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
13274
|
-
const target =
|
|
13697
|
+
const target = path13.resolve(root, item);
|
|
13275
13698
|
if (!inside(root, target))
|
|
13276
13699
|
return failure2("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
|
|
13277
13700
|
let ancestor = target;
|
|
13278
|
-
while (!
|
|
13279
|
-
ancestor =
|
|
13701
|
+
while (!fs10.existsSync(ancestor) && ancestor !== root)
|
|
13702
|
+
ancestor = path13.dirname(ancestor);
|
|
13280
13703
|
try {
|
|
13281
|
-
if (!inside(root,
|
|
13704
|
+
if (!inside(root, fs10.realpathSync(ancestor)))
|
|
13282
13705
|
return failure2("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
|
|
13283
13706
|
} catch {
|
|
13284
13707
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -13290,7 +13713,7 @@ var scopeRoots = (root, scope) => {
|
|
|
13290
13713
|
function captureCandidate(root, scope, environment = []) {
|
|
13291
13714
|
let checkout;
|
|
13292
13715
|
try {
|
|
13293
|
-
checkout =
|
|
13716
|
+
checkout = fs10.realpathSync(root);
|
|
13294
13717
|
} catch {
|
|
13295
13718
|
return failure2("invalid_input", "candidate root does not exist");
|
|
13296
13719
|
}
|
|
@@ -13308,11 +13731,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
13308
13731
|
if (item === ".git" || item === ".workit")
|
|
13309
13732
|
continue;
|
|
13310
13733
|
names.add(item);
|
|
13311
|
-
if (!
|
|
13734
|
+
if (!fs10.existsSync(target))
|
|
13312
13735
|
continue;
|
|
13313
13736
|
let stat;
|
|
13314
13737
|
try {
|
|
13315
|
-
stat =
|
|
13738
|
+
stat = fs10.lstatSync(target);
|
|
13316
13739
|
} catch {
|
|
13317
13740
|
uncertain = true;
|
|
13318
13741
|
continue;
|
|
@@ -13328,17 +13751,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
13328
13751
|
for (const item of [...names].toSorted()) {
|
|
13329
13752
|
if (!scopeMatches(item, normalizedScope))
|
|
13330
13753
|
continue;
|
|
13331
|
-
const target =
|
|
13754
|
+
const target = path13.join(checkout, item);
|
|
13332
13755
|
try {
|
|
13333
|
-
const stat =
|
|
13756
|
+
const stat = fs10.lstatSync(target);
|
|
13334
13757
|
if (stat.isSymbolicLink()) {
|
|
13335
|
-
const link =
|
|
13758
|
+
const link = fs10.readlinkSync(target);
|
|
13336
13759
|
files.push({ path: item, kind: "symlink", digest: digestBytes3(link), executable: null });
|
|
13337
13760
|
} else if (stat.isFile()) {
|
|
13338
13761
|
files.push({
|
|
13339
13762
|
path: item,
|
|
13340
13763
|
kind: "file",
|
|
13341
|
-
digest: digestBytes3(
|
|
13764
|
+
digest: digestBytes3(fs10.readFileSync(target)),
|
|
13342
13765
|
executable: (stat.mode & 73) !== 0
|
|
13343
13766
|
});
|
|
13344
13767
|
} else if (!stat.isDirectory()) {
|
|
@@ -13363,7 +13786,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
13363
13786
|
const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
|
|
13364
13787
|
if (values.some(({ value }) => value === null))
|
|
13365
13788
|
completeness = "uncertain";
|
|
13366
|
-
const headResult =
|
|
13789
|
+
const headResult = spawnSync5("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
|
|
13367
13790
|
if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
|
|
13368
13791
|
completeness = "uncertain";
|
|
13369
13792
|
const head = headResult.status === 0 ? headResult.stdout.trim() : null;
|
|
@@ -13401,7 +13824,24 @@ var sameSession = (left, right) => {
|
|
|
13401
13824
|
return left !== null && other !== null && left.host === other.host && left.handle === other.handle;
|
|
13402
13825
|
};
|
|
13403
13826
|
var evidenceScopes = (task, ids) => (task.policy?.requirements ?? []).filter((requirement) => ids.includes(requirement.id)).map((requirement) => requirement.scope);
|
|
13404
|
-
|
|
13827
|
+
var memo2 = (compute) => {
|
|
13828
|
+
let value;
|
|
13829
|
+
return () => {
|
|
13830
|
+
if (value === undefined)
|
|
13831
|
+
value = compute();
|
|
13832
|
+
return value;
|
|
13833
|
+
};
|
|
13834
|
+
};
|
|
13835
|
+
var treeFreshness = (root) => ({
|
|
13836
|
+
mode: "tree",
|
|
13837
|
+
current: memo2(() => worktreeTree(root)?.key ?? null)
|
|
13838
|
+
});
|
|
13839
|
+
var signalFreshness = (root) => ({
|
|
13840
|
+
mode: "signal",
|
|
13841
|
+
current: memo2(() => worktreeSignal(root))
|
|
13842
|
+
});
|
|
13843
|
+
var isObservedCheck = (entry) => entry.data.kind === "check" && entry.data.observation?.observer === "workit_cli" && entry.provenance?.kind === "host_observed" && entry.provenance.host === "workit_cli";
|
|
13844
|
+
function evaluateEvidence(task, candidate, freshness) {
|
|
13405
13845
|
const current = candidate ?? task.candidates.at(-1) ?? null;
|
|
13406
13846
|
return task.evidence.map((entry) => {
|
|
13407
13847
|
const evidence = entry.data;
|
|
@@ -13411,6 +13851,25 @@ function evaluateEvidence(task, candidate) {
|
|
|
13411
13851
|
status: "failed",
|
|
13412
13852
|
reason: "failed evidence remains historical"
|
|
13413
13853
|
};
|
|
13854
|
+
const observation = isObservedCheck(entry) ? evidence.observation : undefined;
|
|
13855
|
+
if (observation && observation.tree !== null) {
|
|
13856
|
+
if (task.status === "closed")
|
|
13857
|
+
return { evidenceId: entry.id, status: evidence.result, reason: "recorded before close" };
|
|
13858
|
+
if (observation.modifiedWorktree)
|
|
13859
|
+
return {
|
|
13860
|
+
evidenceId: entry.id,
|
|
13861
|
+
status: "stale",
|
|
13862
|
+
reason: "the check changed the worktree while it ran; ignore or clean its outputs"
|
|
13863
|
+
};
|
|
13864
|
+
const recorded = freshness?.mode === "signal" ? observation.signal : observation.tree;
|
|
13865
|
+
if (recorded !== null && freshness && recorded === freshness.current())
|
|
13866
|
+
return { evidenceId: entry.id, status: evidence.result, reason: "worktree is unchanged" };
|
|
13867
|
+
return {
|
|
13868
|
+
evidenceId: entry.id,
|
|
13869
|
+
status: "stale",
|
|
13870
|
+
reason: freshness?.mode === "signal" ? "the worktree may have changed since the check ran (quick signal; inspect re-checks exactly)" : "the worktree changed since the check ran"
|
|
13871
|
+
};
|
|
13872
|
+
}
|
|
13414
13873
|
if (evidence.kind === "check" || evidence.kind === "review") {
|
|
13415
13874
|
if (!evidence.beforeCandidateId || !evidence.candidateId)
|
|
13416
13875
|
return {
|
|
@@ -13494,8 +13953,45 @@ var expectedKinds = (dimension, ruleId) => {
|
|
|
13494
13953
|
return "decision";
|
|
13495
13954
|
return "worker report";
|
|
13496
13955
|
};
|
|
13497
|
-
|
|
13498
|
-
|
|
13956
|
+
var observedGate = (requirement) => (requirement.dimension === "testing" || requirement.dimension === "verification") && requirement.before === "close";
|
|
13957
|
+
function evaluateObservedGate(task, requirement, statuses, config) {
|
|
13958
|
+
const names = gateCheckNames(config, requirement.dimension);
|
|
13959
|
+
const observed = task.evidence.filter(isObservedCheck);
|
|
13960
|
+
const fresh = observed.filter((entry) => statuses.get(entry.id) === "passed" && entry.data.observation?.exitCode === 0);
|
|
13961
|
+
const named = (entry) => {
|
|
13962
|
+
const observation = entry.data.observation;
|
|
13963
|
+
return observation.configured && !observation.shell && names.includes(observation.name ?? "") && matchesNamedCheck(config, observation.name, observation.argv, observation.cwd);
|
|
13964
|
+
};
|
|
13965
|
+
const considered = observed.map((entry) => entry.id);
|
|
13966
|
+
const run = checkCommand(names[0] ?? "<name>", runtimeVersion());
|
|
13967
|
+
if (config.error)
|
|
13968
|
+
return {
|
|
13969
|
+
satisfied: [],
|
|
13970
|
+
considered,
|
|
13971
|
+
reason: `check config is invalid (${config.error}); fix it, then run ${run}`
|
|
13972
|
+
};
|
|
13973
|
+
if (!names.length)
|
|
13974
|
+
return {
|
|
13975
|
+
satisfied: [],
|
|
13976
|
+
considered,
|
|
13977
|
+
reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE} (committed, so it shows in the diff), e.g. {"checks":{"test":"<your test command>"}}, then run ${checkCommand("test", runtimeVersion())}`
|
|
13978
|
+
};
|
|
13979
|
+
const satisfied = fresh.filter(named);
|
|
13980
|
+
if (satisfied.length)
|
|
13981
|
+
return {
|
|
13982
|
+
satisfied: satisfied.map((entry) => entry.id),
|
|
13983
|
+
considered,
|
|
13984
|
+
reason: `a fresh passing configured check (${names.join("|")}) was observed by workit check`
|
|
13985
|
+
};
|
|
13986
|
+
const reported = task.evidence.some((entry) => entry.data.kind === "check" && !isObservedCheck(entry) && entry.data.requirementIds.includes(requirement.id));
|
|
13987
|
+
const reason = fresh.length ? `ad-hoc checks do not satisfy a configured gate; run ${run}` : observed.some((entry) => statuses.get(entry.id) === "stale") ? `observed checks are stale (the worktree changed); run ${run}` : observed.some((entry) => statuses.get(entry.id) === "failed") ? `the observed check failed; fix it and run ${run}` : reported ? `agent-reported checks are notes, not evidence; run ${run}` : `no CLI-observed check (needs kind:check observed by workit check); run ${run}`;
|
|
13988
|
+
return { satisfied: [], considered, reason };
|
|
13989
|
+
}
|
|
13990
|
+
function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller, freshness = treeFreshness(checkoutRoot)) {
|
|
13991
|
+
const evidence = evaluateEvidence(task, candidate, freshness);
|
|
13992
|
+
const statuses = new Map(evidence.map((item) => [item.evidenceId, item.status]));
|
|
13993
|
+
let checkConfig;
|
|
13994
|
+
const configFor = () => checkConfig ??= loadCheckConfig(checkoutRoot);
|
|
13499
13995
|
if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
|
|
13500
13996
|
return task.policy.requirements.map((requirement) => ({
|
|
13501
13997
|
requirementId: requirement.id,
|
|
@@ -13505,8 +14001,19 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
13505
14001
|
reason: "stored policy version is unsupported; reassessment is required"
|
|
13506
14002
|
}));
|
|
13507
14003
|
return (task.policy?.requirements ?? []).map((requirement) => {
|
|
14004
|
+
const gate = observedGate(requirement) ? evaluateObservedGate(task, requirement, statuses, configFor()) : null;
|
|
14005
|
+
if (gate?.satisfied.length)
|
|
14006
|
+
return {
|
|
14007
|
+
requirementId: requirement.id,
|
|
14008
|
+
status: "satisfied",
|
|
14009
|
+
evidenceIds: gate.satisfied,
|
|
14010
|
+
decisionIds: [],
|
|
14011
|
+
reason: gate.reason
|
|
14012
|
+
};
|
|
13508
14013
|
const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
|
|
13509
14014
|
const passed = related.filter(({ entry, evaluation }) => {
|
|
14015
|
+
if (gate)
|
|
14016
|
+
return false;
|
|
13510
14017
|
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
|
|
13511
14018
|
return false;
|
|
13512
14019
|
if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
|
|
@@ -13569,6 +14076,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
13569
14076
|
reason: "an applicable approved limitation permits the missing evidence"
|
|
13570
14077
|
};
|
|
13571
14078
|
const unavailable = capabilities.some((capability) => capability.assurance === "unavailable" && (capability.name === requirement.dimension || capability.surface === requirement.dimension));
|
|
14079
|
+
if (gate)
|
|
14080
|
+
return {
|
|
14081
|
+
requirementId: requirement.id,
|
|
14082
|
+
status: unavailable ? "unavailable" : "unsatisfied",
|
|
14083
|
+
evidenceIds: [...new Set([...gate.considered, ...related.map(({ entry }) => entry.id)])],
|
|
14084
|
+
decisionIds: [],
|
|
14085
|
+
reason: unavailable ? "required capability is unavailable" : gate.reason
|
|
14086
|
+
};
|
|
13572
14087
|
if (!unavailable && related.length > 0 && !passed.length)
|
|
13573
14088
|
return {
|
|
13574
14089
|
requirementId: requirement.id,
|
|
@@ -13745,8 +14260,11 @@ policy, and reapplies the call, returning busy under persistent contention. A
|
|
|
13745
14260
|
revision_conflict means a revision you passed is stale: re-read the record
|
|
13746
14261
|
before deciding whether to retry.
|
|
13747
14262
|
A solo edit does not need writer acquisition; use it when concurrent checkout
|
|
13748
|
-
writers need coordination. Record only observed facts and checks.
|
|
13749
|
-
|
|
14263
|
+
writers need coordination. Record only observed facts and checks. Close-time
|
|
14264
|
+
testing and verification gates accept only a configured check the CLI ran:
|
|
14265
|
+
\`workit check <name>\` (\`npx -y @brainervirus/workit-cli check <name>\` when
|
|
14266
|
+
\`workit\` is not on PATH); a recorded check result is a note and an ad-hoc
|
|
14267
|
+
\`workit check -- <cmd>\` never satisfies a gate. Evidence can become stale when its bound candidate or tree changes; reconcile findings against the
|
|
13750
14268
|
current candidate before recording completion.
|
|
13751
14269
|
|
|
13752
14270
|
Workit validates domain policy against the actual action target, configured
|
|
@@ -13807,7 +14325,7 @@ function reconcileResume(view, observations = []) {
|
|
|
13807
14325
|
const candidate = captureCandidate(view.workspace.root, view.task.intent.data.scope, []);
|
|
13808
14326
|
if (!candidate.ok)
|
|
13809
14327
|
return candidate;
|
|
13810
|
-
const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
|
|
14328
|
+
const staleEvidenceIds = evaluateEvidence(view.task, candidate.data, treeFreshness(view.workspace.root)).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
|
|
13811
14329
|
const blockers = [...view.task.progress.blockers];
|
|
13812
14330
|
if (view.task.workers.some((entry) => isUncertainWorker(entry.data.state)))
|
|
13813
14331
|
blockers.push({
|
|
@@ -13932,7 +14450,7 @@ var shellBranchPolicyViolation = (root, command) => {
|
|
|
13932
14450
|
// packages/workit-core/src/core/external-action.ts
|
|
13933
14451
|
import { execFileSync as execFileSync3 } from "node:child_process";
|
|
13934
14452
|
import { realpathSync as realpathSync6 } from "node:fs";
|
|
13935
|
-
import
|
|
14453
|
+
import path14 from "node:path";
|
|
13936
14454
|
var chainStepSchema = union([
|
|
13937
14455
|
string2().min(1),
|
|
13938
14456
|
object({ branch: string2().min(1) }).strict(),
|
|
@@ -14126,7 +14644,7 @@ var planCommitDescriptor = (content) => {
|
|
|
14126
14644
|
if (!steps || typeof branch !== "string" || !branch)
|
|
14127
14645
|
return null;
|
|
14128
14646
|
const cwd = value.payload.cwd;
|
|
14129
|
-
if (cwd !== undefined && (typeof cwd !== "string" || !
|
|
14647
|
+
if (cwd !== undefined && (typeof cwd !== "string" || !path14.isAbsolute(cwd)))
|
|
14130
14648
|
return null;
|
|
14131
14649
|
const head = value.payload.resolved?.head;
|
|
14132
14650
|
return {
|
|
@@ -14269,7 +14787,7 @@ var descriptorTargetRoot = (store, operation) => {
|
|
|
14269
14787
|
const cwd = descriptor.payload?.cwd;
|
|
14270
14788
|
if (cwd === undefined)
|
|
14271
14789
|
return store.root;
|
|
14272
|
-
return typeof cwd === "string" &&
|
|
14790
|
+
return typeof cwd === "string" && path14.isAbsolute(cwd) ? realpathSync6(cwd) : null;
|
|
14273
14791
|
} catch {
|
|
14274
14792
|
return null;
|
|
14275
14793
|
}
|
|
@@ -14380,7 +14898,7 @@ var branchSetupIntent = (descriptor) => {
|
|
|
14380
14898
|
const { target_branch, resolved } = value.payload;
|
|
14381
14899
|
if (typeof target_branch !== "string" || !target_branch || !resolved)
|
|
14382
14900
|
return null;
|
|
14383
|
-
if (value.payload.cwd !== undefined && (typeof value.payload.cwd !== "string" || !
|
|
14901
|
+
if (value.payload.cwd !== undefined && (typeof value.payload.cwd !== "string" || !path14.isAbsolute(value.payload.cwd)))
|
|
14384
14902
|
return null;
|
|
14385
14903
|
if (typeof resolved.base_branch !== "string" || typeof resolved.target_exists !== "boolean")
|
|
14386
14904
|
return null;
|
|
@@ -14497,7 +15015,7 @@ var externalActionTargetRoot = (operation, coordinationRoot) => {
|
|
|
14497
15015
|
const payload = descriptor.payload;
|
|
14498
15016
|
const cwd = typeof payload.cwd === "string" ? payload.cwd : coordinationRoot;
|
|
14499
15017
|
try {
|
|
14500
|
-
return realpathSync6(
|
|
15018
|
+
return realpathSync6(path14.resolve(coordinationRoot, cwd));
|
|
14501
15019
|
} catch {
|
|
14502
15020
|
return null;
|
|
14503
15021
|
}
|
|
@@ -15746,10 +16264,7 @@ class WorkitCore {
|
|
|
15746
16264
|
if (finding.data.disposition !== "fixed")
|
|
15747
16265
|
return finding;
|
|
15748
16266
|
const withEntry = { ...current, candidates, evidence: [...current.evidence, entry] };
|
|
15749
|
-
const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data).map((item) => [
|
|
15750
|
-
item.evidenceId,
|
|
15751
|
-
item.status
|
|
15752
|
-
]));
|
|
16267
|
+
const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data, treeFreshness(this.store.root)).map((item) => [item.evidenceId, item.status]));
|
|
15753
16268
|
const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
|
|
15754
16269
|
kind: item.data.kind,
|
|
15755
16270
|
candidateId: item.data.candidateId,
|
|
@@ -15764,6 +16279,71 @@ class WorkitCore {
|
|
|
15764
16279
|
return changed;
|
|
15765
16280
|
return success(changed.data.revision, null, changed.data.evidence.at(-1));
|
|
15766
16281
|
}
|
|
16282
|
+
observeCheck(request) {
|
|
16283
|
+
return this.retryOmittedRevisions(request, () => this.observeCheckOnce(request));
|
|
16284
|
+
}
|
|
16285
|
+
observeCheckOnce(request) {
|
|
16286
|
+
const root = this.contextRootError();
|
|
16287
|
+
if (!root.ok)
|
|
16288
|
+
return root;
|
|
16289
|
+
if (this.context.caller.host !== "workit_cli" || (this.context.workerId ?? null) !== null)
|
|
16290
|
+
return failure2("permission_denied", "only the workit CLI records observed checks");
|
|
16291
|
+
const observation = checkObservationSchema.safeParse(request?.observation);
|
|
16292
|
+
if (!observation.success)
|
|
16293
|
+
return failure2("invalid_input", "check observation is invalid", {
|
|
16294
|
+
fields: observation.error.issues.map((issue) => ({
|
|
16295
|
+
path: issue.path.join("."),
|
|
16296
|
+
reason: issue.message
|
|
16297
|
+
}))
|
|
16298
|
+
});
|
|
16299
|
+
const task = this.store.readTask(request.taskId);
|
|
16300
|
+
if (!task.ok)
|
|
16301
|
+
return task;
|
|
16302
|
+
if (task.data.status === "closed")
|
|
16303
|
+
return failure2("invalid_transition", "closed task cannot record evidence");
|
|
16304
|
+
const observed = observation.data;
|
|
16305
|
+
let candidate = null;
|
|
16306
|
+
if (observed.tree === null) {
|
|
16307
|
+
const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
16308
|
+
if (!captured.ok)
|
|
16309
|
+
return captured;
|
|
16310
|
+
candidate = captured.data;
|
|
16311
|
+
}
|
|
16312
|
+
const label = observed.name ?? observed.argv.join(" ");
|
|
16313
|
+
const data = {
|
|
16314
|
+
kind: "check",
|
|
16315
|
+
claim: `workit check ${label}`.slice(0, 500),
|
|
16316
|
+
requirementIds: [],
|
|
16317
|
+
...candidate ? { beforeCandidateId: candidate.id, candidateId: candidate.id } : {},
|
|
16318
|
+
result: observed.exitCode === 0 ? "passed" : "failed",
|
|
16319
|
+
summary: `${observed.configured ? "configured" : "ad-hoc"} check exited ${observed.exitCode}${observed.timedOut ? " (timed out)" : ""} in ${observed.durationMs} ms`,
|
|
16320
|
+
refs: [],
|
|
16321
|
+
exitCode: observed.exitCode,
|
|
16322
|
+
reviewContext: null,
|
|
16323
|
+
observation: observed
|
|
16324
|
+
};
|
|
16325
|
+
const changed = this.store.mutateTask(task.data.id, request.expectedRevision ?? task.data.revision, (current, mutation) => {
|
|
16326
|
+
const candidates = candidate === null || current.candidates.some((item) => item.id === candidate.id) ? current.candidates : [...current.candidates, candidate];
|
|
16327
|
+
const critical = [...new Set([...current.critical ?? [], CHECK_OBSERVATION_PATH])];
|
|
16328
|
+
return success(mutation.revision, null, {
|
|
16329
|
+
...current,
|
|
16330
|
+
critical,
|
|
16331
|
+
candidates,
|
|
16332
|
+
evidence: [
|
|
16333
|
+
...current.evidence,
|
|
16334
|
+
{
|
|
16335
|
+
id: newId(),
|
|
16336
|
+
recordedAt: mutation.now,
|
|
16337
|
+
provenance: provenance(this.context, "host_observed"),
|
|
16338
|
+
data
|
|
16339
|
+
}
|
|
16340
|
+
]
|
|
16341
|
+
});
|
|
16342
|
+
}, trustedNow(this.context));
|
|
16343
|
+
if (!changed.ok)
|
|
16344
|
+
return changed;
|
|
16345
|
+
return success(changed.data.revision, null, changed.data.evidence.at(-1));
|
|
16346
|
+
}
|
|
15767
16347
|
decision(request) {
|
|
15768
16348
|
return this.recordDecision(request);
|
|
15769
16349
|
}
|
|
@@ -15999,7 +16579,7 @@ class WorkitCore {
|
|
|
15999
16579
|
const current = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
16000
16580
|
if (!current.ok)
|
|
16001
16581
|
return current;
|
|
16002
|
-
const evaluations = evaluateEvidence(task.data, current.data);
|
|
16582
|
+
const evaluations = evaluateEvidence(task.data, current.data, treeFreshness(this.store.root));
|
|
16003
16583
|
const verified = evidence.some((entry) => {
|
|
16004
16584
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
16005
16585
|
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
@@ -16703,7 +17283,8 @@ class WorkitCore {
|
|
|
16703
17283
|
if (!current.ok)
|
|
16704
17284
|
return current;
|
|
16705
17285
|
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
16706
|
-
const
|
|
17286
|
+
const tree = treeFreshness(this.store.root);
|
|
17287
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
|
|
16707
17288
|
return success(task.revision, workspace.data.revision, {
|
|
16708
17289
|
id: task.id,
|
|
16709
17290
|
revision: task.revision,
|
|
@@ -16720,7 +17301,7 @@ class WorkitCore {
|
|
|
16720
17301
|
writer: evaluationWorkspace.writer
|
|
16721
17302
|
});
|
|
16722
17303
|
}
|
|
16723
|
-
compactContext(taskId) {
|
|
17304
|
+
compactContext(taskId, freshness) {
|
|
16724
17305
|
const root = this.contextRootError();
|
|
16725
17306
|
if (!root.ok)
|
|
16726
17307
|
return root;
|
|
@@ -16730,12 +17311,12 @@ class WorkitCore {
|
|
|
16730
17311
|
const helper = this.helperTaskGuard(task.data, true);
|
|
16731
17312
|
if (!helper.ok)
|
|
16732
17313
|
return helper;
|
|
16733
|
-
const view = this.view(task.data, false);
|
|
17314
|
+
const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
|
|
16734
17315
|
if (!view.ok)
|
|
16735
17316
|
return view;
|
|
16736
17317
|
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
|
|
16737
17318
|
}
|
|
16738
|
-
view(task, capture = true) {
|
|
17319
|
+
view(task, capture = true, freshness = treeFreshness(this.store.root)) {
|
|
16739
17320
|
const workspace = this.store.readWorkspace();
|
|
16740
17321
|
if (!workspace.ok)
|
|
16741
17322
|
return workspace;
|
|
@@ -16746,13 +17327,14 @@ class WorkitCore {
|
|
|
16746
17327
|
if (!current.ok)
|
|
16747
17328
|
return current;
|
|
16748
17329
|
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
16749
|
-
const
|
|
17330
|
+
const tree = freshness;
|
|
17331
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
|
|
16750
17332
|
return success(task.revision, workspace.data.revision, {
|
|
16751
17333
|
task,
|
|
16752
17334
|
workspace: evaluationWorkspace,
|
|
16753
17335
|
capabilities: this.context.capabilities,
|
|
16754
17336
|
requirements,
|
|
16755
|
-
evidence: evaluateEvidence(task, current.data)
|
|
17337
|
+
evidence: evaluateEvidence(task, current.data, tree)
|
|
16756
17338
|
});
|
|
16757
17339
|
}
|
|
16758
17340
|
reconcileResume(view, observations = []) {
|
|
@@ -16979,13 +17561,14 @@ class WorkitCore {
|
|
|
16979
17561
|
return current;
|
|
16980
17562
|
const withCandidate = current.data;
|
|
16981
17563
|
const taskForView = withCandidate.id === task.data.candidates.at(-1)?.id ? task.data : { ...task.data, candidates: [...task.data.candidates, withCandidate] };
|
|
16982
|
-
const
|
|
17564
|
+
const tree = treeFreshness(this.store.root);
|
|
17565
|
+
const requirements = evaluateRequirements(taskForView, workspace.data, this.context.capabilities, withCandidate, this.store.root, this.context.caller, tree);
|
|
16983
17566
|
const view = {
|
|
16984
17567
|
task: taskForView,
|
|
16985
17568
|
workspace: workspace.data,
|
|
16986
17569
|
capabilities: this.context.capabilities,
|
|
16987
17570
|
requirements,
|
|
16988
|
-
evidence: evaluateEvidence(taskForView, withCandidate)
|
|
17571
|
+
evidence: evaluateEvidence(taskForView, withCandidate, tree)
|
|
16989
17572
|
};
|
|
16990
17573
|
const closure = evaluateClosure(input.outcome, view);
|
|
16991
17574
|
if (!closure.ok)
|
|
@@ -17049,22 +17632,22 @@ function resolveGitRevision(root, value) {
|
|
|
17049
17632
|
}
|
|
17050
17633
|
function resolveInside(root, candidate) {
|
|
17051
17634
|
const base = realpathSync8(root);
|
|
17052
|
-
const target =
|
|
17053
|
-
if (target !== base && !target.startsWith(base +
|
|
17635
|
+
const target = path15.resolve(base, candidate);
|
|
17636
|
+
if (target !== base && !target.startsWith(base + path15.sep)) {
|
|
17054
17637
|
throw new Error("path must stay inside repository root");
|
|
17055
17638
|
}
|
|
17056
17639
|
let ancestor = target;
|
|
17057
17640
|
while (!existsSync6(ancestor))
|
|
17058
|
-
ancestor =
|
|
17641
|
+
ancestor = path15.dirname(ancestor);
|
|
17059
17642
|
const canonicalAncestor = realpathSync8(ancestor);
|
|
17060
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
17643
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path15.sep)) {
|
|
17061
17644
|
throw new Error("path must stay inside repository root");
|
|
17062
17645
|
}
|
|
17063
17646
|
return target;
|
|
17064
17647
|
}
|
|
17065
17648
|
function run2(root, executable, args, env = {}) {
|
|
17066
17649
|
const cwd = realpathSync8(root);
|
|
17067
|
-
const result =
|
|
17650
|
+
const result = spawnSync6(executable, args, {
|
|
17068
17651
|
cwd,
|
|
17069
17652
|
encoding: "utf8",
|
|
17070
17653
|
env: { ...process.env, ...env }
|
|
@@ -17101,7 +17684,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
17101
17684
|
};
|
|
17102
17685
|
});
|
|
17103
17686
|
// packages/workit-core/src/hooks/context.ts
|
|
17104
|
-
import
|
|
17687
|
+
import path16 from "node:path";
|
|
17105
17688
|
var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
|
|
17106
17689
|
var newestFirst = (left, right) => right.updatedAt.localeCompare(left.updatedAt);
|
|
17107
17690
|
var sessionTaskEntry = (entries, session) => entries.filter((entry) => entry.status !== "closed" && boundTo(entry, session)).toSorted(newestFirst)[0] ?? null;
|
|
@@ -17126,6 +17709,19 @@ var filesKey = (files) => {
|
|
|
17126
17709
|
return signatures.some((item) => item !== "-" && racySignature(item)) ? null : signatures.join("\x00");
|
|
17127
17710
|
};
|
|
17128
17711
|
var CACHE_LIMIT = 64;
|
|
17712
|
+
var SIGNAL_TTL_MS = 1500;
|
|
17713
|
+
var signals = new Map;
|
|
17714
|
+
var recentSignal = (root) => {
|
|
17715
|
+
const now = Date.now();
|
|
17716
|
+
const cached = signals.get(root);
|
|
17717
|
+
if (cached && now - cached.at < SIGNAL_TTL_MS)
|
|
17718
|
+
return cached.value;
|
|
17719
|
+
const value = worktreeSignal(root);
|
|
17720
|
+
signals.set(root, { at: now, value });
|
|
17721
|
+
if (signals.size > CACHE_LIMIT)
|
|
17722
|
+
signals.delete(signals.keys().next().value);
|
|
17723
|
+
return value;
|
|
17724
|
+
};
|
|
17129
17725
|
var cache = new Map;
|
|
17130
17726
|
function sessionCompactContext(store, session, context, selection = "session-bound") {
|
|
17131
17727
|
const listed = store.listTaskIndex();
|
|
@@ -17149,18 +17745,26 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
17149
17745
|
capabilities: context.capabilities
|
|
17150
17746
|
});
|
|
17151
17747
|
const hit = cache.get(slot);
|
|
17152
|
-
if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files))
|
|
17748
|
+
if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
|
|
17153
17749
|
return hit.value;
|
|
17154
17750
|
const task = store.readTask(entry.id);
|
|
17155
17751
|
if (!task.ok)
|
|
17156
17752
|
return null;
|
|
17157
|
-
const
|
|
17158
|
-
const observed =
|
|
17159
|
-
const
|
|
17753
|
+
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
17754
|
+
const observed = task.data.evidence.some(isObservedCheck);
|
|
17755
|
+
const files = task.data.decisions.flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path16.resolve(store.root, ref.path)] : []));
|
|
17756
|
+
const observedFiles = filesKey(files);
|
|
17757
|
+
const compact = new WorkitCore(store, context).compactContext(entry.id, freshness);
|
|
17160
17758
|
if (!compact.ok)
|
|
17161
17759
|
return null;
|
|
17162
17760
|
cache.delete(slot);
|
|
17163
|
-
cache.set(slot, {
|
|
17761
|
+
cache.set(slot, {
|
|
17762
|
+
key,
|
|
17763
|
+
files,
|
|
17764
|
+
filesKey: observedFiles,
|
|
17765
|
+
signal: observed ? freshness.current() : null,
|
|
17766
|
+
value: compact.data
|
|
17767
|
+
});
|
|
17164
17768
|
if (cache.size > CACHE_LIMIT)
|
|
17165
17769
|
cache.delete(cache.keys().next().value);
|
|
17166
17770
|
return compact.data;
|
|
@@ -17327,9 +17931,9 @@ var WORKIT_SKILL_ALIASES = {
|
|
|
17327
17931
|
if (false) {}
|
|
17328
17932
|
|
|
17329
17933
|
// packages/workit-core/src/core/pr-create.ts
|
|
17330
|
-
import
|
|
17331
|
-
import
|
|
17332
|
-
import { spawnSync as
|
|
17934
|
+
import fs11 from "node:fs";
|
|
17935
|
+
import path17 from "node:path";
|
|
17936
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
17333
17937
|
import { pathToFileURL } from "node:url";
|
|
17334
17938
|
// packages/workit-core/src/forge/pr-body.ts
|
|
17335
17939
|
function parseGhIssue(value) {
|
|
@@ -17419,13 +18023,13 @@ var cliRepository = (remote, provider, cfg) => {
|
|
|
17419
18023
|
};
|
|
17420
18024
|
function whichOnPath(tool) {
|
|
17421
18025
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
17422
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
18026
|
+
for (const dir of (process.env.PATH ?? "").split(path17.delimiter)) {
|
|
17423
18027
|
if (!dir)
|
|
17424
18028
|
continue;
|
|
17425
18029
|
for (const name of names) {
|
|
17426
|
-
const candidate =
|
|
18030
|
+
const candidate = path17.join(dir, name);
|
|
17427
18031
|
try {
|
|
17428
|
-
|
|
18032
|
+
fs11.accessSync(candidate, fs11.constants.X_OK);
|
|
17429
18033
|
return candidate;
|
|
17430
18034
|
} catch {}
|
|
17431
18035
|
}
|
|
@@ -17434,11 +18038,11 @@ function whichOnPath(tool) {
|
|
|
17434
18038
|
}
|
|
17435
18039
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
17436
18040
|
function repoRoot(cwd) {
|
|
17437
|
-
const result =
|
|
18041
|
+
const result = spawnSync7("git", ["rev-parse", "--show-toplevel"], { cwd, encoding: "utf8" });
|
|
17438
18042
|
return result.status === 0 ? (result.stdout ?? "").trim() : cwd;
|
|
17439
18043
|
}
|
|
17440
18044
|
var pushRemote = (cwd) => {
|
|
17441
|
-
const result =
|
|
18045
|
+
const result = spawnSync7("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
17442
18046
|
cwd,
|
|
17443
18047
|
encoding: "utf8"
|
|
17444
18048
|
});
|
|
@@ -17449,7 +18053,7 @@ var pushRemote = (cwd) => {
|
|
|
17449
18053
|
};
|
|
17450
18054
|
var safePushUrl = (remote) => {
|
|
17451
18055
|
const raw = remote.trim();
|
|
17452
|
-
if (
|
|
18056
|
+
if (path17.isAbsolute(raw))
|
|
17453
18057
|
return pathToFileURL(raw).href;
|
|
17454
18058
|
if (!raw.includes("://")) {
|
|
17455
18059
|
const scp = /^(?:([^@\s]+)@)?([^:]+):(.+)$/u.exec(raw);
|
|
@@ -17474,7 +18078,7 @@ var pushRemoteIdentity = (remote) => {
|
|
|
17474
18078
|
const raw = remote.trim();
|
|
17475
18079
|
if (!raw)
|
|
17476
18080
|
return null;
|
|
17477
|
-
if (
|
|
18081
|
+
if (path17.isAbsolute(raw))
|
|
17478
18082
|
return pathToFileURL(raw).href;
|
|
17479
18083
|
if (!raw.includes("://")) {
|
|
17480
18084
|
const scp = /^(?:([^@\s]+)@)?([^:]+):(.+)$/u.exec(raw);
|
|
@@ -17498,7 +18102,7 @@ var pushTargetIsStable = (cwd, remote) => {
|
|
|
17498
18102
|
const target = safePushUrl(remote);
|
|
17499
18103
|
if (!target)
|
|
17500
18104
|
return false;
|
|
17501
|
-
const result =
|
|
18105
|
+
const result = spawnSync7("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
|
|
17502
18106
|
if (result.status !== 0 && result.status !== 1)
|
|
17503
18107
|
return false;
|
|
17504
18108
|
const prefixes = (result.stdout ?? "").split(/\r?\n/u).map((line) => line.trim().slice(line.trim().indexOf(" ") + 1)).filter(Boolean);
|
|
@@ -17535,7 +18139,7 @@ function prCreate(env, cwd) {
|
|
|
17535
18139
|
return {
|
|
17536
18140
|
error: "origin must have exactly one supported push URL for a reserved hosting action"
|
|
17537
18141
|
};
|
|
17538
|
-
const localOnlyTarget =
|
|
18142
|
+
const localOnlyTarget = path17.isAbsolute(pushUrl) || pushUrl.startsWith("file://");
|
|
17539
18143
|
if ((!localOnlyTarget || env.WORKIT_EXPECTED_API_HOST) && !hostingApiHostMatches(pushUrl, identity.host))
|
|
17540
18144
|
return { error: "hosting API host does not match the push destination" };
|
|
17541
18145
|
if (env.WORKIT_EXPECTED_REMOTE && env.WORKIT_EXPECTED_REMOTE !== targetIdentity)
|
|
@@ -17546,12 +18150,12 @@ function prCreate(env, cwd) {
|
|
|
17546
18150
|
const resolvedDefault = String(cfg.defaultTargetBranch ?? policy.defaultTargetBranch ?? "develop");
|
|
17547
18151
|
const target = targetOverride || resolvedDefault;
|
|
17548
18152
|
const title = String(env.WF_PR_TITLE ?? "");
|
|
17549
|
-
const br =
|
|
18153
|
+
const br = spawnSync7("git", ["rev-parse", "--abbrev-ref", "HEAD"], {
|
|
17550
18154
|
cwd: root,
|
|
17551
18155
|
encoding: "utf8"
|
|
17552
18156
|
});
|
|
17553
18157
|
const branch = br.status === 0 ? (br.stdout ?? "").trim() : "";
|
|
17554
|
-
const sourceResult = branch ?
|
|
18158
|
+
const sourceResult = branch ? spawnSync7("git", ["rev-parse", "--verify", `refs/heads/${branch}`], {
|
|
17555
18159
|
cwd: root,
|
|
17556
18160
|
encoding: "utf8"
|
|
17557
18161
|
}) : null;
|
|
@@ -17560,7 +18164,7 @@ function prCreate(env, cwd) {
|
|
|
17560
18164
|
return { error: "approved PR source branch and commit are incomplete" };
|
|
17561
18165
|
if (env.WORKIT_EXPECTED_SOURCE_BRANCH && env.WORKIT_EXPECTED_SOURCE_BRANCH !== branch || env.WORKIT_EXPECTED_SOURCE_COMMIT && env.WORKIT_EXPECTED_SOURCE_COMMIT !== sourceCommit)
|
|
17562
18166
|
return { error: "approved PR source branch or commit changed before execution" };
|
|
17563
|
-
const mergeBaseResult = policy.integration === "merge" ?
|
|
18167
|
+
const mergeBaseResult = policy.integration === "merge" ? spawnSync7("git", ["rev-parse", "--verify", `refs/heads/${target}`], {
|
|
17564
18168
|
cwd: root,
|
|
17565
18169
|
encoding: "utf8"
|
|
17566
18170
|
}) : null;
|
|
@@ -17576,7 +18180,7 @@ function prCreate(env, cwd) {
|
|
|
17576
18180
|
targetBranch: target,
|
|
17577
18181
|
stderr: "source or target branch commit could not be resolved"
|
|
17578
18182
|
};
|
|
17579
|
-
const merge =
|
|
18183
|
+
const merge = spawnSync7("git", ["merge", "--no-ff", sourceCommit, "-m", title], {
|
|
17580
18184
|
cwd: root,
|
|
17581
18185
|
encoding: "utf8"
|
|
17582
18186
|
});
|
|
@@ -17587,16 +18191,16 @@ function prCreate(env, cwd) {
|
|
|
17587
18191
|
targetBranch: target,
|
|
17588
18192
|
stderr: (merge.stderr ?? "").slice(0, 800)
|
|
17589
18193
|
};
|
|
17590
|
-
const mergedCommit =
|
|
18194
|
+
const mergedCommit = spawnSync7("git", ["rev-parse", "HEAD"], {
|
|
17591
18195
|
cwd: root,
|
|
17592
18196
|
encoding: "utf8"
|
|
17593
18197
|
});
|
|
17594
18198
|
if (mergedCommit.status !== 0)
|
|
17595
18199
|
return { error: "merge commit could not be resolved", mode: "merge" };
|
|
17596
|
-
const updateTarget =
|
|
18200
|
+
const updateTarget = spawnSync7("git", ["update-ref", `refs/heads/${target}`, mergedCommit.stdout.trim(), mergeBaseCommit], { cwd: root, encoding: "utf8" });
|
|
17597
18201
|
if (updateTarget.status !== 0)
|
|
17598
18202
|
return { error: "merge target branch changed before update", mode: "merge" };
|
|
17599
|
-
const push =
|
|
18203
|
+
const push = spawnSync7("git", ["push", targetUrl, `${mergedCommit.stdout.trim()}:refs/heads/${target}`], { cwd: root, encoding: "utf8", env: { ...process.env, PATH: process.env.PATH ?? "" } });
|
|
17600
18204
|
if (push.status !== 0)
|
|
17601
18205
|
return {
|
|
17602
18206
|
error: "push failed",
|
|
@@ -17613,7 +18217,7 @@ function prCreate(env, cwd) {
|
|
|
17613
18217
|
output: (push.stdout ?? "").trim()
|
|
17614
18218
|
};
|
|
17615
18219
|
};
|
|
17616
|
-
const co =
|
|
18220
|
+
const co = spawnSync7("git", ["checkout", "--detach", mergeBaseCommit], {
|
|
17617
18221
|
cwd: root,
|
|
17618
18222
|
encoding: "utf8"
|
|
17619
18223
|
});
|
|
@@ -17625,7 +18229,7 @@ function prCreate(env, cwd) {
|
|
|
17625
18229
|
stderr: (co.stderr ?? "").slice(0, 800)
|
|
17626
18230
|
};
|
|
17627
18231
|
const result = finish();
|
|
17628
|
-
|
|
18232
|
+
spawnSync7("git", ["checkout", branch], { cwd: root, encoding: "utf8" });
|
|
17629
18233
|
return result;
|
|
17630
18234
|
}
|
|
17631
18235
|
const pr = cfg.pr ?? {};
|
|
@@ -17633,9 +18237,9 @@ function prCreate(env, cwd) {
|
|
|
17633
18237
|
const draft = String(env.WF_PR_DRAFT ?? "false").toLowerCase() === "true";
|
|
17634
18238
|
let baseUrl = cfg.youtrack_base_url;
|
|
17635
18239
|
if (!baseUrl) {
|
|
17636
|
-
const ytCfg = process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
18240
|
+
const ytCfg = process.env.WORKFLOW_YOUTRACK_CONFIG ?? path17.join(path17.dirname(String(cfg.configPath)), "youtrack.json");
|
|
17637
18241
|
try {
|
|
17638
|
-
const yt = JSON.parse(
|
|
18242
|
+
const yt = JSON.parse(fs11.readFileSync(ytCfg, "utf8"));
|
|
17639
18243
|
if (yt && typeof yt === "object")
|
|
17640
18244
|
baseUrl = yt.baseUrl;
|
|
17641
18245
|
} catch {}
|
|
@@ -17651,7 +18255,7 @@ function prCreate(env, cwd) {
|
|
|
17651
18255
|
const push = pr.pushBranch !== false;
|
|
17652
18256
|
const skipConfirm = pr.confirmSkip !== false;
|
|
17653
18257
|
const remoteBranchTip = () => {
|
|
17654
|
-
const read =
|
|
18258
|
+
const read = spawnSync7("git", ["ls-remote", targetUrl, `refs/heads/${branch}`], {
|
|
17655
18259
|
cwd: root,
|
|
17656
18260
|
encoding: "utf8",
|
|
17657
18261
|
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
@@ -17667,7 +18271,7 @@ function prCreate(env, cwd) {
|
|
|
17667
18271
|
stderr: branch ? "source branch commit could not be resolved" : "empty current branch (detached HEAD or unborn HEAD)"
|
|
17668
18272
|
};
|
|
17669
18273
|
if (push) {
|
|
17670
|
-
const pushed =
|
|
18274
|
+
const pushed = spawnSync7("git", ["push", "-u", targetUrl, `${sourceCommit}:refs/heads/${branch}`], { cwd: root, encoding: "utf8", env: { ...process.env, PATH: process.env.PATH ?? "" } });
|
|
17671
18275
|
if (pushed.status !== 0)
|
|
17672
18276
|
return { error: "push failed", provider, stderr: (pushed.stderr ?? "").slice(0, 800) };
|
|
17673
18277
|
}
|
|
@@ -17712,12 +18316,12 @@ function prCreate(env, cwd) {
|
|
|
17712
18316
|
cmd.push("--draft");
|
|
17713
18317
|
cmdEnv = { ...process.env, PATH: process.env.PATH ?? "", GH_HOST: identity.host };
|
|
17714
18318
|
}
|
|
17715
|
-
const result =
|
|
18319
|
+
const result = spawnSync7(cmd[0], cmd.slice(1), { cwd: root, encoding: "utf8", env: cmdEnv });
|
|
17716
18320
|
if (result.status !== 0) {
|
|
17717
18321
|
const err = (result.stderr ?? result.stdout ?? "").trim();
|
|
17718
18322
|
let hint = null;
|
|
17719
18323
|
if (provider === "gitlab" && (err.includes("409") || err.toLowerCase().includes("already exists"))) {
|
|
17720
|
-
const list =
|
|
18324
|
+
const list = spawnSync7("glab", ["mr", "list", `--source-branch=${branch}`, "--output=json"], {
|
|
17721
18325
|
cwd: root,
|
|
17722
18326
|
encoding: "utf8",
|
|
17723
18327
|
env: cmdEnv
|
|
@@ -17776,7 +18380,7 @@ function mergePr(cwd, opts) {
|
|
|
17776
18380
|
return { error: "approved hosting account changed before execution" };
|
|
17777
18381
|
if (!/^[a-f0-9]{40,64}$/i.test(opts.sourceCommit))
|
|
17778
18382
|
return { error: "approved merge source commit is invalid" };
|
|
17779
|
-
const currentCommit =
|
|
18383
|
+
const currentCommit = spawnSync7("git", ["rev-parse", "--verify", `refs/heads/${opts.source}`], {
|
|
17780
18384
|
cwd: root,
|
|
17781
18385
|
encoding: "utf8"
|
|
17782
18386
|
});
|
|
@@ -17790,7 +18394,7 @@ function mergePr(cwd, opts) {
|
|
|
17790
18394
|
return { error: `workflow CLI missing (required for ${provider})` };
|
|
17791
18395
|
const owner = repoPath.split("/")[0];
|
|
17792
18396
|
const endpoint = provider === "github" ? `repos/${repository.replace(/^[^/]+\//u, "")}/pulls?state=open&head=${encodeURIComponent(`${owner}:${opts.source}`)}&base=${encodeURIComponent(opts.target)}&per_page=100` : `projects/${encodeURIComponent(repoPath)}/merge_requests?state=opened&source_branch=${encodeURIComponent(opts.source)}&target_branch=${encodeURIComponent(opts.target)}&per_page=100`;
|
|
17793
|
-
const listOpen = () =>
|
|
18397
|
+
const listOpen = () => spawnSync7(provider === "github" ? "gh" : "glab", ["api", endpoint], {
|
|
17794
18398
|
cwd: root,
|
|
17795
18399
|
encoding: "utf8",
|
|
17796
18400
|
env: {
|
|
@@ -17850,7 +18454,7 @@ function mergePr(cwd, opts) {
|
|
|
17850
18454
|
cmd.push("--delete-branch");
|
|
17851
18455
|
cmdEnv = { ...process.env, PATH: process.env.PATH ?? "", GH_HOST: identity.host };
|
|
17852
18456
|
}
|
|
17853
|
-
const result =
|
|
18457
|
+
const result = spawnSync7(cmd[0], cmd.slice(1), { cwd: root, encoding: "utf8", env: cmdEnv });
|
|
17854
18458
|
if (result.status !== 0)
|
|
17855
18459
|
return {
|
|
17856
18460
|
error: "merge failed",
|
|
@@ -17867,9 +18471,9 @@ function mergePr(cwd, opts) {
|
|
|
17867
18471
|
}
|
|
17868
18472
|
|
|
17869
18473
|
// packages/workit-core/src/core/repo-context.ts
|
|
17870
|
-
import { spawnSync as
|
|
18474
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
17871
18475
|
import { existsSync as existsSync7, readFileSync as readFileSync8, readdirSync as readdirSync4 } from "node:fs";
|
|
17872
|
-
import
|
|
18476
|
+
import path18 from "node:path";
|
|
17873
18477
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
17874
18478
|
try {
|
|
17875
18479
|
gitRevisionParts(value);
|
|
@@ -17896,7 +18500,7 @@ var invalidRange = (cwd) => ({
|
|
|
17896
18500
|
cwd
|
|
17897
18501
|
});
|
|
17898
18502
|
var runGit = (cwd, args) => {
|
|
17899
|
-
const result =
|
|
18503
|
+
const result = spawnSync8("git", args, { cwd, encoding: "utf8" });
|
|
17900
18504
|
return {
|
|
17901
18505
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
17902
18506
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -18003,7 +18607,7 @@ var findPrTemplate = (cwd) => {
|
|
|
18003
18607
|
]) {
|
|
18004
18608
|
const parts = rel.split("/");
|
|
18005
18609
|
const base = parts[parts.length - 1];
|
|
18006
|
-
const dir = parts.length > 1 ?
|
|
18610
|
+
const dir = parts.length > 1 ? path18.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
18007
18611
|
const actual = probeCaseInsensitive(dir, base);
|
|
18008
18612
|
if (actual)
|
|
18009
18613
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -18034,7 +18638,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
18034
18638
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
18035
18639
|
};
|
|
18036
18640
|
var packageScripts = (cwd, keys) => {
|
|
18037
|
-
const pkgPath =
|
|
18641
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
18038
18642
|
if (!existsSync7(pkgPath))
|
|
18039
18643
|
return [];
|
|
18040
18644
|
try {
|
|
@@ -18060,9 +18664,9 @@ var documentationFiles = (cwd) => {
|
|
|
18060
18664
|
if (entry.isDirectory()) {
|
|
18061
18665
|
if (excluded.has(entry.name))
|
|
18062
18666
|
continue;
|
|
18063
|
-
walk(
|
|
18667
|
+
walk(path18.join(dir, entry.name), depth + 1);
|
|
18064
18668
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
18065
|
-
matches.push("./" +
|
|
18669
|
+
matches.push("./" + path18.relative(cwd, path18.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
18066
18670
|
}
|
|
18067
18671
|
}
|
|
18068
18672
|
};
|
|
@@ -18078,7 +18682,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
18078
18682
|
`);
|
|
18079
18683
|
};
|
|
18080
18684
|
function prReadyContext(root, range) {
|
|
18081
|
-
const cwd =
|
|
18685
|
+
const cwd = path18.resolve(repoRoot2(root));
|
|
18082
18686
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
18083
18687
|
return invalidRange(cwd);
|
|
18084
18688
|
let stdout = "";
|
|
@@ -18136,7 +18740,7 @@ ${status.stderr}` : "");
|
|
|
18136
18740
|
stdout += `template_path: ${template}
|
|
18137
18741
|
|
|
18138
18742
|
`;
|
|
18139
|
-
stdout += readTrimmed2(
|
|
18743
|
+
stdout += readTrimmed2(path18.join(cwd, template), 220);
|
|
18140
18744
|
} else {
|
|
18141
18745
|
stdout += `template_path: none
|
|
18142
18746
|
|
|
@@ -18146,7 +18750,7 @@ ${status.stderr}` : "");
|
|
|
18146
18750
|
stdout += `
|
|
18147
18751
|
`;
|
|
18148
18752
|
stdout += printSection("Recent Validation Signals");
|
|
18149
|
-
if (existsSync7(
|
|
18753
|
+
if (existsSync7(path18.join(cwd, "package.json"))) {
|
|
18150
18754
|
stdout += `package.json detected. Common scripts:
|
|
18151
18755
|
`;
|
|
18152
18756
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -18155,7 +18759,7 @@ ${status.stderr}` : "");
|
|
|
18155
18759
|
`) + `
|
|
18156
18760
|
`;
|
|
18157
18761
|
}
|
|
18158
|
-
if (existsSync7(
|
|
18762
|
+
if (existsSync7(path18.join(cwd, "Cargo.toml")) || existsSync7(path18.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
18159
18763
|
stdout += `Rust project detected.
|
|
18160
18764
|
`;
|
|
18161
18765
|
}
|
|
@@ -18188,7 +18792,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
18188
18792
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
18189
18793
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
18190
18794
|
function changelogContext(root, range) {
|
|
18191
|
-
const cwd =
|
|
18795
|
+
const cwd = path18.resolve(repoRoot2(root));
|
|
18192
18796
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
18193
18797
|
return invalidRange(cwd);
|
|
18194
18798
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -18206,7 +18810,7 @@ function changelogContext(root, range) {
|
|
|
18206
18810
|
stdout += CHANGELOG_RULES + `
|
|
18207
18811
|
`;
|
|
18208
18812
|
stdout += printSection("Existing CHANGELOG.md");
|
|
18209
|
-
const changelogPath =
|
|
18813
|
+
const changelogPath = path18.join(cwd, "CHANGELOG.md");
|
|
18210
18814
|
if (existsSync7(changelogPath)) {
|
|
18211
18815
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
18212
18816
|
`;
|
|
@@ -18226,7 +18830,7 @@ function changelogContext(root, range) {
|
|
|
18226
18830
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
18227
18831
|
}
|
|
18228
18832
|
function docsRefreshContext(root, range) {
|
|
18229
|
-
const cwd =
|
|
18833
|
+
const cwd = path18.resolve(repoRoot2(root));
|
|
18230
18834
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
18231
18835
|
return invalidRange(cwd);
|
|
18232
18836
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -18248,7 +18852,7 @@ function docsRefreshContext(root, range) {
|
|
|
18248
18852
|
`) + `
|
|
18249
18853
|
`;
|
|
18250
18854
|
stdout += printSection("README Preview");
|
|
18251
|
-
const readme =
|
|
18855
|
+
const readme = path18.join(cwd, "README.md");
|
|
18252
18856
|
if (existsSync7(readme)) {
|
|
18253
18857
|
stdout += readTrimmed2(readme, 220) + `
|
|
18254
18858
|
`;
|
|
@@ -18257,7 +18861,7 @@ function docsRefreshContext(root, range) {
|
|
|
18257
18861
|
`;
|
|
18258
18862
|
}
|
|
18259
18863
|
stdout += printSection("Package Scripts");
|
|
18260
|
-
const pkgPath =
|
|
18864
|
+
const pkgPath = path18.join(cwd, "package.json");
|
|
18261
18865
|
if (existsSync7(pkgPath)) {
|
|
18262
18866
|
try {
|
|
18263
18867
|
const pkg = JSON.parse(readFileSync8(pkgPath, "utf8"));
|
|
@@ -18271,7 +18875,7 @@ function docsRefreshContext(root, range) {
|
|
|
18271
18875
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
18272
18876
|
}
|
|
18273
18877
|
function releaseNotesContext(root, rangeOrTag) {
|
|
18274
|
-
const cwd =
|
|
18878
|
+
const cwd = path18.resolve(repoRoot2(root));
|
|
18275
18879
|
if (!rangeOrTag) {
|
|
18276
18880
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
18277
18881
|
`, exitCode: 1, cwd };
|
|
@@ -18309,7 +18913,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
18309
18913
|
`;
|
|
18310
18914
|
stdout += printSection("Existing Release Files");
|
|
18311
18915
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
18312
|
-
if (existsSync7(
|
|
18916
|
+
if (existsSync7(path18.join(cwd, rel)))
|
|
18313
18917
|
stdout += rel + `
|
|
18314
18918
|
`;
|
|
18315
18919
|
}
|
|
@@ -18337,7 +18941,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
18337
18941
|
}
|
|
18338
18942
|
|
|
18339
18943
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
18340
|
-
import { spawnSync as
|
|
18944
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
18341
18945
|
var repoPathFromRemote = (remote) => {
|
|
18342
18946
|
let rest = (remote || "").trim().replace(/\/+$/, "");
|
|
18343
18947
|
if (!rest)
|
|
@@ -18358,7 +18962,7 @@ var defaultCli = (bin, available) => {
|
|
|
18358
18962
|
if (!available)
|
|
18359
18963
|
return null;
|
|
18360
18964
|
return (args, root) => {
|
|
18361
|
-
const out =
|
|
18965
|
+
const out = spawnSync9(bin, args, {
|
|
18362
18966
|
cwd: root,
|
|
18363
18967
|
encoding: "utf8",
|
|
18364
18968
|
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
@@ -18436,8 +19040,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
18436
19040
|
};
|
|
18437
19041
|
|
|
18438
19042
|
// packages/workit-core/src/core/youtrack.ts
|
|
18439
|
-
import
|
|
18440
|
-
import
|
|
19043
|
+
import fs12 from "node:fs";
|
|
19044
|
+
import path19 from "node:path";
|
|
18441
19045
|
|
|
18442
19046
|
// packages/workit-core/src/core/templates.ts
|
|
18443
19047
|
var repoRoot3 = assetRoot();
|
|
@@ -18450,22 +19054,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
18450
19054
|
// packages/workit-core/src/core/youtrack.ts
|
|
18451
19055
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
18452
19056
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
18453
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
18454
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
19057
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path19.join(configDir(), "youtrack.json");
|
|
19058
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path19.join(resolveConfigDir(), "youtrack.json");
|
|
18455
19059
|
var youTrackTokenModeOk = (p) => {
|
|
18456
19060
|
if (process.platform === "win32")
|
|
18457
19061
|
return true;
|
|
18458
|
-
const mode =
|
|
19062
|
+
const mode = fs12.statSync(p).mode & 511;
|
|
18459
19063
|
return mode === 384;
|
|
18460
19064
|
};
|
|
18461
19065
|
function readYouTrackConfig(required) {
|
|
18462
19066
|
const cfgPath = youTrackConfigPath();
|
|
18463
|
-
if (!
|
|
19067
|
+
if (!fs12.existsSync(cfgPath)) {
|
|
18464
19068
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
18465
19069
|
}
|
|
18466
19070
|
let parsed;
|
|
18467
19071
|
try {
|
|
18468
|
-
parsed = JSON.parse(
|
|
19072
|
+
parsed = JSON.parse(fs12.readFileSync(cfgPath, "utf8"));
|
|
18469
19073
|
} catch {
|
|
18470
19074
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
18471
19075
|
}
|
|
@@ -18476,11 +19080,11 @@ function readYouTrackConfig(required) {
|
|
|
18476
19080
|
}
|
|
18477
19081
|
var readYouTrackContextConfig = () => {
|
|
18478
19082
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
18479
|
-
if (!
|
|
19083
|
+
if (!fs12.existsSync(cfgPath))
|
|
18480
19084
|
return { data: {} };
|
|
18481
19085
|
let parsed;
|
|
18482
19086
|
try {
|
|
18483
|
-
parsed = JSON.parse(
|
|
19087
|
+
parsed = JSON.parse(fs12.readFileSync(cfgPath, "utf8"));
|
|
18484
19088
|
} catch {
|
|
18485
19089
|
return { error: "YouTrack configuration is unavailable" };
|
|
18486
19090
|
}
|
|
@@ -18495,22 +19099,22 @@ function youTrackConfigLoad() {
|
|
|
18495
19099
|
}
|
|
18496
19100
|
const cfgPath = loaded.path;
|
|
18497
19101
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
18498
|
-
const tokenPath = tokenFile ?
|
|
18499
|
-
if (!tokenPath || !
|
|
19102
|
+
const tokenPath = tokenFile ? path19.isAbsolute(tokenFile) ? path19.resolve(tokenFile) : path19.resolve(process.cwd(), tokenFile) : "";
|
|
19103
|
+
if (!tokenPath || !fs12.existsSync(tokenPath)) {
|
|
18500
19104
|
return { error: "missing youtrack.token" };
|
|
18501
19105
|
}
|
|
18502
19106
|
if (!youTrackTokenModeOk(tokenPath)) {
|
|
18503
19107
|
return { error: "youtrack.token mode must be 0600" };
|
|
18504
19108
|
}
|
|
18505
|
-
const token =
|
|
19109
|
+
const token = fs12.readFileSync(tokenPath, "utf8").trim();
|
|
18506
19110
|
if (!token || token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
18507
19111
|
return { error: "token file still placeholder — edit locally, then /wk-status" };
|
|
18508
19112
|
}
|
|
18509
19113
|
const redacted = { ...loaded.config };
|
|
18510
19114
|
delete redacted.tokenFile;
|
|
18511
19115
|
redacted.tokenPresent = true;
|
|
18512
|
-
redacted.configPath =
|
|
18513
|
-
redacted.tokenPath =
|
|
19116
|
+
redacted.configPath = path19.resolve(cfgPath);
|
|
19117
|
+
redacted.tokenPath = path19.resolve(tokenPath);
|
|
18514
19118
|
return { data: redacted };
|
|
18515
19119
|
}
|
|
18516
19120
|
function tzParts(date, tz) {
|
|
@@ -18557,10 +19161,10 @@ function youTrackParseDuration(text) {
|
|
|
18557
19161
|
function youTrackWorkDateMs(dateRaw) {
|
|
18558
19162
|
const cfgPath = youTrackConfigPath();
|
|
18559
19163
|
let effective = effectiveWorkTimezone(null);
|
|
18560
|
-
if (
|
|
19164
|
+
if (fs12.existsSync(cfgPath)) {
|
|
18561
19165
|
let parsed;
|
|
18562
19166
|
try {
|
|
18563
|
-
parsed = JSON.parse(
|
|
19167
|
+
parsed = JSON.parse(fs12.readFileSync(cfgPath, "utf8"));
|
|
18564
19168
|
} catch {
|
|
18565
19169
|
return { error: `${cfgPath} is not valid JSON` };
|
|
18566
19170
|
}
|
|
@@ -18601,12 +19205,12 @@ var youTrackToken = () => {
|
|
|
18601
19205
|
if ("error" in loaded)
|
|
18602
19206
|
return loaded;
|
|
18603
19207
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
18604
|
-
const tokenPath = tokenFile ?
|
|
18605
|
-
if (!tokenPath || !
|
|
19208
|
+
const tokenPath = tokenFile ? path19.isAbsolute(tokenFile) ? path19.resolve(tokenFile) : path19.resolve(process.cwd(), tokenFile) : "";
|
|
19209
|
+
if (!tokenPath || !fs12.existsSync(tokenPath))
|
|
18606
19210
|
return { error: "missing youtrack.token" };
|
|
18607
19211
|
if (!youTrackTokenModeOk(tokenPath))
|
|
18608
19212
|
return { error: "youtrack.token mode must be 0600" };
|
|
18609
|
-
const token =
|
|
19213
|
+
const token = fs12.readFileSync(tokenPath, "utf8").trim();
|
|
18610
19214
|
if (!token)
|
|
18611
19215
|
return { error: "empty token file" };
|
|
18612
19216
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -18733,9 +19337,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
18733
19337
|
} catch {
|
|
18734
19338
|
continue;
|
|
18735
19339
|
}
|
|
18736
|
-
if (!
|
|
19340
|
+
if (!fs12.existsSync(full))
|
|
18737
19341
|
continue;
|
|
18738
|
-
const text =
|
|
19342
|
+
const text = fs12.readFileSync(full, "utf8");
|
|
18739
19343
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
18740
19344
|
if (m)
|
|
18741
19345
|
return m[1];
|
|
@@ -19058,14 +19662,14 @@ async function logTimeUpdate(input, operation = defaultOperations) {
|
|
|
19058
19662
|
}
|
|
19059
19663
|
|
|
19060
19664
|
// packages/workit-core/src/core/external-action-effects.ts
|
|
19061
|
-
import
|
|
19062
|
-
import
|
|
19063
|
-
import { spawnSync as
|
|
19665
|
+
import fs14 from "node:fs";
|
|
19666
|
+
import path21 from "node:path";
|
|
19667
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
19064
19668
|
|
|
19065
19669
|
// packages/workit-core/src/core/changelog.ts
|
|
19066
|
-
import
|
|
19067
|
-
import
|
|
19068
|
-
import { createHash as
|
|
19670
|
+
import fs13 from "node:fs";
|
|
19671
|
+
import path20 from "node:path";
|
|
19672
|
+
import { createHash as createHash9 } from "node:crypto";
|
|
19069
19673
|
var CATEGORIES = ["Added", "Changed", "Deprecated", "Removed", "Fixed", "Security"];
|
|
19070
19674
|
var CAT_RE = /^###\s+(?:Added|Changed|Deprecated|Removed|Fixed|Security)\s*$/i;
|
|
19071
19675
|
var UNRELEASED_RE = /^##\s+\[Unreleased\]\s*$/i;
|
|
@@ -19082,14 +19686,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
19082
19686
|
## [Unreleased]
|
|
19083
19687
|
|
|
19084
19688
|
`;
|
|
19085
|
-
var digestBytes4 = (value) =>
|
|
19689
|
+
var digestBytes4 = (value) => createHash9("sha256").update(value).digest("hex");
|
|
19086
19690
|
var readChangelogSource = (target) => {
|
|
19087
|
-
if (!
|
|
19691
|
+
if (!fs13.existsSync(target)) {
|
|
19088
19692
|
const bytes = Buffer.from(SKELETON, "utf8");
|
|
19089
19693
|
return { exists: false, bytes, text: SKELETON };
|
|
19090
19694
|
}
|
|
19091
19695
|
try {
|
|
19092
|
-
const bytes =
|
|
19696
|
+
const bytes = fs13.readFileSync(target);
|
|
19093
19697
|
const text = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
19094
19698
|
return { exists: true, bytes, text };
|
|
19095
19699
|
} catch {
|
|
@@ -19254,8 +19858,8 @@ function applyChangelog(target, entries, normalizeOnly) {
|
|
|
19254
19858
|
const rendered = renderChangelog(source.text, entries, normalizeOnly);
|
|
19255
19859
|
if ("error" in rendered)
|
|
19256
19860
|
return { error: rendered.error };
|
|
19257
|
-
|
|
19258
|
-
|
|
19861
|
+
fs13.mkdirSync(path20.dirname(target), { recursive: true });
|
|
19862
|
+
fs13.writeFileSync(target, rendered.text, "utf8");
|
|
19259
19863
|
return { ...rendered.result, path: target };
|
|
19260
19864
|
}
|
|
19261
19865
|
function renderChangelog(source, entries, normalizeOnly) {
|
|
@@ -19288,17 +19892,17 @@ function renderChangelog(source, entries, normalizeOnly) {
|
|
|
19288
19892
|
}
|
|
19289
19893
|
var changelogTarget = (workspaceRoot, changelogPath) => {
|
|
19290
19894
|
try {
|
|
19291
|
-
const cwd =
|
|
19292
|
-
const target =
|
|
19293
|
-
if (target !== cwd && !target.startsWith(cwd +
|
|
19895
|
+
const cwd = fs13.realpathSync(resolveWorkspaceRoot(workspaceRoot));
|
|
19896
|
+
const target = path20.resolve(cwd, changelogPath || "CHANGELOG.md");
|
|
19897
|
+
if (target !== cwd && !target.startsWith(cwd + path20.sep))
|
|
19294
19898
|
return { error: "changelog path must be inside workspace_root" };
|
|
19295
19899
|
let ancestor = target;
|
|
19296
|
-
while (!
|
|
19297
|
-
ancestor =
|
|
19298
|
-
const canonicalAncestor =
|
|
19299
|
-
if (canonicalAncestor !== cwd && !canonicalAncestor.startsWith(cwd +
|
|
19900
|
+
while (!fs13.existsSync(ancestor))
|
|
19901
|
+
ancestor = path20.dirname(ancestor);
|
|
19902
|
+
const canonicalAncestor = fs13.realpathSync(ancestor);
|
|
19903
|
+
if (canonicalAncestor !== cwd && !canonicalAncestor.startsWith(cwd + path20.sep))
|
|
19300
19904
|
return { error: "changelog path must be inside workspace_root" };
|
|
19301
|
-
return { cwd, target:
|
|
19905
|
+
return { cwd, target: fs13.existsSync(target) ? fs13.realpathSync(target) : target };
|
|
19302
19906
|
} catch {
|
|
19303
19907
|
return { error: "changelog path must be inside workspace_root" };
|
|
19304
19908
|
}
|
|
@@ -19453,98 +20057,6 @@ function redactText(value) {
|
|
|
19453
20057
|
`);
|
|
19454
20058
|
}
|
|
19455
20059
|
|
|
19456
|
-
// packages/workit-core/src/git/rev.ts
|
|
19457
|
-
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
19458
|
-
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
19459
|
-
var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
19460
|
-
var git = (cwd, args, options = {}) => {
|
|
19461
|
-
const result = spawnSync8("git", args, {
|
|
19462
|
-
cwd,
|
|
19463
|
-
encoding: "utf8",
|
|
19464
|
-
env: options.env ?? process.env,
|
|
19465
|
-
input: options.input,
|
|
19466
|
-
maxBuffer: MAX_BUFFER,
|
|
19467
|
-
timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
|
|
19468
|
-
killSignal: "SIGKILL",
|
|
19469
|
-
windowsHide: true
|
|
19470
|
-
});
|
|
19471
|
-
const error = result.error;
|
|
19472
|
-
return {
|
|
19473
|
-
ok: result.status === 0,
|
|
19474
|
-
stdout: result.stdout ?? "",
|
|
19475
|
-
stderr: result.stderr ?? "",
|
|
19476
|
-
timedOut: error?.code === "ETIMEDOUT"
|
|
19477
|
-
};
|
|
19478
|
-
};
|
|
19479
|
-
var line = (run) => {
|
|
19480
|
-
if (!run.ok)
|
|
19481
|
-
return null;
|
|
19482
|
-
const value = run.stdout.trim();
|
|
19483
|
-
return value ? value : null;
|
|
19484
|
-
};
|
|
19485
|
-
function networkGitInvocation(cwd, args, timeoutMs) {
|
|
19486
|
-
const seconds = Math.max(1, Math.ceil(timeoutMs / 1000));
|
|
19487
|
-
const env = {
|
|
19488
|
-
...process.env,
|
|
19489
|
-
GIT_TERMINAL_PROMPT: "0",
|
|
19490
|
-
GIT_ASKPASS: "",
|
|
19491
|
-
SSH_ASKPASS: "",
|
|
19492
|
-
SSH_ASKPASS_REQUIRE: "never",
|
|
19493
|
-
GCM_INTERACTIVE: "never"
|
|
19494
|
-
};
|
|
19495
|
-
const base = process.env.GIT_SSH_COMMAND || line(git(cwd, ["config", "--get", "core.sshCommand"])) || (process.env.GIT_SSH ? null : "ssh");
|
|
19496
|
-
if (base)
|
|
19497
|
-
env.GIT_SSH_COMMAND = `${base} -o BatchMode=yes -o ConnectTimeout=${Math.min(10, seconds)}`;
|
|
19498
|
-
return {
|
|
19499
|
-
args: ["-c", "http.lowSpeedLimit=1", "-c", `http.lowSpeedTime=${seconds}`, ...args],
|
|
19500
|
-
env
|
|
19501
|
-
};
|
|
19502
|
-
}
|
|
19503
|
-
var WATCHDOG = `
|
|
19504
|
-
const cp = require("node:child_process");
|
|
19505
|
-
const { ms, args } = JSON.parse(process.env.WORKIT_GIT_WATCHDOG);
|
|
19506
|
-
delete process.env.WORKIT_GIT_WATCHDOG;
|
|
19507
|
-
const win = process.platform === "win32";
|
|
19508
|
-
const child = cp.spawn("git", args, { stdio: "inherit", detached: !win, windowsHide: true });
|
|
19509
|
-
const killTree = () => {
|
|
19510
|
-
if (!child.pid) return;
|
|
19511
|
-
if (win) cp.spawnSync("taskkill", ["/pid", String(child.pid), "/T", "/F"], { stdio: "ignore", windowsHide: true });
|
|
19512
|
-
else try { process.kill(-child.pid, "SIGKILL"); } catch {}
|
|
19513
|
-
};
|
|
19514
|
-
let timedOut = false;
|
|
19515
|
-
const timer = setTimeout(() => { timedOut = true; killTree(); }, ms);
|
|
19516
|
-
child.on("error", () => { clearTimeout(timer); process.exit(127); });
|
|
19517
|
-
child.on("exit", (code) => {
|
|
19518
|
-
clearTimeout(timer);
|
|
19519
|
-
if (!win) killTree();
|
|
19520
|
-
process.exit(timedOut ? 124 : code ?? 1);
|
|
19521
|
-
});
|
|
19522
|
-
`;
|
|
19523
|
-
var WATCHDOG_TIMEOUT_EXIT = 124;
|
|
19524
|
-
var gitNetwork = (cwd, args, timeoutMs) => {
|
|
19525
|
-
const invocation = networkGitInvocation(cwd, args, timeoutMs);
|
|
19526
|
-
const result = spawnSync8(process.execPath, ["-e", WATCHDOG], {
|
|
19527
|
-
cwd,
|
|
19528
|
-
encoding: "utf8",
|
|
19529
|
-
env: {
|
|
19530
|
-
...invocation.env,
|
|
19531
|
-
WORKIT_GIT_WATCHDOG: JSON.stringify({ ms: timeoutMs, args: invocation.args })
|
|
19532
|
-
},
|
|
19533
|
-
maxBuffer: MAX_BUFFER,
|
|
19534
|
-
timeout: timeoutMs + 1e4,
|
|
19535
|
-
killSignal: "SIGKILL",
|
|
19536
|
-
windowsHide: true
|
|
19537
|
-
});
|
|
19538
|
-
const error = result.error;
|
|
19539
|
-
return {
|
|
19540
|
-
ok: result.status === 0,
|
|
19541
|
-
stdout: result.stdout ?? "",
|
|
19542
|
-
stderr: result.stderr ?? "",
|
|
19543
|
-
timedOut: result.status === WATCHDOG_TIMEOUT_EXIT || error?.code === "ETIMEDOUT"
|
|
19544
|
-
};
|
|
19545
|
-
};
|
|
19546
|
-
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
19547
|
-
|
|
19548
20060
|
// packages/workit-core/src/ledger.ts
|
|
19549
20061
|
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
19550
20062
|
var FAILING = new Set(["failed", "blocked"]);
|
|
@@ -19659,7 +20171,7 @@ var credentialFreeRemote = (value) => pushRemoteIdentity(value);
|
|
|
19659
20171
|
var actionRoot = (taskRoot, request) => {
|
|
19660
20172
|
const chosen = "cwd" in request.payload ? request.payload.cwd : undefined;
|
|
19661
20173
|
try {
|
|
19662
|
-
const directory =
|
|
20174
|
+
const directory = fs14.realpathSync(chosen ? path21.resolve(taskRoot, chosen) : taskRoot);
|
|
19663
20175
|
if (request.operation === "context.read")
|
|
19664
20176
|
return success(null, null, directory);
|
|
19665
20177
|
const gitAction = request.operation.startsWith("git.") || request.operation.startsWith("hosting.");
|
|
@@ -19670,7 +20182,7 @@ var actionRoot = (taskRoot, request) => {
|
|
|
19670
20182
|
return failure2("capability_unavailable", "action target is not a Git repository", {
|
|
19671
20183
|
outcome: "not_started"
|
|
19672
20184
|
});
|
|
19673
|
-
return success(null, null,
|
|
20185
|
+
return success(null, null, fs14.realpathSync(checkout));
|
|
19674
20186
|
} catch {
|
|
19675
20187
|
return failure2("invalid_input", "action target directory cannot be resolved", {
|
|
19676
20188
|
outcome: "not_started"
|
|
@@ -20002,7 +20514,7 @@ var hostingProvider = (root, remote) => {
|
|
|
20002
20514
|
};
|
|
20003
20515
|
var remoteBranchTip = (root, provider, host, repo, branch) => {
|
|
20004
20516
|
const endpoint = provider === "github" ? `repos/${repo}/git/ref/heads/${branch.split("/").map(encodeURIComponent).join("/")}` : `projects/${encodeURIComponent(repo)}/repository/branches/${encodeURIComponent(branch)}`;
|
|
20005
|
-
const response =
|
|
20517
|
+
const response = spawnSync10(provider === "github" ? "gh" : "glab", ["api", endpoint], {
|
|
20006
20518
|
cwd: root,
|
|
20007
20519
|
encoding: "utf8",
|
|
20008
20520
|
env: hostingCliEnv(provider, host)
|
|
@@ -20010,7 +20522,7 @@ var remoteBranchTip = (root, provider, host, repo, branch) => {
|
|
|
20010
20522
|
if (response.status !== 0) {
|
|
20011
20523
|
if (/\b404\b/.test(response.stderr ?? "")) {
|
|
20012
20524
|
const project = provider === "github" ? `repos/${repo}` : `projects/${encodeURIComponent(repo)}`;
|
|
20013
|
-
const visible =
|
|
20525
|
+
const visible = spawnSync10(provider === "github" ? "gh" : "glab", ["api", project], {
|
|
20014
20526
|
cwd: root,
|
|
20015
20527
|
encoding: "utf8",
|
|
20016
20528
|
env: hostingCliEnv(provider, host)
|
|
@@ -20066,7 +20578,7 @@ var mergedBranch = (root, branch) => {
|
|
|
20066
20578
|
outcome: "not_started"
|
|
20067
20579
|
});
|
|
20068
20580
|
const endpoint = provider === "github" ? `repos/${repo}/pulls?state=closed&head=${encodeURIComponent(`${repo.split("/")[0]}:${branch}`)}&per_page=100` : `projects/${encodeURIComponent(repo)}/merge_requests?state=merged&source_branch=${encodeURIComponent(branch)}&per_page=100`;
|
|
20069
|
-
const response =
|
|
20581
|
+
const response = spawnSync10(provider === "github" ? "gh" : "glab", ["api", endpoint], {
|
|
20070
20582
|
cwd: root,
|
|
20071
20583
|
encoding: "utf8",
|
|
20072
20584
|
env: hostingCliEnv(provider, auth.host)
|
|
@@ -20128,7 +20640,7 @@ var readHostingAction = async (root, resolved, actionRef) => {
|
|
|
20128
20640
|
return unknownHostingEvidence(operation);
|
|
20129
20641
|
const endpoint = cfg.provider === "github" ? `repos/${repo}/pulls?state=all&head=${encodeURIComponent(`${repo.split("/")[0]}:${branch}`)}&base=${encodeURIComponent(target)}&per_page=100&page=1` : `projects/${encodeURIComponent(repo)}/merge_requests?state=all&source_branch=${encodeURIComponent(branch)}&target_branch=${encodeURIComponent(target)}&per_page=100&page=1`;
|
|
20130
20642
|
const cli = cfg.provider === "github" ? "gh" : "glab";
|
|
20131
|
-
const response =
|
|
20643
|
+
const response = spawnSync10(cli, ["api", endpoint], {
|
|
20132
20644
|
cwd: root,
|
|
20133
20645
|
encoding: "utf8",
|
|
20134
20646
|
env: hostingCliEnv(cfg.provider, auth.host)
|
|
@@ -20320,7 +20832,7 @@ var readLocalAction = async (root, resolved, actionRef) => {
|
|
|
20320
20832
|
const sdd = resolved.request.payload.sdd_dir;
|
|
20321
20833
|
if (resolved.request.payload.action === "reapply_stash" && typeof sdd === "string") {
|
|
20322
20834
|
try {
|
|
20323
|
-
const manifest = JSON.parse(
|
|
20835
|
+
const manifest = JSON.parse(fs14.readFileSync(path21.join(sdd, "manifest.json"), "utf8"));
|
|
20324
20836
|
const stashes = gitRaw(root, ["stash", "list", "--format=%H"]);
|
|
20325
20837
|
if (typeof target.stash_commit === "string" && manifest.stash_ref === undefined && stashes !== null && !stashes.split(`
|
|
20326
20838
|
`).includes(target.stash_commit))
|
|
@@ -20328,7 +20840,7 @@ var readLocalAction = async (root, resolved, actionRef) => {
|
|
|
20328
20840
|
} catch {}
|
|
20329
20841
|
} else if (typeof targetBranch === "string" && typeof sdd === "string" && gitValue(root, ["branch", "--show-current"]) === targetBranch && typeof target.target_head === "string" && gitValue(root, ["rev-parse", "HEAD"]) === target.target_head) {
|
|
20330
20842
|
try {
|
|
20331
|
-
const manifest = JSON.parse(
|
|
20843
|
+
const manifest = JSON.parse(fs14.readFileSync(path21.join(sdd, "manifest.json"), "utf8"));
|
|
20332
20844
|
if (manifest.branch === targetBranch)
|
|
20333
20845
|
observed = { branch: targetBranch };
|
|
20334
20846
|
} catch {}
|
|
@@ -20381,7 +20893,7 @@ var readDeletedBranch = async (root, resolved, actionRef) => {
|
|
|
20381
20893
|
};
|
|
20382
20894
|
var youTrackBase = () => {
|
|
20383
20895
|
try {
|
|
20384
|
-
const config = JSON.parse(
|
|
20896
|
+
const config = JSON.parse(fs14.readFileSync(youTrackConfigPath(), "utf8"));
|
|
20385
20897
|
const value = typeof config.baseUrl === "string" ? config.baseUrl.trim() : "";
|
|
20386
20898
|
if (!value || /[\s#?]/.test(value))
|
|
20387
20899
|
return null;
|
|
@@ -20449,7 +20961,7 @@ var resolveExternalActionAtRoot = (root, request, preservedDateMs) => {
|
|
|
20449
20961
|
let stashTarget;
|
|
20450
20962
|
if (action === "reapply_stash") {
|
|
20451
20963
|
try {
|
|
20452
|
-
const manifest = JSON.parse(
|
|
20964
|
+
const manifest = JSON.parse(fs14.readFileSync(path21.join(sdd_dir, "manifest.json"), "utf8"));
|
|
20453
20965
|
const stash_ref = typeof manifest.stash_ref === "string" ? manifest.stash_ref : "";
|
|
20454
20966
|
const stash_commit = stash_ref ? gitValue(root, ["rev-parse", stash_ref]) : null;
|
|
20455
20967
|
if (stash_ref && stash_commit)
|
|
@@ -20724,7 +21236,7 @@ var resolveExternalActionAtRoot = (root, request, preservedDateMs) => {
|
|
|
20724
21236
|
if (preview.error)
|
|
20725
21237
|
return failure2("invalid_input", preview.error);
|
|
20726
21238
|
const target = String(preview.path);
|
|
20727
|
-
const relative =
|
|
21239
|
+
const relative = path21.relative(fs14.realpathSync(root), target).replaceAll(path21.sep, "/") || "CHANGELOG.md";
|
|
20728
21240
|
const normalized = {
|
|
20729
21241
|
operation: request.operation,
|
|
20730
21242
|
payload: {
|
|
@@ -21566,9 +22078,9 @@ import { randomUUID as randomUUID3 } from "node:crypto";
|
|
|
21566
22078
|
// packages/workit-pi/src/worker-protocol.ts
|
|
21567
22079
|
var MAX_WORKER_LINE_BYTES = 64 * 1024;
|
|
21568
22080
|
var MAX_WORKER_STDERR_BYTES = 64 * 1024;
|
|
21569
|
-
var
|
|
22081
|
+
var isRecord4 = (value) => typeof value === "object" && value !== null;
|
|
21570
22082
|
var parseEvent = (value) => {
|
|
21571
|
-
if (!
|
|
22083
|
+
if (!isRecord4(value) || typeof value.type !== "string")
|
|
21572
22084
|
return null;
|
|
21573
22085
|
if (value.type === "workit_worker_ready") {
|
|
21574
22086
|
if (typeof value.workerId !== "string" || typeof value.sessionId !== "string")
|