@brainervirus/workit-codex 2.6.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/.codex-plugin/plugin.json +1 -1
- package/dist/launch-mcp.js +628 -112
- package/dist/workit-hook.js +1366 -821
- package/package.json +3 -3
- package/skills/workit-behavioral-tdd/SKILL.md +15 -9
- package/skills/workit-debug/SKILL.md +2 -1
package/dist/launch-mcp.js
CHANGED
|
@@ -6622,12 +6622,12 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6622
6622
|
|
|
6623
6623
|
// packages/workit-codex/scripts/launch-mcp.ts
|
|
6624
6624
|
import { existsSync as existsSync8, realpathSync as realpathSync9, statSync as statSync3 } from "node:fs";
|
|
6625
|
-
import
|
|
6625
|
+
import path18 from "node:path";
|
|
6626
6626
|
|
|
6627
6627
|
// packages/workit-core/src/core.ts
|
|
6628
|
-
import { spawnSync as
|
|
6628
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
6629
6629
|
import { existsSync as existsSync5, realpathSync as realpathSync7 } from "node:fs";
|
|
6630
|
-
import
|
|
6630
|
+
import path13 from "node:path";
|
|
6631
6631
|
|
|
6632
6632
|
// packages/workit-core/src/core/task-contract.ts
|
|
6633
6633
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -14803,7 +14803,37 @@ var candidateSchema = object({
|
|
|
14803
14803
|
path: ["id"]
|
|
14804
14804
|
});
|
|
14805
14805
|
});
|
|
14806
|
-
var
|
|
14806
|
+
var checkObservationSchema = object({
|
|
14807
|
+
observer: literal("workit_cli"),
|
|
14808
|
+
name: nonEmpty.nullable(),
|
|
14809
|
+
configured: boolean2(),
|
|
14810
|
+
argv: array(text).min(1),
|
|
14811
|
+
shell: boolean2(),
|
|
14812
|
+
cwd: nonEmpty,
|
|
14813
|
+
exitCode: safeInteger,
|
|
14814
|
+
durationMs: safeInteger.min(0),
|
|
14815
|
+
timedOut: boolean2(),
|
|
14816
|
+
head: text.nullable(),
|
|
14817
|
+
tree: text.nullable(),
|
|
14818
|
+
dirty: boolean2().nullable(),
|
|
14819
|
+
signal: text.nullable(),
|
|
14820
|
+
treeAfter: text.nullable(),
|
|
14821
|
+
modifiedWorktree: boolean2(),
|
|
14822
|
+
base: text.nullable(),
|
|
14823
|
+
patchId: text.nullable(),
|
|
14824
|
+
environment: object({
|
|
14825
|
+
platform: nonEmpty,
|
|
14826
|
+
arch: nonEmpty,
|
|
14827
|
+
vars: record(nonEmpty, text.nullable())
|
|
14828
|
+
}).strict(),
|
|
14829
|
+
logDigest: text.nullable(),
|
|
14830
|
+
logRef: text.nullable(),
|
|
14831
|
+
logTail: array(text).max(40),
|
|
14832
|
+
ledgerRowId: text.nullable(),
|
|
14833
|
+
attestation: object({ host: nonEmpty, session: text.nullable(), agentId: text.nullable() }).strict().nullable()
|
|
14834
|
+
}).strict();
|
|
14835
|
+
var CHECK_OBSERVATION_PATH = "evidence.*.data.observation";
|
|
14836
|
+
var evidenceFields = {
|
|
14807
14837
|
kind: _enum(["check", "review", "investigation", "artifact"]),
|
|
14808
14838
|
claim: text,
|
|
14809
14839
|
requirementIds: array(digest),
|
|
@@ -14814,7 +14844,9 @@ var evidenceSchema = object({
|
|
|
14814
14844
|
refs: array(refSchema),
|
|
14815
14845
|
exitCode: safeInteger.nullable(),
|
|
14816
14846
|
reviewContext: refSchema.nullable()
|
|
14817
|
-
}
|
|
14847
|
+
};
|
|
14848
|
+
var evidenceSchema = object({ ...evidenceFields, observation: checkObservationSchema.optional() }).strict();
|
|
14849
|
+
var reportedEvidenceSchema = object(evidenceFields).strict();
|
|
14818
14850
|
var evidenceEvaluationSchema = object({
|
|
14819
14851
|
evidenceId: id,
|
|
14820
14852
|
status: _enum(["passed", "failed", "missing", "skipped", "stale"]),
|
|
@@ -15125,7 +15157,7 @@ var evidenceOperations = {
|
|
|
15125
15157
|
action: literal("record"),
|
|
15126
15158
|
...taskId,
|
|
15127
15159
|
expectedRevision: revision.optional(),
|
|
15128
|
-
evidence:
|
|
15160
|
+
evidence: reportedEvidenceSchema
|
|
15129
15161
|
})
|
|
15130
15162
|
};
|
|
15131
15163
|
var findingOperations = {
|
|
@@ -17625,10 +17657,10 @@ function assertProductWriteAllowed(input) {
|
|
|
17625
17657
|
}
|
|
17626
17658
|
|
|
17627
17659
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
17628
|
-
import { createHash as
|
|
17629
|
-
import * as
|
|
17630
|
-
import { spawnSync as
|
|
17631
|
-
import
|
|
17660
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
17661
|
+
import * as fs10 from "node:fs";
|
|
17662
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
17663
|
+
import path12 from "node:path";
|
|
17632
17664
|
|
|
17633
17665
|
// packages/workit-core/src/core/authority.ts
|
|
17634
17666
|
import { createHash as createHash5 } from "node:crypto";
|
|
@@ -19323,16 +19355,346 @@ function reconcileAction(input) {
|
|
|
19323
19355
|
var bindingCovers = scopeCovers;
|
|
19324
19356
|
var verifyDecisionContent = verifyContentRefs;
|
|
19325
19357
|
|
|
19358
|
+
// packages/workit-core/src/check-config.ts
|
|
19359
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
19360
|
+
import fs8 from "node:fs";
|
|
19361
|
+
import path10 from "node:path";
|
|
19362
|
+
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
19363
|
+
var CHECKS_FILE = "workit.checks.json";
|
|
19364
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
19365
|
+
var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
|
|
19366
|
+
function splitCommand(command) {
|
|
19367
|
+
const argv = [];
|
|
19368
|
+
let current = "";
|
|
19369
|
+
let quote = null;
|
|
19370
|
+
let started = false;
|
|
19371
|
+
for (const char of command) {
|
|
19372
|
+
if (quote) {
|
|
19373
|
+
if (char === quote)
|
|
19374
|
+
quote = null;
|
|
19375
|
+
else
|
|
19376
|
+
current += char;
|
|
19377
|
+
continue;
|
|
19378
|
+
}
|
|
19379
|
+
if (char === '"' || char === "'") {
|
|
19380
|
+
quote = char;
|
|
19381
|
+
started = true;
|
|
19382
|
+
continue;
|
|
19383
|
+
}
|
|
19384
|
+
if (/\s/u.test(char)) {
|
|
19385
|
+
if (started)
|
|
19386
|
+
argv.push(current);
|
|
19387
|
+
current = "";
|
|
19388
|
+
started = false;
|
|
19389
|
+
continue;
|
|
19390
|
+
}
|
|
19391
|
+
if (SHELL_META.test(char))
|
|
19392
|
+
return new Error(`"${command}" uses shell syntax (${char}); configure an argv array instead`);
|
|
19393
|
+
current += char;
|
|
19394
|
+
started = true;
|
|
19395
|
+
}
|
|
19396
|
+
if (quote)
|
|
19397
|
+
return new Error(`"${command}" has an unterminated quote`);
|
|
19398
|
+
if (started)
|
|
19399
|
+
argv.push(current);
|
|
19400
|
+
return argv.length ? argv : new Error("empty command");
|
|
19401
|
+
}
|
|
19402
|
+
var git = (cwd, args) => {
|
|
19403
|
+
const run = spawnSync3("git", args, {
|
|
19404
|
+
cwd,
|
|
19405
|
+
encoding: "utf8",
|
|
19406
|
+
timeout: 20000,
|
|
19407
|
+
killSignal: "SIGKILL",
|
|
19408
|
+
windowsHide: true
|
|
19409
|
+
});
|
|
19410
|
+
const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
|
|
19411
|
+
return value || null;
|
|
19412
|
+
};
|
|
19413
|
+
function checkRoot(cwd) {
|
|
19414
|
+
return path10.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
19415
|
+
}
|
|
19416
|
+
var packageManager = (root) => {
|
|
19417
|
+
const has = (file) => fs8.existsSync(path10.join(root, file));
|
|
19418
|
+
if (has("bun.lock") || has("bun.lockb"))
|
|
19419
|
+
return "bun";
|
|
19420
|
+
if (has("pnpm-lock.yaml"))
|
|
19421
|
+
return "pnpm";
|
|
19422
|
+
if (has("yarn.lock"))
|
|
19423
|
+
return "yarn";
|
|
19424
|
+
return "npm";
|
|
19425
|
+
};
|
|
19426
|
+
var scriptCheck = (pm, name) => {
|
|
19427
|
+
const argv = [pm, "run", name];
|
|
19428
|
+
const accepts = [argv];
|
|
19429
|
+
if (pm === "pnpm" || pm === "yarn" || pm === "npm" && name === "test")
|
|
19430
|
+
accepts.push([pm, name]);
|
|
19431
|
+
return { name, argv, accepts };
|
|
19432
|
+
};
|
|
19433
|
+
var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
19434
|
+
function detectedChecks(root) {
|
|
19435
|
+
const read = (file) => {
|
|
19436
|
+
try {
|
|
19437
|
+
return fs8.readFileSync(path10.join(root, file), "utf8");
|
|
19438
|
+
} catch {
|
|
19439
|
+
return null;
|
|
19440
|
+
}
|
|
19441
|
+
};
|
|
19442
|
+
const checks = [];
|
|
19443
|
+
const add = (check) => {
|
|
19444
|
+
if (!checks.some((item) => item.name === check.name))
|
|
19445
|
+
checks.push(check);
|
|
19446
|
+
};
|
|
19447
|
+
const manifestText = read("package.json");
|
|
19448
|
+
if (manifestText !== null) {
|
|
19449
|
+
let manifest = null;
|
|
19450
|
+
try {
|
|
19451
|
+
manifest = JSON.parse(manifestText);
|
|
19452
|
+
} catch {}
|
|
19453
|
+
const scripts = isRecord2(manifest) && isRecord2(manifest.scripts) ? manifest.scripts : {};
|
|
19454
|
+
const pm = packageManager(root);
|
|
19455
|
+
for (const name of SCRIPT_CHECKS) {
|
|
19456
|
+
const script = scripts[name];
|
|
19457
|
+
if (typeof script === "string" && script.trim() !== "")
|
|
19458
|
+
add(scriptCheck(pm, name));
|
|
19459
|
+
}
|
|
19460
|
+
}
|
|
19461
|
+
const plain = (name, argv) => ({ name, argv, accepts: [argv] });
|
|
19462
|
+
if (read("go.mod") !== null)
|
|
19463
|
+
add(plain("test", ["go", "test", "./..."]));
|
|
19464
|
+
if (read("Cargo.toml") !== null)
|
|
19465
|
+
add(plain("test", ["cargo", "test"]));
|
|
19466
|
+
if (read("pytest.ini") !== null || /\bpytest\b/u.test(read("pyproject.toml") ?? ""))
|
|
19467
|
+
add(plain("test", ["pytest"]));
|
|
19468
|
+
const makefile = read("Makefile") ?? read("makefile") ?? read("GNUmakefile");
|
|
19469
|
+
if (makefile !== null && /^test\s*:(?!=)/mu.test(makefile))
|
|
19470
|
+
add(plain("test", ["make", "test"]));
|
|
19471
|
+
return checks;
|
|
19472
|
+
}
|
|
19473
|
+
var GATES = ["testing", "verification"];
|
|
19474
|
+
function loadCheckConfig(cwd) {
|
|
19475
|
+
const root = checkRoot(cwd);
|
|
19476
|
+
const file = path10.join(root, CHECKS_FILE);
|
|
19477
|
+
const none = (error = null) => ({
|
|
19478
|
+
source: error ? CHECKS_FILE : "none",
|
|
19479
|
+
root,
|
|
19480
|
+
checks: [],
|
|
19481
|
+
gates: {},
|
|
19482
|
+
error
|
|
19483
|
+
});
|
|
19484
|
+
let text = null;
|
|
19485
|
+
try {
|
|
19486
|
+
text = fs8.readFileSync(file, "utf8");
|
|
19487
|
+
} catch (error) {
|
|
19488
|
+
if (error.code !== "ENOENT")
|
|
19489
|
+
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
19490
|
+
}
|
|
19491
|
+
if (text === null) {
|
|
19492
|
+
const checks = detectedChecks(root);
|
|
19493
|
+
return checks.length ? { source: "defaults", root, checks, gates: {}, error: null } : none();
|
|
19494
|
+
}
|
|
19495
|
+
let parsed;
|
|
19496
|
+
try {
|
|
19497
|
+
parsed = JSON.parse(text);
|
|
19498
|
+
} catch {
|
|
19499
|
+
return none(`${CHECKS_FILE} is not valid JSON`);
|
|
19500
|
+
}
|
|
19501
|
+
if (!isRecord2(parsed) || !isRecord2(parsed.checks))
|
|
19502
|
+
return none(`${CHECKS_FILE} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
|
|
19503
|
+
const checks = [];
|
|
19504
|
+
for (const [name, value] of Object.entries(parsed.checks)) {
|
|
19505
|
+
if (!CHECK_NAME.test(name))
|
|
19506
|
+
return none(`${CHECKS_FILE}: check name "${name}" must match ${CHECK_NAME.source}`);
|
|
19507
|
+
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");
|
|
19508
|
+
if (argv instanceof Error)
|
|
19509
|
+
return none(`${CHECKS_FILE}: check "${name}": ${argv.message}`);
|
|
19510
|
+
checks.push({ name, argv, accepts: [argv] });
|
|
19511
|
+
}
|
|
19512
|
+
const gates = {};
|
|
19513
|
+
if (parsed.gates !== undefined) {
|
|
19514
|
+
if (!isRecord2(parsed.gates))
|
|
19515
|
+
return none(`${CHECKS_FILE}: gates must be {"testing":"<name>"|["<name>",…]}`);
|
|
19516
|
+
for (const [gate, value] of Object.entries(parsed.gates)) {
|
|
19517
|
+
if (!GATES.includes(gate))
|
|
19518
|
+
return none(`${CHECKS_FILE}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
|
|
19519
|
+
const names = typeof value === "string" ? [value] : value;
|
|
19520
|
+
if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
|
|
19521
|
+
return none(`${CHECKS_FILE}: gates.${gate} must name configured checks`);
|
|
19522
|
+
gates[gate] = names;
|
|
19523
|
+
}
|
|
19524
|
+
}
|
|
19525
|
+
return { source: CHECKS_FILE, root, checks, gates, error: null };
|
|
19526
|
+
}
|
|
19527
|
+
var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
|
|
19528
|
+
function matchesNamedCheck(config, name, argv, cwd) {
|
|
19529
|
+
if (config.error || name === null || cwd !== ".")
|
|
19530
|
+
return false;
|
|
19531
|
+
const check = config.checks.find((item) => item.name === name);
|
|
19532
|
+
return Boolean(check?.accepts.some((accepted) => sameArgv(accepted, argv)));
|
|
19533
|
+
}
|
|
19534
|
+
function gateCheckNames(config, dimension) {
|
|
19535
|
+
const explicit = config.gates[dimension];
|
|
19536
|
+
if (explicit)
|
|
19537
|
+
return explicit;
|
|
19538
|
+
const names = config.checks.map((check) => check.name);
|
|
19539
|
+
if (dimension === "testing")
|
|
19540
|
+
return names.includes("test") ? ["test"] : [];
|
|
19541
|
+
return names;
|
|
19542
|
+
}
|
|
19543
|
+
var workitOnPath;
|
|
19544
|
+
function workitAvailable(env = process.env) {
|
|
19545
|
+
if (workitOnPath !== undefined)
|
|
19546
|
+
return workitOnPath;
|
|
19547
|
+
const windows = process.platform === "win32";
|
|
19548
|
+
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
19549
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path10.delimiter).filter(Boolean);
|
|
19550
|
+
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
19551
|
+
try {
|
|
19552
|
+
return fs8.statSync(path10.join(dir, `workit${ext}`)).isFile();
|
|
19553
|
+
} catch {
|
|
19554
|
+
return false;
|
|
19555
|
+
}
|
|
19556
|
+
}));
|
|
19557
|
+
return workitOnPath;
|
|
19558
|
+
}
|
|
19559
|
+
function checkCommand(args, version, onPath = workitAvailable()) {
|
|
19560
|
+
return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
|
|
19561
|
+
}
|
|
19562
|
+
|
|
19563
|
+
// packages/workit-core/src/git/rev.ts
|
|
19564
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
19565
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
19566
|
+
import fs9 from "node:fs";
|
|
19567
|
+
import os2 from "node:os";
|
|
19568
|
+
import path11 from "node:path";
|
|
19569
|
+
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
19570
|
+
var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
19571
|
+
var git2 = (cwd, args, options = {}) => {
|
|
19572
|
+
const result = spawnSync4("git", args, {
|
|
19573
|
+
cwd,
|
|
19574
|
+
encoding: "utf8",
|
|
19575
|
+
env: options.env ?? process.env,
|
|
19576
|
+
input: options.input,
|
|
19577
|
+
maxBuffer: MAX_BUFFER,
|
|
19578
|
+
timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
|
|
19579
|
+
killSignal: "SIGKILL",
|
|
19580
|
+
windowsHide: true
|
|
19581
|
+
});
|
|
19582
|
+
const error = result.error;
|
|
19583
|
+
return {
|
|
19584
|
+
ok: result.status === 0,
|
|
19585
|
+
stdout: result.stdout ?? "",
|
|
19586
|
+
stderr: result.stderr ?? "",
|
|
19587
|
+
timedOut: error?.code === "ETIMEDOUT"
|
|
19588
|
+
};
|
|
19589
|
+
};
|
|
19590
|
+
var line = (run) => {
|
|
19591
|
+
if (!run.ok)
|
|
19592
|
+
return null;
|
|
19593
|
+
const value = run.stdout.trim();
|
|
19594
|
+
return value ? value : null;
|
|
19595
|
+
};
|
|
19596
|
+
function headSha(cwd) {
|
|
19597
|
+
return line(git2(cwd, ["rev-parse", "--verify", "-q", "HEAD^{commit}"]));
|
|
19598
|
+
}
|
|
19599
|
+
var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
|
|
19600
|
+
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
19601
|
+
var seedIndex = (from, to) => {
|
|
19602
|
+
fs9.copyFileSync(from, to);
|
|
19603
|
+
const { atimeNs, mtimeNs } = fs9.statSync(from, { bigint: true });
|
|
19604
|
+
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
19605
|
+
fs9.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
19606
|
+
};
|
|
19607
|
+
function worktreeTree(cwd, options = {}) {
|
|
19608
|
+
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
19609
|
+
const indexPath = line(git2(cwd, ["rev-parse", "--path-format=absolute", "--git-path", "index"]));
|
|
19610
|
+
if (!top || !indexPath)
|
|
19611
|
+
return null;
|
|
19612
|
+
const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
|
|
19613
|
+
const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
|
|
19614
|
+
const scratch = fs9.mkdtempSync(path11.join(os2.tmpdir(), "workit-tree-"));
|
|
19615
|
+
try {
|
|
19616
|
+
const tempIndex = path11.join(scratch, "index");
|
|
19617
|
+
if (fs9.existsSync(indexPath))
|
|
19618
|
+
seedIndex(indexPath, tempIndex);
|
|
19619
|
+
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
19620
|
+
const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
19621
|
+
env,
|
|
19622
|
+
timeoutMs
|
|
19623
|
+
});
|
|
19624
|
+
if (!untracked.ok)
|
|
19625
|
+
return null;
|
|
19626
|
+
const skipped = [];
|
|
19627
|
+
for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
|
|
19628
|
+
try {
|
|
19629
|
+
const stat = fs9.statSync(path11.join(top, file));
|
|
19630
|
+
if (stat.isFile() && stat.size > cap)
|
|
19631
|
+
skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
|
|
19632
|
+
} catch {}
|
|
19633
|
+
}
|
|
19634
|
+
skipped.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
19635
|
+
const added = skipped.length ? git2(top, ["add", "-A", "--pathspec-from-file=-", "--pathspec-file-nul"], {
|
|
19636
|
+
env,
|
|
19637
|
+
timeoutMs,
|
|
19638
|
+
input: [".", ...skipped.map((file) => `:(exclude,literal)${file.path}`)].join("\x00")
|
|
19639
|
+
}) : git2(top, ["add", "-A"], { env, timeoutMs });
|
|
19640
|
+
if (!added.ok)
|
|
19641
|
+
return null;
|
|
19642
|
+
const tree = line(git2(top, ["write-tree"], { env, timeoutMs }));
|
|
19643
|
+
if (!tree)
|
|
19644
|
+
return null;
|
|
19645
|
+
const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
|
|
19646
|
+
const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
|
|
19647
|
+
return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
|
|
19648
|
+
} finally {
|
|
19649
|
+
fs9.rmSync(scratch, { recursive: true, force: true });
|
|
19650
|
+
}
|
|
19651
|
+
}
|
|
19652
|
+
function worktreeSignal(cwd, options = {}) {
|
|
19653
|
+
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
19654
|
+
if (!top)
|
|
19655
|
+
return null;
|
|
19656
|
+
const status = git2(top, [
|
|
19657
|
+
"status",
|
|
19658
|
+
"--porcelain=v1",
|
|
19659
|
+
"-z",
|
|
19660
|
+
"--untracked-files=all",
|
|
19661
|
+
"--no-renames",
|
|
19662
|
+
"--ignore-submodules=dirty"
|
|
19663
|
+
], {
|
|
19664
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
|
|
19665
|
+
timeoutMs: options.timeoutMs ?? GIT_TIMEOUTS.worktree
|
|
19666
|
+
});
|
|
19667
|
+
if (!status.ok)
|
|
19668
|
+
return null;
|
|
19669
|
+
const hash = createHash6("sha256");
|
|
19670
|
+
hash.update(`${headSha(top) ?? "unborn"}\x00`);
|
|
19671
|
+
for (const entry of status.stdout.split("\x00")) {
|
|
19672
|
+
if (entry.length < 4)
|
|
19673
|
+
continue;
|
|
19674
|
+
const file = entry.slice(3);
|
|
19675
|
+
const absolute = path11.join(top, file);
|
|
19676
|
+
let stat = "-";
|
|
19677
|
+
try {
|
|
19678
|
+
const info = fs9.lstatSync(absolute, { bigint: true });
|
|
19679
|
+
stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
|
|
19680
|
+
if (info.isDirectory())
|
|
19681
|
+
stat += `:${headSha(absolute) ?? "-"}`;
|
|
19682
|
+
} catch {}
|
|
19683
|
+
hash.update(`${entry.slice(0, 2)}\x00${file}\x00${stat}\x00`);
|
|
19684
|
+
}
|
|
19685
|
+
return `sig:${hash.digest("hex")}`;
|
|
19686
|
+
}
|
|
19687
|
+
|
|
19326
19688
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
19327
|
-
var digestBytes3 = (value) =>
|
|
19328
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
19329
|
-
var relative = (root, candidate) =>
|
|
19689
|
+
var digestBytes3 = (value) => createHash7("sha256").update(value).digest("hex");
|
|
19690
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path12.sep}`);
|
|
19691
|
+
var relative = (root, candidate) => path12.relative(root, candidate).split(path12.sep).join("/") || ".";
|
|
19330
19692
|
var canonicalPath = (value) => {
|
|
19331
19693
|
if (typeof value !== "string" || !value)
|
|
19332
19694
|
return null;
|
|
19333
19695
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
19334
19696
|
return null;
|
|
19335
|
-
const normalized =
|
|
19697
|
+
const normalized = path12.posix.normalize(value);
|
|
19336
19698
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
19337
19699
|
return null;
|
|
19338
19700
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -19368,7 +19730,7 @@ var compareCodeUnits2 = (left, right) => {
|
|
|
19368
19730
|
return left.length - right.length;
|
|
19369
19731
|
};
|
|
19370
19732
|
var gitPaths = (root) => {
|
|
19371
|
-
const result =
|
|
19733
|
+
const result = spawnSync5("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
19372
19734
|
cwd: root,
|
|
19373
19735
|
encoding: "buffer"
|
|
19374
19736
|
});
|
|
@@ -19380,21 +19742,21 @@ var gitPaths = (root) => {
|
|
|
19380
19742
|
git: !/not a git repository/i.test(stderr)
|
|
19381
19743
|
};
|
|
19382
19744
|
}
|
|
19383
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
19384
|
-
const stagedDeleted =
|
|
19745
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path12.sep).join("/")) : [];
|
|
19746
|
+
const stagedDeleted = spawnSync5("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
19385
19747
|
if (stagedDeleted.status !== 0) {
|
|
19386
19748
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
19387
19749
|
if (!/not a git repository/i.test(stderr))
|
|
19388
19750
|
return { paths, uncertain: true, git: true };
|
|
19389
19751
|
} else if (stagedDeleted.stdout) {
|
|
19390
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
19752
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path12.sep).join("/")));
|
|
19391
19753
|
}
|
|
19392
19754
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
19393
19755
|
};
|
|
19394
19756
|
var walk = (root, directory, output) => {
|
|
19395
19757
|
let names;
|
|
19396
19758
|
try {
|
|
19397
|
-
names =
|
|
19759
|
+
names = fs10.readdirSync(directory);
|
|
19398
19760
|
} catch {
|
|
19399
19761
|
return true;
|
|
19400
19762
|
}
|
|
@@ -19402,10 +19764,10 @@ var walk = (root, directory, output) => {
|
|
|
19402
19764
|
for (const name of names) {
|
|
19403
19765
|
if (name === ".git" || name === ".workit")
|
|
19404
19766
|
continue;
|
|
19405
|
-
const target =
|
|
19767
|
+
const target = path12.join(directory, name);
|
|
19406
19768
|
let stat;
|
|
19407
19769
|
try {
|
|
19408
|
-
stat =
|
|
19770
|
+
stat = fs10.lstatSync(target);
|
|
19409
19771
|
} catch {
|
|
19410
19772
|
uncertain = true;
|
|
19411
19773
|
continue;
|
|
@@ -19420,14 +19782,14 @@ var walk = (root, directory, output) => {
|
|
|
19420
19782
|
var scopeRoots = (root, scope) => {
|
|
19421
19783
|
const roots = [];
|
|
19422
19784
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
19423
|
-
const target =
|
|
19785
|
+
const target = path12.resolve(root, item);
|
|
19424
19786
|
if (!inside(root, target))
|
|
19425
19787
|
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` }] });
|
|
19426
19788
|
let ancestor = target;
|
|
19427
|
-
while (!
|
|
19428
|
-
ancestor =
|
|
19789
|
+
while (!fs10.existsSync(ancestor) && ancestor !== root)
|
|
19790
|
+
ancestor = path12.dirname(ancestor);
|
|
19429
19791
|
try {
|
|
19430
|
-
if (!inside(root,
|
|
19792
|
+
if (!inside(root, fs10.realpathSync(ancestor)))
|
|
19431
19793
|
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` }] });
|
|
19432
19794
|
} catch {
|
|
19433
19795
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -19439,7 +19801,7 @@ var scopeRoots = (root, scope) => {
|
|
|
19439
19801
|
function captureCandidate(root, scope, environment = []) {
|
|
19440
19802
|
let checkout;
|
|
19441
19803
|
try {
|
|
19442
|
-
checkout =
|
|
19804
|
+
checkout = fs10.realpathSync(root);
|
|
19443
19805
|
} catch {
|
|
19444
19806
|
return failure2("invalid_input", "candidate root does not exist");
|
|
19445
19807
|
}
|
|
@@ -19457,11 +19819,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
19457
19819
|
if (item === ".git" || item === ".workit")
|
|
19458
19820
|
continue;
|
|
19459
19821
|
names.add(item);
|
|
19460
|
-
if (!
|
|
19822
|
+
if (!fs10.existsSync(target))
|
|
19461
19823
|
continue;
|
|
19462
19824
|
let stat;
|
|
19463
19825
|
try {
|
|
19464
|
-
stat =
|
|
19826
|
+
stat = fs10.lstatSync(target);
|
|
19465
19827
|
} catch {
|
|
19466
19828
|
uncertain = true;
|
|
19467
19829
|
continue;
|
|
@@ -19477,17 +19839,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
19477
19839
|
for (const item of [...names].toSorted()) {
|
|
19478
19840
|
if (!scopeMatches(item, normalizedScope))
|
|
19479
19841
|
continue;
|
|
19480
|
-
const target =
|
|
19842
|
+
const target = path12.join(checkout, item);
|
|
19481
19843
|
try {
|
|
19482
|
-
const stat =
|
|
19844
|
+
const stat = fs10.lstatSync(target);
|
|
19483
19845
|
if (stat.isSymbolicLink()) {
|
|
19484
|
-
const link =
|
|
19846
|
+
const link = fs10.readlinkSync(target);
|
|
19485
19847
|
files.push({ path: item, kind: "symlink", digest: digestBytes3(link), executable: null });
|
|
19486
19848
|
} else if (stat.isFile()) {
|
|
19487
19849
|
files.push({
|
|
19488
19850
|
path: item,
|
|
19489
19851
|
kind: "file",
|
|
19490
|
-
digest: digestBytes3(
|
|
19852
|
+
digest: digestBytes3(fs10.readFileSync(target)),
|
|
19491
19853
|
executable: (stat.mode & 73) !== 0
|
|
19492
19854
|
});
|
|
19493
19855
|
} else if (!stat.isDirectory()) {
|
|
@@ -19512,7 +19874,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
19512
19874
|
const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
|
|
19513
19875
|
if (values.some(({ value }) => value === null))
|
|
19514
19876
|
completeness = "uncertain";
|
|
19515
|
-
const headResult =
|
|
19877
|
+
const headResult = spawnSync5("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
|
|
19516
19878
|
if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
|
|
19517
19879
|
completeness = "uncertain";
|
|
19518
19880
|
const head = headResult.status === 0 ? headResult.stdout.trim() : null;
|
|
@@ -19550,7 +19912,24 @@ var sameSession = (left, right) => {
|
|
|
19550
19912
|
return left !== null && other !== null && left.host === other.host && left.handle === other.handle;
|
|
19551
19913
|
};
|
|
19552
19914
|
var evidenceScopes = (task, ids) => (task.policy?.requirements ?? []).filter((requirement) => ids.includes(requirement.id)).map((requirement) => requirement.scope);
|
|
19553
|
-
|
|
19915
|
+
var memo2 = (compute) => {
|
|
19916
|
+
let value;
|
|
19917
|
+
return () => {
|
|
19918
|
+
if (value === undefined)
|
|
19919
|
+
value = compute();
|
|
19920
|
+
return value;
|
|
19921
|
+
};
|
|
19922
|
+
};
|
|
19923
|
+
var treeFreshness = (root) => ({
|
|
19924
|
+
mode: "tree",
|
|
19925
|
+
current: memo2(() => worktreeTree(root)?.key ?? null)
|
|
19926
|
+
});
|
|
19927
|
+
var signalFreshness = (root) => ({
|
|
19928
|
+
mode: "signal",
|
|
19929
|
+
current: memo2(() => worktreeSignal(root))
|
|
19930
|
+
});
|
|
19931
|
+
var isObservedCheck = (entry) => entry.data.kind === "check" && entry.data.observation?.observer === "workit_cli" && entry.provenance?.kind === "host_observed" && entry.provenance.host === "workit_cli";
|
|
19932
|
+
function evaluateEvidence(task, candidate, freshness) {
|
|
19554
19933
|
const current = candidate ?? task.candidates.at(-1) ?? null;
|
|
19555
19934
|
return task.evidence.map((entry) => {
|
|
19556
19935
|
const evidence = entry.data;
|
|
@@ -19560,6 +19939,25 @@ function evaluateEvidence(task, candidate) {
|
|
|
19560
19939
|
status: "failed",
|
|
19561
19940
|
reason: "failed evidence remains historical"
|
|
19562
19941
|
};
|
|
19942
|
+
const observation = isObservedCheck(entry) ? evidence.observation : undefined;
|
|
19943
|
+
if (observation && observation.tree !== null) {
|
|
19944
|
+
if (task.status === "closed")
|
|
19945
|
+
return { evidenceId: entry.id, status: evidence.result, reason: "recorded before close" };
|
|
19946
|
+
if (observation.modifiedWorktree)
|
|
19947
|
+
return {
|
|
19948
|
+
evidenceId: entry.id,
|
|
19949
|
+
status: "stale",
|
|
19950
|
+
reason: "the check changed the worktree while it ran; ignore or clean its outputs"
|
|
19951
|
+
};
|
|
19952
|
+
const recorded = freshness?.mode === "signal" ? observation.signal : observation.tree;
|
|
19953
|
+
if (recorded !== null && freshness && recorded === freshness.current())
|
|
19954
|
+
return { evidenceId: entry.id, status: evidence.result, reason: "worktree is unchanged" };
|
|
19955
|
+
return {
|
|
19956
|
+
evidenceId: entry.id,
|
|
19957
|
+
status: "stale",
|
|
19958
|
+
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"
|
|
19959
|
+
};
|
|
19960
|
+
}
|
|
19563
19961
|
if (evidence.kind === "check" || evidence.kind === "review") {
|
|
19564
19962
|
if (!evidence.beforeCandidateId || !evidence.candidateId)
|
|
19565
19963
|
return {
|
|
@@ -19643,8 +20041,45 @@ var expectedKinds = (dimension, ruleId) => {
|
|
|
19643
20041
|
return "decision";
|
|
19644
20042
|
return "worker report";
|
|
19645
20043
|
};
|
|
19646
|
-
|
|
19647
|
-
|
|
20044
|
+
var observedGate = (requirement) => (requirement.dimension === "testing" || requirement.dimension === "verification") && requirement.before === "close";
|
|
20045
|
+
function evaluateObservedGate(task, requirement, statuses, config) {
|
|
20046
|
+
const names = gateCheckNames(config, requirement.dimension);
|
|
20047
|
+
const observed = task.evidence.filter(isObservedCheck);
|
|
20048
|
+
const fresh = observed.filter((entry) => statuses.get(entry.id) === "passed" && entry.data.observation?.exitCode === 0);
|
|
20049
|
+
const named = (entry) => {
|
|
20050
|
+
const observation = entry.data.observation;
|
|
20051
|
+
return observation.configured && !observation.shell && names.includes(observation.name ?? "") && matchesNamedCheck(config, observation.name, observation.argv, observation.cwd);
|
|
20052
|
+
};
|
|
20053
|
+
const considered = observed.map((entry) => entry.id);
|
|
20054
|
+
const run = checkCommand(names[0] ?? "<name>", runtimeVersion());
|
|
20055
|
+
if (config.error)
|
|
20056
|
+
return {
|
|
20057
|
+
satisfied: [],
|
|
20058
|
+
considered,
|
|
20059
|
+
reason: `check config is invalid (${config.error}); fix it, then run ${run}`
|
|
20060
|
+
};
|
|
20061
|
+
if (!names.length)
|
|
20062
|
+
return {
|
|
20063
|
+
satisfied: [],
|
|
20064
|
+
considered,
|
|
20065
|
+
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())}`
|
|
20066
|
+
};
|
|
20067
|
+
const satisfied = fresh.filter(named);
|
|
20068
|
+
if (satisfied.length)
|
|
20069
|
+
return {
|
|
20070
|
+
satisfied: satisfied.map((entry) => entry.id),
|
|
20071
|
+
considered,
|
|
20072
|
+
reason: `a fresh passing configured check (${names.join("|")}) was observed by workit check`
|
|
20073
|
+
};
|
|
20074
|
+
const reported = task.evidence.some((entry) => entry.data.kind === "check" && !isObservedCheck(entry) && entry.data.requirementIds.includes(requirement.id));
|
|
20075
|
+
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}`;
|
|
20076
|
+
return { satisfied: [], considered, reason };
|
|
20077
|
+
}
|
|
20078
|
+
function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller, freshness = treeFreshness(checkoutRoot)) {
|
|
20079
|
+
const evidence = evaluateEvidence(task, candidate, freshness);
|
|
20080
|
+
const statuses = new Map(evidence.map((item) => [item.evidenceId, item.status]));
|
|
20081
|
+
let checkConfig;
|
|
20082
|
+
const configFor = () => checkConfig ??= loadCheckConfig(checkoutRoot);
|
|
19648
20083
|
if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
|
|
19649
20084
|
return task.policy.requirements.map((requirement) => ({
|
|
19650
20085
|
requirementId: requirement.id,
|
|
@@ -19654,8 +20089,19 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
19654
20089
|
reason: "stored policy version is unsupported; reassessment is required"
|
|
19655
20090
|
}));
|
|
19656
20091
|
return (task.policy?.requirements ?? []).map((requirement) => {
|
|
20092
|
+
const gate = observedGate(requirement) ? evaluateObservedGate(task, requirement, statuses, configFor()) : null;
|
|
20093
|
+
if (gate?.satisfied.length)
|
|
20094
|
+
return {
|
|
20095
|
+
requirementId: requirement.id,
|
|
20096
|
+
status: "satisfied",
|
|
20097
|
+
evidenceIds: gate.satisfied,
|
|
20098
|
+
decisionIds: [],
|
|
20099
|
+
reason: gate.reason
|
|
20100
|
+
};
|
|
19657
20101
|
const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
|
|
19658
20102
|
const passed = related.filter(({ entry, evaluation }) => {
|
|
20103
|
+
if (gate)
|
|
20104
|
+
return false;
|
|
19659
20105
|
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
|
|
19660
20106
|
return false;
|
|
19661
20107
|
if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
|
|
@@ -19718,6 +20164,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
19718
20164
|
reason: "an applicable approved limitation permits the missing evidence"
|
|
19719
20165
|
};
|
|
19720
20166
|
const unavailable = capabilities.some((capability) => capability.assurance === "unavailable" && (capability.name === requirement.dimension || capability.surface === requirement.dimension));
|
|
20167
|
+
if (gate)
|
|
20168
|
+
return {
|
|
20169
|
+
requirementId: requirement.id,
|
|
20170
|
+
status: unavailable ? "unavailable" : "unsatisfied",
|
|
20171
|
+
evidenceIds: [...new Set([...gate.considered, ...related.map(({ entry }) => entry.id)])],
|
|
20172
|
+
decisionIds: [],
|
|
20173
|
+
reason: unavailable ? "required capability is unavailable" : gate.reason
|
|
20174
|
+
};
|
|
19721
20175
|
if (!unavailable && related.length > 0 && !passed.length)
|
|
19722
20176
|
return {
|
|
19723
20177
|
requirementId: requirement.id,
|
|
@@ -19870,7 +20324,7 @@ function reconcileResume(view, observations = []) {
|
|
|
19870
20324
|
const candidate = captureCandidate(view.workspace.root, view.task.intent.data.scope, []);
|
|
19871
20325
|
if (!candidate.ok)
|
|
19872
20326
|
return candidate;
|
|
19873
|
-
const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
|
|
20327
|
+
const staleEvidenceIds = evaluateEvidence(view.task, candidate.data, treeFreshness(view.workspace.root)).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
|
|
19874
20328
|
const blockers = [...view.task.progress.blockers];
|
|
19875
20329
|
if (view.task.workers.some((entry) => isUncertainWorker(entry.data.state)))
|
|
19876
20330
|
blockers.push({
|
|
@@ -21210,10 +21664,7 @@ class WorkitCore {
|
|
|
21210
21664
|
if (finding.data.disposition !== "fixed")
|
|
21211
21665
|
return finding;
|
|
21212
21666
|
const withEntry = { ...current, candidates, evidence: [...current.evidence, entry] };
|
|
21213
|
-
const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data).map((item) => [
|
|
21214
|
-
item.evidenceId,
|
|
21215
|
-
item.status
|
|
21216
|
-
]));
|
|
21667
|
+
const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data, treeFreshness(this.store.root)).map((item) => [item.evidenceId, item.status]));
|
|
21217
21668
|
const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
|
|
21218
21669
|
kind: item.data.kind,
|
|
21219
21670
|
candidateId: item.data.candidateId,
|
|
@@ -21228,6 +21679,71 @@ class WorkitCore {
|
|
|
21228
21679
|
return changed;
|
|
21229
21680
|
return success(changed.data.revision, null, changed.data.evidence.at(-1));
|
|
21230
21681
|
}
|
|
21682
|
+
observeCheck(request) {
|
|
21683
|
+
return this.retryOmittedRevisions(request, () => this.observeCheckOnce(request));
|
|
21684
|
+
}
|
|
21685
|
+
observeCheckOnce(request) {
|
|
21686
|
+
const root = this.contextRootError();
|
|
21687
|
+
if (!root.ok)
|
|
21688
|
+
return root;
|
|
21689
|
+
if (this.context.caller.host !== "workit_cli" || (this.context.workerId ?? null) !== null)
|
|
21690
|
+
return failure2("permission_denied", "only the workit CLI records observed checks");
|
|
21691
|
+
const observation = checkObservationSchema.safeParse(request?.observation);
|
|
21692
|
+
if (!observation.success)
|
|
21693
|
+
return failure2("invalid_input", "check observation is invalid", {
|
|
21694
|
+
fields: observation.error.issues.map((issue) => ({
|
|
21695
|
+
path: issue.path.join("."),
|
|
21696
|
+
reason: issue.message
|
|
21697
|
+
}))
|
|
21698
|
+
});
|
|
21699
|
+
const task = this.store.readTask(request.taskId);
|
|
21700
|
+
if (!task.ok)
|
|
21701
|
+
return task;
|
|
21702
|
+
if (task.data.status === "closed")
|
|
21703
|
+
return failure2("invalid_transition", "closed task cannot record evidence");
|
|
21704
|
+
const observed = observation.data;
|
|
21705
|
+
let candidate = null;
|
|
21706
|
+
if (observed.tree === null) {
|
|
21707
|
+
const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
21708
|
+
if (!captured.ok)
|
|
21709
|
+
return captured;
|
|
21710
|
+
candidate = captured.data;
|
|
21711
|
+
}
|
|
21712
|
+
const label = observed.name ?? observed.argv.join(" ");
|
|
21713
|
+
const data = {
|
|
21714
|
+
kind: "check",
|
|
21715
|
+
claim: `workit check ${label}`.slice(0, 500),
|
|
21716
|
+
requirementIds: [],
|
|
21717
|
+
...candidate ? { beforeCandidateId: candidate.id, candidateId: candidate.id } : {},
|
|
21718
|
+
result: observed.exitCode === 0 ? "passed" : "failed",
|
|
21719
|
+
summary: `${observed.configured ? "configured" : "ad-hoc"} check exited ${observed.exitCode}${observed.timedOut ? " (timed out)" : ""} in ${observed.durationMs} ms`,
|
|
21720
|
+
refs: [],
|
|
21721
|
+
exitCode: observed.exitCode,
|
|
21722
|
+
reviewContext: null,
|
|
21723
|
+
observation: observed
|
|
21724
|
+
};
|
|
21725
|
+
const changed = this.store.mutateTask(task.data.id, request.expectedRevision ?? task.data.revision, (current, mutation) => {
|
|
21726
|
+
const candidates = candidate === null || current.candidates.some((item) => item.id === candidate.id) ? current.candidates : [...current.candidates, candidate];
|
|
21727
|
+
const critical = [...new Set([...current.critical ?? [], CHECK_OBSERVATION_PATH])];
|
|
21728
|
+
return success(mutation.revision, null, {
|
|
21729
|
+
...current,
|
|
21730
|
+
critical,
|
|
21731
|
+
candidates,
|
|
21732
|
+
evidence: [
|
|
21733
|
+
...current.evidence,
|
|
21734
|
+
{
|
|
21735
|
+
id: newId(),
|
|
21736
|
+
recordedAt: mutation.now,
|
|
21737
|
+
provenance: provenance(this.context, "host_observed"),
|
|
21738
|
+
data
|
|
21739
|
+
}
|
|
21740
|
+
]
|
|
21741
|
+
});
|
|
21742
|
+
}, trustedNow(this.context));
|
|
21743
|
+
if (!changed.ok)
|
|
21744
|
+
return changed;
|
|
21745
|
+
return success(changed.data.revision, null, changed.data.evidence.at(-1));
|
|
21746
|
+
}
|
|
21231
21747
|
decision(request) {
|
|
21232
21748
|
return this.recordDecision(request);
|
|
21233
21749
|
}
|
|
@@ -21463,7 +21979,7 @@ class WorkitCore {
|
|
|
21463
21979
|
const current = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
21464
21980
|
if (!current.ok)
|
|
21465
21981
|
return current;
|
|
21466
|
-
const evaluations = evaluateEvidence(task.data, current.data);
|
|
21982
|
+
const evaluations = evaluateEvidence(task.data, current.data, treeFreshness(this.store.root));
|
|
21467
21983
|
const verified = evidence.some((entry) => {
|
|
21468
21984
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
21469
21985
|
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
@@ -22167,7 +22683,8 @@ class WorkitCore {
|
|
|
22167
22683
|
if (!current.ok)
|
|
22168
22684
|
return current;
|
|
22169
22685
|
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
22170
|
-
const
|
|
22686
|
+
const tree = treeFreshness(this.store.root);
|
|
22687
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
|
|
22171
22688
|
return success(task.revision, workspace.data.revision, {
|
|
22172
22689
|
id: task.id,
|
|
22173
22690
|
revision: task.revision,
|
|
@@ -22184,7 +22701,7 @@ class WorkitCore {
|
|
|
22184
22701
|
writer: evaluationWorkspace.writer
|
|
22185
22702
|
});
|
|
22186
22703
|
}
|
|
22187
|
-
compactContext(taskId) {
|
|
22704
|
+
compactContext(taskId, freshness) {
|
|
22188
22705
|
const root = this.contextRootError();
|
|
22189
22706
|
if (!root.ok)
|
|
22190
22707
|
return root;
|
|
@@ -22194,12 +22711,12 @@ class WorkitCore {
|
|
|
22194
22711
|
const helper = this.helperTaskGuard(task.data, true);
|
|
22195
22712
|
if (!helper.ok)
|
|
22196
22713
|
return helper;
|
|
22197
|
-
const view = this.view(task.data, false);
|
|
22714
|
+
const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
|
|
22198
22715
|
if (!view.ok)
|
|
22199
22716
|
return view;
|
|
22200
22717
|
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
|
|
22201
22718
|
}
|
|
22202
|
-
view(task, capture = true) {
|
|
22719
|
+
view(task, capture = true, freshness = treeFreshness(this.store.root)) {
|
|
22203
22720
|
const workspace = this.store.readWorkspace();
|
|
22204
22721
|
if (!workspace.ok)
|
|
22205
22722
|
return workspace;
|
|
@@ -22210,13 +22727,14 @@ class WorkitCore {
|
|
|
22210
22727
|
if (!current.ok)
|
|
22211
22728
|
return current;
|
|
22212
22729
|
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
22213
|
-
const
|
|
22730
|
+
const tree = freshness;
|
|
22731
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
|
|
22214
22732
|
return success(task.revision, workspace.data.revision, {
|
|
22215
22733
|
task,
|
|
22216
22734
|
workspace: evaluationWorkspace,
|
|
22217
22735
|
capabilities: this.context.capabilities,
|
|
22218
22736
|
requirements,
|
|
22219
|
-
evidence: evaluateEvidence(task, current.data)
|
|
22737
|
+
evidence: evaluateEvidence(task, current.data, tree)
|
|
22220
22738
|
});
|
|
22221
22739
|
}
|
|
22222
22740
|
reconcileResume(view, observations = []) {
|
|
@@ -22443,13 +22961,14 @@ class WorkitCore {
|
|
|
22443
22961
|
return current;
|
|
22444
22962
|
const withCandidate = current.data;
|
|
22445
22963
|
const taskForView = withCandidate.id === task.data.candidates.at(-1)?.id ? task.data : { ...task.data, candidates: [...task.data.candidates, withCandidate] };
|
|
22446
|
-
const
|
|
22964
|
+
const tree = treeFreshness(this.store.root);
|
|
22965
|
+
const requirements = evaluateRequirements(taskForView, workspace.data, this.context.capabilities, withCandidate, this.store.root, this.context.caller, tree);
|
|
22447
22966
|
const view = {
|
|
22448
22967
|
task: taskForView,
|
|
22449
22968
|
workspace: workspace.data,
|
|
22450
22969
|
capabilities: this.context.capabilities,
|
|
22451
22970
|
requirements,
|
|
22452
|
-
evidence: evaluateEvidence(taskForView, withCandidate)
|
|
22971
|
+
evidence: evaluateEvidence(taskForView, withCandidate, tree)
|
|
22453
22972
|
};
|
|
22454
22973
|
const closure = evaluateClosure(input.outcome, view);
|
|
22455
22974
|
if (!closure.ok)
|
|
@@ -22506,22 +23025,22 @@ function resolveGitRevision(root, value) {
|
|
|
22506
23025
|
}
|
|
22507
23026
|
function resolveInside(root, candidate) {
|
|
22508
23027
|
const base = realpathSync7(root);
|
|
22509
|
-
const target =
|
|
22510
|
-
if (target !== base && !target.startsWith(base +
|
|
23028
|
+
const target = path13.resolve(base, candidate);
|
|
23029
|
+
if (target !== base && !target.startsWith(base + path13.sep)) {
|
|
22511
23030
|
throw new Error("path must stay inside repository root");
|
|
22512
23031
|
}
|
|
22513
23032
|
let ancestor = target;
|
|
22514
23033
|
while (!existsSync5(ancestor))
|
|
22515
|
-
ancestor =
|
|
23034
|
+
ancestor = path13.dirname(ancestor);
|
|
22516
23035
|
const canonicalAncestor = realpathSync7(ancestor);
|
|
22517
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
23036
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path13.sep)) {
|
|
22518
23037
|
throw new Error("path must stay inside repository root");
|
|
22519
23038
|
}
|
|
22520
23039
|
return target;
|
|
22521
23040
|
}
|
|
22522
23041
|
function run2(root, executable, args, env = {}) {
|
|
22523
23042
|
const cwd = realpathSync7(root);
|
|
22524
|
-
const result =
|
|
23043
|
+
const result = spawnSync6(executable, args, {
|
|
22525
23044
|
cwd,
|
|
22526
23045
|
encoding: "utf8",
|
|
22527
23046
|
env: { ...process.env, ...env }
|
|
@@ -22546,7 +23065,7 @@ var SUPPORT_MATRIX = {
|
|
|
22546
23065
|
|
|
22547
23066
|
// packages/workit-mcp/src/server.ts
|
|
22548
23067
|
import { existsSync as existsSync7, readFileSync as readFileSync8, realpathSync as realpathSync8 } from "node:fs";
|
|
22549
|
-
import
|
|
23068
|
+
import path17 from "node:path";
|
|
22550
23069
|
// node_modules/@modelcontextprotocol/sdk/dist/esm/server/zod-compat.js
|
|
22551
23070
|
function isZ4Schema(s) {
|
|
22552
23071
|
const schema = s;
|
|
@@ -24925,7 +25444,7 @@ class StdioServerTransport {
|
|
|
24925
25444
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
24926
25445
|
|
|
24927
25446
|
// packages/workit-core/src/core/logger.ts
|
|
24928
|
-
import
|
|
25447
|
+
import os3 from "node:os";
|
|
24929
25448
|
var REDACTED = "[REDACTED]";
|
|
24930
25449
|
var SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
|
|
24931
25450
|
var KEY_EQ_VALUE = /\b([A-Za-z0-9_-]*(?:token|secret|password|passwd|apikey|api[_-]?key|authorization|credential|bearer)[A-Za-z0-9_-]*)([:=]\s*).+/gi;
|
|
@@ -24968,7 +25487,7 @@ var SENSITIVE_WORDS = new Set([
|
|
|
24968
25487
|
]);
|
|
24969
25488
|
var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
|
|
24970
25489
|
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
24971
|
-
var homePattern = new RegExp(`${escapeRegExp(
|
|
25490
|
+
var homePattern = new RegExp(`${escapeRegExp(os3.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
|
|
24972
25491
|
var applyPatterns = (value) => {
|
|
24973
25492
|
let out = value;
|
|
24974
25493
|
out = out.replace(SECRET_VALUE, REDACTED);
|
|
@@ -24982,9 +25501,9 @@ var applyPatterns = (value) => {
|
|
|
24982
25501
|
var redactSecrets = (value) => applyPatterns(value);
|
|
24983
25502
|
|
|
24984
25503
|
// packages/workit-core/src/core/pr-create.ts
|
|
24985
|
-
import
|
|
24986
|
-
import
|
|
24987
|
-
import { spawnSync as
|
|
25504
|
+
import fs11 from "node:fs";
|
|
25505
|
+
import path14 from "node:path";
|
|
25506
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
24988
25507
|
// packages/workit-core/src/forge/pr-body.ts
|
|
24989
25508
|
function parseGhIssue(value) {
|
|
24990
25509
|
const m = /issues\/(\d+)/.exec(value);
|
|
@@ -24994,13 +25513,13 @@ function parseGhIssue(value) {
|
|
|
24994
25513
|
// packages/workit-core/src/core/pr-create.ts
|
|
24995
25514
|
function whichOnPath(tool) {
|
|
24996
25515
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
24997
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
25516
|
+
for (const dir of (process.env.PATH ?? "").split(path14.delimiter)) {
|
|
24998
25517
|
if (!dir)
|
|
24999
25518
|
continue;
|
|
25000
25519
|
for (const name of names) {
|
|
25001
|
-
const candidate =
|
|
25520
|
+
const candidate = path14.join(dir, name);
|
|
25002
25521
|
try {
|
|
25003
|
-
|
|
25522
|
+
fs11.accessSync(candidate, fs11.constants.X_OK);
|
|
25004
25523
|
return candidate;
|
|
25005
25524
|
} catch {}
|
|
25006
25525
|
}
|
|
@@ -25009,7 +25528,7 @@ function whichOnPath(tool) {
|
|
|
25009
25528
|
}
|
|
25010
25529
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
25011
25530
|
var pushRemote = (cwd) => {
|
|
25012
|
-
const result =
|
|
25531
|
+
const result = spawnSync7("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
25013
25532
|
cwd,
|
|
25014
25533
|
encoding: "utf8"
|
|
25015
25534
|
});
|
|
@@ -25020,9 +25539,9 @@ var pushRemote = (cwd) => {
|
|
|
25020
25539
|
};
|
|
25021
25540
|
|
|
25022
25541
|
// packages/workit-core/src/core/repo-context.ts
|
|
25023
|
-
import { spawnSync as
|
|
25542
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
25024
25543
|
import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
|
|
25025
|
-
import
|
|
25544
|
+
import path15 from "node:path";
|
|
25026
25545
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
25027
25546
|
try {
|
|
25028
25547
|
gitRevisionParts(value);
|
|
@@ -25049,7 +25568,7 @@ var invalidRange = (cwd) => ({
|
|
|
25049
25568
|
cwd
|
|
25050
25569
|
});
|
|
25051
25570
|
var runGit = (cwd, args) => {
|
|
25052
|
-
const result =
|
|
25571
|
+
const result = spawnSync8("git", args, { cwd, encoding: "utf8" });
|
|
25053
25572
|
return {
|
|
25054
25573
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
25055
25574
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -25156,7 +25675,7 @@ var findPrTemplate = (cwd) => {
|
|
|
25156
25675
|
]) {
|
|
25157
25676
|
const parts = rel.split("/");
|
|
25158
25677
|
const base = parts[parts.length - 1];
|
|
25159
|
-
const dir = parts.length > 1 ?
|
|
25678
|
+
const dir = parts.length > 1 ? path15.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
25160
25679
|
const actual = probeCaseInsensitive(dir, base);
|
|
25161
25680
|
if (actual)
|
|
25162
25681
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -25187,7 +25706,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
25187
25706
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
25188
25707
|
};
|
|
25189
25708
|
var packageScripts = (cwd, keys) => {
|
|
25190
|
-
const pkgPath =
|
|
25709
|
+
const pkgPath = path15.join(cwd, "package.json");
|
|
25191
25710
|
if (!existsSync6(pkgPath))
|
|
25192
25711
|
return [];
|
|
25193
25712
|
try {
|
|
@@ -25213,9 +25732,9 @@ var documentationFiles = (cwd) => {
|
|
|
25213
25732
|
if (entry.isDirectory()) {
|
|
25214
25733
|
if (excluded.has(entry.name))
|
|
25215
25734
|
continue;
|
|
25216
|
-
walk(
|
|
25735
|
+
walk(path15.join(dir, entry.name), depth + 1);
|
|
25217
25736
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
25218
|
-
matches.push("./" +
|
|
25737
|
+
matches.push("./" + path15.relative(cwd, path15.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
25219
25738
|
}
|
|
25220
25739
|
}
|
|
25221
25740
|
};
|
|
@@ -25231,7 +25750,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
25231
25750
|
`);
|
|
25232
25751
|
};
|
|
25233
25752
|
function prReadyContext(root, range) {
|
|
25234
|
-
const cwd =
|
|
25753
|
+
const cwd = path15.resolve(repoRoot(root));
|
|
25235
25754
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25236
25755
|
return invalidRange(cwd);
|
|
25237
25756
|
let stdout = "";
|
|
@@ -25289,7 +25808,7 @@ ${status.stderr}` : "");
|
|
|
25289
25808
|
stdout += `template_path: ${template}
|
|
25290
25809
|
|
|
25291
25810
|
`;
|
|
25292
|
-
stdout += readTrimmed2(
|
|
25811
|
+
stdout += readTrimmed2(path15.join(cwd, template), 220);
|
|
25293
25812
|
} else {
|
|
25294
25813
|
stdout += `template_path: none
|
|
25295
25814
|
|
|
@@ -25299,7 +25818,7 @@ ${status.stderr}` : "");
|
|
|
25299
25818
|
stdout += `
|
|
25300
25819
|
`;
|
|
25301
25820
|
stdout += printSection("Recent Validation Signals");
|
|
25302
|
-
if (existsSync6(
|
|
25821
|
+
if (existsSync6(path15.join(cwd, "package.json"))) {
|
|
25303
25822
|
stdout += `package.json detected. Common scripts:
|
|
25304
25823
|
`;
|
|
25305
25824
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -25308,7 +25827,7 @@ ${status.stderr}` : "");
|
|
|
25308
25827
|
`) + `
|
|
25309
25828
|
`;
|
|
25310
25829
|
}
|
|
25311
|
-
if (existsSync6(
|
|
25830
|
+
if (existsSync6(path15.join(cwd, "Cargo.toml")) || existsSync6(path15.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
25312
25831
|
stdout += `Rust project detected.
|
|
25313
25832
|
`;
|
|
25314
25833
|
}
|
|
@@ -25341,7 +25860,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
25341
25860
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
25342
25861
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
25343
25862
|
function changelogContext(root, range) {
|
|
25344
|
-
const cwd =
|
|
25863
|
+
const cwd = path15.resolve(repoRoot(root));
|
|
25345
25864
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25346
25865
|
return invalidRange(cwd);
|
|
25347
25866
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -25359,7 +25878,7 @@ function changelogContext(root, range) {
|
|
|
25359
25878
|
stdout += CHANGELOG_RULES + `
|
|
25360
25879
|
`;
|
|
25361
25880
|
stdout += printSection("Existing CHANGELOG.md");
|
|
25362
|
-
const changelogPath =
|
|
25881
|
+
const changelogPath = path15.join(cwd, "CHANGELOG.md");
|
|
25363
25882
|
if (existsSync6(changelogPath)) {
|
|
25364
25883
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
25365
25884
|
`;
|
|
@@ -25379,7 +25898,7 @@ function changelogContext(root, range) {
|
|
|
25379
25898
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
25380
25899
|
}
|
|
25381
25900
|
function docsRefreshContext(root, range) {
|
|
25382
|
-
const cwd =
|
|
25901
|
+
const cwd = path15.resolve(repoRoot(root));
|
|
25383
25902
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25384
25903
|
return invalidRange(cwd);
|
|
25385
25904
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -25401,7 +25920,7 @@ function docsRefreshContext(root, range) {
|
|
|
25401
25920
|
`) + `
|
|
25402
25921
|
`;
|
|
25403
25922
|
stdout += printSection("README Preview");
|
|
25404
|
-
const readme =
|
|
25923
|
+
const readme = path15.join(cwd, "README.md");
|
|
25405
25924
|
if (existsSync6(readme)) {
|
|
25406
25925
|
stdout += readTrimmed2(readme, 220) + `
|
|
25407
25926
|
`;
|
|
@@ -25410,7 +25929,7 @@ function docsRefreshContext(root, range) {
|
|
|
25410
25929
|
`;
|
|
25411
25930
|
}
|
|
25412
25931
|
stdout += printSection("Package Scripts");
|
|
25413
|
-
const pkgPath =
|
|
25932
|
+
const pkgPath = path15.join(cwd, "package.json");
|
|
25414
25933
|
if (existsSync6(pkgPath)) {
|
|
25415
25934
|
try {
|
|
25416
25935
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
@@ -25424,7 +25943,7 @@ function docsRefreshContext(root, range) {
|
|
|
25424
25943
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
25425
25944
|
}
|
|
25426
25945
|
function releaseNotesContext(root, rangeOrTag) {
|
|
25427
|
-
const cwd =
|
|
25946
|
+
const cwd = path15.resolve(repoRoot(root));
|
|
25428
25947
|
if (!rangeOrTag) {
|
|
25429
25948
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
25430
25949
|
`, exitCode: 1, cwd };
|
|
@@ -25462,7 +25981,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
25462
25981
|
`;
|
|
25463
25982
|
stdout += printSection("Existing Release Files");
|
|
25464
25983
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
25465
|
-
if (existsSync6(
|
|
25984
|
+
if (existsSync6(path15.join(cwd, rel)))
|
|
25466
25985
|
stdout += rel + `
|
|
25467
25986
|
`;
|
|
25468
25987
|
}
|
|
@@ -25490,7 +26009,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
25490
26009
|
}
|
|
25491
26010
|
|
|
25492
26011
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
25493
|
-
import { spawnSync as
|
|
26012
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
25494
26013
|
var repoPathFromRemote = (remote) => {
|
|
25495
26014
|
let rest = (remote || "").trim().replace(/\/+$/, "");
|
|
25496
26015
|
if (!rest)
|
|
@@ -25511,7 +26030,7 @@ var defaultCli = (bin, available) => {
|
|
|
25511
26030
|
if (!available)
|
|
25512
26031
|
return null;
|
|
25513
26032
|
return (args, root) => {
|
|
25514
|
-
const out =
|
|
26033
|
+
const out = spawnSync9(bin, args, {
|
|
25515
26034
|
cwd: root,
|
|
25516
26035
|
encoding: "utf8",
|
|
25517
26036
|
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
@@ -25589,8 +26108,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
25589
26108
|
};
|
|
25590
26109
|
|
|
25591
26110
|
// packages/workit-core/src/core/youtrack.ts
|
|
25592
|
-
import
|
|
25593
|
-
import
|
|
26111
|
+
import fs12 from "node:fs";
|
|
26112
|
+
import path16 from "node:path";
|
|
25594
26113
|
|
|
25595
26114
|
// packages/workit-core/src/core/templates.ts
|
|
25596
26115
|
var repoRoot2 = assetRoot();
|
|
@@ -25603,22 +26122,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
25603
26122
|
// packages/workit-core/src/core/youtrack.ts
|
|
25604
26123
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
25605
26124
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
25606
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
25607
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
26125
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path16.join(configDir(), "youtrack.json");
|
|
26126
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path16.join(resolveConfigDir(), "youtrack.json");
|
|
25608
26127
|
var youTrackTokenModeOk = (p) => {
|
|
25609
26128
|
if (process.platform === "win32")
|
|
25610
26129
|
return true;
|
|
25611
|
-
const mode =
|
|
26130
|
+
const mode = fs12.statSync(p).mode & 511;
|
|
25612
26131
|
return mode === 384;
|
|
25613
26132
|
};
|
|
25614
26133
|
function readYouTrackConfig(required) {
|
|
25615
26134
|
const cfgPath = youTrackConfigPath();
|
|
25616
|
-
if (!
|
|
26135
|
+
if (!fs12.existsSync(cfgPath)) {
|
|
25617
26136
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
25618
26137
|
}
|
|
25619
26138
|
let parsed;
|
|
25620
26139
|
try {
|
|
25621
|
-
parsed = JSON.parse(
|
|
26140
|
+
parsed = JSON.parse(fs12.readFileSync(cfgPath, "utf8"));
|
|
25622
26141
|
} catch {
|
|
25623
26142
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
25624
26143
|
}
|
|
@@ -25629,11 +26148,11 @@ function readYouTrackConfig(required) {
|
|
|
25629
26148
|
}
|
|
25630
26149
|
var readYouTrackContextConfig = () => {
|
|
25631
26150
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
25632
|
-
if (!
|
|
26151
|
+
if (!fs12.existsSync(cfgPath))
|
|
25633
26152
|
return { data: {} };
|
|
25634
26153
|
let parsed;
|
|
25635
26154
|
try {
|
|
25636
|
-
parsed = JSON.parse(
|
|
26155
|
+
parsed = JSON.parse(fs12.readFileSync(cfgPath, "utf8"));
|
|
25637
26156
|
} catch {
|
|
25638
26157
|
return { error: "YouTrack configuration is unavailable" };
|
|
25639
26158
|
}
|
|
@@ -25672,12 +26191,12 @@ var youTrackToken = () => {
|
|
|
25672
26191
|
if ("error" in loaded)
|
|
25673
26192
|
return loaded;
|
|
25674
26193
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
25675
|
-
const tokenPath = tokenFile ?
|
|
25676
|
-
if (!tokenPath || !
|
|
26194
|
+
const tokenPath = tokenFile ? path16.isAbsolute(tokenFile) ? path16.resolve(tokenFile) : path16.resolve(process.cwd(), tokenFile) : "";
|
|
26195
|
+
if (!tokenPath || !fs12.existsSync(tokenPath))
|
|
25677
26196
|
return { error: "missing youtrack.token" };
|
|
25678
26197
|
if (!youTrackTokenModeOk(tokenPath))
|
|
25679
26198
|
return { error: "youtrack.token mode must be 0600" };
|
|
25680
|
-
const token =
|
|
26199
|
+
const token = fs12.readFileSync(tokenPath, "utf8").trim();
|
|
25681
26200
|
if (!token)
|
|
25682
26201
|
return { error: "empty token file" };
|
|
25683
26202
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -25742,9 +26261,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
25742
26261
|
} catch {
|
|
25743
26262
|
continue;
|
|
25744
26263
|
}
|
|
25745
|
-
if (!
|
|
26264
|
+
if (!fs12.existsSync(full))
|
|
25746
26265
|
continue;
|
|
25747
|
-
const text =
|
|
26266
|
+
const text = fs12.readFileSync(full, "utf8");
|
|
25748
26267
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
25749
26268
|
if (m)
|
|
25750
26269
|
return m[1];
|
|
@@ -25897,10 +26416,6 @@ var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\
|
|
|
25897
26416
|
var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
|
|
25898
26417
|
var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
|
|
25899
26418
|
|
|
25900
|
-
// packages/workit-core/src/git/rev.ts
|
|
25901
|
-
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
25902
|
-
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
25903
|
-
|
|
25904
26419
|
// packages/workit-core/src/ledger.ts
|
|
25905
26420
|
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
25906
26421
|
var FAILING = new Set(["failed", "blocked"]);
|
|
@@ -26042,13 +26557,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
26042
26557
|
if (!workspaceRoot)
|
|
26043
26558
|
return [];
|
|
26044
26559
|
const roots = new Set;
|
|
26045
|
-
for (const candidate of [workspaceRoot,
|
|
26046
|
-
if (
|
|
26560
|
+
for (const candidate of [workspaceRoot, path17.resolve(workspaceRoot)]) {
|
|
26561
|
+
if (path17.isAbsolute(candidate) && candidate !== path17.parse(candidate).root)
|
|
26047
26562
|
roots.add(candidate);
|
|
26048
26563
|
}
|
|
26049
26564
|
try {
|
|
26050
26565
|
const canonical = realpathSync8(workspaceRoot);
|
|
26051
|
-
if (canonical !==
|
|
26566
|
+
if (canonical !== path17.parse(canonical).root)
|
|
26052
26567
|
roots.add(canonical);
|
|
26053
26568
|
} catch {}
|
|
26054
26569
|
return [...roots].toSorted((left, right) => right.length - left.length);
|
|
@@ -26149,11 +26664,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
26149
26664
|
};
|
|
26150
26665
|
function createMcpServer(host, contextProvider) {
|
|
26151
26666
|
assertMcpHost(host);
|
|
26152
|
-
const here =
|
|
26667
|
+
const here = path17.dirname(fileURLToPath2(import.meta.url));
|
|
26153
26668
|
const sourceMarker = markSourcesLoaded([
|
|
26154
|
-
|
|
26155
|
-
|
|
26156
|
-
|
|
26669
|
+
path17.join(here, "server.ts"),
|
|
26670
|
+
path17.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
26671
|
+
path17.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
26157
26672
|
].filter((file) => existsSync7(file)));
|
|
26158
26673
|
let staleWarned = false;
|
|
26159
26674
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
@@ -26343,6 +26858,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
26343
26858
|
};
|
|
26344
26859
|
});
|
|
26345
26860
|
// packages/workit-core/src/hooks/context.ts
|
|
26861
|
+
var signals = new Map;
|
|
26346
26862
|
var cache = new Map;
|
|
26347
26863
|
// packages/workit-core/src/hooks/handle.ts
|
|
26348
26864
|
var offered = new Set;
|
|
@@ -26490,8 +27006,8 @@ var codexQualification = (host) => host === "codex_cli" ? { surface: host, cli:
|
|
|
26490
27006
|
var resolveCodexWorkspaceRoot = (pluginRoot, env) => {
|
|
26491
27007
|
const explicit = env.WORKFLOW_WORKSPACE_ROOT;
|
|
26492
27008
|
const inherited = env.PWD;
|
|
26493
|
-
const candidate = explicit ?? (inherited &&
|
|
26494
|
-
if (!candidate || !
|
|
27009
|
+
const candidate = explicit ?? (inherited && path18.resolve(inherited) !== path18.resolve(pluginRoot) ? inherited : undefined);
|
|
27010
|
+
if (!candidate || !path18.isAbsolute(candidate) || !existsSync8(candidate))
|
|
26495
27011
|
return null;
|
|
26496
27012
|
try {
|
|
26497
27013
|
if (!statSync3(candidate).isDirectory())
|