@brainervirus/workit-cursor 2.6.0 → 2.8.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/.cursor-plugin/plugin.json +1 -1
- package/commands/wk-bdd.md +7 -0
- package/commands/wk-test-audit.md +7 -0
- package/dist/cursor-session-start.js +608 -72
- package/dist/mcp-server.js +628 -112
- package/dist/workit-hook.js +615 -69
- package/package.json +3 -3
- package/skills/workit-bdd/SKILL.md +42 -0
- package/skills/workit-behavioral-tdd/SKILL.md +17 -9
- package/skills/workit-debug/SKILL.md +2 -1
- package/skills/workit-test-audit/SKILL.md +43 -0
package/dist/mcp-server.js
CHANGED
|
@@ -6622,11 +6622,11 @@ var require_dist = __commonJS(function(exports, module) {
|
|
|
6622
6622
|
|
|
6623
6623
|
// packages/workit-mcp/src/index.ts
|
|
6624
6624
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
6625
|
-
import
|
|
6625
|
+
import path18 from "node:path";
|
|
6626
6626
|
|
|
6627
6627
|
// packages/workit-mcp/src/server.ts
|
|
6628
6628
|
import { existsSync as existsSync7, readFileSync as readFileSync8, realpathSync as realpathSync8 } from "node:fs";
|
|
6629
|
-
import
|
|
6629
|
+
import path17 from "node:path";
|
|
6630
6630
|
|
|
6631
6631
|
// node_modules/zod/v4/core/util.js
|
|
6632
6632
|
function getEnumValues(entries) {
|
|
@@ -16785,9 +16785,9 @@ class StdioServerTransport {
|
|
|
16785
16785
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
16786
16786
|
|
|
16787
16787
|
// packages/workit-core/src/core.ts
|
|
16788
|
-
import { spawnSync as
|
|
16788
|
+
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
16789
16789
|
import { existsSync as existsSync5, realpathSync as realpathSync7 } from "node:fs";
|
|
16790
|
-
import
|
|
16790
|
+
import path13 from "node:path";
|
|
16791
16791
|
|
|
16792
16792
|
// packages/workit-core/src/core/task-contract.ts
|
|
16793
16793
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -17181,7 +17181,37 @@ var candidateSchema = object2({
|
|
|
17181
17181
|
path: ["id"]
|
|
17182
17182
|
});
|
|
17183
17183
|
});
|
|
17184
|
-
var
|
|
17184
|
+
var checkObservationSchema = object2({
|
|
17185
|
+
observer: literal("workit_cli"),
|
|
17186
|
+
name: nonEmpty.nullable(),
|
|
17187
|
+
configured: boolean2(),
|
|
17188
|
+
argv: array(text).min(1),
|
|
17189
|
+
shell: boolean2(),
|
|
17190
|
+
cwd: nonEmpty,
|
|
17191
|
+
exitCode: safeInteger,
|
|
17192
|
+
durationMs: safeInteger.min(0),
|
|
17193
|
+
timedOut: boolean2(),
|
|
17194
|
+
head: text.nullable(),
|
|
17195
|
+
tree: text.nullable(),
|
|
17196
|
+
dirty: boolean2().nullable(),
|
|
17197
|
+
signal: text.nullable(),
|
|
17198
|
+
treeAfter: text.nullable(),
|
|
17199
|
+
modifiedWorktree: boolean2(),
|
|
17200
|
+
base: text.nullable(),
|
|
17201
|
+
patchId: text.nullable(),
|
|
17202
|
+
environment: object2({
|
|
17203
|
+
platform: nonEmpty,
|
|
17204
|
+
arch: nonEmpty,
|
|
17205
|
+
vars: record(nonEmpty, text.nullable())
|
|
17206
|
+
}).strict(),
|
|
17207
|
+
logDigest: text.nullable(),
|
|
17208
|
+
logRef: text.nullable(),
|
|
17209
|
+
logTail: array(text).max(40),
|
|
17210
|
+
ledgerRowId: text.nullable(),
|
|
17211
|
+
attestation: object2({ host: nonEmpty, session: text.nullable(), agentId: text.nullable() }).strict().nullable()
|
|
17212
|
+
}).strict();
|
|
17213
|
+
var CHECK_OBSERVATION_PATH = "evidence.*.data.observation";
|
|
17214
|
+
var evidenceFields = {
|
|
17185
17215
|
kind: _enum(["check", "review", "investigation", "artifact"]),
|
|
17186
17216
|
claim: text,
|
|
17187
17217
|
requirementIds: array(digest),
|
|
@@ -17192,7 +17222,9 @@ var evidenceSchema = object2({
|
|
|
17192
17222
|
refs: array(refSchema),
|
|
17193
17223
|
exitCode: safeInteger.nullable(),
|
|
17194
17224
|
reviewContext: refSchema.nullable()
|
|
17195
|
-
}
|
|
17225
|
+
};
|
|
17226
|
+
var evidenceSchema = object2({ ...evidenceFields, observation: checkObservationSchema.optional() }).strict();
|
|
17227
|
+
var reportedEvidenceSchema = object2(evidenceFields).strict();
|
|
17196
17228
|
var evidenceEvaluationSchema = object2({
|
|
17197
17229
|
evidenceId: id,
|
|
17198
17230
|
status: _enum(["passed", "failed", "missing", "skipped", "stale"]),
|
|
@@ -17503,7 +17535,7 @@ var evidenceOperations = {
|
|
|
17503
17535
|
action: literal("record"),
|
|
17504
17536
|
...taskId,
|
|
17505
17537
|
expectedRevision: revision.optional(),
|
|
17506
|
-
evidence:
|
|
17538
|
+
evidence: reportedEvidenceSchema
|
|
17507
17539
|
})
|
|
17508
17540
|
};
|
|
17509
17541
|
var findingOperations = {
|
|
@@ -20003,10 +20035,10 @@ function assertProductWriteAllowed(input) {
|
|
|
20003
20035
|
}
|
|
20004
20036
|
|
|
20005
20037
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
20006
|
-
import { createHash as
|
|
20007
|
-
import * as
|
|
20008
|
-
import { spawnSync as
|
|
20009
|
-
import
|
|
20038
|
+
import { createHash as createHash7 } from "node:crypto";
|
|
20039
|
+
import * as fs10 from "node:fs";
|
|
20040
|
+
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
20041
|
+
import path12 from "node:path";
|
|
20010
20042
|
|
|
20011
20043
|
// packages/workit-core/src/core/authority.ts
|
|
20012
20044
|
import { createHash as createHash5 } from "node:crypto";
|
|
@@ -21701,16 +21733,346 @@ function reconcileAction(input) {
|
|
|
21701
21733
|
var bindingCovers = scopeCovers;
|
|
21702
21734
|
var verifyDecisionContent = verifyContentRefs;
|
|
21703
21735
|
|
|
21736
|
+
// packages/workit-core/src/check-config.ts
|
|
21737
|
+
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
21738
|
+
import fs8 from "node:fs";
|
|
21739
|
+
import path10 from "node:path";
|
|
21740
|
+
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
21741
|
+
var CHECKS_FILE = "workit.checks.json";
|
|
21742
|
+
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21743
|
+
var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
|
|
21744
|
+
function splitCommand(command) {
|
|
21745
|
+
const argv = [];
|
|
21746
|
+
let current = "";
|
|
21747
|
+
let quote = null;
|
|
21748
|
+
let started = false;
|
|
21749
|
+
for (const char of command) {
|
|
21750
|
+
if (quote) {
|
|
21751
|
+
if (char === quote)
|
|
21752
|
+
quote = null;
|
|
21753
|
+
else
|
|
21754
|
+
current += char;
|
|
21755
|
+
continue;
|
|
21756
|
+
}
|
|
21757
|
+
if (char === '"' || char === "'") {
|
|
21758
|
+
quote = char;
|
|
21759
|
+
started = true;
|
|
21760
|
+
continue;
|
|
21761
|
+
}
|
|
21762
|
+
if (/\s/u.test(char)) {
|
|
21763
|
+
if (started)
|
|
21764
|
+
argv.push(current);
|
|
21765
|
+
current = "";
|
|
21766
|
+
started = false;
|
|
21767
|
+
continue;
|
|
21768
|
+
}
|
|
21769
|
+
if (SHELL_META.test(char))
|
|
21770
|
+
return new Error(`"${command}" uses shell syntax (${char}); configure an argv array instead`);
|
|
21771
|
+
current += char;
|
|
21772
|
+
started = true;
|
|
21773
|
+
}
|
|
21774
|
+
if (quote)
|
|
21775
|
+
return new Error(`"${command}" has an unterminated quote`);
|
|
21776
|
+
if (started)
|
|
21777
|
+
argv.push(current);
|
|
21778
|
+
return argv.length ? argv : new Error("empty command");
|
|
21779
|
+
}
|
|
21780
|
+
var git = (cwd, args) => {
|
|
21781
|
+
const run = spawnSync3("git", args, {
|
|
21782
|
+
cwd,
|
|
21783
|
+
encoding: "utf8",
|
|
21784
|
+
timeout: 20000,
|
|
21785
|
+
killSignal: "SIGKILL",
|
|
21786
|
+
windowsHide: true
|
|
21787
|
+
});
|
|
21788
|
+
const value = run.status === 0 ? (run.stdout ?? "").trim() : "";
|
|
21789
|
+
return value || null;
|
|
21790
|
+
};
|
|
21791
|
+
function checkRoot(cwd) {
|
|
21792
|
+
return path10.resolve(git(cwd, ["rev-parse", "--show-toplevel"]) ?? cwd);
|
|
21793
|
+
}
|
|
21794
|
+
var packageManager = (root) => {
|
|
21795
|
+
const has = (file) => fs8.existsSync(path10.join(root, file));
|
|
21796
|
+
if (has("bun.lock") || has("bun.lockb"))
|
|
21797
|
+
return "bun";
|
|
21798
|
+
if (has("pnpm-lock.yaml"))
|
|
21799
|
+
return "pnpm";
|
|
21800
|
+
if (has("yarn.lock"))
|
|
21801
|
+
return "yarn";
|
|
21802
|
+
return "npm";
|
|
21803
|
+
};
|
|
21804
|
+
var scriptCheck = (pm, name) => {
|
|
21805
|
+
const argv = [pm, "run", name];
|
|
21806
|
+
const accepts = [argv];
|
|
21807
|
+
if (pm === "pnpm" || pm === "yarn" || pm === "npm" && name === "test")
|
|
21808
|
+
accepts.push([pm, name]);
|
|
21809
|
+
return { name, argv, accepts };
|
|
21810
|
+
};
|
|
21811
|
+
var CHECK_NAME = /^[a-z0-9][a-z0-9:._-]{0,63}$/u;
|
|
21812
|
+
function detectedChecks(root) {
|
|
21813
|
+
const read = (file) => {
|
|
21814
|
+
try {
|
|
21815
|
+
return fs8.readFileSync(path10.join(root, file), "utf8");
|
|
21816
|
+
} catch {
|
|
21817
|
+
return null;
|
|
21818
|
+
}
|
|
21819
|
+
};
|
|
21820
|
+
const checks = [];
|
|
21821
|
+
const add = (check) => {
|
|
21822
|
+
if (!checks.some((item) => item.name === check.name))
|
|
21823
|
+
checks.push(check);
|
|
21824
|
+
};
|
|
21825
|
+
const manifestText = read("package.json");
|
|
21826
|
+
if (manifestText !== null) {
|
|
21827
|
+
let manifest = null;
|
|
21828
|
+
try {
|
|
21829
|
+
manifest = JSON.parse(manifestText);
|
|
21830
|
+
} catch {}
|
|
21831
|
+
const scripts = isRecord2(manifest) && isRecord2(manifest.scripts) ? manifest.scripts : {};
|
|
21832
|
+
const pm = packageManager(root);
|
|
21833
|
+
for (const name of SCRIPT_CHECKS) {
|
|
21834
|
+
const script = scripts[name];
|
|
21835
|
+
if (typeof script === "string" && script.trim() !== "")
|
|
21836
|
+
add(scriptCheck(pm, name));
|
|
21837
|
+
}
|
|
21838
|
+
}
|
|
21839
|
+
const plain = (name, argv) => ({ name, argv, accepts: [argv] });
|
|
21840
|
+
if (read("go.mod") !== null)
|
|
21841
|
+
add(plain("test", ["go", "test", "./..."]));
|
|
21842
|
+
if (read("Cargo.toml") !== null)
|
|
21843
|
+
add(plain("test", ["cargo", "test"]));
|
|
21844
|
+
if (read("pytest.ini") !== null || /\bpytest\b/u.test(read("pyproject.toml") ?? ""))
|
|
21845
|
+
add(plain("test", ["pytest"]));
|
|
21846
|
+
const makefile = read("Makefile") ?? read("makefile") ?? read("GNUmakefile");
|
|
21847
|
+
if (makefile !== null && /^test\s*:(?!=)/mu.test(makefile))
|
|
21848
|
+
add(plain("test", ["make", "test"]));
|
|
21849
|
+
return checks;
|
|
21850
|
+
}
|
|
21851
|
+
var GATES = ["testing", "verification"];
|
|
21852
|
+
function loadCheckConfig(cwd) {
|
|
21853
|
+
const root = checkRoot(cwd);
|
|
21854
|
+
const file = path10.join(root, CHECKS_FILE);
|
|
21855
|
+
const none = (error = null) => ({
|
|
21856
|
+
source: error ? CHECKS_FILE : "none",
|
|
21857
|
+
root,
|
|
21858
|
+
checks: [],
|
|
21859
|
+
gates: {},
|
|
21860
|
+
error
|
|
21861
|
+
});
|
|
21862
|
+
let text = null;
|
|
21863
|
+
try {
|
|
21864
|
+
text = fs8.readFileSync(file, "utf8");
|
|
21865
|
+
} catch (error) {
|
|
21866
|
+
if (error.code !== "ENOENT")
|
|
21867
|
+
return none(`cannot read ${CHECKS_FILE}: ${error.message}`);
|
|
21868
|
+
}
|
|
21869
|
+
if (text === null) {
|
|
21870
|
+
const checks = detectedChecks(root);
|
|
21871
|
+
return checks.length ? { source: "defaults", root, checks, gates: {}, error: null } : none();
|
|
21872
|
+
}
|
|
21873
|
+
let parsed;
|
|
21874
|
+
try {
|
|
21875
|
+
parsed = JSON.parse(text);
|
|
21876
|
+
} catch {
|
|
21877
|
+
return none(`${CHECKS_FILE} is not valid JSON`);
|
|
21878
|
+
}
|
|
21879
|
+
if (!isRecord2(parsed) || !isRecord2(parsed.checks))
|
|
21880
|
+
return none(`${CHECKS_FILE} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
|
|
21881
|
+
const checks = [];
|
|
21882
|
+
for (const [name, value] of Object.entries(parsed.checks)) {
|
|
21883
|
+
if (!CHECK_NAME.test(name))
|
|
21884
|
+
return none(`${CHECKS_FILE}: check name "${name}" must match ${CHECK_NAME.source}`);
|
|
21885
|
+
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");
|
|
21886
|
+
if (argv instanceof Error)
|
|
21887
|
+
return none(`${CHECKS_FILE}: check "${name}": ${argv.message}`);
|
|
21888
|
+
checks.push({ name, argv, accepts: [argv] });
|
|
21889
|
+
}
|
|
21890
|
+
const gates = {};
|
|
21891
|
+
if (parsed.gates !== undefined) {
|
|
21892
|
+
if (!isRecord2(parsed.gates))
|
|
21893
|
+
return none(`${CHECKS_FILE}: gates must be {"testing":"<name>"|["<name>",…]}`);
|
|
21894
|
+
for (const [gate, value] of Object.entries(parsed.gates)) {
|
|
21895
|
+
if (!GATES.includes(gate))
|
|
21896
|
+
return none(`${CHECKS_FILE}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
|
|
21897
|
+
const names = typeof value === "string" ? [value] : value;
|
|
21898
|
+
if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
|
|
21899
|
+
return none(`${CHECKS_FILE}: gates.${gate} must name configured checks`);
|
|
21900
|
+
gates[gate] = names;
|
|
21901
|
+
}
|
|
21902
|
+
}
|
|
21903
|
+
return { source: CHECKS_FILE, root, checks, gates, error: null };
|
|
21904
|
+
}
|
|
21905
|
+
var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
|
|
21906
|
+
function matchesNamedCheck(config, name, argv, cwd) {
|
|
21907
|
+
if (config.error || name === null || cwd !== ".")
|
|
21908
|
+
return false;
|
|
21909
|
+
const check = config.checks.find((item) => item.name === name);
|
|
21910
|
+
return Boolean(check?.accepts.some((accepted) => sameArgv(accepted, argv)));
|
|
21911
|
+
}
|
|
21912
|
+
function gateCheckNames(config, dimension) {
|
|
21913
|
+
const explicit = config.gates[dimension];
|
|
21914
|
+
if (explicit)
|
|
21915
|
+
return explicit;
|
|
21916
|
+
const names = config.checks.map((check) => check.name);
|
|
21917
|
+
if (dimension === "testing")
|
|
21918
|
+
return names.includes("test") ? ["test"] : [];
|
|
21919
|
+
return names;
|
|
21920
|
+
}
|
|
21921
|
+
var workitOnPath;
|
|
21922
|
+
function workitAvailable(env = process.env) {
|
|
21923
|
+
if (workitOnPath !== undefined)
|
|
21924
|
+
return workitOnPath;
|
|
21925
|
+
const windows = process.platform === "win32";
|
|
21926
|
+
const exts = windows ? (env.PATHEXT ?? ".COM;.EXE;.BAT;.CMD").split(";").filter(Boolean) : [""];
|
|
21927
|
+
const dirs = (env.PATH ?? env.Path ?? "").split(path10.delimiter).filter(Boolean);
|
|
21928
|
+
workitOnPath = dirs.some((dir) => exts.some((ext) => {
|
|
21929
|
+
try {
|
|
21930
|
+
return fs8.statSync(path10.join(dir, `workit${ext}`)).isFile();
|
|
21931
|
+
} catch {
|
|
21932
|
+
return false;
|
|
21933
|
+
}
|
|
21934
|
+
}));
|
|
21935
|
+
return workitOnPath;
|
|
21936
|
+
}
|
|
21937
|
+
function checkCommand(args, version, onPath = workitAvailable()) {
|
|
21938
|
+
return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
|
|
21939
|
+
}
|
|
21940
|
+
|
|
21941
|
+
// packages/workit-core/src/git/rev.ts
|
|
21942
|
+
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
21943
|
+
import { createHash as createHash6 } from "node:crypto";
|
|
21944
|
+
import fs9 from "node:fs";
|
|
21945
|
+
import os2 from "node:os";
|
|
21946
|
+
import path11 from "node:path";
|
|
21947
|
+
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
21948
|
+
var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
|
|
21949
|
+
var git2 = (cwd, args, options = {}) => {
|
|
21950
|
+
const result = spawnSync4("git", args, {
|
|
21951
|
+
cwd,
|
|
21952
|
+
encoding: "utf8",
|
|
21953
|
+
env: options.env ?? process.env,
|
|
21954
|
+
input: options.input,
|
|
21955
|
+
maxBuffer: MAX_BUFFER,
|
|
21956
|
+
timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
|
|
21957
|
+
killSignal: "SIGKILL",
|
|
21958
|
+
windowsHide: true
|
|
21959
|
+
});
|
|
21960
|
+
const error = result.error;
|
|
21961
|
+
return {
|
|
21962
|
+
ok: result.status === 0,
|
|
21963
|
+
stdout: result.stdout ?? "",
|
|
21964
|
+
stderr: result.stderr ?? "",
|
|
21965
|
+
timedOut: error?.code === "ETIMEDOUT"
|
|
21966
|
+
};
|
|
21967
|
+
};
|
|
21968
|
+
var line = (run) => {
|
|
21969
|
+
if (!run.ok)
|
|
21970
|
+
return null;
|
|
21971
|
+
const value = run.stdout.trim();
|
|
21972
|
+
return value ? value : null;
|
|
21973
|
+
};
|
|
21974
|
+
function headSha(cwd) {
|
|
21975
|
+
return line(git2(cwd, ["rev-parse", "--verify", "-q", "HEAD^{commit}"]));
|
|
21976
|
+
}
|
|
21977
|
+
var emptyTree = (cwd) => line(git2(cwd, ["mktree"], { input: "" }));
|
|
21978
|
+
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
21979
|
+
var seedIndex = (from, to) => {
|
|
21980
|
+
fs9.copyFileSync(from, to);
|
|
21981
|
+
const { atimeNs, mtimeNs } = fs9.statSync(from, { bigint: true });
|
|
21982
|
+
const seconds = (ns) => (Number(ns / 1000n) - 1) / 1e6;
|
|
21983
|
+
fs9.utimesSync(to, seconds(atimeNs), seconds(mtimeNs));
|
|
21984
|
+
};
|
|
21985
|
+
function worktreeTree(cwd, options = {}) {
|
|
21986
|
+
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
21987
|
+
const indexPath = line(git2(cwd, ["rev-parse", "--path-format=absolute", "--git-path", "index"]));
|
|
21988
|
+
if (!top || !indexPath)
|
|
21989
|
+
return null;
|
|
21990
|
+
const cap = options.maxUntrackedBytes ?? MAX_UNTRACKED_BYTES;
|
|
21991
|
+
const timeoutMs = options.timeoutMs ?? GIT_TIMEOUTS.worktree;
|
|
21992
|
+
const scratch = fs9.mkdtempSync(path11.join(os2.tmpdir(), "workit-tree-"));
|
|
21993
|
+
try {
|
|
21994
|
+
const tempIndex = path11.join(scratch, "index");
|
|
21995
|
+
if (fs9.existsSync(indexPath))
|
|
21996
|
+
seedIndex(indexPath, tempIndex);
|
|
21997
|
+
const env = { ...process.env, GIT_INDEX_FILE: tempIndex };
|
|
21998
|
+
const untracked = git2(top, ["ls-files", "--others", "--exclude-standard", "-z"], {
|
|
21999
|
+
env,
|
|
22000
|
+
timeoutMs
|
|
22001
|
+
});
|
|
22002
|
+
if (!untracked.ok)
|
|
22003
|
+
return null;
|
|
22004
|
+
const skipped = [];
|
|
22005
|
+
for (const file of untracked.stdout.split("\x00").filter(Boolean)) {
|
|
22006
|
+
try {
|
|
22007
|
+
const stat = fs9.statSync(path11.join(top, file));
|
|
22008
|
+
if (stat.isFile() && stat.size > cap)
|
|
22009
|
+
skipped.push({ path: file, size: stat.size, mtimeMs: Math.trunc(stat.mtimeMs) });
|
|
22010
|
+
} catch {}
|
|
22011
|
+
}
|
|
22012
|
+
skipped.sort((a, b) => a.path < b.path ? -1 : a.path > b.path ? 1 : 0);
|
|
22013
|
+
const added = skipped.length ? git2(top, ["add", "-A", "--pathspec-from-file=-", "--pathspec-file-nul"], {
|
|
22014
|
+
env,
|
|
22015
|
+
timeoutMs,
|
|
22016
|
+
input: [".", ...skipped.map((file) => `:(exclude,literal)${file.path}`)].join("\x00")
|
|
22017
|
+
}) : git2(top, ["add", "-A"], { env, timeoutMs });
|
|
22018
|
+
if (!added.ok)
|
|
22019
|
+
return null;
|
|
22020
|
+
const tree = line(git2(top, ["write-tree"], { env, timeoutMs }));
|
|
22021
|
+
if (!tree)
|
|
22022
|
+
return null;
|
|
22023
|
+
const base = line(git2(top, ["rev-parse", "--verify", "-q", "HEAD^{tree}"])) ?? emptyTree(top);
|
|
22024
|
+
const key = skipped.length ? `sha256:${createHash6("sha256").update(JSON.stringify({ tree, skipped })).digest("hex")}` : tree;
|
|
22025
|
+
return { tree, key, dirty: tree !== base || skipped.length > 0, skipped };
|
|
22026
|
+
} finally {
|
|
22027
|
+
fs9.rmSync(scratch, { recursive: true, force: true });
|
|
22028
|
+
}
|
|
22029
|
+
}
|
|
22030
|
+
function worktreeSignal(cwd, options = {}) {
|
|
22031
|
+
const top = line(git2(cwd, ["rev-parse", "--show-toplevel"]));
|
|
22032
|
+
if (!top)
|
|
22033
|
+
return null;
|
|
22034
|
+
const status = git2(top, [
|
|
22035
|
+
"status",
|
|
22036
|
+
"--porcelain=v1",
|
|
22037
|
+
"-z",
|
|
22038
|
+
"--untracked-files=all",
|
|
22039
|
+
"--no-renames",
|
|
22040
|
+
"--ignore-submodules=dirty"
|
|
22041
|
+
], {
|
|
22042
|
+
env: { ...process.env, GIT_OPTIONAL_LOCKS: "0" },
|
|
22043
|
+
timeoutMs: options.timeoutMs ?? GIT_TIMEOUTS.worktree
|
|
22044
|
+
});
|
|
22045
|
+
if (!status.ok)
|
|
22046
|
+
return null;
|
|
22047
|
+
const hash = createHash6("sha256");
|
|
22048
|
+
hash.update(`${headSha(top) ?? "unborn"}\x00`);
|
|
22049
|
+
for (const entry of status.stdout.split("\x00")) {
|
|
22050
|
+
if (entry.length < 4)
|
|
22051
|
+
continue;
|
|
22052
|
+
const file = entry.slice(3);
|
|
22053
|
+
const absolute = path11.join(top, file);
|
|
22054
|
+
let stat = "-";
|
|
22055
|
+
try {
|
|
22056
|
+
const info = fs9.lstatSync(absolute, { bigint: true });
|
|
22057
|
+
stat = `${info.mode.toString(8)}:${info.size}:${info.mtimeNs}`;
|
|
22058
|
+
if (info.isDirectory())
|
|
22059
|
+
stat += `:${headSha(absolute) ?? "-"}`;
|
|
22060
|
+
} catch {}
|
|
22061
|
+
hash.update(`${entry.slice(0, 2)}\x00${file}\x00${stat}\x00`);
|
|
22062
|
+
}
|
|
22063
|
+
return `sig:${hash.digest("hex")}`;
|
|
22064
|
+
}
|
|
22065
|
+
|
|
21704
22066
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
21705
|
-
var digestBytes3 = (value) =>
|
|
21706
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
21707
|
-
var relative = (root, candidate) =>
|
|
22067
|
+
var digestBytes3 = (value) => createHash7("sha256").update(value).digest("hex");
|
|
22068
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path12.sep}`);
|
|
22069
|
+
var relative = (root, candidate) => path12.relative(root, candidate).split(path12.sep).join("/") || ".";
|
|
21708
22070
|
var canonicalPath = (value) => {
|
|
21709
22071
|
if (typeof value !== "string" || !value)
|
|
21710
22072
|
return null;
|
|
21711
22073
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
21712
22074
|
return null;
|
|
21713
|
-
const normalized =
|
|
22075
|
+
const normalized = path12.posix.normalize(value);
|
|
21714
22076
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
21715
22077
|
return null;
|
|
21716
22078
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -21746,7 +22108,7 @@ var compareCodeUnits2 = (left, right) => {
|
|
|
21746
22108
|
return left.length - right.length;
|
|
21747
22109
|
};
|
|
21748
22110
|
var gitPaths = (root) => {
|
|
21749
|
-
const result =
|
|
22111
|
+
const result = spawnSync5("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
21750
22112
|
cwd: root,
|
|
21751
22113
|
encoding: "buffer"
|
|
21752
22114
|
});
|
|
@@ -21758,21 +22120,21 @@ var gitPaths = (root) => {
|
|
|
21758
22120
|
git: !/not a git repository/i.test(stderr)
|
|
21759
22121
|
};
|
|
21760
22122
|
}
|
|
21761
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
21762
|
-
const stagedDeleted =
|
|
22123
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path12.sep).join("/")) : [];
|
|
22124
|
+
const stagedDeleted = spawnSync5("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
21763
22125
|
if (stagedDeleted.status !== 0) {
|
|
21764
22126
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
21765
22127
|
if (!/not a git repository/i.test(stderr))
|
|
21766
22128
|
return { paths, uncertain: true, git: true };
|
|
21767
22129
|
} else if (stagedDeleted.stdout) {
|
|
21768
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
22130
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path12.sep).join("/")));
|
|
21769
22131
|
}
|
|
21770
22132
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
21771
22133
|
};
|
|
21772
22134
|
var walk = (root, directory, output) => {
|
|
21773
22135
|
let names;
|
|
21774
22136
|
try {
|
|
21775
|
-
names =
|
|
22137
|
+
names = fs10.readdirSync(directory);
|
|
21776
22138
|
} catch {
|
|
21777
22139
|
return true;
|
|
21778
22140
|
}
|
|
@@ -21780,10 +22142,10 @@ var walk = (root, directory, output) => {
|
|
|
21780
22142
|
for (const name of names) {
|
|
21781
22143
|
if (name === ".git" || name === ".workit")
|
|
21782
22144
|
continue;
|
|
21783
|
-
const target =
|
|
22145
|
+
const target = path12.join(directory, name);
|
|
21784
22146
|
let stat;
|
|
21785
22147
|
try {
|
|
21786
|
-
stat =
|
|
22148
|
+
stat = fs10.lstatSync(target);
|
|
21787
22149
|
} catch {
|
|
21788
22150
|
uncertain = true;
|
|
21789
22151
|
continue;
|
|
@@ -21798,14 +22160,14 @@ var walk = (root, directory, output) => {
|
|
|
21798
22160
|
var scopeRoots = (root, scope) => {
|
|
21799
22161
|
const roots = [];
|
|
21800
22162
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
21801
|
-
const target =
|
|
22163
|
+
const target = path12.resolve(root, item);
|
|
21802
22164
|
if (!inside(root, target))
|
|
21803
22165
|
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` }] });
|
|
21804
22166
|
let ancestor = target;
|
|
21805
|
-
while (!
|
|
21806
|
-
ancestor =
|
|
22167
|
+
while (!fs10.existsSync(ancestor) && ancestor !== root)
|
|
22168
|
+
ancestor = path12.dirname(ancestor);
|
|
21807
22169
|
try {
|
|
21808
|
-
if (!inside(root,
|
|
22170
|
+
if (!inside(root, fs10.realpathSync(ancestor)))
|
|
21809
22171
|
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` }] });
|
|
21810
22172
|
} catch {
|
|
21811
22173
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -21817,7 +22179,7 @@ var scopeRoots = (root, scope) => {
|
|
|
21817
22179
|
function captureCandidate(root, scope, environment = []) {
|
|
21818
22180
|
let checkout;
|
|
21819
22181
|
try {
|
|
21820
|
-
checkout =
|
|
22182
|
+
checkout = fs10.realpathSync(root);
|
|
21821
22183
|
} catch {
|
|
21822
22184
|
return failure2("invalid_input", "candidate root does not exist");
|
|
21823
22185
|
}
|
|
@@ -21835,11 +22197,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
21835
22197
|
if (item === ".git" || item === ".workit")
|
|
21836
22198
|
continue;
|
|
21837
22199
|
names.add(item);
|
|
21838
|
-
if (!
|
|
22200
|
+
if (!fs10.existsSync(target))
|
|
21839
22201
|
continue;
|
|
21840
22202
|
let stat;
|
|
21841
22203
|
try {
|
|
21842
|
-
stat =
|
|
22204
|
+
stat = fs10.lstatSync(target);
|
|
21843
22205
|
} catch {
|
|
21844
22206
|
uncertain = true;
|
|
21845
22207
|
continue;
|
|
@@ -21855,17 +22217,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
21855
22217
|
for (const item of [...names].toSorted()) {
|
|
21856
22218
|
if (!scopeMatches(item, normalizedScope))
|
|
21857
22219
|
continue;
|
|
21858
|
-
const target =
|
|
22220
|
+
const target = path12.join(checkout, item);
|
|
21859
22221
|
try {
|
|
21860
|
-
const stat =
|
|
22222
|
+
const stat = fs10.lstatSync(target);
|
|
21861
22223
|
if (stat.isSymbolicLink()) {
|
|
21862
|
-
const link =
|
|
22224
|
+
const link = fs10.readlinkSync(target);
|
|
21863
22225
|
files.push({ path: item, kind: "symlink", digest: digestBytes3(link), executable: null });
|
|
21864
22226
|
} else if (stat.isFile()) {
|
|
21865
22227
|
files.push({
|
|
21866
22228
|
path: item,
|
|
21867
22229
|
kind: "file",
|
|
21868
|
-
digest: digestBytes3(
|
|
22230
|
+
digest: digestBytes3(fs10.readFileSync(target)),
|
|
21869
22231
|
executable: (stat.mode & 73) !== 0
|
|
21870
22232
|
});
|
|
21871
22233
|
} else if (!stat.isDirectory()) {
|
|
@@ -21890,7 +22252,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
21890
22252
|
const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
|
|
21891
22253
|
if (values.some(({ value }) => value === null))
|
|
21892
22254
|
completeness = "uncertain";
|
|
21893
|
-
const headResult =
|
|
22255
|
+
const headResult = spawnSync5("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
|
|
21894
22256
|
if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
|
|
21895
22257
|
completeness = "uncertain";
|
|
21896
22258
|
const head = headResult.status === 0 ? headResult.stdout.trim() : null;
|
|
@@ -21928,7 +22290,24 @@ var sameSession = (left, right) => {
|
|
|
21928
22290
|
return left !== null && other !== null && left.host === other.host && left.handle === other.handle;
|
|
21929
22291
|
};
|
|
21930
22292
|
var evidenceScopes = (task, ids) => (task.policy?.requirements ?? []).filter((requirement) => ids.includes(requirement.id)).map((requirement) => requirement.scope);
|
|
21931
|
-
|
|
22293
|
+
var memo2 = (compute) => {
|
|
22294
|
+
let value;
|
|
22295
|
+
return () => {
|
|
22296
|
+
if (value === undefined)
|
|
22297
|
+
value = compute();
|
|
22298
|
+
return value;
|
|
22299
|
+
};
|
|
22300
|
+
};
|
|
22301
|
+
var treeFreshness = (root) => ({
|
|
22302
|
+
mode: "tree",
|
|
22303
|
+
current: memo2(() => worktreeTree(root)?.key ?? null)
|
|
22304
|
+
});
|
|
22305
|
+
var signalFreshness = (root) => ({
|
|
22306
|
+
mode: "signal",
|
|
22307
|
+
current: memo2(() => worktreeSignal(root))
|
|
22308
|
+
});
|
|
22309
|
+
var isObservedCheck = (entry) => entry.data.kind === "check" && entry.data.observation?.observer === "workit_cli" && entry.provenance?.kind === "host_observed" && entry.provenance.host === "workit_cli";
|
|
22310
|
+
function evaluateEvidence(task, candidate, freshness) {
|
|
21932
22311
|
const current = candidate ?? task.candidates.at(-1) ?? null;
|
|
21933
22312
|
return task.evidence.map((entry) => {
|
|
21934
22313
|
const evidence = entry.data;
|
|
@@ -21938,6 +22317,25 @@ function evaluateEvidence(task, candidate) {
|
|
|
21938
22317
|
status: "failed",
|
|
21939
22318
|
reason: "failed evidence remains historical"
|
|
21940
22319
|
};
|
|
22320
|
+
const observation = isObservedCheck(entry) ? evidence.observation : undefined;
|
|
22321
|
+
if (observation && observation.tree !== null) {
|
|
22322
|
+
if (task.status === "closed")
|
|
22323
|
+
return { evidenceId: entry.id, status: evidence.result, reason: "recorded before close" };
|
|
22324
|
+
if (observation.modifiedWorktree)
|
|
22325
|
+
return {
|
|
22326
|
+
evidenceId: entry.id,
|
|
22327
|
+
status: "stale",
|
|
22328
|
+
reason: "the check changed the worktree while it ran; ignore or clean its outputs"
|
|
22329
|
+
};
|
|
22330
|
+
const recorded = freshness?.mode === "signal" ? observation.signal : observation.tree;
|
|
22331
|
+
if (recorded !== null && freshness && recorded === freshness.current())
|
|
22332
|
+
return { evidenceId: entry.id, status: evidence.result, reason: "worktree is unchanged" };
|
|
22333
|
+
return {
|
|
22334
|
+
evidenceId: entry.id,
|
|
22335
|
+
status: "stale",
|
|
22336
|
+
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"
|
|
22337
|
+
};
|
|
22338
|
+
}
|
|
21941
22339
|
if (evidence.kind === "check" || evidence.kind === "review") {
|
|
21942
22340
|
if (!evidence.beforeCandidateId || !evidence.candidateId)
|
|
21943
22341
|
return {
|
|
@@ -22021,8 +22419,45 @@ var expectedKinds = (dimension, ruleId) => {
|
|
|
22021
22419
|
return "decision";
|
|
22022
22420
|
return "worker report";
|
|
22023
22421
|
};
|
|
22024
|
-
|
|
22025
|
-
|
|
22422
|
+
var observedGate = (requirement) => (requirement.dimension === "testing" || requirement.dimension === "verification") && requirement.before === "close";
|
|
22423
|
+
function evaluateObservedGate(task, requirement, statuses, config) {
|
|
22424
|
+
const names = gateCheckNames(config, requirement.dimension);
|
|
22425
|
+
const observed = task.evidence.filter(isObservedCheck);
|
|
22426
|
+
const fresh = observed.filter((entry) => statuses.get(entry.id) === "passed" && entry.data.observation?.exitCode === 0);
|
|
22427
|
+
const named = (entry) => {
|
|
22428
|
+
const observation = entry.data.observation;
|
|
22429
|
+
return observation.configured && !observation.shell && names.includes(observation.name ?? "") && matchesNamedCheck(config, observation.name, observation.argv, observation.cwd);
|
|
22430
|
+
};
|
|
22431
|
+
const considered = observed.map((entry) => entry.id);
|
|
22432
|
+
const run = checkCommand(names[0] ?? "<name>", runtimeVersion());
|
|
22433
|
+
if (config.error)
|
|
22434
|
+
return {
|
|
22435
|
+
satisfied: [],
|
|
22436
|
+
considered,
|
|
22437
|
+
reason: `check config is invalid (${config.error}); fix it, then run ${run}`
|
|
22438
|
+
};
|
|
22439
|
+
if (!names.length)
|
|
22440
|
+
return {
|
|
22441
|
+
satisfied: [],
|
|
22442
|
+
considered,
|
|
22443
|
+
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())}`
|
|
22444
|
+
};
|
|
22445
|
+
const satisfied = fresh.filter(named);
|
|
22446
|
+
if (satisfied.length)
|
|
22447
|
+
return {
|
|
22448
|
+
satisfied: satisfied.map((entry) => entry.id),
|
|
22449
|
+
considered,
|
|
22450
|
+
reason: `a fresh passing configured check (${names.join("|")}) was observed by workit check`
|
|
22451
|
+
};
|
|
22452
|
+
const reported = task.evidence.some((entry) => entry.data.kind === "check" && !isObservedCheck(entry) && entry.data.requirementIds.includes(requirement.id));
|
|
22453
|
+
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}`;
|
|
22454
|
+
return { satisfied: [], considered, reason };
|
|
22455
|
+
}
|
|
22456
|
+
function evaluateRequirements(task, workspace, capabilities, candidate, checkoutRoot, _caller, freshness = treeFreshness(checkoutRoot)) {
|
|
22457
|
+
const evidence = evaluateEvidence(task, candidate, freshness);
|
|
22458
|
+
const statuses = new Map(evidence.map((item) => [item.evidenceId, item.status]));
|
|
22459
|
+
let checkConfig;
|
|
22460
|
+
const configFor = () => checkConfig ??= loadCheckConfig(checkoutRoot);
|
|
22026
22461
|
if (task.policy && task.policy.policyVersion !== POLICY_VERSION)
|
|
22027
22462
|
return task.policy.requirements.map((requirement) => ({
|
|
22028
22463
|
requirementId: requirement.id,
|
|
@@ -22032,8 +22467,19 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
22032
22467
|
reason: "stored policy version is unsupported; reassessment is required"
|
|
22033
22468
|
}));
|
|
22034
22469
|
return (task.policy?.requirements ?? []).map((requirement) => {
|
|
22470
|
+
const gate = observedGate(requirement) ? evaluateObservedGate(task, requirement, statuses, configFor()) : null;
|
|
22471
|
+
if (gate?.satisfied.length)
|
|
22472
|
+
return {
|
|
22473
|
+
requirementId: requirement.id,
|
|
22474
|
+
status: "satisfied",
|
|
22475
|
+
evidenceIds: gate.satisfied,
|
|
22476
|
+
decisionIds: [],
|
|
22477
|
+
reason: gate.reason
|
|
22478
|
+
};
|
|
22035
22479
|
const related = task.evidence.map((entry, index) => ({ entry, evaluation: evidence[index] })).filter(({ entry }) => entry.data.requirementIds.includes(requirement.id));
|
|
22036
22480
|
const passed = related.filter(({ entry, evaluation }) => {
|
|
22481
|
+
if (gate)
|
|
22482
|
+
return false;
|
|
22037
22483
|
if (!evidenceMatchesRequirement(evaluation.status, entry.data.kind, requirement.dimension, requirement.ruleId))
|
|
22038
22484
|
return false;
|
|
22039
22485
|
if ((entry.data.kind === "check" || entry.data.kind === "review") && (!entry.data.beforeCandidateId || !entry.data.candidateId || entry.data.beforeCandidateId !== entry.data.candidateId))
|
|
@@ -22096,6 +22542,14 @@ function evaluateRequirements(task, workspace, capabilities, candidate, checkout
|
|
|
22096
22542
|
reason: "an applicable approved limitation permits the missing evidence"
|
|
22097
22543
|
};
|
|
22098
22544
|
const unavailable = capabilities.some((capability) => capability.assurance === "unavailable" && (capability.name === requirement.dimension || capability.surface === requirement.dimension));
|
|
22545
|
+
if (gate)
|
|
22546
|
+
return {
|
|
22547
|
+
requirementId: requirement.id,
|
|
22548
|
+
status: unavailable ? "unavailable" : "unsatisfied",
|
|
22549
|
+
evidenceIds: [...new Set([...gate.considered, ...related.map(({ entry }) => entry.id)])],
|
|
22550
|
+
decisionIds: [],
|
|
22551
|
+
reason: unavailable ? "required capability is unavailable" : gate.reason
|
|
22552
|
+
};
|
|
22099
22553
|
if (!unavailable && related.length > 0 && !passed.length)
|
|
22100
22554
|
return {
|
|
22101
22555
|
requirementId: requirement.id,
|
|
@@ -22248,7 +22702,7 @@ function reconcileResume(view, observations = []) {
|
|
|
22248
22702
|
const candidate = captureCandidate(view.workspace.root, view.task.intent.data.scope, []);
|
|
22249
22703
|
if (!candidate.ok)
|
|
22250
22704
|
return candidate;
|
|
22251
|
-
const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
|
|
22705
|
+
const staleEvidenceIds = evaluateEvidence(view.task, candidate.data, treeFreshness(view.workspace.root)).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
|
|
22252
22706
|
const blockers = [...view.task.progress.blockers];
|
|
22253
22707
|
if (view.task.workers.some((entry) => isUncertainWorker(entry.data.state)))
|
|
22254
22708
|
blockers.push({
|
|
@@ -23588,10 +24042,7 @@ class WorkitCore {
|
|
|
23588
24042
|
if (finding.data.disposition !== "fixed")
|
|
23589
24043
|
return finding;
|
|
23590
24044
|
const withEntry = { ...current, candidates, evidence: [...current.evidence, entry] };
|
|
23591
|
-
const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data).map((item) => [
|
|
23592
|
-
item.evidenceId,
|
|
23593
|
-
item.status
|
|
23594
|
-
]));
|
|
24045
|
+
const statuses = new Map(evaluateEvidence(withEntry, currentCandidate.data, treeFreshness(this.store.root)).map((item) => [item.evidenceId, item.status]));
|
|
23595
24046
|
const verified = withEntry.evidence.some((item) => findingVerificationPasses(finding.data.candidateId, {
|
|
23596
24047
|
kind: item.data.kind,
|
|
23597
24048
|
candidateId: item.data.candidateId,
|
|
@@ -23606,6 +24057,71 @@ class WorkitCore {
|
|
|
23606
24057
|
return changed;
|
|
23607
24058
|
return success(changed.data.revision, null, changed.data.evidence.at(-1));
|
|
23608
24059
|
}
|
|
24060
|
+
observeCheck(request) {
|
|
24061
|
+
return this.retryOmittedRevisions(request, () => this.observeCheckOnce(request));
|
|
24062
|
+
}
|
|
24063
|
+
observeCheckOnce(request) {
|
|
24064
|
+
const root = this.contextRootError();
|
|
24065
|
+
if (!root.ok)
|
|
24066
|
+
return root;
|
|
24067
|
+
if (this.context.caller.host !== "workit_cli" || (this.context.workerId ?? null) !== null)
|
|
24068
|
+
return failure2("permission_denied", "only the workit CLI records observed checks");
|
|
24069
|
+
const observation = checkObservationSchema.safeParse(request?.observation);
|
|
24070
|
+
if (!observation.success)
|
|
24071
|
+
return failure2("invalid_input", "check observation is invalid", {
|
|
24072
|
+
fields: observation.error.issues.map((issue) => ({
|
|
24073
|
+
path: issue.path.join("."),
|
|
24074
|
+
reason: issue.message
|
|
24075
|
+
}))
|
|
24076
|
+
});
|
|
24077
|
+
const task = this.store.readTask(request.taskId);
|
|
24078
|
+
if (!task.ok)
|
|
24079
|
+
return task;
|
|
24080
|
+
if (task.data.status === "closed")
|
|
24081
|
+
return failure2("invalid_transition", "closed task cannot record evidence");
|
|
24082
|
+
const observed = observation.data;
|
|
24083
|
+
let candidate = null;
|
|
24084
|
+
if (observed.tree === null) {
|
|
24085
|
+
const captured = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
24086
|
+
if (!captured.ok)
|
|
24087
|
+
return captured;
|
|
24088
|
+
candidate = captured.data;
|
|
24089
|
+
}
|
|
24090
|
+
const label = observed.name ?? observed.argv.join(" ");
|
|
24091
|
+
const data = {
|
|
24092
|
+
kind: "check",
|
|
24093
|
+
claim: `workit check ${label}`.slice(0, 500),
|
|
24094
|
+
requirementIds: [],
|
|
24095
|
+
...candidate ? { beforeCandidateId: candidate.id, candidateId: candidate.id } : {},
|
|
24096
|
+
result: observed.exitCode === 0 ? "passed" : "failed",
|
|
24097
|
+
summary: `${observed.configured ? "configured" : "ad-hoc"} check exited ${observed.exitCode}${observed.timedOut ? " (timed out)" : ""} in ${observed.durationMs} ms`,
|
|
24098
|
+
refs: [],
|
|
24099
|
+
exitCode: observed.exitCode,
|
|
24100
|
+
reviewContext: null,
|
|
24101
|
+
observation: observed
|
|
24102
|
+
};
|
|
24103
|
+
const changed = this.store.mutateTask(task.data.id, request.expectedRevision ?? task.data.revision, (current, mutation) => {
|
|
24104
|
+
const candidates = candidate === null || current.candidates.some((item) => item.id === candidate.id) ? current.candidates : [...current.candidates, candidate];
|
|
24105
|
+
const critical = [...new Set([...current.critical ?? [], CHECK_OBSERVATION_PATH])];
|
|
24106
|
+
return success(mutation.revision, null, {
|
|
24107
|
+
...current,
|
|
24108
|
+
critical,
|
|
24109
|
+
candidates,
|
|
24110
|
+
evidence: [
|
|
24111
|
+
...current.evidence,
|
|
24112
|
+
{
|
|
24113
|
+
id: newId(),
|
|
24114
|
+
recordedAt: mutation.now,
|
|
24115
|
+
provenance: provenance(this.context, "host_observed"),
|
|
24116
|
+
data
|
|
24117
|
+
}
|
|
24118
|
+
]
|
|
24119
|
+
});
|
|
24120
|
+
}, trustedNow(this.context));
|
|
24121
|
+
if (!changed.ok)
|
|
24122
|
+
return changed;
|
|
24123
|
+
return success(changed.data.revision, null, changed.data.evidence.at(-1));
|
|
24124
|
+
}
|
|
23609
24125
|
decision(request) {
|
|
23610
24126
|
return this.recordDecision(request);
|
|
23611
24127
|
}
|
|
@@ -23841,7 +24357,7 @@ class WorkitCore {
|
|
|
23841
24357
|
const current = captureCandidate(this.store.root, task.data.intent.data.scope, environment());
|
|
23842
24358
|
if (!current.ok)
|
|
23843
24359
|
return current;
|
|
23844
|
-
const evaluations = evaluateEvidence(task.data, current.data);
|
|
24360
|
+
const evaluations = evaluateEvidence(task.data, current.data, treeFreshness(this.store.root));
|
|
23845
24361
|
const verified = evidence.some((entry) => {
|
|
23846
24362
|
const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
|
|
23847
24363
|
return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
|
|
@@ -24545,7 +25061,8 @@ class WorkitCore {
|
|
|
24545
25061
|
if (!current.ok)
|
|
24546
25062
|
return current;
|
|
24547
25063
|
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
24548
|
-
const
|
|
25064
|
+
const tree = treeFreshness(this.store.root);
|
|
25065
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
|
|
24549
25066
|
return success(task.revision, workspace.data.revision, {
|
|
24550
25067
|
id: task.id,
|
|
24551
25068
|
revision: task.revision,
|
|
@@ -24562,7 +25079,7 @@ class WorkitCore {
|
|
|
24562
25079
|
writer: evaluationWorkspace.writer
|
|
24563
25080
|
});
|
|
24564
25081
|
}
|
|
24565
|
-
compactContext(taskId) {
|
|
25082
|
+
compactContext(taskId, freshness) {
|
|
24566
25083
|
const root = this.contextRootError();
|
|
24567
25084
|
if (!root.ok)
|
|
24568
25085
|
return root;
|
|
@@ -24572,12 +25089,12 @@ class WorkitCore {
|
|
|
24572
25089
|
const helper = this.helperTaskGuard(task.data, true);
|
|
24573
25090
|
if (!helper.ok)
|
|
24574
25091
|
return helper;
|
|
24575
|
-
const view = this.view(task.data, false);
|
|
25092
|
+
const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
|
|
24576
25093
|
if (!view.ok)
|
|
24577
25094
|
return view;
|
|
24578
25095
|
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
|
|
24579
25096
|
}
|
|
24580
|
-
view(task, capture = true) {
|
|
25097
|
+
view(task, capture = true, freshness = treeFreshness(this.store.root)) {
|
|
24581
25098
|
const workspace = this.store.readWorkspace();
|
|
24582
25099
|
if (!workspace.ok)
|
|
24583
25100
|
return workspace;
|
|
@@ -24588,13 +25105,14 @@ class WorkitCore {
|
|
|
24588
25105
|
if (!current.ok)
|
|
24589
25106
|
return current;
|
|
24590
25107
|
const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
|
|
24591
|
-
const
|
|
25108
|
+
const tree = freshness;
|
|
25109
|
+
const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller, tree);
|
|
24592
25110
|
return success(task.revision, workspace.data.revision, {
|
|
24593
25111
|
task,
|
|
24594
25112
|
workspace: evaluationWorkspace,
|
|
24595
25113
|
capabilities: this.context.capabilities,
|
|
24596
25114
|
requirements,
|
|
24597
|
-
evidence: evaluateEvidence(task, current.data)
|
|
25115
|
+
evidence: evaluateEvidence(task, current.data, tree)
|
|
24598
25116
|
});
|
|
24599
25117
|
}
|
|
24600
25118
|
reconcileResume(view, observations = []) {
|
|
@@ -24821,13 +25339,14 @@ class WorkitCore {
|
|
|
24821
25339
|
return current;
|
|
24822
25340
|
const withCandidate = current.data;
|
|
24823
25341
|
const taskForView = withCandidate.id === task.data.candidates.at(-1)?.id ? task.data : { ...task.data, candidates: [...task.data.candidates, withCandidate] };
|
|
24824
|
-
const
|
|
25342
|
+
const tree = treeFreshness(this.store.root);
|
|
25343
|
+
const requirements = evaluateRequirements(taskForView, workspace.data, this.context.capabilities, withCandidate, this.store.root, this.context.caller, tree);
|
|
24825
25344
|
const view = {
|
|
24826
25345
|
task: taskForView,
|
|
24827
25346
|
workspace: workspace.data,
|
|
24828
25347
|
capabilities: this.context.capabilities,
|
|
24829
25348
|
requirements,
|
|
24830
|
-
evidence: evaluateEvidence(taskForView, withCandidate)
|
|
25349
|
+
evidence: evaluateEvidence(taskForView, withCandidate, tree)
|
|
24831
25350
|
};
|
|
24832
25351
|
const closure = evaluateClosure(input.outcome, view);
|
|
24833
25352
|
if (!closure.ok)
|
|
@@ -24884,22 +25403,22 @@ function resolveGitRevision(root, value) {
|
|
|
24884
25403
|
}
|
|
24885
25404
|
function resolveInside(root, candidate) {
|
|
24886
25405
|
const base = realpathSync7(root);
|
|
24887
|
-
const target =
|
|
24888
|
-
if (target !== base && !target.startsWith(base +
|
|
25406
|
+
const target = path13.resolve(base, candidate);
|
|
25407
|
+
if (target !== base && !target.startsWith(base + path13.sep)) {
|
|
24889
25408
|
throw new Error("path must stay inside repository root");
|
|
24890
25409
|
}
|
|
24891
25410
|
let ancestor = target;
|
|
24892
25411
|
while (!existsSync5(ancestor))
|
|
24893
|
-
ancestor =
|
|
25412
|
+
ancestor = path13.dirname(ancestor);
|
|
24894
25413
|
const canonicalAncestor = realpathSync7(ancestor);
|
|
24895
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
25414
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path13.sep)) {
|
|
24896
25415
|
throw new Error("path must stay inside repository root");
|
|
24897
25416
|
}
|
|
24898
25417
|
return target;
|
|
24899
25418
|
}
|
|
24900
25419
|
function run2(root, executable, args, env = {}) {
|
|
24901
25420
|
const cwd = realpathSync7(root);
|
|
24902
|
-
const result =
|
|
25421
|
+
const result = spawnSync6(executable, args, {
|
|
24903
25422
|
cwd,
|
|
24904
25423
|
encoding: "utf8",
|
|
24905
25424
|
env: { ...process.env, ...env }
|
|
@@ -24913,7 +25432,7 @@ function run2(root, executable, args, env = {}) {
|
|
|
24913
25432
|
}
|
|
24914
25433
|
|
|
24915
25434
|
// packages/workit-core/src/core/logger.ts
|
|
24916
|
-
import
|
|
25435
|
+
import os3 from "node:os";
|
|
24917
25436
|
var REDACTED = "[REDACTED]";
|
|
24918
25437
|
var SECRET_VALUE = /\b(?:Bearer|Basic|Digest|Token)\s+\S+/gi;
|
|
24919
25438
|
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;
|
|
@@ -24956,7 +25475,7 @@ var SENSITIVE_WORDS = new Set([
|
|
|
24956
25475
|
]);
|
|
24957
25476
|
var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
|
|
24958
25477
|
var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
24959
|
-
var homePattern = new RegExp(`${escapeRegExp(
|
|
25478
|
+
var homePattern = new RegExp(`${escapeRegExp(os3.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
|
|
24960
25479
|
var applyPatterns = (value) => {
|
|
24961
25480
|
let out = value;
|
|
24962
25481
|
out = out.replace(SECRET_VALUE, REDACTED);
|
|
@@ -24970,9 +25489,9 @@ var applyPatterns = (value) => {
|
|
|
24970
25489
|
var redactSecrets = (value) => applyPatterns(value);
|
|
24971
25490
|
|
|
24972
25491
|
// packages/workit-core/src/core/pr-create.ts
|
|
24973
|
-
import
|
|
24974
|
-
import
|
|
24975
|
-
import { spawnSync as
|
|
25492
|
+
import fs11 from "node:fs";
|
|
25493
|
+
import path14 from "node:path";
|
|
25494
|
+
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
24976
25495
|
// packages/workit-core/src/forge/pr-body.ts
|
|
24977
25496
|
function parseGhIssue(value) {
|
|
24978
25497
|
const m = /issues\/(\d+)/.exec(value);
|
|
@@ -24982,13 +25501,13 @@ function parseGhIssue(value) {
|
|
|
24982
25501
|
// packages/workit-core/src/core/pr-create.ts
|
|
24983
25502
|
function whichOnPath(tool) {
|
|
24984
25503
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
24985
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
25504
|
+
for (const dir of (process.env.PATH ?? "").split(path14.delimiter)) {
|
|
24986
25505
|
if (!dir)
|
|
24987
25506
|
continue;
|
|
24988
25507
|
for (const name of names) {
|
|
24989
|
-
const candidate =
|
|
25508
|
+
const candidate = path14.join(dir, name);
|
|
24990
25509
|
try {
|
|
24991
|
-
|
|
25510
|
+
fs11.accessSync(candidate, fs11.constants.X_OK);
|
|
24992
25511
|
return candidate;
|
|
24993
25512
|
} catch {}
|
|
24994
25513
|
}
|
|
@@ -24997,7 +25516,7 @@ function whichOnPath(tool) {
|
|
|
24997
25516
|
}
|
|
24998
25517
|
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
24999
25518
|
var pushRemote = (cwd) => {
|
|
25000
|
-
const result =
|
|
25519
|
+
const result = spawnSync7("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
25001
25520
|
cwd,
|
|
25002
25521
|
encoding: "utf8"
|
|
25003
25522
|
});
|
|
@@ -25008,9 +25527,9 @@ var pushRemote = (cwd) => {
|
|
|
25008
25527
|
};
|
|
25009
25528
|
|
|
25010
25529
|
// packages/workit-core/src/core/repo-context.ts
|
|
25011
|
-
import { spawnSync as
|
|
25530
|
+
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
25012
25531
|
import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
|
|
25013
|
-
import
|
|
25532
|
+
import path15 from "node:path";
|
|
25014
25533
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
25015
25534
|
try {
|
|
25016
25535
|
gitRevisionParts(value);
|
|
@@ -25037,7 +25556,7 @@ var invalidRange = (cwd) => ({
|
|
|
25037
25556
|
cwd
|
|
25038
25557
|
});
|
|
25039
25558
|
var runGit = (cwd, args) => {
|
|
25040
|
-
const result =
|
|
25559
|
+
const result = spawnSync8("git", args, { cwd, encoding: "utf8" });
|
|
25041
25560
|
return {
|
|
25042
25561
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
25043
25562
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -25144,7 +25663,7 @@ var findPrTemplate = (cwd) => {
|
|
|
25144
25663
|
]) {
|
|
25145
25664
|
const parts = rel.split("/");
|
|
25146
25665
|
const base = parts[parts.length - 1];
|
|
25147
|
-
const dir = parts.length > 1 ?
|
|
25666
|
+
const dir = parts.length > 1 ? path15.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
25148
25667
|
const actual = probeCaseInsensitive(dir, base);
|
|
25149
25668
|
if (actual)
|
|
25150
25669
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -25175,7 +25694,7 @@ var diffStatForRange = (cwd, range) => {
|
|
|
25175
25694
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
25176
25695
|
};
|
|
25177
25696
|
var packageScripts = (cwd, keys) => {
|
|
25178
|
-
const pkgPath =
|
|
25697
|
+
const pkgPath = path15.join(cwd, "package.json");
|
|
25179
25698
|
if (!existsSync6(pkgPath))
|
|
25180
25699
|
return [];
|
|
25181
25700
|
try {
|
|
@@ -25201,9 +25720,9 @@ var documentationFiles = (cwd) => {
|
|
|
25201
25720
|
if (entry.isDirectory()) {
|
|
25202
25721
|
if (excluded.has(entry.name))
|
|
25203
25722
|
continue;
|
|
25204
|
-
walk(
|
|
25723
|
+
walk(path15.join(dir, entry.name), depth + 1);
|
|
25205
25724
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
25206
|
-
matches.push("./" +
|
|
25725
|
+
matches.push("./" + path15.relative(cwd, path15.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
25207
25726
|
}
|
|
25208
25727
|
}
|
|
25209
25728
|
};
|
|
@@ -25219,7 +25738,7 @@ var readTrimmed2 = (file, maxLines) => {
|
|
|
25219
25738
|
`);
|
|
25220
25739
|
};
|
|
25221
25740
|
function prReadyContext(root, range) {
|
|
25222
|
-
const cwd =
|
|
25741
|
+
const cwd = path15.resolve(repoRoot(root));
|
|
25223
25742
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25224
25743
|
return invalidRange(cwd);
|
|
25225
25744
|
let stdout = "";
|
|
@@ -25277,7 +25796,7 @@ ${status.stderr}` : "");
|
|
|
25277
25796
|
stdout += `template_path: ${template}
|
|
25278
25797
|
|
|
25279
25798
|
`;
|
|
25280
|
-
stdout += readTrimmed2(
|
|
25799
|
+
stdout += readTrimmed2(path15.join(cwd, template), 220);
|
|
25281
25800
|
} else {
|
|
25282
25801
|
stdout += `template_path: none
|
|
25283
25802
|
|
|
@@ -25287,7 +25806,7 @@ ${status.stderr}` : "");
|
|
|
25287
25806
|
stdout += `
|
|
25288
25807
|
`;
|
|
25289
25808
|
stdout += printSection("Recent Validation Signals");
|
|
25290
|
-
if (existsSync6(
|
|
25809
|
+
if (existsSync6(path15.join(cwd, "package.json"))) {
|
|
25291
25810
|
stdout += `package.json detected. Common scripts:
|
|
25292
25811
|
`;
|
|
25293
25812
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -25296,7 +25815,7 @@ ${status.stderr}` : "");
|
|
|
25296
25815
|
`) + `
|
|
25297
25816
|
`;
|
|
25298
25817
|
}
|
|
25299
|
-
if (existsSync6(
|
|
25818
|
+
if (existsSync6(path15.join(cwd, "Cargo.toml")) || existsSync6(path15.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
25300
25819
|
stdout += `Rust project detected.
|
|
25301
25820
|
`;
|
|
25302
25821
|
}
|
|
@@ -25329,7 +25848,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
25329
25848
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
25330
25849
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
25331
25850
|
function changelogContext(root, range) {
|
|
25332
|
-
const cwd =
|
|
25851
|
+
const cwd = path15.resolve(repoRoot(root));
|
|
25333
25852
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25334
25853
|
return invalidRange(cwd);
|
|
25335
25854
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -25347,7 +25866,7 @@ function changelogContext(root, range) {
|
|
|
25347
25866
|
stdout += CHANGELOG_RULES + `
|
|
25348
25867
|
`;
|
|
25349
25868
|
stdout += printSection("Existing CHANGELOG.md");
|
|
25350
|
-
const changelogPath =
|
|
25869
|
+
const changelogPath = path15.join(cwd, "CHANGELOG.md");
|
|
25351
25870
|
if (existsSync6(changelogPath)) {
|
|
25352
25871
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
25353
25872
|
`;
|
|
@@ -25367,7 +25886,7 @@ function changelogContext(root, range) {
|
|
|
25367
25886
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
25368
25887
|
}
|
|
25369
25888
|
function docsRefreshContext(root, range) {
|
|
25370
|
-
const cwd =
|
|
25889
|
+
const cwd = path15.resolve(repoRoot(root));
|
|
25371
25890
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
25372
25891
|
return invalidRange(cwd);
|
|
25373
25892
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -25389,7 +25908,7 @@ function docsRefreshContext(root, range) {
|
|
|
25389
25908
|
`) + `
|
|
25390
25909
|
`;
|
|
25391
25910
|
stdout += printSection("README Preview");
|
|
25392
|
-
const readme =
|
|
25911
|
+
const readme = path15.join(cwd, "README.md");
|
|
25393
25912
|
if (existsSync6(readme)) {
|
|
25394
25913
|
stdout += readTrimmed2(readme, 220) + `
|
|
25395
25914
|
`;
|
|
@@ -25398,7 +25917,7 @@ function docsRefreshContext(root, range) {
|
|
|
25398
25917
|
`;
|
|
25399
25918
|
}
|
|
25400
25919
|
stdout += printSection("Package Scripts");
|
|
25401
|
-
const pkgPath =
|
|
25920
|
+
const pkgPath = path15.join(cwd, "package.json");
|
|
25402
25921
|
if (existsSync6(pkgPath)) {
|
|
25403
25922
|
try {
|
|
25404
25923
|
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
@@ -25412,7 +25931,7 @@ function docsRefreshContext(root, range) {
|
|
|
25412
25931
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
25413
25932
|
}
|
|
25414
25933
|
function releaseNotesContext(root, rangeOrTag) {
|
|
25415
|
-
const cwd =
|
|
25934
|
+
const cwd = path15.resolve(repoRoot(root));
|
|
25416
25935
|
if (!rangeOrTag) {
|
|
25417
25936
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
25418
25937
|
`, exitCode: 1, cwd };
|
|
@@ -25450,7 +25969,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
25450
25969
|
`;
|
|
25451
25970
|
stdout += printSection("Existing Release Files");
|
|
25452
25971
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
25453
|
-
if (existsSync6(
|
|
25972
|
+
if (existsSync6(path15.join(cwd, rel)))
|
|
25454
25973
|
stdout += rel + `
|
|
25455
25974
|
`;
|
|
25456
25975
|
}
|
|
@@ -25478,7 +25997,7 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
25478
25997
|
}
|
|
25479
25998
|
|
|
25480
25999
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
25481
|
-
import { spawnSync as
|
|
26000
|
+
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
25482
26001
|
var repoPathFromRemote = (remote) => {
|
|
25483
26002
|
let rest = (remote || "").trim().replace(/\/+$/, "");
|
|
25484
26003
|
if (!rest)
|
|
@@ -25499,7 +26018,7 @@ var defaultCli = (bin, available) => {
|
|
|
25499
26018
|
if (!available)
|
|
25500
26019
|
return null;
|
|
25501
26020
|
return (args, root) => {
|
|
25502
|
-
const out =
|
|
26021
|
+
const out = spawnSync9(bin, args, {
|
|
25503
26022
|
cwd: root,
|
|
25504
26023
|
encoding: "utf8",
|
|
25505
26024
|
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
@@ -25577,8 +26096,8 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
25577
26096
|
};
|
|
25578
26097
|
|
|
25579
26098
|
// packages/workit-core/src/core/youtrack.ts
|
|
25580
|
-
import
|
|
25581
|
-
import
|
|
26099
|
+
import fs12 from "node:fs";
|
|
26100
|
+
import path16 from "node:path";
|
|
25582
26101
|
|
|
25583
26102
|
// packages/workit-core/src/core/templates.ts
|
|
25584
26103
|
var repoRoot2 = assetRoot();
|
|
@@ -25591,22 +26110,22 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
25591
26110
|
// packages/workit-core/src/core/youtrack.ts
|
|
25592
26111
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
25593
26112
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
25594
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
25595
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
26113
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path16.join(configDir(), "youtrack.json");
|
|
26114
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path16.join(resolveConfigDir(), "youtrack.json");
|
|
25596
26115
|
var youTrackTokenModeOk = (p) => {
|
|
25597
26116
|
if (process.platform === "win32")
|
|
25598
26117
|
return true;
|
|
25599
|
-
const mode =
|
|
26118
|
+
const mode = fs12.statSync(p).mode & 511;
|
|
25600
26119
|
return mode === 384;
|
|
25601
26120
|
};
|
|
25602
26121
|
function readYouTrackConfig(required) {
|
|
25603
26122
|
const cfgPath = youTrackConfigPath();
|
|
25604
|
-
if (!
|
|
26123
|
+
if (!fs12.existsSync(cfgPath)) {
|
|
25605
26124
|
return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
|
|
25606
26125
|
}
|
|
25607
26126
|
let parsed;
|
|
25608
26127
|
try {
|
|
25609
|
-
parsed = JSON.parse(
|
|
26128
|
+
parsed = JSON.parse(fs12.readFileSync(cfgPath, "utf8"));
|
|
25610
26129
|
} catch {
|
|
25611
26130
|
return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
|
|
25612
26131
|
}
|
|
@@ -25617,11 +26136,11 @@ function readYouTrackConfig(required) {
|
|
|
25617
26136
|
}
|
|
25618
26137
|
var readYouTrackContextConfig = () => {
|
|
25619
26138
|
const cfgPath = youTrackReadOnlyConfigPath();
|
|
25620
|
-
if (!
|
|
26139
|
+
if (!fs12.existsSync(cfgPath))
|
|
25621
26140
|
return { data: {} };
|
|
25622
26141
|
let parsed;
|
|
25623
26142
|
try {
|
|
25624
|
-
parsed = JSON.parse(
|
|
26143
|
+
parsed = JSON.parse(fs12.readFileSync(cfgPath, "utf8"));
|
|
25625
26144
|
} catch {
|
|
25626
26145
|
return { error: "YouTrack configuration is unavailable" };
|
|
25627
26146
|
}
|
|
@@ -25660,12 +26179,12 @@ var youTrackToken = () => {
|
|
|
25660
26179
|
if ("error" in loaded)
|
|
25661
26180
|
return loaded;
|
|
25662
26181
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
25663
|
-
const tokenPath = tokenFile ?
|
|
25664
|
-
if (!tokenPath || !
|
|
26182
|
+
const tokenPath = tokenFile ? path16.isAbsolute(tokenFile) ? path16.resolve(tokenFile) : path16.resolve(process.cwd(), tokenFile) : "";
|
|
26183
|
+
if (!tokenPath || !fs12.existsSync(tokenPath))
|
|
25665
26184
|
return { error: "missing youtrack.token" };
|
|
25666
26185
|
if (!youTrackTokenModeOk(tokenPath))
|
|
25667
26186
|
return { error: "youtrack.token mode must be 0600" };
|
|
25668
|
-
const token =
|
|
26187
|
+
const token = fs12.readFileSync(tokenPath, "utf8").trim();
|
|
25669
26188
|
if (!token)
|
|
25670
26189
|
return { error: "empty token file" };
|
|
25671
26190
|
if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
|
|
@@ -25730,9 +26249,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
|
|
|
25730
26249
|
} catch {
|
|
25731
26250
|
continue;
|
|
25732
26251
|
}
|
|
25733
|
-
if (!
|
|
26252
|
+
if (!fs12.existsSync(full))
|
|
25734
26253
|
continue;
|
|
25735
|
-
const text =
|
|
26254
|
+
const text = fs12.readFileSync(full, "utf8");
|
|
25736
26255
|
const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
|
|
25737
26256
|
if (m)
|
|
25738
26257
|
return m[1];
|
|
@@ -25885,10 +26404,6 @@ var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\
|
|
|
25885
26404
|
var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
|
|
25886
26405
|
var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
|
|
25887
26406
|
|
|
25888
|
-
// packages/workit-core/src/git/rev.ts
|
|
25889
|
-
var MAX_BUFFER = 256 * 1024 * 1024;
|
|
25890
|
-
var MAX_UNTRACKED_BYTES = 5 * 1024 * 1024;
|
|
25891
|
-
|
|
25892
26407
|
// packages/workit-core/src/ledger.ts
|
|
25893
26408
|
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
25894
26409
|
var FAILING = new Set(["failed", "blocked"]);
|
|
@@ -26030,13 +26545,13 @@ var rootVariants = (workspaceRoot) => {
|
|
|
26030
26545
|
if (!workspaceRoot)
|
|
26031
26546
|
return [];
|
|
26032
26547
|
const roots = new Set;
|
|
26033
|
-
for (const candidate of [workspaceRoot,
|
|
26034
|
-
if (
|
|
26548
|
+
for (const candidate of [workspaceRoot, path17.resolve(workspaceRoot)]) {
|
|
26549
|
+
if (path17.isAbsolute(candidate) && candidate !== path17.parse(candidate).root)
|
|
26035
26550
|
roots.add(candidate);
|
|
26036
26551
|
}
|
|
26037
26552
|
try {
|
|
26038
26553
|
const canonical = realpathSync8(workspaceRoot);
|
|
26039
|
-
if (canonical !==
|
|
26554
|
+
if (canonical !== path17.parse(canonical).root)
|
|
26040
26555
|
roots.add(canonical);
|
|
26041
26556
|
} catch {}
|
|
26042
26557
|
return [...roots].toSorted((left, right) => right.length - left.length);
|
|
@@ -26137,11 +26652,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
|
|
|
26137
26652
|
};
|
|
26138
26653
|
function createMcpServer(host, contextProvider) {
|
|
26139
26654
|
assertMcpHost(host);
|
|
26140
|
-
const here =
|
|
26655
|
+
const here = path17.dirname(fileURLToPath2(import.meta.url));
|
|
26141
26656
|
const sourceMarker = markSourcesLoaded([
|
|
26142
|
-
|
|
26143
|
-
|
|
26144
|
-
|
|
26657
|
+
path17.join(here, "server.ts"),
|
|
26658
|
+
path17.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
|
|
26659
|
+
path17.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
26145
26660
|
].filter((file) => existsSync7(file)));
|
|
26146
26661
|
let staleWarned = false;
|
|
26147
26662
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
@@ -26321,8 +26836,8 @@ var main = async () => {
|
|
|
26321
26836
|
process.exitCode = 2;
|
|
26322
26837
|
}
|
|
26323
26838
|
};
|
|
26324
|
-
var entryPath = process.argv[1] ?
|
|
26325
|
-
if (entryPath ===
|
|
26839
|
+
var entryPath = process.argv[1] ? path18.resolve(process.argv[1]) : "";
|
|
26840
|
+
if (entryPath === path18.resolve(fileURLToPath3(import.meta.url)))
|
|
26326
26841
|
await main();
|
|
26327
26842
|
// packages/workit-core/src/hooks/descriptor.ts
|
|
26328
26843
|
var support = (descriptor, axis) => {
|
|
@@ -26349,6 +26864,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
26349
26864
|
};
|
|
26350
26865
|
});
|
|
26351
26866
|
// packages/workit-core/src/hooks/context.ts
|
|
26867
|
+
var signals = new Map;
|
|
26352
26868
|
var cache = new Map;
|
|
26353
26869
|
// packages/workit-core/src/hooks/handle.ts
|
|
26354
26870
|
var offered = new Set;
|