@brainervirus/workit-opencode 8.22.3 → 8.24.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/assets/skills/workit-fanout/SKILL.md +5 -5
- package/assets/skills/workit-fanout/references/brief.md +17 -11
- package/assets/skills/workit-fanout/references/orchestration.md +6 -6
- package/assets/skills/workit-implement/SKILL.md +7 -5
- package/assets/skills/workit-review/SKILL.md +3 -0
- package/assets/skills/workit-ship/SKILL.md +1 -2
- package/dist/plugin.js +1666 -682
- package/package.json +2 -2
package/dist/plugin.js
CHANGED
|
@@ -4,9 +4,9 @@ function define(plugin) {
|
|
|
4
4
|
}
|
|
5
5
|
|
|
6
6
|
// packages/workit-core/src/core.ts
|
|
7
|
-
import { spawnSync as
|
|
8
|
-
import { existsSync as
|
|
9
|
-
import
|
|
7
|
+
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
8
|
+
import { existsSync as existsSync10, realpathSync as realpathSync5 } from "node:fs";
|
|
9
|
+
import path27 from "node:path";
|
|
10
10
|
|
|
11
11
|
// packages/workit-core/src/core/task-contract.ts
|
|
12
12
|
import { createHash, randomUUID } from "node:crypto";
|
|
@@ -8430,7 +8430,7 @@ var workspaceConfigSchema = object({
|
|
|
8430
8430
|
}).passthrough().optional(),
|
|
8431
8431
|
autoApprove: union([boolean2(), array(nonBlank)]).optional(),
|
|
8432
8432
|
defaultEndpoint: unknown().optional(),
|
|
8433
|
-
verification: _enum(["self", "independent"]).optional(),
|
|
8433
|
+
verification: _enum(["proportional", "self", "independent"]).optional(),
|
|
8434
8434
|
delegation: unknown().optional(),
|
|
8435
8435
|
trailers: boolean2().optional(),
|
|
8436
8436
|
liveSessionGuard: boolean2().optional(),
|
|
@@ -9015,6 +9015,124 @@ function resolveRef(cwd, ref) {
|
|
|
9015
9015
|
return null;
|
|
9016
9016
|
return line(git(cwd, ["rev-parse", "--verify", "-q", `${ref}^{commit}`]));
|
|
9017
9017
|
}
|
|
9018
|
+
var LANE_DIFF = [
|
|
9019
|
+
"-c",
|
|
9020
|
+
"core.quotePath=false",
|
|
9021
|
+
"diff",
|
|
9022
|
+
"--no-color",
|
|
9023
|
+
"--no-ext-diff",
|
|
9024
|
+
"--no-textconv",
|
|
9025
|
+
"--no-renames"
|
|
9026
|
+
];
|
|
9027
|
+
var laneRange = (fork, head) => {
|
|
9028
|
+
if (!safeArg(fork) || head !== null && !safeArg(head))
|
|
9029
|
+
return null;
|
|
9030
|
+
return head === null ? [fork] : [fork, head];
|
|
9031
|
+
};
|
|
9032
|
+
function diffNumstat(cwd, fork, head) {
|
|
9033
|
+
const range = laneRange(fork, head);
|
|
9034
|
+
if (!range)
|
|
9035
|
+
return null;
|
|
9036
|
+
const run = git(cwd, [...LANE_DIFF, "--numstat", "-z", ...range, "--"]);
|
|
9037
|
+
if (!run.ok)
|
|
9038
|
+
return null;
|
|
9039
|
+
const out = [];
|
|
9040
|
+
for (const record of run.stdout.split("\x00")) {
|
|
9041
|
+
const match = /^(-|\d+)\t(-|\d+)\t(.+)$/su.exec(record.replace(/^\n/u, ""));
|
|
9042
|
+
if (!match)
|
|
9043
|
+
continue;
|
|
9044
|
+
const binary = match[1] === "-";
|
|
9045
|
+
out.push({
|
|
9046
|
+
path: match[3],
|
|
9047
|
+
added: binary ? 0 : Number(match[1]),
|
|
9048
|
+
deleted: binary ? 0 : Number(match[2]),
|
|
9049
|
+
binary
|
|
9050
|
+
});
|
|
9051
|
+
}
|
|
9052
|
+
return out;
|
|
9053
|
+
}
|
|
9054
|
+
function diffRaw(cwd, fork, head) {
|
|
9055
|
+
const range = laneRange(fork, head);
|
|
9056
|
+
if (!range)
|
|
9057
|
+
return null;
|
|
9058
|
+
const run = git(cwd, [...LANE_DIFF, "--raw", "-z", "--no-abbrev", ...range, "--"]);
|
|
9059
|
+
if (!run.ok)
|
|
9060
|
+
return null;
|
|
9061
|
+
const fields = run.stdout.split("\x00");
|
|
9062
|
+
const out = [];
|
|
9063
|
+
for (let index = 0;index + 1 < fields.length; index += 2) {
|
|
9064
|
+
const meta = /^:(\d{6}) (\d{6}) ([0-9a-f]+) ([0-9a-f]+) [A-Z]\d*$/u.exec(fields[index].replace(/^\n/u, ""));
|
|
9065
|
+
if (!meta)
|
|
9066
|
+
return null;
|
|
9067
|
+
out.push({
|
|
9068
|
+
path: fields[index + 1],
|
|
9069
|
+
oldMode: meta[1],
|
|
9070
|
+
newMode: meta[2],
|
|
9071
|
+
oldBlob: meta[3],
|
|
9072
|
+
newBlob: meta[4]
|
|
9073
|
+
});
|
|
9074
|
+
}
|
|
9075
|
+
return out;
|
|
9076
|
+
}
|
|
9077
|
+
function readBlobs(cwd, ids) {
|
|
9078
|
+
const out = new Map;
|
|
9079
|
+
const wanted = [...new Set(ids)].filter((id) => /^[0-9a-f]{40,64}$/u.test(id));
|
|
9080
|
+
if (!wanted.length)
|
|
9081
|
+
return out;
|
|
9082
|
+
const run = spawnSync3("git", ["cat-file", "--batch"], {
|
|
9083
|
+
cwd,
|
|
9084
|
+
input: `${wanted.join(`
|
|
9085
|
+
`)}
|
|
9086
|
+
`,
|
|
9087
|
+
maxBuffer: MAX_BUFFER,
|
|
9088
|
+
timeout: GIT_TIMEOUTS.local,
|
|
9089
|
+
killSignal: "SIGKILL",
|
|
9090
|
+
windowsHide: true
|
|
9091
|
+
});
|
|
9092
|
+
if (run.status !== 0 || !run.stdout)
|
|
9093
|
+
return null;
|
|
9094
|
+
const bytes = run.stdout;
|
|
9095
|
+
let at = 0;
|
|
9096
|
+
for (const id of wanted) {
|
|
9097
|
+
const end = bytes.indexOf(10, at);
|
|
9098
|
+
if (end < 0)
|
|
9099
|
+
return null;
|
|
9100
|
+
const header = bytes.subarray(at, end).toString("utf8").split(" ");
|
|
9101
|
+
at = end + 1;
|
|
9102
|
+
if (header[1] === "missing")
|
|
9103
|
+
continue;
|
|
9104
|
+
const size = Number(header[2]);
|
|
9105
|
+
if (header[0] !== id || !Number.isSafeInteger(size))
|
|
9106
|
+
return null;
|
|
9107
|
+
out.set(id, bytes.subarray(at, at + size));
|
|
9108
|
+
at += size + 1;
|
|
9109
|
+
}
|
|
9110
|
+
return out;
|
|
9111
|
+
}
|
|
9112
|
+
function treeFilesNamed(cwd, rev, name) {
|
|
9113
|
+
if (!safeArg(rev))
|
|
9114
|
+
return null;
|
|
9115
|
+
const run = git(cwd, ["ls-tree", "-r", "-z", "--full-tree", rev]);
|
|
9116
|
+
if (!run.ok)
|
|
9117
|
+
return null;
|
|
9118
|
+
const out = new Map;
|
|
9119
|
+
for (const record of run.stdout.split("\x00")) {
|
|
9120
|
+
const match = /^\d{6} blob ([0-9a-f]+)\t(.+)$/su.exec(record);
|
|
9121
|
+
if (match && (match[2] === name || match[2].endsWith(`/${name}`)))
|
|
9122
|
+
out.set(match[2], match[1]);
|
|
9123
|
+
}
|
|
9124
|
+
return out;
|
|
9125
|
+
}
|
|
9126
|
+
function fileAt(cwd, rev, file) {
|
|
9127
|
+
if (!safeArg(rev))
|
|
9128
|
+
return null;
|
|
9129
|
+
const run = git(cwd, ["cat-file", "blob", `${rev}:${file}`]);
|
|
9130
|
+
return run.ok ? run.stdout : null;
|
|
9131
|
+
}
|
|
9132
|
+
function untrackedFiles(cwd) {
|
|
9133
|
+
const run = git(cwd, ["ls-files", "-z", "--others", "--exclude-standard"]);
|
|
9134
|
+
return run.ok ? run.stdout.split("\x00").filter(Boolean) : [];
|
|
9135
|
+
}
|
|
9018
9136
|
var config2 = (cwd, key) => line(git(cwd, ["config", "--get", key]));
|
|
9019
9137
|
function currentBranch(cwd) {
|
|
9020
9138
|
return line(git(cwd, ["symbolic-ref", "--short", "-q", "HEAD"]));
|
|
@@ -12775,9 +12893,9 @@ var holdsLiveState = (state, dirty) => state === "running" || dirty === true;
|
|
|
12775
12893
|
|
|
12776
12894
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
12777
12895
|
import { createHash as createHash12 } from "node:crypto";
|
|
12778
|
-
import * as
|
|
12779
|
-
import { spawnSync as
|
|
12780
|
-
import
|
|
12896
|
+
import * as fs15 from "node:fs";
|
|
12897
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
12898
|
+
import path20 from "node:path";
|
|
12781
12899
|
|
|
12782
12900
|
// packages/workit-core/src/check-config.ts
|
|
12783
12901
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
@@ -13007,8 +13125,8 @@ function checkCommand(args, version, onPath = workitAvailable()) {
|
|
|
13007
13125
|
}
|
|
13008
13126
|
|
|
13009
13127
|
// packages/workit-core/src/core/policy/derive.ts
|
|
13010
|
-
import { existsSync as
|
|
13011
|
-
import
|
|
13128
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
13129
|
+
import path19 from "node:path";
|
|
13012
13130
|
|
|
13013
13131
|
// packages/workit-core/src/ledger.ts
|
|
13014
13132
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
@@ -13912,7 +14030,8 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
13912
14030
|
verdict: failing?.verdict ?? null,
|
|
13913
14031
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
13914
14032
|
};
|
|
13915
|
-
const
|
|
14033
|
+
const latestSelf = failing ? undefined : verdicts.findLast((entry) => entry.current && !entry.independent && !untrusted(entry.reasons) && (STRONG_RESULTS.has(String(entry.verdict.result)) || FAILING.has(String(entry.verdict.result))));
|
|
14034
|
+
const selfEntry = latestSelf && STRONG_RESULTS.has(String(latestSelf.verdict.result)) ? latestSelf : undefined;
|
|
13916
14035
|
return {
|
|
13917
14036
|
branch,
|
|
13918
14037
|
head,
|
|
@@ -13956,151 +14075,1001 @@ var OBSERVED_ONLY = new Set([
|
|
|
13956
14075
|
...PR_ROW_TYPES
|
|
13957
14076
|
]);
|
|
13958
14077
|
|
|
13959
|
-
// packages/workit-core/src/core/policy/
|
|
13960
|
-
|
|
13961
|
-
|
|
13962
|
-
|
|
13963
|
-
|
|
13964
|
-
|
|
13965
|
-
|
|
13966
|
-
|
|
13967
|
-
|
|
13968
|
-
|
|
13969
|
-
|
|
13970
|
-
|
|
13971
|
-
|
|
13972
|
-
|
|
13973
|
-
|
|
13974
|
-
|
|
13975
|
-
|
|
13976
|
-
|
|
13977
|
-
});
|
|
13978
|
-
var sorted = (values) => [...new Set(values)].toSorted();
|
|
13979
|
-
var normalizedScope = (scope) => ({
|
|
13980
|
-
description: scope.description,
|
|
13981
|
-
paths: sorted(scope.paths),
|
|
13982
|
-
exclusions: sorted(scope.exclusions)
|
|
13983
|
-
});
|
|
13984
|
-
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
13985
|
-
const out = [];
|
|
13986
|
-
const base = { scope, dependentAction: null };
|
|
13987
|
-
if (judgment.behaviorChange)
|
|
13988
|
-
out.push(derived2({
|
|
13989
|
-
...base,
|
|
13990
|
-
ruleId: RULES.test,
|
|
13991
|
-
dimension: "testing",
|
|
13992
|
-
reason: "Behavior changes: the configured test check must pass on the final tree.",
|
|
13993
|
-
satisfaction: "Run `workit check test` on the final tree.",
|
|
13994
|
-
before: "close",
|
|
13995
|
-
acceptanceAllowed: false
|
|
13996
|
-
}));
|
|
13997
|
-
if (judgment.behaviorChange && judgment.riskTier === "normal")
|
|
13998
|
-
out.push(verification === "independent" ? derived2({
|
|
13999
|
-
...base,
|
|
14000
|
-
ruleId: RULES.nonAuthor,
|
|
14001
|
-
dimension: "review",
|
|
14002
|
-
reason: "This workspace requires independent verification: a session that did not author the change records the verdict.",
|
|
14003
|
-
satisfaction: "An independent session records a passing verdict: `workit ledger verdict verified --how <what it exercised>`.",
|
|
14004
|
-
before: "close",
|
|
14005
|
-
acceptanceAllowed: false
|
|
14006
|
-
}) : derived2({
|
|
14007
|
-
...base,
|
|
14008
|
-
ruleId: RULES.self,
|
|
14009
|
-
dimension: "review",
|
|
14010
|
-
reason: "A behavior change needs a recorded verdict; the author's own is allowed and is shown as self-reviewed, never verified.",
|
|
14011
|
-
satisfaction: "After an observed passing `workit check test`, record `workit ledger verdict tests-verified --self --how <what you ran>` (or have a verifier record one).",
|
|
14012
|
-
before: "close",
|
|
14013
|
-
acceptanceAllowed: false
|
|
14014
|
-
}));
|
|
14015
|
-
if (judgment.riskTier === "high")
|
|
14016
|
-
out.push(derived2({
|
|
14017
|
-
...base,
|
|
14018
|
-
ruleId: RULES.verified,
|
|
14019
|
-
dimension: "review",
|
|
14020
|
-
reason: "High risk: an independent verifier must exercise the live surface.",
|
|
14021
|
-
satisfaction: "An independent verifier runs the real surface and records `workit ledger verdict verified --kind live --how <what it exercised>`.",
|
|
14022
|
-
before: "close",
|
|
14023
|
-
acceptanceAllowed: false
|
|
14024
|
-
}));
|
|
14025
|
-
if (judgment.productChoiceOpen)
|
|
14026
|
-
out.push(derived2({
|
|
14027
|
-
...base,
|
|
14028
|
-
ruleId: RULES.product,
|
|
14029
|
-
dimension: "decisions",
|
|
14030
|
-
reason: "A product or preference choice is open; the user decides it before code is written.",
|
|
14031
|
-
satisfaction: 'Ask the user (with your recommendation), then record the answer: `workit ledger decision "<choice>" --why "<reason>"`.',
|
|
14032
|
-
before: "write",
|
|
14033
|
-
acceptanceAllowed: false
|
|
14034
|
-
}));
|
|
14035
|
-
if (judgment.needsPlan || judgment.riskTier === "high")
|
|
14036
|
-
out.push(derived2({
|
|
14037
|
-
...base,
|
|
14038
|
-
ruleId: RULES.plan,
|
|
14039
|
-
dimension: "artifacts",
|
|
14040
|
-
reason: judgment.needsPlan ? "The work needs a written plan or spec before implementation." : "High-risk work needs a written plan before implementation.",
|
|
14041
|
-
satisfaction: "Write the plan or spec (e.g. docs/plans/<slug>.md; docs and Markdown writes stay allowed), then record it: `workit policy assess --ref <path>`.",
|
|
14042
|
-
before: "write",
|
|
14043
|
-
acceptanceAllowed: true
|
|
14044
|
-
}));
|
|
14045
|
-
for (const constraint of constraints)
|
|
14046
|
-
for (const obligation of constraint.requires)
|
|
14047
|
-
out.push(derived2({
|
|
14048
|
-
ruleId: `project-constraint:${constraint.id}:${obligation.kind}:${sha256({
|
|
14049
|
-
kind: obligation.kind,
|
|
14050
|
-
scope: normalizedScope(obligation.scope),
|
|
14051
|
-
refs: obligation.refs.map((ref) => JSON.stringify(ref)).toSorted(),
|
|
14052
|
-
method: obligation.method,
|
|
14053
|
-
before: obligation.before,
|
|
14054
|
-
dependentAction: obligation.dependentAction
|
|
14055
|
-
})}`,
|
|
14056
|
-
dimension: obligation.method ?? (obligation.kind === "check" ? "verification" : obligation.kind === "decision" ? "decisions" : "challenge"),
|
|
14057
|
-
scope: obligation.scope,
|
|
14058
|
-
reason: `${constraint.kind} constraint requires ${obligation.kind}: ${constraint.statement}`,
|
|
14059
|
-
satisfaction: `Satisfy the constraint obligation using its referenced ${obligation.kind}.`,
|
|
14060
|
-
before: obligation.before,
|
|
14061
|
-
dependentAction: obligation.dependentAction,
|
|
14062
|
-
acceptanceAllowed: constraint.acceptanceAllowed && obligation.kind !== "decision"
|
|
14063
|
-
}));
|
|
14064
|
-
const seen = new Set;
|
|
14065
|
-
return out.filter((item) => seen.has(item.id) ? false : (seen.add(item.id), true));
|
|
14078
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
14079
|
+
import { existsSync as existsSync5, readFileSync as readFileSync5 } from "node:fs";
|
|
14080
|
+
import path18 from "node:path";
|
|
14081
|
+
|
|
14082
|
+
// packages/workit-core/src/git/ops.ts
|
|
14083
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
14084
|
+
import { existsSync as existsSync4, readFileSync as readFileSync4 } from "node:fs";
|
|
14085
|
+
import path17 from "node:path";
|
|
14086
|
+
|
|
14087
|
+
// packages/workit-core/src/core/pr-create.ts
|
|
14088
|
+
import fs14 from "node:fs";
|
|
14089
|
+
import path16 from "node:path";
|
|
14090
|
+
import { spawnSync as spawnSync10 } from "node:child_process";
|
|
14091
|
+
import { pathToFileURL } from "node:url";
|
|
14092
|
+
// packages/workit-core/src/forge/pr-body.ts
|
|
14093
|
+
function parseGhIssue(value) {
|
|
14094
|
+
const m = /issues\/(\d+)/.exec(value);
|
|
14095
|
+
return m ? m[1] : String(value).trim().replace(/^#/, "");
|
|
14066
14096
|
}
|
|
14067
|
-
|
|
14068
|
-
|
|
14069
|
-
|
|
14070
|
-
|
|
14071
|
-
|
|
14072
|
-
|
|
14073
|
-
|
|
14074
|
-
|
|
14075
|
-
|
|
14076
|
-
|
|
14077
|
-
|
|
14078
|
-
|
|
14097
|
+
|
|
14098
|
+
// packages/workit-core/src/core/pr-create.ts
|
|
14099
|
+
function whichOnPath(tool) {
|
|
14100
|
+
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
14101
|
+
for (const dir of (process.env.PATH ?? "").split(path16.delimiter)) {
|
|
14102
|
+
if (!dir)
|
|
14103
|
+
continue;
|
|
14104
|
+
for (const name of names) {
|
|
14105
|
+
const candidate = path16.join(dir, name);
|
|
14106
|
+
try {
|
|
14107
|
+
fs14.accessSync(candidate, fs14.constants.X_OK);
|
|
14108
|
+
return candidate;
|
|
14109
|
+
} catch {}
|
|
14079
14110
|
}
|
|
14080
|
-
}
|
|
14111
|
+
}
|
|
14112
|
+
return null;
|
|
14113
|
+
}
|
|
14114
|
+
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
14115
|
+
var pushRemote = (cwd) => {
|
|
14116
|
+
const result = spawnSync10("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
14117
|
+
cwd,
|
|
14118
|
+
encoding: "utf8"
|
|
14119
|
+
});
|
|
14120
|
+
if (result.status !== 0)
|
|
14121
|
+
return null;
|
|
14122
|
+
const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
|
|
14123
|
+
return urls.length === 1 ? urls[0] : null;
|
|
14081
14124
|
};
|
|
14082
|
-
var
|
|
14083
|
-
const
|
|
14084
|
-
if (
|
|
14085
|
-
return
|
|
14086
|
-
|
|
14087
|
-
|
|
14088
|
-
|
|
14089
|
-
|
|
14090
|
-
|
|
14091
|
-
|
|
14092
|
-
|
|
14093
|
-
|
|
14094
|
-
|
|
14095
|
-
|
|
14096
|
-
|
|
14097
|
-
|
|
14125
|
+
var safePushUrl = (remote) => {
|
|
14126
|
+
const raw = remote.trim();
|
|
14127
|
+
if (path16.isAbsolute(raw))
|
|
14128
|
+
return pathToFileURL(raw).href;
|
|
14129
|
+
if (!raw.includes("://")) {
|
|
14130
|
+
const scp = /^(?:([^@\s]+)@)?([^:]+):(.+)$/u.exec(raw);
|
|
14131
|
+
if (scp)
|
|
14132
|
+
return `${scp[1] || "git"}@${scp[2]}:${scp[3]}`;
|
|
14133
|
+
}
|
|
14134
|
+
try {
|
|
14135
|
+
const url = new URL(raw);
|
|
14136
|
+
if (!["file:", "git:", "http:", "https:", "ssh:"].includes(url.protocol))
|
|
14137
|
+
return null;
|
|
14138
|
+
url.password = "";
|
|
14139
|
+
if (url.protocol !== "ssh:")
|
|
14140
|
+
url.username = "";
|
|
14141
|
+
url.search = "";
|
|
14142
|
+
url.hash = "";
|
|
14143
|
+
return url.toString();
|
|
14144
|
+
} catch {
|
|
14145
|
+
return null;
|
|
14146
|
+
}
|
|
14098
14147
|
};
|
|
14099
|
-
var
|
|
14100
|
-
const
|
|
14101
|
-
if (
|
|
14102
|
-
return
|
|
14103
|
-
|
|
14148
|
+
var pushTargetIsStable = (cwd, remote) => {
|
|
14149
|
+
const target = safePushUrl(remote);
|
|
14150
|
+
if (!target)
|
|
14151
|
+
return false;
|
|
14152
|
+
const result = spawnSync10("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
|
|
14153
|
+
if (result.status !== 0 && result.status !== 1)
|
|
14154
|
+
return false;
|
|
14155
|
+
const prefixes = (result.stdout ?? "").split(/\r?\n/u).map((line) => line.trim().slice(line.trim().indexOf(" ") + 1)).filter(Boolean);
|
|
14156
|
+
return !prefixes.some((prefix) => target.startsWith(prefix));
|
|
14157
|
+
};
|
|
14158
|
+
|
|
14159
|
+
// packages/workit-core/src/forge/redact.ts
|
|
14160
|
+
var KEY = String.raw`[A-Za-z0-9_.-]*?(?:token|secret|password|passwd|pwd|api[_-]?key|access[_-]?key|account[_-]?key|private[_-]?key|client[_-]?secret|credentials?|authorization|cookie|signature)[A-Za-z0-9_.-]*`;
|
|
14161
|
+
var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
|
|
14162
|
+
var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
|
|
14163
|
+
var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
|
|
14164
|
+
|
|
14165
|
+
// packages/workit-core/src/forge/types.ts
|
|
14166
|
+
var success2 = (data) => ({ ok: true, data });
|
|
14167
|
+
var failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
|
|
14168
|
+
|
|
14169
|
+
// packages/workit-core/src/git/ops.ts
|
|
14170
|
+
var COMMIT_TIMEOUT_MS = 10 * 60000;
|
|
14171
|
+
var git5 = (cwd, args, options = {}) => {
|
|
14172
|
+
const result = spawnSync11("git", args, {
|
|
14173
|
+
cwd,
|
|
14174
|
+
encoding: "utf8",
|
|
14175
|
+
env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
|
|
14176
|
+
maxBuffer: 64 * 1024 * 1024,
|
|
14177
|
+
timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
|
|
14178
|
+
killSignal: "SIGKILL",
|
|
14179
|
+
windowsHide: true,
|
|
14180
|
+
...options.input === undefined ? { stdio: ["ignore", "pipe", "pipe"] } : { input: options.input, stdio: ["pipe", "pipe", "pipe"] }
|
|
14181
|
+
});
|
|
14182
|
+
return {
|
|
14183
|
+
ok: result.status === 0,
|
|
14184
|
+
status: result.status,
|
|
14185
|
+
stdout: result.stdout ?? "",
|
|
14186
|
+
stderr: result.stderr ?? ""
|
|
14187
|
+
};
|
|
14188
|
+
};
|
|
14189
|
+
function operationInProgress(cwd) {
|
|
14190
|
+
const markers = [
|
|
14191
|
+
["rebase-merge", "rebase"],
|
|
14192
|
+
["rebase-apply", "rebase"],
|
|
14193
|
+
["MERGE_HEAD", "merge"],
|
|
14194
|
+
["CHERRY_PICK_HEAD", "cherry-pick"],
|
|
14195
|
+
["REVERT_HEAD", "revert"]
|
|
14196
|
+
];
|
|
14197
|
+
const run = git5(cwd, ["rev-parse", ...markers.flatMap(([name]) => ["--git-path", name])]);
|
|
14198
|
+
if (!run.ok)
|
|
14199
|
+
return null;
|
|
14200
|
+
const found = run.stdout.split(`
|
|
14201
|
+
`).map((line) => path17.resolve(cwd, line.trim()));
|
|
14202
|
+
const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
|
|
14203
|
+
for (const [index, [, operation]] of markers.entries()) {
|
|
14204
|
+
const marker = found[index];
|
|
14205
|
+
if (!marker || !existsSync4(marker))
|
|
14206
|
+
continue;
|
|
14207
|
+
if (index === 1 && existsSync4(path17.join(marker, "applying")))
|
|
14208
|
+
return { operation: "am", commitAllowed: false, branch: null };
|
|
14209
|
+
if (index === 0 && existsSync4(path17.join(marker, "amend")) && !conflicted()) {
|
|
14210
|
+
let headName = "";
|
|
14211
|
+
try {
|
|
14212
|
+
headName = readFileSync4(path17.join(marker, "head-name"), "utf8").trim();
|
|
14213
|
+
} catch {}
|
|
14214
|
+
const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
|
|
14215
|
+
return { operation, commitAllowed: branch !== null, branch };
|
|
14216
|
+
}
|
|
14217
|
+
if ((operation === "revert" || operation === "merge") && !conflicted())
|
|
14218
|
+
return { operation, commitAllowed: true, branch: null };
|
|
14219
|
+
return { operation, commitAllowed: false, branch: null };
|
|
14220
|
+
}
|
|
14221
|
+
return null;
|
|
14222
|
+
}
|
|
14223
|
+
function inProgressFailure(operation, verb) {
|
|
14224
|
+
const command = operation === "am" ? "git am" : `git ${operation}`;
|
|
14225
|
+
return failure3("blocked", `${operation.replace("-", "_")}_in_progress: a ${operation === "am" ? "git am" : operation} is in progress; finish or abort it before workit can ${verb}`, `resolve the conflicts, git add <files>, then ${command} --continue # or ${command} --abort to return to where you started`);
|
|
14226
|
+
}
|
|
14227
|
+
function pushPreflight(cwd, options = {}) {
|
|
14228
|
+
const progress = operationInProgress(cwd);
|
|
14229
|
+
if (progress)
|
|
14230
|
+
return inProgressFailure(progress.operation, "push");
|
|
14231
|
+
const branch = options.branch ?? currentBranch(cwd);
|
|
14232
|
+
if (!branch)
|
|
14233
|
+
return failure3("invalid_input", "HEAD is detached; push a branch");
|
|
14234
|
+
if (isProtectedTarget(cwd, branch))
|
|
14235
|
+
return failure3("blocked", `protected_branch: ${branch} is protected by the workspace branch policy; workit git push never pushes to it`, releaseBranch(cwd, branch) ? `workit git push --release ${branch} # finishing a release or hotfix; needs the release grant` : "workit git branch <feature/name> # push a feature branch and open a PR");
|
|
14236
|
+
const sha = resolveRef(cwd, `refs/heads/${branch}`);
|
|
14237
|
+
if (!sha)
|
|
14238
|
+
return failure3("not_found", `branch ${branch} has no commit to push`);
|
|
14239
|
+
const remote = pushRemoteName(cwd, branch);
|
|
14240
|
+
if (!remote)
|
|
14241
|
+
return failure3("not_found", "no push remote is configured for this branch", "git remote add origin <url>");
|
|
14242
|
+
const raw = pushUrl(cwd, remote);
|
|
14243
|
+
if (!raw)
|
|
14244
|
+
return failure3("blocked", `remote "${remote}" has no single push URL`, `git remote get-url --push --all ${remote} # keep exactly one push URL`);
|
|
14245
|
+
const parsed = parseRemoteUrl(raw);
|
|
14246
|
+
const local = parsed?.protocol === "file";
|
|
14247
|
+
if (!local && !pushTargetIsStable(cwd, raw))
|
|
14248
|
+
return failure3("blocked", `push_target_rewritten: the push URL of "${remote}" is rewritten by a url.*.insteadOf rule, so the destination is not the one checked`, "git config --get-regexp '^url\\.' # remove the rewrite for this remote");
|
|
14249
|
+
return success2({
|
|
14250
|
+
branch,
|
|
14251
|
+
sha,
|
|
14252
|
+
remote,
|
|
14253
|
+
url: redactRemote(raw),
|
|
14254
|
+
rawUrl: raw,
|
|
14255
|
+
local
|
|
14256
|
+
});
|
|
14257
|
+
}
|
|
14258
|
+
|
|
14259
|
+
// packages/workit-core/src/core/policy/attributes.ts
|
|
14260
|
+
function patternRegex(dir, pattern) {
|
|
14261
|
+
if (!pattern || pattern.endsWith("/") || pattern.startsWith('"') || pattern.startsWith("!"))
|
|
14262
|
+
return null;
|
|
14263
|
+
const anchored = pattern.includes("/");
|
|
14264
|
+
const body = pattern.replace(/^\//u, "");
|
|
14265
|
+
let source = "";
|
|
14266
|
+
for (let index = 0;index < body.length; index += 1) {
|
|
14267
|
+
const char = body[index];
|
|
14268
|
+
if (char === "*" && body[index + 1] === "*") {
|
|
14269
|
+
const slash = body[index + 2] === "/";
|
|
14270
|
+
source += slash ? "(?:.*/)?" : ".*";
|
|
14271
|
+
index += slash ? 2 : 1;
|
|
14272
|
+
} else if (char === "*")
|
|
14273
|
+
source += "[^/]*";
|
|
14274
|
+
else if (char === "?")
|
|
14275
|
+
source += "[^/]";
|
|
14276
|
+
else if (char === "[") {
|
|
14277
|
+
const end = body.indexOf("]", index + 2);
|
|
14278
|
+
if (end < 0)
|
|
14279
|
+
return null;
|
|
14280
|
+
const set = body.slice(index + 1, end).replace(/^!/u, "^").replace(/\\/gu, "\\\\");
|
|
14281
|
+
source += `[${set}]`;
|
|
14282
|
+
index = end;
|
|
14283
|
+
} else if (char === "\\" && index + 1 < body.length) {
|
|
14284
|
+
source += body[index + 1].replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
14285
|
+
index += 1;
|
|
14286
|
+
} else
|
|
14287
|
+
source += char.replace(/[.*+?^${}()|[\]\\/]/gu, "\\$&");
|
|
14288
|
+
}
|
|
14289
|
+
const prefix = dir ? `${dir.replace(/[.*+?^${}()|[\]\\]/gu, "\\$&")}/` : "";
|
|
14290
|
+
try {
|
|
14291
|
+
return new RegExp(anchored ? `^${prefix}${source}$` : `^${prefix}(?:.*/)?${source}$`, "u");
|
|
14292
|
+
} catch {
|
|
14293
|
+
return null;
|
|
14294
|
+
}
|
|
14295
|
+
}
|
|
14296
|
+
function rulesOf(dir, text) {
|
|
14297
|
+
const out = [];
|
|
14298
|
+
for (const raw of text.split(`
|
|
14299
|
+
`)) {
|
|
14300
|
+
const line = raw.trim();
|
|
14301
|
+
if (!line || line.startsWith("#") || line.startsWith("[attr]"))
|
|
14302
|
+
continue;
|
|
14303
|
+
const [pattern, ...attributes] = line.split(/\s+/u);
|
|
14304
|
+
let generated = null;
|
|
14305
|
+
for (const attribute of attributes) {
|
|
14306
|
+
if (attribute === "linguist-generated" || attribute === "linguist-generated=true")
|
|
14307
|
+
generated = true;
|
|
14308
|
+
else if (attribute === "-linguist-generated" || attribute === "!linguist-generated" || attribute.startsWith("linguist-generated="))
|
|
14309
|
+
generated = false;
|
|
14310
|
+
}
|
|
14311
|
+
if (generated === null)
|
|
14312
|
+
continue;
|
|
14313
|
+
const regex = patternRegex(dir, pattern);
|
|
14314
|
+
if (regex)
|
|
14315
|
+
out.push({ regex, generated });
|
|
14316
|
+
}
|
|
14317
|
+
return out;
|
|
14318
|
+
}
|
|
14319
|
+
function generatedByAttributes(files, paths) {
|
|
14320
|
+
const rules = [...files].map(([file, text]) => {
|
|
14321
|
+
const dir = file.includes("/") ? file.slice(0, file.lastIndexOf("/")) : "";
|
|
14322
|
+
return {
|
|
14323
|
+
depth: dir ? dir.split("/").length : 0,
|
|
14324
|
+
rules: rulesOf(dir, text)
|
|
14325
|
+
};
|
|
14326
|
+
}).toSorted((left, right) => left.depth - right.depth).flatMap((entry) => entry.rules);
|
|
14327
|
+
const out = new Set;
|
|
14328
|
+
for (const file of paths) {
|
|
14329
|
+
const last = rules.findLast((rule) => rule.regex.test(file));
|
|
14330
|
+
if (last?.generated)
|
|
14331
|
+
out.add(file);
|
|
14332
|
+
}
|
|
14333
|
+
return out;
|
|
14334
|
+
}
|
|
14335
|
+
|
|
14336
|
+
// packages/workit-core/src/core/policy/substance.ts
|
|
14337
|
+
var SLASH_BLOCK = [["/*", "*/"]];
|
|
14338
|
+
var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
|
|
14339
|
+
var SYNTAXES = Object.fromEntries([
|
|
14340
|
+
...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
|
|
14341
|
+
...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
|
|
14342
|
+
ext,
|
|
14343
|
+
{ ...C_LIKE, triple: true }
|
|
14344
|
+
]),
|
|
14345
|
+
["go", { ...C_LIKE, rawBacktick: true }],
|
|
14346
|
+
...["css", "scss", "less"].map((ext) => [
|
|
14347
|
+
ext,
|
|
14348
|
+
{ line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
|
|
14349
|
+
]),
|
|
14350
|
+
...["py", "pyi"].map((ext) => [
|
|
14351
|
+
ext,
|
|
14352
|
+
{ line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
|
|
14353
|
+
]),
|
|
14354
|
+
["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
|
|
14355
|
+
...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
|
|
14356
|
+
ext,
|
|
14357
|
+
{ line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
|
|
14358
|
+
])
|
|
14359
|
+
]);
|
|
14360
|
+
var DIRECTIVE = /@ts-|eslint|prettier|biome|oxlint|istanbul|\bc8\b|\bv8\b|@jsx|@flow|@vite|webpack|__PURE__|__NO_SIDE_EFFECTS__|source(?:Mapping)?URL|^\/\s*<|go:|\+build|nolint|noqa|type:|pyright|mypy|pylint|pragma|coding[:=]|fmt:|NOSONAR|@license|@preserve|@refresh|environment|language=|cython|ruff|isort|deno-|@vitest|@jest|swiftlint|ktlint|checkstyle|suppress|nullable|#(?:end)?region/iu;
|
|
14361
|
+
var extensionOf = (file) => {
|
|
14362
|
+
const name = file.slice(file.lastIndexOf("/") + 1);
|
|
14363
|
+
const dot = name.lastIndexOf(".");
|
|
14364
|
+
return dot > 0 ? name.slice(dot + 1).toLowerCase() : "";
|
|
14365
|
+
};
|
|
14366
|
+
var CI_PATH = /^\.github\/|(?:^|\/)(?:\.gitlab-ci\.ya?ml|Jenkinsfile|azure-pipelines\.ya?ml|\.pre-commit-config\.ya?ml|\.circleci\/.*)$/u;
|
|
14367
|
+
var syntaxOf = (file) => CI_PATH.test(file) ? null : SYNTAXES[extensionOf(file)] ?? null;
|
|
14368
|
+
var SPACE = /[ \t\r\f\v]/u;
|
|
14369
|
+
var WORD = /[\w$]/u;
|
|
14370
|
+
var NO_SPACE_AFTER = "([{,;";
|
|
14371
|
+
var NO_SPACE_BEFORE = ")]},;";
|
|
14372
|
+
var REGEX_AFTER = "(,=:[!&|?{};+-*%<>~^";
|
|
14373
|
+
var REGEX_KEYWORDS = new Set([
|
|
14374
|
+
"return",
|
|
14375
|
+
"typeof",
|
|
14376
|
+
"instanceof",
|
|
14377
|
+
"case",
|
|
14378
|
+
"do",
|
|
14379
|
+
"else",
|
|
14380
|
+
"in",
|
|
14381
|
+
"of",
|
|
14382
|
+
"new",
|
|
14383
|
+
"delete",
|
|
14384
|
+
"void",
|
|
14385
|
+
"throw",
|
|
14386
|
+
"yield",
|
|
14387
|
+
"await"
|
|
14388
|
+
]);
|
|
14389
|
+
function substanceOf(file, text) {
|
|
14390
|
+
const syntax = syntaxOf(file);
|
|
14391
|
+
if (!syntax)
|
|
14392
|
+
return plainSubstance(text);
|
|
14393
|
+
const markup = syntax.block.some(([open]) => open === "<!--");
|
|
14394
|
+
if (markup && (/<(?:script|style)\b|<!\[CDATA\[|<\?/iu.test(text) || /[^>\s][ \t]*<!--/u.test(text)))
|
|
14395
|
+
return plainSubstance(text);
|
|
14396
|
+
return lex(syntax, text);
|
|
14397
|
+
}
|
|
14398
|
+
function plainSubstance(text) {
|
|
14399
|
+
return text.split(`
|
|
14400
|
+
`).map((line) => line.replace(/[ \t\r\f\v]+$/u, "")).filter(Boolean).join(`
|
|
14401
|
+
`);
|
|
14402
|
+
}
|
|
14403
|
+
function lex(syntax, text) {
|
|
14404
|
+
const collapse = !syntax.indented;
|
|
14405
|
+
let out = "";
|
|
14406
|
+
let lineStart = 0;
|
|
14407
|
+
let kept = 0;
|
|
14408
|
+
let pendingSpace = false;
|
|
14409
|
+
const frames = [{ kind: "code", braces: 0 }];
|
|
14410
|
+
let index = 0;
|
|
14411
|
+
if (text.startsWith("#!")) {
|
|
14412
|
+
const end = text.indexOf(`
|
|
14413
|
+
`);
|
|
14414
|
+
out = end < 0 ? text : text.slice(0, end);
|
|
14415
|
+
kept = out.length;
|
|
14416
|
+
index = end < 0 ? text.length : end;
|
|
14417
|
+
}
|
|
14418
|
+
const emitCode = (chunk) => {
|
|
14419
|
+
if (collapse && pendingSpace && out.length > lineStart) {
|
|
14420
|
+
const before = out[out.length - 1];
|
|
14421
|
+
if (!NO_SPACE_AFTER.includes(before) && !NO_SPACE_BEFORE.includes(chunk[0]))
|
|
14422
|
+
out += " ";
|
|
14423
|
+
}
|
|
14424
|
+
pendingSpace = false;
|
|
14425
|
+
out += chunk;
|
|
14426
|
+
};
|
|
14427
|
+
const emitVerbatim = (chunk) => {
|
|
14428
|
+
emitCode(chunk);
|
|
14429
|
+
kept = out.length;
|
|
14430
|
+
};
|
|
14431
|
+
const newline = () => {
|
|
14432
|
+
if (!collapse) {
|
|
14433
|
+
let end = out.length;
|
|
14434
|
+
while (end > Math.max(lineStart, kept) && SPACE.test(out[end - 1]))
|
|
14435
|
+
end -= 1;
|
|
14436
|
+
out = out.slice(0, end);
|
|
14437
|
+
}
|
|
14438
|
+
pendingSpace = false;
|
|
14439
|
+
if (out.length > lineStart) {
|
|
14440
|
+
out += `
|
|
14441
|
+
`;
|
|
14442
|
+
lineStart = out.length;
|
|
14443
|
+
}
|
|
14444
|
+
};
|
|
14445
|
+
const regexAllowed = () => {
|
|
14446
|
+
let end = out.length;
|
|
14447
|
+
while (end > 0 && /\s/u.test(out[end - 1]))
|
|
14448
|
+
end -= 1;
|
|
14449
|
+
if (end === 0 || REGEX_AFTER.includes(out[end - 1]))
|
|
14450
|
+
return true;
|
|
14451
|
+
let start = end;
|
|
14452
|
+
while (start > 0 && WORD.test(out[start - 1]))
|
|
14453
|
+
start -= 1;
|
|
14454
|
+
return REGEX_KEYWORDS.has(out.slice(start, end));
|
|
14455
|
+
};
|
|
14456
|
+
const scanString = (quote, escapes, multiline) => {
|
|
14457
|
+
const prefix = text.slice(Math.max(0, index - 2), index).match(/[a-z]*$/iu)?.[0] ?? "";
|
|
14458
|
+
const fstring = syntax.fstring === true && /f/iu.test(prefix);
|
|
14459
|
+
let end = index + quote.length;
|
|
14460
|
+
while (end < text.length) {
|
|
14461
|
+
const char = text[end];
|
|
14462
|
+
if (escapes && char === "\\") {
|
|
14463
|
+
end += 2;
|
|
14464
|
+
continue;
|
|
14465
|
+
}
|
|
14466
|
+
if (text.startsWith(quote, end)) {
|
|
14467
|
+
if (fstring && unbalanced(text.slice(index + quote.length, end)))
|
|
14468
|
+
return false;
|
|
14469
|
+
emitVerbatim(text.slice(index, end + quote.length));
|
|
14470
|
+
index = end + quote.length;
|
|
14471
|
+
return true;
|
|
14472
|
+
}
|
|
14473
|
+
if (char === `
|
|
14474
|
+
` && !multiline)
|
|
14475
|
+
return false;
|
|
14476
|
+
end += 1;
|
|
14477
|
+
}
|
|
14478
|
+
return false;
|
|
14479
|
+
};
|
|
14480
|
+
while (index < text.length) {
|
|
14481
|
+
const frame = frames[frames.length - 1];
|
|
14482
|
+
const char = text[index];
|
|
14483
|
+
if (frame.kind === "template") {
|
|
14484
|
+
if (char === "\\") {
|
|
14485
|
+
emitVerbatim(text.slice(index, index + 2));
|
|
14486
|
+
index += 2;
|
|
14487
|
+
} else if (char === "`") {
|
|
14488
|
+
emitVerbatim(char);
|
|
14489
|
+
frames.pop();
|
|
14490
|
+
index += 1;
|
|
14491
|
+
} else if (text.startsWith("${", index)) {
|
|
14492
|
+
emitVerbatim("${");
|
|
14493
|
+
frames.push({ kind: "code", braces: 0 });
|
|
14494
|
+
index += 2;
|
|
14495
|
+
} else {
|
|
14496
|
+
out += char;
|
|
14497
|
+
kept = out.length;
|
|
14498
|
+
index += 1;
|
|
14499
|
+
}
|
|
14500
|
+
continue;
|
|
14501
|
+
}
|
|
14502
|
+
if (char === `
|
|
14503
|
+
`) {
|
|
14504
|
+
newline();
|
|
14505
|
+
index += 1;
|
|
14506
|
+
continue;
|
|
14507
|
+
}
|
|
14508
|
+
if (SPACE.test(char)) {
|
|
14509
|
+
if (collapse)
|
|
14510
|
+
pendingSpace = true;
|
|
14511
|
+
else
|
|
14512
|
+
out += char;
|
|
14513
|
+
index += 1;
|
|
14514
|
+
continue;
|
|
14515
|
+
}
|
|
14516
|
+
const lineOpener = syntax.line.find((opener) => text.startsWith(opener, index));
|
|
14517
|
+
if (lineOpener) {
|
|
14518
|
+
const end = text.indexOf(`
|
|
14519
|
+
`, index);
|
|
14520
|
+
const comment = text.slice(index, end < 0 ? text.length : end);
|
|
14521
|
+
if (DIRECTIVE.test(comment.slice(lineOpener.length)))
|
|
14522
|
+
emitVerbatim(comment);
|
|
14523
|
+
else
|
|
14524
|
+
pendingSpace = true;
|
|
14525
|
+
index = end < 0 ? text.length : end;
|
|
14526
|
+
continue;
|
|
14527
|
+
}
|
|
14528
|
+
const block = syntax.block.find(([open]) => text.startsWith(open, index));
|
|
14529
|
+
if (block) {
|
|
14530
|
+
const end = text.indexOf(block[1], index + block[0].length);
|
|
14531
|
+
if (end < 0)
|
|
14532
|
+
return null;
|
|
14533
|
+
const comment = text.slice(index, end + block[1].length);
|
|
14534
|
+
if (DIRECTIVE.test(comment.slice(block[0].length, -block[1].length)))
|
|
14535
|
+
emitVerbatim(comment);
|
|
14536
|
+
else
|
|
14537
|
+
pendingSpace = true;
|
|
14538
|
+
if (comment.includes(`
|
|
14539
|
+
`))
|
|
14540
|
+
newline();
|
|
14541
|
+
index = end + block[1].length;
|
|
14542
|
+
continue;
|
|
14543
|
+
}
|
|
14544
|
+
if (syntax.triple && (char === '"' || char === "'") && text.startsWith(char.repeat(3), index)) {
|
|
14545
|
+
if (!scanString(char.repeat(3), !syntax.literal?.includes(char), true))
|
|
14546
|
+
return null;
|
|
14547
|
+
continue;
|
|
14548
|
+
}
|
|
14549
|
+
if (syntax.quotes.includes(char)) {
|
|
14550
|
+
if (!scanString(char, true, Boolean(syntax.multiline)))
|
|
14551
|
+
return null;
|
|
14552
|
+
continue;
|
|
14553
|
+
}
|
|
14554
|
+
if (syntax.literal?.includes(char)) {
|
|
14555
|
+
if (!scanString(char, false, false))
|
|
14556
|
+
return null;
|
|
14557
|
+
continue;
|
|
14558
|
+
}
|
|
14559
|
+
if (syntax.rawBacktick && char === "`") {
|
|
14560
|
+
if (!scanString("`", false, true))
|
|
14561
|
+
return null;
|
|
14562
|
+
continue;
|
|
14563
|
+
}
|
|
14564
|
+
if (syntax.js && char === "`") {
|
|
14565
|
+
emitVerbatim(char);
|
|
14566
|
+
frames.push({ kind: "template" });
|
|
14567
|
+
index += 1;
|
|
14568
|
+
continue;
|
|
14569
|
+
}
|
|
14570
|
+
if (syntax.cssUrl && /^url\(\s*[^"')\s]/iu.test(text.slice(index, index + 64)))
|
|
14571
|
+
return null;
|
|
14572
|
+
if (syntax.js && char === "/" && !regexAllowed() && out.trimEnd().endsWith(")")) {
|
|
14573
|
+
const lineEnd = text.indexOf(`
|
|
14574
|
+
`, index);
|
|
14575
|
+
if (text.slice(index + 1, lineEnd < 0 ? text.length : lineEnd).includes("/"))
|
|
14576
|
+
return null;
|
|
14577
|
+
}
|
|
14578
|
+
if (syntax.js && char === "/" && regexAllowed()) {
|
|
14579
|
+
let end = index + 1;
|
|
14580
|
+
let inClass = false;
|
|
14581
|
+
while (end < text.length && (inClass || text[end] !== "/")) {
|
|
14582
|
+
if (text[end] === `
|
|
14583
|
+
`)
|
|
14584
|
+
return null;
|
|
14585
|
+
if (text[end] === "\\")
|
|
14586
|
+
end += 1;
|
|
14587
|
+
else if (text[end] === "[")
|
|
14588
|
+
inClass = true;
|
|
14589
|
+
else if (text[end] === "]")
|
|
14590
|
+
inClass = false;
|
|
14591
|
+
end += 1;
|
|
14592
|
+
}
|
|
14593
|
+
if (end >= text.length)
|
|
14594
|
+
return null;
|
|
14595
|
+
emitVerbatim(text.slice(index, end + 1));
|
|
14596
|
+
index = end + 1;
|
|
14597
|
+
continue;
|
|
14598
|
+
}
|
|
14599
|
+
if (frames.length > 1 && frame.kind === "code") {
|
|
14600
|
+
if (char === "{")
|
|
14601
|
+
frame.braces += 1;
|
|
14602
|
+
else if (char === "}") {
|
|
14603
|
+
if (frame.braces === 0) {
|
|
14604
|
+
emitVerbatim(char);
|
|
14605
|
+
frames.pop();
|
|
14606
|
+
index += 1;
|
|
14607
|
+
continue;
|
|
14608
|
+
}
|
|
14609
|
+
frame.braces -= 1;
|
|
14610
|
+
}
|
|
14611
|
+
}
|
|
14612
|
+
emitCode(char);
|
|
14613
|
+
index += 1;
|
|
14614
|
+
}
|
|
14615
|
+
if (frames.length !== 1)
|
|
14616
|
+
return null;
|
|
14617
|
+
newline();
|
|
14618
|
+
return out;
|
|
14619
|
+
}
|
|
14620
|
+
function unbalanced(body) {
|
|
14621
|
+
let depth = 0;
|
|
14622
|
+
for (const part of body.replaceAll("{{", "").replaceAll("}}", "")) {
|
|
14623
|
+
if (part === "{")
|
|
14624
|
+
depth += 1;
|
|
14625
|
+
else if (part === "}")
|
|
14626
|
+
depth = Math.max(0, depth - 1);
|
|
14627
|
+
}
|
|
14628
|
+
return depth > 0;
|
|
14629
|
+
}
|
|
14630
|
+
function hasOpaqueLineBreak(text) {
|
|
14631
|
+
return /\r(?!\n)|[\u2028\u2029]/u.test(text);
|
|
14632
|
+
}
|
|
14633
|
+
var decoder = new TextDecoder("utf-8", { fatal: true });
|
|
14634
|
+
function textOf(bytes) {
|
|
14635
|
+
if (bytes.includes(0))
|
|
14636
|
+
return null;
|
|
14637
|
+
try {
|
|
14638
|
+
return decoder.decode(bytes);
|
|
14639
|
+
} catch {
|
|
14640
|
+
return null;
|
|
14641
|
+
}
|
|
14642
|
+
}
|
|
14643
|
+
function cosmeticChange(file, before, after) {
|
|
14644
|
+
const left = textOf(before);
|
|
14645
|
+
const right = textOf(after);
|
|
14646
|
+
if (left === null || right === null)
|
|
14647
|
+
return false;
|
|
14648
|
+
if (hasOpaqueLineBreak(left) || hasOpaqueLineBreak(right))
|
|
14649
|
+
return false;
|
|
14650
|
+
const a = substanceOf(file, left);
|
|
14651
|
+
return a !== null && a === substanceOf(file, right);
|
|
14652
|
+
}
|
|
14653
|
+
|
|
14654
|
+
// packages/workit-core/src/core/policy/lane.ts
|
|
14655
|
+
var LANE_THRESHOLDS = {
|
|
14656
|
+
selfMaxLines: 100,
|
|
14657
|
+
selfMaxFiles: 5,
|
|
14658
|
+
fullMinLines: 500
|
|
14659
|
+
};
|
|
14660
|
+
var RISK_TOKENS = new Set([
|
|
14661
|
+
"auth",
|
|
14662
|
+
"authn",
|
|
14663
|
+
"authz",
|
|
14664
|
+
"oauth",
|
|
14665
|
+
"security",
|
|
14666
|
+
"permission",
|
|
14667
|
+
"permissions",
|
|
14668
|
+
"secret",
|
|
14669
|
+
"secrets",
|
|
14670
|
+
"crypto",
|
|
14671
|
+
"gate",
|
|
14672
|
+
"gates",
|
|
14673
|
+
"policy",
|
|
14674
|
+
"policies",
|
|
14675
|
+
"hook",
|
|
14676
|
+
"hooks",
|
|
14677
|
+
"husky",
|
|
14678
|
+
"lefthook",
|
|
14679
|
+
"release",
|
|
14680
|
+
"releases",
|
|
14681
|
+
"migration",
|
|
14682
|
+
"migrations",
|
|
14683
|
+
"migrate",
|
|
14684
|
+
"workflows"
|
|
14685
|
+
]);
|
|
14686
|
+
var RISK_FILES = new Set([
|
|
14687
|
+
".gitlab-ci.yml",
|
|
14688
|
+
"Jenkinsfile",
|
|
14689
|
+
"azure-pipelines.yml",
|
|
14690
|
+
".pre-commit-config.yaml",
|
|
14691
|
+
".workit/risk-paths"
|
|
14692
|
+
]);
|
|
14693
|
+
var RISK_NAMES = new Set([".gitattributes"]);
|
|
14694
|
+
var RISK_PATHS_FILE = ".workit/risk-paths";
|
|
14695
|
+
var LOCKFILES = new Set([
|
|
14696
|
+
"bun.lock",
|
|
14697
|
+
"bun.lockb",
|
|
14698
|
+
"package-lock.json",
|
|
14699
|
+
"npm-shrinkwrap.json",
|
|
14700
|
+
"yarn.lock",
|
|
14701
|
+
"pnpm-lock.yaml",
|
|
14702
|
+
"Cargo.lock",
|
|
14703
|
+
"go.sum",
|
|
14704
|
+
"poetry.lock",
|
|
14705
|
+
"uv.lock",
|
|
14706
|
+
"Pipfile.lock",
|
|
14707
|
+
"Gemfile.lock",
|
|
14708
|
+
"composer.lock"
|
|
14709
|
+
]);
|
|
14710
|
+
var DOC_EXT = /\.(?:md|mdx|markdown|rst|adoc|txt)$/iu;
|
|
14711
|
+
var DOC_NAMES = /^(?:license|licence|notice|authors|changelog|changes|contributors)$/iu;
|
|
14712
|
+
var INSTRUCTIONS = /(?:^|\/)(?:AGENTS|CLAUDE|GEMINI|SKILL)\.md$|(?:^|\/)(?:skills|agents|commands|rules|prompts)\/[^/]+/iu;
|
|
14713
|
+
var TEST_PATH = /(?:^|\/)(?:test|tests|__tests__|spec|specs|fixtures)\/|\.(?:test|spec)\.[^/]+$/iu;
|
|
14714
|
+
var basename = (file) => file.slice(file.lastIndexOf("/") + 1);
|
|
14715
|
+
var isDoc = (file) => !INSTRUCTIONS.test(file) && (DOC_EXT.test(file) || DOC_NAMES.test(basename(file)));
|
|
14716
|
+
function kindOf(file) {
|
|
14717
|
+
if (file.binary)
|
|
14718
|
+
return "behavior";
|
|
14719
|
+
if (file.generated)
|
|
14720
|
+
return "generated";
|
|
14721
|
+
if (LOCKFILES.has(basename(file.path)))
|
|
14722
|
+
return "lockfile";
|
|
14723
|
+
if (isDoc(file.path))
|
|
14724
|
+
return "doc";
|
|
14725
|
+
if (file.cosmetic)
|
|
14726
|
+
return "cosmetic";
|
|
14727
|
+
return TEST_PATH.test(file.path) ? "test" : "behavior";
|
|
14728
|
+
}
|
|
14729
|
+
var globCache = new Map;
|
|
14730
|
+
function globMatch(pattern, file) {
|
|
14731
|
+
let regex = globCache.get(pattern);
|
|
14732
|
+
if (!regex) {
|
|
14733
|
+
let source = "";
|
|
14734
|
+
for (let index = 0;index < pattern.length; index += 1) {
|
|
14735
|
+
const char = pattern[index];
|
|
14736
|
+
if (char === "*" && pattern[index + 1] === "*") {
|
|
14737
|
+
source += pattern[index + 2] === "/" ? "(?:[^/]*/)*" : ".*";
|
|
14738
|
+
index += pattern[index + 2] === "/" ? 2 : 1;
|
|
14739
|
+
} else if (char === "*")
|
|
14740
|
+
source += "[^/]*";
|
|
14741
|
+
else if (char === "?")
|
|
14742
|
+
source += "[^/]";
|
|
14743
|
+
else
|
|
14744
|
+
source += char.replace(/[.+^${}()|[\]\\]/gu, "\\$&");
|
|
14745
|
+
}
|
|
14746
|
+
regex = new RegExp(`^${source}$`, "u");
|
|
14747
|
+
globCache.set(pattern, regex);
|
|
14748
|
+
}
|
|
14749
|
+
return regex.test(file) || file.startsWith(`${pattern.replace(/\/$/u, "")}/`);
|
|
14750
|
+
}
|
|
14751
|
+
function isRiskPath(file, extra = []) {
|
|
14752
|
+
if (extra.some((pattern) => globMatch(pattern, file)))
|
|
14753
|
+
return true;
|
|
14754
|
+
if (RISK_FILES.has(file) || RISK_NAMES.has(basename(file)))
|
|
14755
|
+
return true;
|
|
14756
|
+
if (TEST_PATH.test(file) || isDoc(file))
|
|
14757
|
+
return false;
|
|
14758
|
+
return file.split("/").map((segment, index, all) => index === all.length - 1 ? segment.replace(/\.[^.]+$/u, "") : segment).some((segment) => segment.toLowerCase().split(/[-_.]/u).some((token) => RISK_TOKENS.has(token)));
|
|
14759
|
+
}
|
|
14760
|
+
var parseRiskPaths = (text) => text.split(`
|
|
14761
|
+
`).map((line) => line.trim()).filter((line) => line && !line.startsWith("#")).map((line) => line.replace(/^\//u, ""));
|
|
14762
|
+
function computeLane(input) {
|
|
14763
|
+
const riskFiles = input.files.filter((file) => isRiskPath(file.path, input.riskPaths)).map((file) => file.path);
|
|
14764
|
+
const risky = new Set(riskFiles);
|
|
14765
|
+
const kinds = input.files.map((file) => ({
|
|
14766
|
+
file,
|
|
14767
|
+
kind: risky.has(file.path) ? "behavior" : kindOf(file)
|
|
14768
|
+
}));
|
|
14769
|
+
const behavior = kinds.filter((entry) => entry.kind === "behavior" || entry.kind === "test" || entry.kind === "lockfile");
|
|
14770
|
+
const lines = behavior.filter((entry) => entry.kind !== "lockfile").reduce((sum, entry) => sum + entry.file.added + entry.file.deleted, 0);
|
|
14771
|
+
const opaque = behavior.filter((entry) => entry.kind === "lockfile" || entry.file.binary);
|
|
14772
|
+
const result = (lane, reasons) => ({
|
|
14773
|
+
lane,
|
|
14774
|
+
reasons,
|
|
14775
|
+
lines,
|
|
14776
|
+
files: behavior.length,
|
|
14777
|
+
riskFiles
|
|
14778
|
+
});
|
|
14779
|
+
const { selfMaxLines, selfMaxFiles, fullMinLines } = LANE_THRESHOLDS;
|
|
14780
|
+
if (input.riskTier === "high")
|
|
14781
|
+
return result("full", ["the task judgment records high risk"]);
|
|
14782
|
+
if (riskFiles.length)
|
|
14783
|
+
return result("full", [
|
|
14784
|
+
`touches a risk path: ${riskFiles.slice(0, 3).join(", ")}${riskFiles.length > 3 ? ", …" : ""}`
|
|
14785
|
+
]);
|
|
14786
|
+
if (!behavior.length)
|
|
14787
|
+
return result("none", [
|
|
14788
|
+
input.files.length ? "only docs, comments, formatting or generated copies changed" : "no changes"
|
|
14789
|
+
]);
|
|
14790
|
+
if (lines >= fullMinLines)
|
|
14791
|
+
return result("full", [`large diff: ${lines} lines (≥ ${fullMinLines})`]);
|
|
14792
|
+
if (lines > selfMaxLines)
|
|
14793
|
+
return result("focused", [`${lines} lines changed (> ${selfMaxLines})`]);
|
|
14794
|
+
if (behavior.length > selfMaxFiles)
|
|
14795
|
+
return result("focused", [`${behavior.length} behavior files changed (> ${selfMaxFiles})`]);
|
|
14796
|
+
if (opaque.length)
|
|
14797
|
+
return result("focused", [
|
|
14798
|
+
`a lockfile or binary changed: ${opaque.slice(0, 3).map((entry) => entry.file.path).join(", ")}${opaque.length > 3 ? ", …" : ""}`
|
|
14799
|
+
]);
|
|
14800
|
+
return result("self", [
|
|
14801
|
+
`small change: ${lines} line${lines === 1 ? "" : "s"} in ${behavior.length} file${behavior.length === 1 ? "" : "s"}, no risk path`
|
|
14802
|
+
]);
|
|
14803
|
+
}
|
|
14804
|
+
function untrackedStat(cwd, file) {
|
|
14805
|
+
try {
|
|
14806
|
+
const bytes = readFileSync5(path18.join(cwd, file));
|
|
14807
|
+
if (bytes.includes(0))
|
|
14808
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
14809
|
+
const text = bytes.toString("utf8");
|
|
14810
|
+
if (hasOpaqueLineBreak(text))
|
|
14811
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
14812
|
+
const lines = text.split(`
|
|
14813
|
+
`).length - (text.endsWith(`
|
|
14814
|
+
`) ? 1 : 0);
|
|
14815
|
+
return { path: file, added: lines, deleted: 0 };
|
|
14816
|
+
} catch {
|
|
14817
|
+
return { path: file, added: 0, deleted: 0, binary: true };
|
|
14818
|
+
}
|
|
14819
|
+
}
|
|
14820
|
+
var REGULAR = new Set(["100644", "100755"]);
|
|
14821
|
+
var ABSENT = /^0+$/u;
|
|
14822
|
+
function cosmeticFiles(cwd, fork, head) {
|
|
14823
|
+
const raw = diffRaw(cwd, fork, head);
|
|
14824
|
+
if (!raw)
|
|
14825
|
+
return null;
|
|
14826
|
+
const candidates = raw.filter((entry) => REGULAR.has(entry.oldMode) && entry.oldMode === entry.newMode && !ABSENT.test(entry.oldBlob) && (head === null || !ABSENT.test(entry.newBlob)));
|
|
14827
|
+
const blobs = readBlobs(cwd, candidates.flatMap((entry) => [entry.oldBlob, entry.newBlob]));
|
|
14828
|
+
if (!blobs)
|
|
14829
|
+
return null;
|
|
14830
|
+
const out = new Set;
|
|
14831
|
+
const opaque = new Set;
|
|
14832
|
+
for (const entry of candidates) {
|
|
14833
|
+
const before = blobs.get(entry.oldBlob);
|
|
14834
|
+
let after = blobs.get(entry.newBlob);
|
|
14835
|
+
if (!after && head === null && ABSENT.test(entry.newBlob))
|
|
14836
|
+
try {
|
|
14837
|
+
after = readFileSync5(path18.join(cwd, entry.path));
|
|
14838
|
+
} catch {
|
|
14839
|
+
after = undefined;
|
|
14840
|
+
}
|
|
14841
|
+
if (before && after && cosmeticChange(entry.path, before, after))
|
|
14842
|
+
out.add(entry.path);
|
|
14843
|
+
else if ([before, after].some((bytes) => bytes && hasOpaqueLineBreak(bytes.toString("utf8"))))
|
|
14844
|
+
opaque.add(entry.path);
|
|
14845
|
+
}
|
|
14846
|
+
return { cosmetic: out, opaque };
|
|
14847
|
+
}
|
|
14848
|
+
function generatedAt(cwd, fork, paths) {
|
|
14849
|
+
const files = treeFilesNamed(cwd, fork, ".gitattributes");
|
|
14850
|
+
const blobs = files?.size ? readBlobs(cwd, [...files.values()]) : null;
|
|
14851
|
+
if (!files || !blobs)
|
|
14852
|
+
return new Set;
|
|
14853
|
+
const texts = new Map;
|
|
14854
|
+
for (const [file, id] of files) {
|
|
14855
|
+
const bytes = blobs.get(id);
|
|
14856
|
+
if (bytes)
|
|
14857
|
+
texts.set(file, bytes.toString("utf8"));
|
|
14858
|
+
}
|
|
14859
|
+
return generatedByAttributes(texts, paths);
|
|
14860
|
+
}
|
|
14861
|
+
var forkOf = (cwd, base, head) => mergeBase(cwd, base, head ?? "HEAD");
|
|
14862
|
+
function laneFiles(cwd, fork, head) {
|
|
14863
|
+
const stats = diffNumstat(cwd, fork, head);
|
|
14864
|
+
const cosmetic = stats ? cosmeticFiles(cwd, fork, head) : null;
|
|
14865
|
+
if (!stats || !cosmetic)
|
|
14866
|
+
return null;
|
|
14867
|
+
const files = [
|
|
14868
|
+
...stats.map((entry) => ({
|
|
14869
|
+
...entry,
|
|
14870
|
+
...cosmetic.opaque.has(entry.path) ? { binary: true } : {},
|
|
14871
|
+
cosmetic: !entry.binary && cosmetic.cosmetic.has(entry.path)
|
|
14872
|
+
})),
|
|
14873
|
+
...head === null ? untrackedFiles(cwd).map((file) => untrackedStat(cwd, file)) : []
|
|
14874
|
+
];
|
|
14875
|
+
const generated = generatedAt(cwd, fork, files.map((entry) => entry.path));
|
|
14876
|
+
return files.map((entry) => ({
|
|
14877
|
+
...entry,
|
|
14878
|
+
generated: generated.has(entry.path)
|
|
14879
|
+
}));
|
|
14880
|
+
}
|
|
14881
|
+
function branchLane(cwd, options) {
|
|
14882
|
+
const candidates = options.base ? [`origin/${options.base}`, options.base] : [];
|
|
14883
|
+
const base = candidates.find((ref) => resolveRef(cwd, ref) !== null) ?? defaultBase(cwd) ?? null;
|
|
14884
|
+
const head = options.workingTree ? null : options.head;
|
|
14885
|
+
const fork = base && (options.workingTree || options.head) ? forkOf(cwd, base, head) : null;
|
|
14886
|
+
const files = fork ? laneFiles(cwd, fork, head) : null;
|
|
14887
|
+
const riskTier = options.judgment?.riskTier ?? null;
|
|
14888
|
+
if (!fork || !files) {
|
|
14889
|
+
const high = riskTier === "high";
|
|
14890
|
+
return {
|
|
14891
|
+
lane: high ? "full" : "focused",
|
|
14892
|
+
reasons: [
|
|
14893
|
+
`the diff of ${options.head?.slice(0, 12) ?? "(no head)"} against ${base ?? "(no base)"} cannot be read`,
|
|
14894
|
+
...high ? ["the task judgment records high risk"] : []
|
|
14895
|
+
],
|
|
14896
|
+
lines: 0,
|
|
14897
|
+
files: 0,
|
|
14898
|
+
riskFiles: [],
|
|
14899
|
+
base,
|
|
14900
|
+
head: options.head
|
|
14901
|
+
};
|
|
14902
|
+
}
|
|
14903
|
+
const repoRisk = parseRiskPaths(fileAt(cwd, fork, RISK_PATHS_FILE) ?? "");
|
|
14904
|
+
return {
|
|
14905
|
+
...computeLane({
|
|
14906
|
+
files,
|
|
14907
|
+
riskTier,
|
|
14908
|
+
riskPaths: [...repoRisk, ...options.riskPaths ?? []]
|
|
14909
|
+
}),
|
|
14910
|
+
base,
|
|
14911
|
+
head: options.head
|
|
14912
|
+
};
|
|
14913
|
+
}
|
|
14914
|
+
var laneNeed = (lane) => lane === "none" ? "none" : lane === "self" ? "self" : "non-author";
|
|
14915
|
+
|
|
14916
|
+
// packages/workit-core/src/core/policy/derive.ts
|
|
14917
|
+
var RULES = {
|
|
14918
|
+
test: "check:test",
|
|
14919
|
+
self: "verdict:self",
|
|
14920
|
+
nonAuthor: "verdict:non-author",
|
|
14921
|
+
verified: "verdict:verified",
|
|
14922
|
+
lane: "verdict:lane",
|
|
14923
|
+
product: "decision:product",
|
|
14924
|
+
plan: "plan"
|
|
14925
|
+
};
|
|
14926
|
+
var derived2 = (spec) => ({
|
|
14927
|
+
...spec,
|
|
14928
|
+
id: requirementId({
|
|
14929
|
+
ruleId: spec.ruleId,
|
|
14930
|
+
scope: spec.scope,
|
|
14931
|
+
satisfaction: spec.satisfaction,
|
|
14932
|
+
before: spec.before,
|
|
14933
|
+
dependentAction: spec.dependentAction
|
|
14934
|
+
})
|
|
14935
|
+
});
|
|
14936
|
+
var sorted = (values) => [...new Set(values)].toSorted();
|
|
14937
|
+
var normalizedScope = (scope) => ({
|
|
14938
|
+
description: scope.description,
|
|
14939
|
+
paths: sorted(scope.paths),
|
|
14940
|
+
exclusions: sorted(scope.exclusions)
|
|
14941
|
+
});
|
|
14942
|
+
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
14943
|
+
const out = [];
|
|
14944
|
+
const base = { scope, dependentAction: null };
|
|
14945
|
+
const proportional = verification === "proportional";
|
|
14946
|
+
if (judgment.behaviorChange)
|
|
14947
|
+
out.push(derived2({
|
|
14948
|
+
...base,
|
|
14949
|
+
ruleId: RULES.test,
|
|
14950
|
+
dimension: "testing",
|
|
14951
|
+
reason: "Behavior changes: the configured test check must pass on the final tree.",
|
|
14952
|
+
satisfaction: "Run `workit check test` on the final tree.",
|
|
14953
|
+
before: "close",
|
|
14954
|
+
acceptanceAllowed: false
|
|
14955
|
+
}));
|
|
14956
|
+
if (proportional && judgment.riskTier === "normal")
|
|
14957
|
+
out.push(derived2({
|
|
14958
|
+
...base,
|
|
14959
|
+
ruleId: RULES.lane,
|
|
14960
|
+
dimension: "review",
|
|
14961
|
+
reason: "The review lane computed from the diff decides the verdict: none needs none, self the author's own (shown as self-reviewed), focused and full a non-author one.",
|
|
14962
|
+
satisfaction: "Read the lane in `workit policy assess` or `workit pr status`; record what it asks: `workit ledger verdict tests-verified --self --how <what you ran>` for self, or hand focused and full to a verifier.",
|
|
14963
|
+
before: "close",
|
|
14964
|
+
acceptanceAllowed: false
|
|
14965
|
+
}));
|
|
14966
|
+
if (!proportional && judgment.behaviorChange && judgment.riskTier === "normal")
|
|
14967
|
+
out.push(verification === "independent" ? derived2({
|
|
14968
|
+
...base,
|
|
14969
|
+
ruleId: RULES.nonAuthor,
|
|
14970
|
+
dimension: "review",
|
|
14971
|
+
reason: "This workspace requires independent verification: a session that did not author the change records the verdict.",
|
|
14972
|
+
satisfaction: "An independent session records a passing verdict: `workit ledger verdict verified --how <what it exercised>`.",
|
|
14973
|
+
before: "close",
|
|
14974
|
+
acceptanceAllowed: false
|
|
14975
|
+
}) : derived2({
|
|
14976
|
+
...base,
|
|
14977
|
+
ruleId: RULES.self,
|
|
14978
|
+
dimension: "review",
|
|
14979
|
+
reason: "A behavior change needs a recorded verdict; the author's own is allowed and is shown as self-reviewed, never verified.",
|
|
14980
|
+
satisfaction: "After an observed passing `workit check test`, record `workit ledger verdict tests-verified --self --how <what you ran>` (or have a verifier record one).",
|
|
14981
|
+
before: "close",
|
|
14982
|
+
acceptanceAllowed: false
|
|
14983
|
+
}));
|
|
14984
|
+
if (judgment.riskTier === "high")
|
|
14985
|
+
out.push(derived2({
|
|
14986
|
+
...base,
|
|
14987
|
+
ruleId: RULES.verified,
|
|
14988
|
+
dimension: "review",
|
|
14989
|
+
reason: "High risk: an independent verifier must exercise the live surface.",
|
|
14990
|
+
satisfaction: "An independent verifier runs the real surface and records `workit ledger verdict verified --kind live --how <what it exercised>`.",
|
|
14991
|
+
before: "close",
|
|
14992
|
+
acceptanceAllowed: false
|
|
14993
|
+
}));
|
|
14994
|
+
if (judgment.productChoiceOpen)
|
|
14995
|
+
out.push(derived2({
|
|
14996
|
+
...base,
|
|
14997
|
+
ruleId: RULES.product,
|
|
14998
|
+
dimension: "decisions",
|
|
14999
|
+
reason: "A product or preference choice is open; the user decides it before code is written.",
|
|
15000
|
+
satisfaction: 'Ask the user (with your recommendation), then record the answer: `workit ledger decision "<choice>" --why "<reason>"`.',
|
|
15001
|
+
before: "write",
|
|
15002
|
+
acceptanceAllowed: false
|
|
15003
|
+
}));
|
|
15004
|
+
if (judgment.needsPlan || judgment.riskTier === "high")
|
|
15005
|
+
out.push(derived2({
|
|
15006
|
+
...base,
|
|
15007
|
+
ruleId: RULES.plan,
|
|
15008
|
+
dimension: "artifacts",
|
|
15009
|
+
reason: judgment.needsPlan ? "The work needs a written plan or spec before implementation." : "High-risk work needs a written plan before implementation.",
|
|
15010
|
+
satisfaction: "Write the plan or spec (e.g. docs/plans/<slug>.md; docs and Markdown writes stay allowed), then record it: `workit policy assess --ref <path>`.",
|
|
15011
|
+
before: "write",
|
|
15012
|
+
acceptanceAllowed: true
|
|
15013
|
+
}));
|
|
15014
|
+
for (const constraint of constraints)
|
|
15015
|
+
for (const obligation of constraint.requires)
|
|
15016
|
+
out.push(derived2({
|
|
15017
|
+
ruleId: `project-constraint:${constraint.id}:${obligation.kind}:${sha256({
|
|
15018
|
+
kind: obligation.kind,
|
|
15019
|
+
scope: normalizedScope(obligation.scope),
|
|
15020
|
+
refs: obligation.refs.map((ref) => JSON.stringify(ref)).toSorted(),
|
|
15021
|
+
method: obligation.method,
|
|
15022
|
+
before: obligation.before,
|
|
15023
|
+
dependentAction: obligation.dependentAction
|
|
15024
|
+
})}`,
|
|
15025
|
+
dimension: obligation.method ?? (obligation.kind === "check" ? "verification" : obligation.kind === "decision" ? "decisions" : "challenge"),
|
|
15026
|
+
scope: obligation.scope,
|
|
15027
|
+
reason: `${constraint.kind} constraint requires ${obligation.kind}: ${constraint.statement}`,
|
|
15028
|
+
satisfaction: `Satisfy the constraint obligation using its referenced ${obligation.kind}.`,
|
|
15029
|
+
before: obligation.before,
|
|
15030
|
+
dependentAction: obligation.dependentAction,
|
|
15031
|
+
acceptanceAllowed: constraint.acceptanceAllowed && obligation.kind !== "decision"
|
|
15032
|
+
}));
|
|
15033
|
+
const seen = new Set;
|
|
15034
|
+
return out.filter((item) => seen.has(item.id) ? false : (seen.add(item.id), true));
|
|
15035
|
+
}
|
|
15036
|
+
var latestJudgment = (task) => task.judgments?.at(-1)?.data ?? null;
|
|
15037
|
+
var ledgerView = (root) => {
|
|
15038
|
+
let branch;
|
|
15039
|
+
let rows;
|
|
15040
|
+
return {
|
|
15041
|
+
branch: () => branch === undefined ? branch = currentBranch(root) : branch,
|
|
15042
|
+
rows: () => {
|
|
15043
|
+
if (rows === undefined) {
|
|
15044
|
+
const read = readLedger(root);
|
|
15045
|
+
rows = read.ok ? read.value.rows : [];
|
|
15046
|
+
}
|
|
15047
|
+
return rows;
|
|
15048
|
+
}
|
|
15049
|
+
};
|
|
15050
|
+
};
|
|
15051
|
+
var productDecision = (task, ledger) => {
|
|
15052
|
+
const decisions = task.decisions.filter((entry) => entry.data.revoked === null && (entry.data.response === "approved" || entry.data.response === "stated") && entry.data.purpose !== "limitation");
|
|
15053
|
+
if (decisions.length)
|
|
15054
|
+
return {
|
|
15055
|
+
met: true,
|
|
15056
|
+
reason: "the user's decision is recorded on the task",
|
|
15057
|
+
decisionIds: decisions.map((entry) => entry.id)
|
|
15058
|
+
};
|
|
15059
|
+
const since = Date.parse(task.createdAt);
|
|
15060
|
+
const branch = ledger.branch();
|
|
15061
|
+
const row = ledger.rows().findLast((item) => item.type === "decision" && item.by !== "decider" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
15062
|
+
return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
|
|
15063
|
+
met: false,
|
|
15064
|
+
reason: 'open product choice: ask the user, then `workit ledger decision "<choice>" --why "<reason>"` (or, if it is not open, `workit policy assess --judge product-choice=no --why "<reason>"`)',
|
|
15065
|
+
decisionIds: []
|
|
15066
|
+
};
|
|
15067
|
+
};
|
|
15068
|
+
var planRecorded = (task, root) => {
|
|
15069
|
+
const cited = (latestJudgment(task)?.refs ?? []).find((ref) => ref.kind === "external" || ref.kind === "file" && existsSync6(path19.resolve(root, ref.path)));
|
|
15070
|
+
if (cited)
|
|
15071
|
+
return {
|
|
15072
|
+
met: true,
|
|
14104
15073
|
reason: `plan recorded: ${cited.kind === "file" ? cited.path : cited.kind === "external" ? cited.url : ""}`,
|
|
14105
15074
|
decisionIds: []
|
|
14106
15075
|
};
|
|
@@ -14111,14 +15080,30 @@ var planRecorded = (task, root) => {
|
|
|
14111
15080
|
decisionIds: []
|
|
14112
15081
|
};
|
|
14113
15082
|
};
|
|
14114
|
-
var
|
|
15083
|
+
var workingLane = (task, root) => branchLane(root, { head: null, workingTree: true, judgment: latestJudgment(task) });
|
|
15084
|
+
var laneVerdict = (task, root, ledger) => {
|
|
15085
|
+
if (!ledger.branch() && latestJudgment(task)?.behaviorChange === false)
|
|
15086
|
+
return {
|
|
15087
|
+
met: true,
|
|
15088
|
+
reason: "no branch to read a lane from, and the judgment records no behavior change",
|
|
15089
|
+
decisionIds: []
|
|
15090
|
+
};
|
|
15091
|
+
const lane = workingLane(task, root);
|
|
15092
|
+
const label = `lane ${lane.lane} (${lane.reasons.join("; ")})`;
|
|
15093
|
+
const need = laneNeed(lane.lane);
|
|
15094
|
+
if (need === "none")
|
|
15095
|
+
return { met: true, reason: `${label}: no verdict needed`, decisionIds: [] };
|
|
15096
|
+
const status = verdict(need === "self" ? RULES.self : RULES.nonAuthor, root, ledger);
|
|
15097
|
+
return { ...status, reason: `${label}: ${status.reason}` };
|
|
15098
|
+
};
|
|
15099
|
+
var verdict = (ruleId, root, ledger) => {
|
|
14115
15100
|
const branch = ledger.branch();
|
|
14116
15101
|
if (!branch)
|
|
14117
15102
|
return { met: false, reason: "no branch is checked out to hold a verdict", decisionIds: [] };
|
|
14118
15103
|
const check = checkVerdicts(root, branch, ledger.rows());
|
|
14119
15104
|
const failing = check.accepted.reasons.includes("failing_verdict");
|
|
14120
15105
|
const strong = (entry) => STRONG_RESULTS.has(String(entry.verdict.result));
|
|
14121
|
-
const met = !failing && (
|
|
15106
|
+
const met = !failing && (ruleId === RULES.verified ? check.verdicts.find((entry) => entry.accepted && entry.kind === "live" && String(entry.verdict.result) === "verified") : ruleId === RULES.nonAuthor ? check.verdicts.find((entry) => entry.accepted && strong(entry)) : check.verdicts.find((entry) => entry.current && strong(entry)));
|
|
14122
15107
|
if (met)
|
|
14123
15108
|
return {
|
|
14124
15109
|
met: true,
|
|
@@ -14126,7 +15111,7 @@ var verdict = (requirement, root, ledger) => {
|
|
|
14126
15111
|
decisionIds: []
|
|
14127
15112
|
};
|
|
14128
15113
|
const why = failing ? "a current independent verdict fails" : check.verdicts.some((entry) => entry.current && entry.verdict.result === "type-check-only") ? "type-check-only does not prove a behavior change" : (check.accepted.reasons.join(", ") || "no_verdict").replace(/_/g, " ");
|
|
14129
|
-
const unblock =
|
|
15114
|
+
const unblock = ruleId === RULES.self ? "record your own verdict after an observed `workit check test`: `workit ledger verdict tests-verified --self --how <what you ran>` (shown as self-reviewed), or have a verifier record one" : `an independent session records \`workit ledger verdict verified${ruleId === RULES.verified ? " --kind live" : ""} --how <what it exercised>\``;
|
|
14130
15115
|
return { met: false, reason: `${why}: ${unblock}`, decisionIds: [] };
|
|
14131
15116
|
};
|
|
14132
15117
|
function derivedRuleStatus(task, requirement, root, ledger) {
|
|
@@ -14138,7 +15123,9 @@ function derivedRuleStatus(task, requirement, root, ledger) {
|
|
|
14138
15123
|
case RULES.self:
|
|
14139
15124
|
case RULES.nonAuthor:
|
|
14140
15125
|
case RULES.verified:
|
|
14141
|
-
return verdict(requirement, root, ledger);
|
|
15126
|
+
return verdict(requirement.ruleId, root, ledger);
|
|
15127
|
+
case RULES.lane:
|
|
15128
|
+
return laneVerdict(task, root, ledger);
|
|
14142
15129
|
default:
|
|
14143
15130
|
return null;
|
|
14144
15131
|
}
|
|
@@ -14157,14 +15144,14 @@ function writeBlockers(task, root) {
|
|
|
14157
15144
|
|
|
14158
15145
|
// packages/workit-core/src/core/task-evaluation.ts
|
|
14159
15146
|
var digestBytes = (value) => createHash12("sha256").update(value).digest("hex");
|
|
14160
|
-
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${
|
|
14161
|
-
var relative = (root, candidate) =>
|
|
15147
|
+
var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path20.sep}`);
|
|
15148
|
+
var relative = (root, candidate) => path20.relative(root, candidate).split(path20.sep).join("/") || ".";
|
|
14162
15149
|
var canonicalPath = (value) => {
|
|
14163
15150
|
if (typeof value !== "string" || !value)
|
|
14164
15151
|
return null;
|
|
14165
15152
|
if (value.includes("\\") || value.split("/").includes(".."))
|
|
14166
15153
|
return null;
|
|
14167
|
-
const normalized =
|
|
15154
|
+
const normalized = path20.posix.normalize(value);
|
|
14168
15155
|
if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
|
|
14169
15156
|
return null;
|
|
14170
15157
|
return normalized === "." ? "." : normalized.replace(/\/$/, "");
|
|
@@ -14200,7 +15187,7 @@ var compareCodeUnits2 = (left, right) => {
|
|
|
14200
15187
|
return left.length - right.length;
|
|
14201
15188
|
};
|
|
14202
15189
|
var gitPaths = (root) => {
|
|
14203
|
-
const result =
|
|
15190
|
+
const result = spawnSync12("git", ["ls-files", "-z", "--cached", "--others", "--exclude-standard"], {
|
|
14204
15191
|
cwd: root,
|
|
14205
15192
|
encoding: "buffer"
|
|
14206
15193
|
});
|
|
@@ -14212,21 +15199,21 @@ var gitPaths = (root) => {
|
|
|
14212
15199
|
git: !/not a git repository/i.test(stderr)
|
|
14213
15200
|
};
|
|
14214
15201
|
}
|
|
14215
|
-
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
14216
|
-
const stagedDeleted =
|
|
15202
|
+
const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path20.sep).join("/")) : [];
|
|
15203
|
+
const stagedDeleted = spawnSync12("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
|
|
14217
15204
|
if (stagedDeleted.status !== 0) {
|
|
14218
15205
|
const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
|
|
14219
15206
|
if (!/not a git repository/i.test(stderr))
|
|
14220
15207
|
return { paths, uncertain: true, git: true };
|
|
14221
15208
|
} else if (stagedDeleted.stdout) {
|
|
14222
|
-
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(
|
|
15209
|
+
paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path20.sep).join("/")));
|
|
14223
15210
|
}
|
|
14224
15211
|
return { paths: [...new Set(paths)], uncertain: false, git: true };
|
|
14225
15212
|
};
|
|
14226
15213
|
var walk = (root, directory, output) => {
|
|
14227
15214
|
let names;
|
|
14228
15215
|
try {
|
|
14229
|
-
names =
|
|
15216
|
+
names = fs15.readdirSync(directory);
|
|
14230
15217
|
} catch {
|
|
14231
15218
|
return true;
|
|
14232
15219
|
}
|
|
@@ -14234,10 +15221,10 @@ var walk = (root, directory, output) => {
|
|
|
14234
15221
|
for (const name of names) {
|
|
14235
15222
|
if (name === ".git" || name === ".workit")
|
|
14236
15223
|
continue;
|
|
14237
|
-
const target =
|
|
15224
|
+
const target = path20.join(directory, name);
|
|
14238
15225
|
let stat;
|
|
14239
15226
|
try {
|
|
14240
|
-
stat =
|
|
15227
|
+
stat = fs15.lstatSync(target);
|
|
14241
15228
|
} catch {
|
|
14242
15229
|
uncertain = true;
|
|
14243
15230
|
continue;
|
|
@@ -14252,14 +15239,14 @@ var walk = (root, directory, output) => {
|
|
|
14252
15239
|
var scopeRoots = (root, scope) => {
|
|
14253
15240
|
const roots = [];
|
|
14254
15241
|
for (const item of scope.paths.length ? scope.paths : ["."]) {
|
|
14255
|
-
const target =
|
|
15242
|
+
const target = path20.resolve(root, item);
|
|
14256
15243
|
if (!inside(root, target))
|
|
14257
15244
|
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` }] });
|
|
14258
15245
|
let ancestor = target;
|
|
14259
|
-
while (!
|
|
14260
|
-
ancestor =
|
|
15246
|
+
while (!fs15.existsSync(ancestor) && ancestor !== root)
|
|
15247
|
+
ancestor = path20.dirname(ancestor);
|
|
14261
15248
|
try {
|
|
14262
|
-
if (!inside(root,
|
|
15249
|
+
if (!inside(root, fs15.realpathSync(ancestor)))
|
|
14263
15250
|
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` }] });
|
|
14264
15251
|
} catch {
|
|
14265
15252
|
return failure2("invalid_input", `candidate scope cannot be inspected: ${item}`);
|
|
@@ -14271,7 +15258,7 @@ var scopeRoots = (root, scope) => {
|
|
|
14271
15258
|
function captureCandidate(root, scope, environment = []) {
|
|
14272
15259
|
let checkout;
|
|
14273
15260
|
try {
|
|
14274
|
-
checkout =
|
|
15261
|
+
checkout = fs15.realpathSync(root);
|
|
14275
15262
|
} catch {
|
|
14276
15263
|
return failure2("invalid_input", "candidate root does not exist");
|
|
14277
15264
|
}
|
|
@@ -14289,11 +15276,11 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14289
15276
|
if (item === ".git" || item === ".workit")
|
|
14290
15277
|
continue;
|
|
14291
15278
|
names.add(item);
|
|
14292
|
-
if (!
|
|
15279
|
+
if (!fs15.existsSync(target))
|
|
14293
15280
|
continue;
|
|
14294
15281
|
let stat;
|
|
14295
15282
|
try {
|
|
14296
|
-
stat =
|
|
15283
|
+
stat = fs15.lstatSync(target);
|
|
14297
15284
|
} catch {
|
|
14298
15285
|
uncertain = true;
|
|
14299
15286
|
continue;
|
|
@@ -14309,17 +15296,17 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14309
15296
|
for (const item of [...names].toSorted()) {
|
|
14310
15297
|
if (!scopeMatches(item, normalizedScope))
|
|
14311
15298
|
continue;
|
|
14312
|
-
const target =
|
|
15299
|
+
const target = path20.join(checkout, item);
|
|
14313
15300
|
try {
|
|
14314
|
-
const stat =
|
|
15301
|
+
const stat = fs15.lstatSync(target);
|
|
14315
15302
|
if (stat.isSymbolicLink()) {
|
|
14316
|
-
const link =
|
|
15303
|
+
const link = fs15.readlinkSync(target);
|
|
14317
15304
|
files.push({ path: item, kind: "symlink", digest: digestBytes(link), executable: null });
|
|
14318
15305
|
} else if (stat.isFile()) {
|
|
14319
15306
|
files.push({
|
|
14320
15307
|
path: item,
|
|
14321
15308
|
kind: "file",
|
|
14322
|
-
digest: digestBytes(
|
|
15309
|
+
digest: digestBytes(fs15.readFileSync(target)),
|
|
14323
15310
|
executable: (stat.mode & 73) !== 0
|
|
14324
15311
|
});
|
|
14325
15312
|
} else if (!stat.isDirectory()) {
|
|
@@ -14344,7 +15331,7 @@ function captureCandidate(root, scope, environment = []) {
|
|
|
14344
15331
|
const values = supplied.toSorted((left, right) => compareCodeUnits2(left.name, right.name)).map(({ name, value }) => ({ name, value, refs: [] }));
|
|
14345
15332
|
if (values.some(({ value }) => value === null))
|
|
14346
15333
|
completeness = "uncertain";
|
|
14347
|
-
const headResult =
|
|
15334
|
+
const headResult = spawnSync12("git", ["rev-parse", "HEAD"], { cwd: checkout, encoding: "utf8" });
|
|
14348
15335
|
if (headResult.status !== 0 && !/not a git repository/i.test(headResult.stderr ?? ""))
|
|
14349
15336
|
completeness = "uncertain";
|
|
14350
15337
|
const head = headResult.status === 0 ? headResult.stdout.trim() : null;
|
|
@@ -14484,7 +15471,7 @@ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.statu
|
|
|
14484
15471
|
var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
14485
15472
|
let root;
|
|
14486
15473
|
try {
|
|
14487
|
-
root =
|
|
15474
|
+
root = fs15.realpathSync(checkoutRoot);
|
|
14488
15475
|
} catch {
|
|
14489
15476
|
return failure2("permission_denied", "checkout root is unavailable");
|
|
14490
15477
|
}
|
|
@@ -14493,23 +15480,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
14493
15480
|
continue;
|
|
14494
15481
|
if (!reference.digest)
|
|
14495
15482
|
return failure2("invalid_input", "document references require a byte digest");
|
|
14496
|
-
const target =
|
|
14497
|
-
if (target !== root && !target.startsWith(`${root}${
|
|
15483
|
+
const target = path20.resolve(root, reference.path);
|
|
15484
|
+
if (target !== root && !target.startsWith(`${root}${path20.sep}`))
|
|
14498
15485
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14499
15486
|
fields: [
|
|
14500
15487
|
{ path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
|
|
14501
15488
|
]
|
|
14502
15489
|
});
|
|
14503
15490
|
try {
|
|
14504
|
-
const below =
|
|
15491
|
+
const below = path20.relative(root, target);
|
|
14505
15492
|
let current = root;
|
|
14506
|
-
for (const segment of below.split(
|
|
15493
|
+
for (const segment of below.split(path20.sep)) {
|
|
14507
15494
|
if (!segment)
|
|
14508
15495
|
continue;
|
|
14509
|
-
current =
|
|
14510
|
-
const stat =
|
|
14511
|
-
const real =
|
|
14512
|
-
if (real !== root && !real.startsWith(`${root}${
|
|
15496
|
+
current = path20.join(current, segment);
|
|
15497
|
+
const stat = fs15.lstatSync(current);
|
|
15498
|
+
const real = fs15.realpathSync(current);
|
|
15499
|
+
if (real !== root && !real.startsWith(`${root}${path20.sep}`))
|
|
14513
15500
|
return failure2("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
|
|
14514
15501
|
fields: [
|
|
14515
15502
|
{
|
|
@@ -14525,7 +15512,7 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
|
|
|
14525
15512
|
if (current !== target && !stat.isDirectory())
|
|
14526
15513
|
return failure2("invalid_input", "document reference ancestor is not a directory");
|
|
14527
15514
|
}
|
|
14528
|
-
if (digestBytes(
|
|
15515
|
+
if (digestBytes(fs15.readFileSync(target)) !== reference.digest)
|
|
14529
15516
|
return failure2("permission_denied", "decision document bytes have changed");
|
|
14530
15517
|
} catch {
|
|
14531
15518
|
return failure2("invalid_input", "decision document is unavailable");
|
|
@@ -14942,9 +15929,9 @@ Autonomy contract:
|
|
|
14942
15929
|
Prose is a note: only a configured check the CLI ran (\`workit check <name>\`)
|
|
14943
15930
|
satisfies a gate; an ad-hoc \`workit check -- <cmd>\` never does.
|
|
14944
15931
|
- Author is not verifier: your own verdict is \`--self\`, shown as
|
|
14945
|
-
self-reviewed, never verified. High risk,
|
|
14946
|
-
merge need a verifier with its
|
|
14947
|
-
\`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
15932
|
+
self-reviewed, never verified. High risk, a focused or full lane and
|
|
15933
|
+
merge under \`verification: self|independent\` need a verifier with its
|
|
15934
|
+
own host-derived session (a subagent's \`<lead>:<child>\`); never hand-mint one (\`<lead>-v1\`).
|
|
14948
15935
|
- A local-commit endpoint does not imply PR readiness, and a local commit alone
|
|
14949
15936
|
is not evidence of a requested remote push. Before reporting delivery,
|
|
14950
15937
|
reconcile every requested item and observe it (\`workit verify-delivery\`).
|
|
@@ -15106,21 +16093,21 @@ var exportDigest = (bundle) => sha256({
|
|
|
15106
16093
|
task: bundle.task
|
|
15107
16094
|
});
|
|
15108
16095
|
// packages/workit-core/src/core/route-intent.ts
|
|
15109
|
-
import { spawnSync as
|
|
15110
|
-
import { existsSync as
|
|
15111
|
-
import
|
|
16096
|
+
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
16097
|
+
import { existsSync as existsSync8 } from "node:fs";
|
|
16098
|
+
import path23 from "node:path";
|
|
15112
16099
|
|
|
15113
16100
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
15114
|
-
import { spawnSync as
|
|
16101
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
15115
16102
|
import { createHash as createHash14 } from "node:crypto";
|
|
15116
|
-
import
|
|
16103
|
+
import fs17 from "node:fs";
|
|
15117
16104
|
import os4 from "node:os";
|
|
15118
|
-
import
|
|
16105
|
+
import path22 from "node:path";
|
|
15119
16106
|
|
|
15120
16107
|
// packages/workit-core/src/hooks/session-marker.ts
|
|
15121
16108
|
import { createHash as createHash13 } from "node:crypto";
|
|
15122
|
-
import
|
|
15123
|
-
import
|
|
16109
|
+
import fs16 from "node:fs";
|
|
16110
|
+
import path21 from "node:path";
|
|
15124
16111
|
var sessionMarkerFile = (input, session, store) => {
|
|
15125
16112
|
let dir = store;
|
|
15126
16113
|
if (dir === undefined) {
|
|
@@ -15130,13 +16117,13 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
15130
16117
|
dir = location.dir;
|
|
15131
16118
|
}
|
|
15132
16119
|
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
15133
|
-
return
|
|
16120
|
+
return path21.join(dir, "hooks", `skills-${name}.json`);
|
|
15134
16121
|
};
|
|
15135
16122
|
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
15136
16123
|
var EMPTY = { nudged: [], loaded: [], raw: [], stops: [], judges: [] };
|
|
15137
16124
|
var readRaw = (file) => {
|
|
15138
16125
|
try {
|
|
15139
|
-
return
|
|
16126
|
+
return fs16.readFileSync(file, "utf8");
|
|
15140
16127
|
} catch {
|
|
15141
16128
|
return "";
|
|
15142
16129
|
}
|
|
@@ -15157,13 +16144,13 @@ var parseMarker = (text) => {
|
|
|
15157
16144
|
};
|
|
15158
16145
|
var readSessionMarker = (file) => parseMarker(readRaw(file));
|
|
15159
16146
|
var writeAtomic = (file, marker) => {
|
|
15160
|
-
|
|
16147
|
+
fs16.mkdirSync(path21.dirname(file), { recursive: true });
|
|
15161
16148
|
const temp = `${file}.${process.pid}.${Math.random().toString(36).slice(2)}.tmp`;
|
|
15162
16149
|
try {
|
|
15163
|
-
|
|
15164
|
-
|
|
16150
|
+
fs16.writeFileSync(temp, JSON.stringify(marker));
|
|
16151
|
+
fs16.renameSync(temp, file);
|
|
15165
16152
|
} catch (error) {
|
|
15166
|
-
|
|
16153
|
+
fs16.rmSync(temp, { force: true });
|
|
15167
16154
|
throw error;
|
|
15168
16155
|
}
|
|
15169
16156
|
};
|
|
@@ -15675,17 +16662,17 @@ var GIT_VALUE_OPTIONS = new Set([
|
|
|
15675
16662
|
]);
|
|
15676
16663
|
var GIT_CONFIG_OPTIONS = new Set(["-c", "--config-env", "--git-dir"]);
|
|
15677
16664
|
var GIT_CONFIG_ENV = /^GIT_(?:CONFIG\w*|DIR|COMMON_DIR|WORK_TREE)$/;
|
|
15678
|
-
var
|
|
16665
|
+
var ASSIGNMENT2 = /^([A-Za-z_]\w*)=([\s\S]*)$/;
|
|
15679
16666
|
var MAX_DEPTH = 4;
|
|
15680
16667
|
var MAX_EXPANSIONS = 16;
|
|
15681
|
-
var isAbsolute = (value) =>
|
|
16668
|
+
var isAbsolute = (value) => path22.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
15682
16669
|
var HERE = /^(?:\$PWD|\$\{PWD\}|\$\(\s*pwd\s*\)|`pwd`|\$\(\s*git\s+rev-parse\s+--show-toplevel\s*\))(?=$|[\\/])/;
|
|
15683
16670
|
var joinDir = (base, raw) => {
|
|
15684
16671
|
const next = raw.replace(HERE, ".");
|
|
15685
16672
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
15686
16673
|
return;
|
|
15687
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
15688
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
16674
|
+
const expanded = next === "~" || next.startsWith("~/") ? path22.join(os4.homedir(), next.slice(1)) : next;
|
|
16675
|
+
return base === null || isAbsolute(expanded) ? expanded : path22.join(base, expanded);
|
|
15689
16676
|
};
|
|
15690
16677
|
var rebase = (base, inner) => inner === null ? base : inner === undefined ? undefined : joinDir(base, inner);
|
|
15691
16678
|
var cdTarget = (words) => {
|
|
@@ -15702,7 +16689,7 @@ var stripPrefixes = (input) => {
|
|
|
15702
16689
|
const first = words[0];
|
|
15703
16690
|
if (first === undefined)
|
|
15704
16691
|
return { words, env };
|
|
15705
|
-
const assignment =
|
|
16692
|
+
const assignment = ASSIGNMENT2.exec(first);
|
|
15706
16693
|
if (assignment) {
|
|
15707
16694
|
if (GIT_CONFIG_ENV.test(assignment[1]))
|
|
15708
16695
|
env[assignment[1]] = assignment[2];
|
|
@@ -15761,10 +16748,10 @@ var assignments = (words, vars) => {
|
|
|
15761
16748
|
while (rest[0]?.startsWith("-"))
|
|
15762
16749
|
rest = rest.slice(1);
|
|
15763
16750
|
}
|
|
15764
|
-
if (rest.length === 0 || !rest.every((word) =>
|
|
16751
|
+
if (rest.length === 0 || !rest.every((word) => ASSIGNMENT2.test(word)))
|
|
15765
16752
|
return null;
|
|
15766
16753
|
return rest.map((word) => {
|
|
15767
|
-
const [, name, value] =
|
|
16754
|
+
const [, name, value] = ASSIGNMENT2.exec(word);
|
|
15768
16755
|
return [name, expandWord(value, vars)];
|
|
15769
16756
|
});
|
|
15770
16757
|
};
|
|
@@ -15952,7 +16939,7 @@ var splitString = (line) => {
|
|
|
15952
16939
|
return line.slice(index).join(" ").slice(15);
|
|
15953
16940
|
if (/^-S./.test(word))
|
|
15954
16941
|
return [word.slice(2), ...line.slice(index + 1)].join(" ");
|
|
15955
|
-
if (!word.startsWith("-") && !
|
|
16942
|
+
if (!word.startsWith("-") && !ASSIGNMENT2.test(word))
|
|
15956
16943
|
return null;
|
|
15957
16944
|
}
|
|
15958
16945
|
return null;
|
|
@@ -16219,6 +17206,24 @@ function workitInvocations(command, dialect = "posix", depth = 0) {
|
|
|
16219
17206
|
}
|
|
16220
17207
|
return found;
|
|
16221
17208
|
}
|
|
17209
|
+
var WORKIT_GLOBAL = /^--(?:cwd|session|token|as)(=|$)/;
|
|
17210
|
+
function skipWorkitGlobals(args) {
|
|
17211
|
+
let cwd = null;
|
|
17212
|
+
let at = 0;
|
|
17213
|
+
for (;at < args.length; at++) {
|
|
17214
|
+
const match = WORKIT_GLOBAL.exec(args[at]);
|
|
17215
|
+
if (match?.[1] === "=") {
|
|
17216
|
+
if (args[at].startsWith("--cwd="))
|
|
17217
|
+
cwd = args[at].slice(6);
|
|
17218
|
+
} else if (match && args[at + 1] !== undefined) {
|
|
17219
|
+
if (args[at] === "--cwd")
|
|
17220
|
+
cwd = args[at + 1];
|
|
17221
|
+
at++;
|
|
17222
|
+
} else if (args[at] !== "--json")
|
|
17223
|
+
break;
|
|
17224
|
+
}
|
|
17225
|
+
return { rest: args.slice(at), cwd };
|
|
17226
|
+
}
|
|
16222
17227
|
var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
|
|
16223
17228
|
var CURRENT_BRANCH = /^\$\(\s*git\s+(?:branch\s+--show-current|rev-parse\s+--abbrev-ref\s+HEAD|symbolic-ref\s+--short\s+HEAD)\s*\)$/;
|
|
16224
17229
|
var pushDestination = (destination) => {
|
|
@@ -16497,7 +17502,7 @@ var repoAt = (dir) => {
|
|
|
16497
17502
|
return { location, dir, branch: resolveTaskKey(dir, location).branch };
|
|
16498
17503
|
};
|
|
16499
17504
|
var isWorkitWorkspace = (repo) => {
|
|
16500
|
-
if (
|
|
17505
|
+
if (fs17.existsSync(repo.location.dir))
|
|
16501
17506
|
return true;
|
|
16502
17507
|
try {
|
|
16503
17508
|
return resolveRuntimeWorkspaceVcs(repo.location.top ?? repo.dir) !== null;
|
|
@@ -16518,17 +17523,17 @@ var headSha2 = (repo) => {
|
|
|
16518
17523
|
if (!git || git.reftable)
|
|
16519
17524
|
return null;
|
|
16520
17525
|
try {
|
|
16521
|
-
const head =
|
|
17526
|
+
const head = fs17.readFileSync(path22.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
16522
17527
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
16523
17528
|
return head;
|
|
16524
17529
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
16525
17530
|
if (!ref)
|
|
16526
17531
|
return null;
|
|
16527
|
-
const common =
|
|
17532
|
+
const common = path22.dirname(repo.location.dir);
|
|
16528
17533
|
try {
|
|
16529
|
-
return
|
|
17534
|
+
return fs17.readFileSync(path22.join(common, ref), "utf8").trim() || null;
|
|
16530
17535
|
} catch {}
|
|
16531
|
-
const packed =
|
|
17536
|
+
const packed = fs17.readFileSync(path22.join(common, "packed-refs"), "utf8");
|
|
16532
17537
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
16533
17538
|
} catch {
|
|
16534
17539
|
return null;
|
|
@@ -16540,7 +17545,7 @@ var gitConfig = (dir, invocation, cache) => {
|
|
|
16540
17545
|
if (cached)
|
|
16541
17546
|
return cached;
|
|
16542
17547
|
const config = new Map;
|
|
16543
|
-
const listed =
|
|
17548
|
+
const listed = spawnSync13("git", [...invocation.config ?? [], "config", "-z", "--list"], {
|
|
16544
17549
|
cwd: dir,
|
|
16545
17550
|
encoding: "utf8",
|
|
16546
17551
|
timeout: 3000,
|
|
@@ -16616,8 +17621,8 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
16616
17621
|
const sub = invocation.args[0];
|
|
16617
17622
|
if (invocation.tool !== "git" || invocation.dir === undefined || !sub || sub.startsWith("-") || GIT_COMMANDS.has(sub) || depth >= MAX_DEPTH)
|
|
16618
17623
|
return [invocation];
|
|
16619
|
-
const dir =
|
|
16620
|
-
if (!
|
|
17624
|
+
const dir = path22.resolve(root, invocation.dir ?? ".");
|
|
17625
|
+
if (!fs17.existsSync(dir))
|
|
16621
17626
|
return [invocation];
|
|
16622
17627
|
const config = gitConfig(dir, invocation, cache);
|
|
16623
17628
|
const alias = configValue(config, `alias.${sub.toLowerCase()}`);
|
|
@@ -16639,11 +17644,11 @@ var expandGitAliases = (root, invocation, cache = new Map, depth = 0) => {
|
|
|
16639
17644
|
var normalizedUrl = (url, dir) => {
|
|
16640
17645
|
const trimmed = url.trim();
|
|
16641
17646
|
if (/^file:\/\//i.test(trimmed))
|
|
16642
|
-
return
|
|
17647
|
+
return path22.resolve(dir, trimmed.slice(7)).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
16643
17648
|
const scheme = /^[a-z][a-z0-9+.-]*:\/\//i.exec(trimmed);
|
|
16644
17649
|
const scp = !scheme && /^(?:[^@/\\]+@)?[^/\\:]{2,}:/.test(trimmed);
|
|
16645
17650
|
if (!scheme && !scp)
|
|
16646
|
-
return
|
|
17651
|
+
return path22.resolve(dir, trimmed).replace(/\/+$/, "").replace(/\.git$/, "");
|
|
16647
17652
|
const value = scheme ? trimmed.slice(scheme[0].length).replace(/^[^@/]*@/, "").replace(/^([^/:]+):\d+(?=\/)/, "$1") : trimmed.replace(/^[^@/]*@/, "").replace(":", "/");
|
|
16648
17653
|
return value.toLowerCase().replace(/\/{2,}/g, "/").replace(/^www\./, "").replace(/\/+$/, "").replace(/\.git$/, "");
|
|
16649
17654
|
};
|
|
@@ -16704,7 +17709,7 @@ var refusal = (action, branch, dir) => {
|
|
|
16704
17709
|
};
|
|
16705
17710
|
var refFacts = (repo, remote) => {
|
|
16706
17711
|
const facts = { heads: new Set, tags: new Set, remoteHeads: new Set };
|
|
16707
|
-
const listed =
|
|
17712
|
+
const listed = spawnSync13("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/tags", `refs/remotes/${remote}`], { cwd: repo.dir, encoding: "utf8", timeout: 5000 });
|
|
16708
17713
|
const remotePrefix = `refs/remotes/${remote}/`;
|
|
16709
17714
|
for (const ref of (listed.stdout ?? "").split(`
|
|
16710
17715
|
`)) {
|
|
@@ -16721,7 +17726,7 @@ var globBranches = (repo, glob) => {
|
|
|
16721
17726
|
const source = glob.source.startsWith("refs/") ? glob.source : `refs/heads/${glob.source}`;
|
|
16722
17727
|
const [sourcePrefix, sourceSuffix = ""] = source.split("*");
|
|
16723
17728
|
const [destinationPrefix, destinationSuffix = ""] = glob.destination.split("*");
|
|
16724
|
-
const listed =
|
|
17729
|
+
const listed = spawnSync13("git", [
|
|
16725
17730
|
"for-each-ref",
|
|
16726
17731
|
"--format=%(refname)",
|
|
16727
17732
|
sourcePrefix.slice(0, sourcePrefix.lastIndexOf("/") + 1)
|
|
@@ -16798,7 +17803,7 @@ var tagRefusal = (action, facts, dir) => {
|
|
|
16798
17803
|
return `workit: raw ${quote(`git push${action.allTags ? " --tags" : ""}`)} of ${tags.length ? `tag ${tags.join(", ")}` : "tags"} bypasses the release grant. Push a release tag with ${quote(`workit git push --release ${named}`)} (${shape} tags whose commit the production branch already has; a tag never moves), under the ${quote("release")} grant (if refused, ask the user to run ${quote("workit grant set <workspace> release=true")}).`;
|
|
16799
17804
|
};
|
|
16800
17805
|
var remoteHeads = (repo, remote) => {
|
|
16801
|
-
const listed =
|
|
17806
|
+
const listed = spawnSync13("git", ["ls-remote", "--heads", remote], {
|
|
16802
17807
|
cwd: repo.dir,
|
|
16803
17808
|
encoding: "utf8",
|
|
16804
17809
|
timeout: 3000,
|
|
@@ -16867,7 +17872,7 @@ var actionsIn = (cwd, command, dialect, cache = new Map) => {
|
|
|
16867
17872
|
const out = [];
|
|
16868
17873
|
const repos = new Map;
|
|
16869
17874
|
const repoFor = (relative) => {
|
|
16870
|
-
const dir =
|
|
17875
|
+
const dir = path22.resolve(cwd, relative ?? ".");
|
|
16871
17876
|
if (!repos.has(dir)) {
|
|
16872
17877
|
const repo = repoAt(dir);
|
|
16873
17878
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -16952,7 +17957,7 @@ var analysisDenial = (error) => ({
|
|
|
16952
17957
|
unblock: SPLIT_IT
|
|
16953
17958
|
});
|
|
16954
17959
|
var MARKER_TTL_SECONDS = 3600;
|
|
16955
|
-
var markerFile = (repo, session, key) =>
|
|
17960
|
+
var markerFile = (repo, session, key) => path22.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
16956
17961
|
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
|
|
16957
17962
|
function noteRawCommit(input, command, key) {
|
|
16958
17963
|
try {
|
|
@@ -16963,14 +17968,14 @@ function noteRawCommit(input, command, key) {
|
|
|
16963
17968
|
if (action.kind !== "commit")
|
|
16964
17969
|
continue;
|
|
16965
17970
|
const file = markerFile(repo, input.session.id, key);
|
|
16966
|
-
|
|
16967
|
-
|
|
17971
|
+
fs17.mkdirSync(path22.dirname(file), { recursive: true });
|
|
17972
|
+
fs17.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
16968
17973
|
`);
|
|
16969
17974
|
}
|
|
16970
17975
|
} catch {}
|
|
16971
17976
|
}
|
|
16972
17977
|
var headCommit = (dir) => {
|
|
16973
|
-
const run =
|
|
17978
|
+
const run = spawnSync13("git", [
|
|
16974
17979
|
"log",
|
|
16975
17980
|
"-1",
|
|
16976
17981
|
"--format=%H%x1f%ct%x1f%s%x1f%(trailers:key=Workit-Session,valueonly,separator=%x2c)"
|
|
@@ -16987,11 +17992,11 @@ var headCommit = (dir) => {
|
|
|
16987
17992
|
var settle2 = (input, repo, file) => {
|
|
16988
17993
|
let marker;
|
|
16989
17994
|
try {
|
|
16990
|
-
marker = JSON.parse(
|
|
17995
|
+
marker = JSON.parse(fs17.readFileSync(file, "utf8"));
|
|
16991
17996
|
} catch {
|
|
16992
17997
|
return;
|
|
16993
17998
|
}
|
|
16994
|
-
|
|
17999
|
+
fs17.rmSync(file, { force: true });
|
|
16995
18000
|
const session = input.session.id;
|
|
16996
18001
|
const now = Date.now() / 1000;
|
|
16997
18002
|
if (typeof marker.at !== "number" || now - marker.at > MARKER_TTL_SECONDS)
|
|
@@ -17172,7 +18177,7 @@ var targetsOf = (invocations) => invocations.flatMap((invocation) => invocation.
|
|
|
17172
18177
|
branch,
|
|
17173
18178
|
dir: invocation.dir
|
|
17174
18179
|
})) : []);
|
|
17175
|
-
var localBranchExists = (dir, branch) =>
|
|
18180
|
+
var localBranchExists = (dir, branch) => spawnSync14("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
|
|
17176
18181
|
cwd: dir,
|
|
17177
18182
|
stdio: "ignore",
|
|
17178
18183
|
timeout: 5000
|
|
@@ -17182,8 +18187,8 @@ var judgedDir = (root, dir) => {
|
|
|
17182
18187
|
return null;
|
|
17183
18188
|
if (dir === null)
|
|
17184
18189
|
return root;
|
|
17185
|
-
const resolved =
|
|
17186
|
-
return
|
|
18190
|
+
const resolved = path23.resolve(root, dir);
|
|
18191
|
+
return existsSync8(resolved) && inWorkitWorkspace(resolved) ? resolved : null;
|
|
17187
18192
|
};
|
|
17188
18193
|
var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
17189
18194
|
let judged = false;
|
|
@@ -17418,7 +18423,7 @@ var judgeInputSummary = (raw) => {
|
|
|
17418
18423
|
|
|
17419
18424
|
// packages/workit-core/src/autonomy.ts
|
|
17420
18425
|
import os5 from "node:os";
|
|
17421
|
-
import
|
|
18426
|
+
import path24 from "node:path";
|
|
17422
18427
|
|
|
17423
18428
|
// packages/workit-core/src/core/safe-write.ts
|
|
17424
18429
|
import {
|
|
@@ -17452,10 +18457,10 @@ var accountHome = () => {
|
|
|
17452
18457
|
}
|
|
17453
18458
|
};
|
|
17454
18459
|
var grantsHome = { resolve: accountHome };
|
|
17455
|
-
var grantsDir = () =>
|
|
18460
|
+
var grantsDir = () => path24.join(grantsHome.resolve(), ".config", "workit");
|
|
17456
18461
|
var grantsOverride = () => {
|
|
17457
|
-
const configured =
|
|
17458
|
-
return configured ===
|
|
18462
|
+
const configured = path24.resolve(resolveConfigDir());
|
|
18463
|
+
return configured === path24.resolve(grantsDir()) ? null : `the config directory is redirected to ${configured} (WORKFLOW_TOOLKIT_CONFIG, WORKFLOW_TOOLKIT_CONFIG_DIR or XDG_CONFIG_HOME); grants are read only from ${path24.join(grantsDir(), "workspaces.json")}, so the D4 defaults apply`;
|
|
17459
18464
|
};
|
|
17460
18465
|
var GRANT_KINDS = ["push", "pr", "merge", "release", "rerun"];
|
|
17461
18466
|
var DEFAULT_GRANTS = Object.freeze({
|
|
@@ -17508,8 +18513,16 @@ function endpointView(entry, context) {
|
|
|
17508
18513
|
...issue ? { endpointIssue: issue } : {}
|
|
17509
18514
|
};
|
|
17510
18515
|
}
|
|
17511
|
-
var
|
|
17512
|
-
|
|
18516
|
+
var VERIFICATION_MODES = [
|
|
18517
|
+
"proportional",
|
|
18518
|
+
"self",
|
|
18519
|
+
"independent"
|
|
18520
|
+
];
|
|
18521
|
+
var DEFAULT_VERIFICATION = "proportional";
|
|
18522
|
+
var verificationOf = (entry) => {
|
|
18523
|
+
const value = entry?.verification;
|
|
18524
|
+
return VERIFICATION_MODES.includes(value) ? value : DEFAULT_VERIFICATION;
|
|
18525
|
+
};
|
|
17513
18526
|
var isGrantValue = (value) => value === true || value === false || value === "verified";
|
|
17514
18527
|
var normalized = (kind, value) => value === "verified" && kind !== "merge" ? true : value;
|
|
17515
18528
|
var fromAutoApprove = (value) => {
|
|
@@ -19256,9 +20269,9 @@ class WorkitCore {
|
|
|
19256
20269
|
}
|
|
19257
20270
|
}
|
|
19258
20271
|
// packages/workit-core/src/core/repo-context.ts
|
|
19259
|
-
import { spawnSync as
|
|
19260
|
-
import { existsSync as
|
|
19261
|
-
import
|
|
20272
|
+
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
20273
|
+
import { existsSync as existsSync9, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
|
|
20274
|
+
import path25 from "node:path";
|
|
19262
20275
|
var isSafeContextRange = (value) => value.length <= 4096 && (() => {
|
|
19263
20276
|
try {
|
|
19264
20277
|
gitRevisionParts(value);
|
|
@@ -19285,7 +20298,7 @@ var invalidRange = (cwd) => ({
|
|
|
19285
20298
|
cwd
|
|
19286
20299
|
});
|
|
19287
20300
|
var runGit = (cwd, args) => {
|
|
19288
|
-
const result =
|
|
20301
|
+
const result = spawnSync15("git", args, { cwd, encoding: "utf8" });
|
|
19289
20302
|
return {
|
|
19290
20303
|
stdout: (result.stdout ?? "").trimEnd(),
|
|
19291
20304
|
stderr: (result.stderr ?? "").trimEnd(),
|
|
@@ -19392,7 +20405,7 @@ var findPrTemplate = (cwd) => {
|
|
|
19392
20405
|
]) {
|
|
19393
20406
|
const parts = rel.split("/");
|
|
19394
20407
|
const base = parts[parts.length - 1];
|
|
19395
|
-
const dir = parts.length > 1 ?
|
|
20408
|
+
const dir = parts.length > 1 ? path25.join(cwd, ...parts.slice(0, -1)) : cwd;
|
|
19396
20409
|
const actual = probeCaseInsensitive(dir, base);
|
|
19397
20410
|
if (actual)
|
|
19398
20411
|
return [...parts.slice(0, -1), actual].join("/");
|
|
@@ -19423,11 +20436,11 @@ var diffStatForRange = (cwd, range) => {
|
|
|
19423
20436
|
return runGit(cwd, ["diff", "--stat", "--"]).stdout;
|
|
19424
20437
|
};
|
|
19425
20438
|
var packageScripts = (cwd, keys) => {
|
|
19426
|
-
const pkgPath =
|
|
19427
|
-
if (!
|
|
20439
|
+
const pkgPath = path25.join(cwd, "package.json");
|
|
20440
|
+
if (!existsSync9(pkgPath))
|
|
19428
20441
|
return [];
|
|
19429
20442
|
try {
|
|
19430
|
-
const pkg = JSON.parse(
|
|
20443
|
+
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
19431
20444
|
return keys.filter((k) => pkg.scripts && typeof pkg.scripts[k] === "string").map((k) => `${k}: ${pkg.scripts?.[k]}`);
|
|
19432
20445
|
} catch {
|
|
19433
20446
|
return [];
|
|
@@ -19449,9 +20462,9 @@ var documentationFiles = (cwd) => {
|
|
|
19449
20462
|
if (entry.isDirectory()) {
|
|
19450
20463
|
if (excluded.has(entry.name))
|
|
19451
20464
|
continue;
|
|
19452
|
-
walk(
|
|
20465
|
+
walk(path25.join(dir, entry.name), depth + 1);
|
|
19453
20466
|
} else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
|
|
19454
|
-
matches.push("./" +
|
|
20467
|
+
matches.push("./" + path25.relative(cwd, path25.join(dir, entry.name)).replaceAll("\\", "/"));
|
|
19455
20468
|
}
|
|
19456
20469
|
}
|
|
19457
20470
|
};
|
|
@@ -19459,15 +20472,15 @@ var documentationFiles = (cwd) => {
|
|
|
19459
20472
|
return matches.toSorted().slice(0, 200);
|
|
19460
20473
|
};
|
|
19461
20474
|
var readTrimmed2 = (file, maxLines) => {
|
|
19462
|
-
if (!
|
|
20475
|
+
if (!existsSync9(file))
|
|
19463
20476
|
return "";
|
|
19464
|
-
const lines =
|
|
20477
|
+
const lines = readFileSync7(file, "utf8").split(`
|
|
19465
20478
|
`);
|
|
19466
20479
|
return lines.slice(0, maxLines).join(`
|
|
19467
20480
|
`);
|
|
19468
20481
|
};
|
|
19469
20482
|
function prReadyContext(root, range) {
|
|
19470
|
-
const cwd =
|
|
20483
|
+
const cwd = path25.resolve(repoRoot(root));
|
|
19471
20484
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
19472
20485
|
return invalidRange(cwd);
|
|
19473
20486
|
let stdout = "";
|
|
@@ -19525,7 +20538,7 @@ ${status.stderr}` : "");
|
|
|
19525
20538
|
stdout += `template_path: ${template}
|
|
19526
20539
|
|
|
19527
20540
|
`;
|
|
19528
|
-
stdout += readTrimmed2(
|
|
20541
|
+
stdout += readTrimmed2(path25.join(cwd, template), 220);
|
|
19529
20542
|
} else {
|
|
19530
20543
|
stdout += `template_path: none
|
|
19531
20544
|
|
|
@@ -19535,7 +20548,7 @@ ${status.stderr}` : "");
|
|
|
19535
20548
|
stdout += `
|
|
19536
20549
|
`;
|
|
19537
20550
|
stdout += printSection("Recent Validation Signals");
|
|
19538
|
-
if (
|
|
20551
|
+
if (existsSync9(path25.join(cwd, "package.json"))) {
|
|
19539
20552
|
stdout += `package.json detected. Common scripts:
|
|
19540
20553
|
`;
|
|
19541
20554
|
const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
|
|
@@ -19544,7 +20557,7 @@ ${status.stderr}` : "");
|
|
|
19544
20557
|
`) + `
|
|
19545
20558
|
`;
|
|
19546
20559
|
}
|
|
19547
|
-
if (
|
|
20560
|
+
if (existsSync9(path25.join(cwd, "Cargo.toml")) || existsSync9(path25.join(cwd, "src-tauri/Cargo.toml"))) {
|
|
19548
20561
|
stdout += `Rust project detected.
|
|
19549
20562
|
`;
|
|
19550
20563
|
}
|
|
@@ -19577,7 +20590,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
|
|
|
19577
20590
|
- Apply with the host-native editor, preserving the repository changelog conventions.
|
|
19578
20591
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
19579
20592
|
function changelogContext(root, range) {
|
|
19580
|
-
const cwd =
|
|
20593
|
+
const cwd = path25.resolve(repoRoot(root));
|
|
19581
20594
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
19582
20595
|
return invalidRange(cwd);
|
|
19583
20596
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -19595,8 +20608,8 @@ function changelogContext(root, range) {
|
|
|
19595
20608
|
stdout += CHANGELOG_RULES + `
|
|
19596
20609
|
`;
|
|
19597
20610
|
stdout += printSection("Existing CHANGELOG.md");
|
|
19598
|
-
const changelogPath =
|
|
19599
|
-
if (
|
|
20611
|
+
const changelogPath = path25.join(cwd, "CHANGELOG.md");
|
|
20612
|
+
if (existsSync9(changelogPath)) {
|
|
19600
20613
|
stdout += readTrimmed2(changelogPath, 260) + `
|
|
19601
20614
|
`;
|
|
19602
20615
|
} else {
|
|
@@ -19615,7 +20628,7 @@ function changelogContext(root, range) {
|
|
|
19615
20628
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
19616
20629
|
}
|
|
19617
20630
|
function docsRefreshContext(root, range) {
|
|
19618
|
-
const cwd =
|
|
20631
|
+
const cwd = path25.resolve(repoRoot(root));
|
|
19619
20632
|
if (range !== undefined && !isResolvableContextRange(cwd, range))
|
|
19620
20633
|
return invalidRange(cwd);
|
|
19621
20634
|
const resolvedRange = rangeArgOrDefault(range, cwd);
|
|
@@ -19637,8 +20650,8 @@ function docsRefreshContext(root, range) {
|
|
|
19637
20650
|
`) + `
|
|
19638
20651
|
`;
|
|
19639
20652
|
stdout += printSection("README Preview");
|
|
19640
|
-
const readme =
|
|
19641
|
-
if (
|
|
20653
|
+
const readme = path25.join(cwd, "README.md");
|
|
20654
|
+
if (existsSync9(readme)) {
|
|
19642
20655
|
stdout += readTrimmed2(readme, 220) + `
|
|
19643
20656
|
`;
|
|
19644
20657
|
} else {
|
|
@@ -19646,10 +20659,10 @@ function docsRefreshContext(root, range) {
|
|
|
19646
20659
|
`;
|
|
19647
20660
|
}
|
|
19648
20661
|
stdout += printSection("Package Scripts");
|
|
19649
|
-
const pkgPath =
|
|
19650
|
-
if (
|
|
20662
|
+
const pkgPath = path25.join(cwd, "package.json");
|
|
20663
|
+
if (existsSync9(pkgPath)) {
|
|
19651
20664
|
try {
|
|
19652
|
-
const pkg = JSON.parse(
|
|
20665
|
+
const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
|
|
19653
20666
|
stdout += JSON.stringify({ name: pkg.name, version: pkg.version, scripts: pkg.scripts }, null, 2) + `
|
|
19654
20667
|
`;
|
|
19655
20668
|
} catch {}
|
|
@@ -19660,7 +20673,7 @@ function docsRefreshContext(root, range) {
|
|
|
19660
20673
|
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
19661
20674
|
}
|
|
19662
20675
|
function releaseNotesContext(root, rangeOrTag) {
|
|
19663
|
-
const cwd =
|
|
20676
|
+
const cwd = path25.resolve(repoRoot(root));
|
|
19664
20677
|
if (!rangeOrTag) {
|
|
19665
20678
|
return { stdout: "", stderr: `ERROR: release tag or range required
|
|
19666
20679
|
`, exitCode: 1, cwd };
|
|
@@ -19698,109 +20711,35 @@ function releaseNotesContext(root, rangeOrTag) {
|
|
|
19698
20711
|
`;
|
|
19699
20712
|
stdout += printSection("Existing Release Files");
|
|
19700
20713
|
for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
|
|
19701
|
-
if (
|
|
20714
|
+
if (existsSync9(path25.join(cwd, rel)))
|
|
19702
20715
|
stdout += rel + `
|
|
19703
|
-
`;
|
|
19704
|
-
}
|
|
19705
|
-
stdout += printSection("Release Notes Draft");
|
|
19706
|
-
stdout += `## Release notes (${resolvedRange})
|
|
19707
|
-
|
|
19708
|
-
`;
|
|
19709
|
-
const commits = commitLogForRange(cwd, resolvedRange).split(`
|
|
19710
|
-
`).map((line) => line.trim()).filter(Boolean);
|
|
19711
|
-
stdout += commits.length ? `${commits.map((line) => `- ${line}`).join(`
|
|
19712
|
-
`)}
|
|
19713
|
-
` : `- No commits in range.
|
|
19714
|
-
`;
|
|
19715
|
-
stdout += `
|
|
19716
|
-
### Changed files
|
|
19717
|
-
|
|
19718
|
-
`;
|
|
19719
|
-
const files = changedFilesForRange(cwd, resolvedRange).split(`
|
|
19720
|
-
`).map((line) => line.trim()).filter(Boolean);
|
|
19721
|
-
stdout += files.length ? `${files.map((line) => `- ${line}`).join(`
|
|
19722
|
-
`)}
|
|
19723
|
-
` : `- None.
|
|
19724
|
-
`;
|
|
19725
|
-
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
19726
|
-
}
|
|
19727
|
-
|
|
19728
|
-
// packages/workit-core/src/core/tracker-issues.ts
|
|
19729
|
-
import { spawnSync as spawnSync15 } from "node:child_process";
|
|
19730
|
-
|
|
19731
|
-
// packages/workit-core/src/core/pr-create.ts
|
|
19732
|
-
import fs17 from "node:fs";
|
|
19733
|
-
import path23 from "node:path";
|
|
19734
|
-
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
19735
|
-
import { pathToFileURL } from "node:url";
|
|
19736
|
-
// packages/workit-core/src/forge/pr-body.ts
|
|
19737
|
-
function parseGhIssue(value) {
|
|
19738
|
-
const m = /issues\/(\d+)/.exec(value);
|
|
19739
|
-
return m ? m[1] : String(value).trim().replace(/^#/, "");
|
|
19740
|
-
}
|
|
19741
|
-
|
|
19742
|
-
// packages/workit-core/src/core/pr-create.ts
|
|
19743
|
-
function whichOnPath(tool) {
|
|
19744
|
-
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
19745
|
-
for (const dir of (process.env.PATH ?? "").split(path23.delimiter)) {
|
|
19746
|
-
if (!dir)
|
|
19747
|
-
continue;
|
|
19748
|
-
for (const name of names) {
|
|
19749
|
-
const candidate = path23.join(dir, name);
|
|
19750
|
-
try {
|
|
19751
|
-
fs17.accessSync(candidate, fs17.constants.X_OK);
|
|
19752
|
-
return candidate;
|
|
19753
|
-
} catch {}
|
|
19754
|
-
}
|
|
19755
|
-
}
|
|
19756
|
-
return null;
|
|
19757
|
-
}
|
|
19758
|
-
var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
|
|
19759
|
-
var pushRemote = (cwd) => {
|
|
19760
|
-
const result = spawnSync14("git", ["remote", "get-url", "--push", "--all", "origin"], {
|
|
19761
|
-
cwd,
|
|
19762
|
-
encoding: "utf8"
|
|
19763
|
-
});
|
|
19764
|
-
if (result.status !== 0)
|
|
19765
|
-
return null;
|
|
19766
|
-
const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
|
|
19767
|
-
return urls.length === 1 ? urls[0] : null;
|
|
19768
|
-
};
|
|
19769
|
-
var safePushUrl = (remote) => {
|
|
19770
|
-
const raw = remote.trim();
|
|
19771
|
-
if (path23.isAbsolute(raw))
|
|
19772
|
-
return pathToFileURL(raw).href;
|
|
19773
|
-
if (!raw.includes("://")) {
|
|
19774
|
-
const scp = /^(?:([^@\s]+)@)?([^:]+):(.+)$/u.exec(raw);
|
|
19775
|
-
if (scp)
|
|
19776
|
-
return `${scp[1] || "git"}@${scp[2]}:${scp[3]}`;
|
|
19777
|
-
}
|
|
19778
|
-
try {
|
|
19779
|
-
const url = new URL(raw);
|
|
19780
|
-
if (!["file:", "git:", "http:", "https:", "ssh:"].includes(url.protocol))
|
|
19781
|
-
return null;
|
|
19782
|
-
url.password = "";
|
|
19783
|
-
if (url.protocol !== "ssh:")
|
|
19784
|
-
url.username = "";
|
|
19785
|
-
url.search = "";
|
|
19786
|
-
url.hash = "";
|
|
19787
|
-
return url.toString();
|
|
19788
|
-
} catch {
|
|
19789
|
-
return null;
|
|
19790
|
-
}
|
|
19791
|
-
};
|
|
19792
|
-
var pushTargetIsStable = (cwd, remote) => {
|
|
19793
|
-
const target = safePushUrl(remote);
|
|
19794
|
-
if (!target)
|
|
19795
|
-
return false;
|
|
19796
|
-
const result = spawnSync14("git", ["config", "--get-regexp", "^url\\..*\\.(insteadof|pushinsteadof)$"], { cwd, encoding: "utf8" });
|
|
19797
|
-
if (result.status !== 0 && result.status !== 1)
|
|
19798
|
-
return false;
|
|
19799
|
-
const prefixes = (result.stdout ?? "").split(/\r?\n/u).map((line) => line.trim().slice(line.trim().indexOf(" ") + 1)).filter(Boolean);
|
|
19800
|
-
return !prefixes.some((prefix) => target.startsWith(prefix));
|
|
19801
|
-
};
|
|
20716
|
+
`;
|
|
20717
|
+
}
|
|
20718
|
+
stdout += printSection("Release Notes Draft");
|
|
20719
|
+
stdout += `## Release notes (${resolvedRange})
|
|
20720
|
+
|
|
20721
|
+
`;
|
|
20722
|
+
const commits = commitLogForRange(cwd, resolvedRange).split(`
|
|
20723
|
+
`).map((line) => line.trim()).filter(Boolean);
|
|
20724
|
+
stdout += commits.length ? `${commits.map((line) => `- ${line}`).join(`
|
|
20725
|
+
`)}
|
|
20726
|
+
` : `- No commits in range.
|
|
20727
|
+
`;
|
|
20728
|
+
stdout += `
|
|
20729
|
+
### Changed files
|
|
20730
|
+
|
|
20731
|
+
`;
|
|
20732
|
+
const files = changedFilesForRange(cwd, resolvedRange).split(`
|
|
20733
|
+
`).map((line) => line.trim()).filter(Boolean);
|
|
20734
|
+
stdout += files.length ? `${files.map((line) => `- ${line}`).join(`
|
|
20735
|
+
`)}
|
|
20736
|
+
` : `- None.
|
|
20737
|
+
`;
|
|
20738
|
+
return { stdout, stderr: "", exitCode: 0, cwd };
|
|
20739
|
+
}
|
|
19802
20740
|
|
|
19803
20741
|
// packages/workit-core/src/core/tracker-issues.ts
|
|
20742
|
+
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
19804
20743
|
var repoPathFromRemote = (remote) => {
|
|
19805
20744
|
let rest = (remote || "").trim().replace(/\/+$/, "");
|
|
19806
20745
|
if (!rest)
|
|
@@ -19821,7 +20760,7 @@ var defaultCli = (bin, available) => {
|
|
|
19821
20760
|
if (!available)
|
|
19822
20761
|
return null;
|
|
19823
20762
|
return (args, root) => {
|
|
19824
|
-
const out =
|
|
20763
|
+
const out = spawnSync16(bin, args, {
|
|
19825
20764
|
cwd: root,
|
|
19826
20765
|
encoding: "utf8",
|
|
19827
20766
|
env: { ...process.env, PATH: process.env.PATH ?? "" }
|
|
@@ -19900,7 +20839,7 @@ var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
|
|
|
19900
20839
|
|
|
19901
20840
|
// packages/workit-core/src/core/youtrack.ts
|
|
19902
20841
|
import fs18 from "node:fs";
|
|
19903
|
-
import
|
|
20842
|
+
import path26 from "node:path";
|
|
19904
20843
|
|
|
19905
20844
|
// packages/workit-core/src/core/templates.ts
|
|
19906
20845
|
var repoRoot2 = assetRoot();
|
|
@@ -19913,8 +20852,8 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
|
19913
20852
|
// packages/workit-core/src/core/youtrack.ts
|
|
19914
20853
|
var ISSUE_RE = /^[A-Z]+-\d+$/;
|
|
19915
20854
|
var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
|
|
19916
|
-
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
19917
|
-
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
20855
|
+
var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path26.join(configDir(), "youtrack.json");
|
|
20856
|
+
var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path26.join(resolveConfigDir(), "youtrack.json");
|
|
19918
20857
|
var youTrackTokenModeOk = (p) => {
|
|
19919
20858
|
if (process.platform === "win32")
|
|
19920
20859
|
return true;
|
|
@@ -19982,7 +20921,7 @@ var youTrackToken = () => {
|
|
|
19982
20921
|
if ("error" in loaded)
|
|
19983
20922
|
return loaded;
|
|
19984
20923
|
const tokenFile = String(loaded.config.tokenFile ?? "");
|
|
19985
|
-
const tokenPath = tokenFile ?
|
|
20924
|
+
const tokenPath = tokenFile ? path26.isAbsolute(tokenFile) ? path26.resolve(tokenFile) : path26.resolve(process.cwd(), tokenFile) : "";
|
|
19986
20925
|
if (!tokenPath || !fs18.existsSync(tokenPath))
|
|
19987
20926
|
return { error: "missing youtrack.token" };
|
|
19988
20927
|
if (!youTrackTokenModeOk(tokenPath))
|
|
@@ -20033,7 +20972,7 @@ function youTrackTokenCreateUrl() {
|
|
|
20033
20972
|
const desc = String(defaults.description ?? "OpenCode workit — /wk-issue-update and /wk-meetings");
|
|
20034
20973
|
const scopes = Array.isArray(defaults.scopes) ? defaults.scopes : ["YouTrack"];
|
|
20035
20974
|
const base = String(config.baseUrl ?? "").replace(/\/+$/, "");
|
|
20036
|
-
const tokenFile = String(config.tokenFile ??
|
|
20975
|
+
const tokenFile = String(config.tokenFile ?? path26.join(path26.dirname(loaded.path), "youtrack.token"));
|
|
20037
20976
|
const tab = String(defaults.profileTab ?? "account-security");
|
|
20038
20977
|
const createUrl = base ? `${base}/users/me?${new URLSearchParams({ tab })}` : null;
|
|
20039
20978
|
const docsUrl = "https://www.jetbrains.com/help/youtrack/cloud/manage-permanent-token.html";
|
|
@@ -20042,7 +20981,7 @@ function youTrackTokenCreateUrl() {
|
|
|
20042
20981
|
tokenName: name,
|
|
20043
20982
|
tokenDescription: desc,
|
|
20044
20983
|
scopes,
|
|
20045
|
-
tokenFile:
|
|
20984
|
+
tokenFile: path26.resolve(tokenFile),
|
|
20046
20985
|
createUrl,
|
|
20047
20986
|
docsUrl,
|
|
20048
20987
|
prefillSupported: false,
|
|
@@ -20382,22 +21321,22 @@ function resolveGitRevision(root, value) {
|
|
|
20382
21321
|
}
|
|
20383
21322
|
function resolveInside(root, candidate) {
|
|
20384
21323
|
const base = realpathSync5(root);
|
|
20385
|
-
const target =
|
|
20386
|
-
if (target !== base && !target.startsWith(base +
|
|
21324
|
+
const target = path27.resolve(base, candidate);
|
|
21325
|
+
if (target !== base && !target.startsWith(base + path27.sep)) {
|
|
20387
21326
|
throw new Error("path must stay inside repository root");
|
|
20388
21327
|
}
|
|
20389
21328
|
let ancestor = target;
|
|
20390
|
-
while (!
|
|
20391
|
-
ancestor =
|
|
21329
|
+
while (!existsSync10(ancestor))
|
|
21330
|
+
ancestor = path27.dirname(ancestor);
|
|
20392
21331
|
const canonicalAncestor = realpathSync5(ancestor);
|
|
20393
|
-
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base +
|
|
21332
|
+
if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path27.sep)) {
|
|
20394
21333
|
throw new Error("path must stay inside repository root");
|
|
20395
21334
|
}
|
|
20396
21335
|
return target;
|
|
20397
21336
|
}
|
|
20398
21337
|
function run2(root, executable, args, env = {}) {
|
|
20399
21338
|
const cwd = realpathSync5(root);
|
|
20400
|
-
const result =
|
|
21339
|
+
const result = spawnSync17(executable, args, {
|
|
20401
21340
|
cwd,
|
|
20402
21341
|
encoding: "utf8",
|
|
20403
21342
|
env: { ...process.env, ...env }
|
|
@@ -20434,7 +21373,7 @@ var capabilitiesFor = (descriptor, observed = {}) => descriptor.capabilities.map
|
|
|
20434
21373
|
};
|
|
20435
21374
|
});
|
|
20436
21375
|
// packages/workit-core/src/hooks/context.ts
|
|
20437
|
-
import
|
|
21376
|
+
import path28 from "node:path";
|
|
20438
21377
|
var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task status`";
|
|
20439
21378
|
var migrationPending = (result) => !result.ok && result.code === "needs_input" && (result.error ?? "").startsWith(MIGRATION_PENDING);
|
|
20440
21379
|
var boundTo = (entry, session) => entry.sessions.some((item) => item.host === session.host && item.handle === session.handle);
|
|
@@ -20519,7 +21458,7 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
20519
21458
|
return null;
|
|
20520
21459
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
20521
21460
|
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
20522
|
-
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [
|
|
21461
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path28.resolve(store.root, ref.path)] : []));
|
|
20523
21462
|
const observedFiles = filesKey(files);
|
|
20524
21463
|
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
20525
21464
|
if (!compact.ok || !compact.data)
|
|
@@ -20552,7 +21491,7 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
20552
21491
|
// packages/workit-core/src/core/heavy-rules.ts
|
|
20553
21492
|
import fs19 from "node:fs";
|
|
20554
21493
|
import os6 from "node:os";
|
|
20555
|
-
import
|
|
21494
|
+
import path29 from "node:path";
|
|
20556
21495
|
var EMPTY2 = () => ({ deny: [], allow: [] });
|
|
20557
21496
|
var readText2 = (file) => {
|
|
20558
21497
|
try {
|
|
@@ -20593,19 +21532,19 @@ function parseJsonc(text) {
|
|
|
20593
21532
|
return JSON.parse(out.replace(/,(\s*[}\]])/gu, "$1"));
|
|
20594
21533
|
}
|
|
20595
21534
|
function gitTop(cwd) {
|
|
20596
|
-
for (let dir =
|
|
20597
|
-
if (fs19.existsSync(
|
|
21535
|
+
for (let dir = path29.resolve(cwd);; dir = path29.dirname(dir)) {
|
|
21536
|
+
if (fs19.existsSync(path29.join(dir, ".git")))
|
|
20598
21537
|
return dir;
|
|
20599
|
-
if (
|
|
21538
|
+
if (path29.dirname(dir) === dir)
|
|
20600
21539
|
return null;
|
|
20601
21540
|
}
|
|
20602
21541
|
}
|
|
20603
21542
|
var projectDirs = (cwd) => {
|
|
20604
21543
|
const top = gitTop(cwd);
|
|
20605
21544
|
const dirs = [];
|
|
20606
|
-
for (let dir =
|
|
21545
|
+
for (let dir = path29.resolve(cwd);; dir = path29.dirname(dir)) {
|
|
20607
21546
|
dirs.push(dir);
|
|
20608
|
-
if (!top || dir === top ||
|
|
21547
|
+
if (!top || dir === top || path29.dirname(dir) === dir)
|
|
20609
21548
|
return dirs;
|
|
20610
21549
|
}
|
|
20611
21550
|
};
|
|
@@ -20617,12 +21556,12 @@ var CLAUDE_MANAGED = {
|
|
|
20617
21556
|
};
|
|
20618
21557
|
function claudeSettingsFiles(cwd, env, platform = process.platform) {
|
|
20619
21558
|
const project = env.CLAUDE_PROJECT_DIR || gitTop(cwd) || cwd;
|
|
20620
|
-
const user = env.CLAUDE_CONFIG_DIR ||
|
|
21559
|
+
const user = env.CLAUDE_CONFIG_DIR || path29.join(homeOf(env), ".claude");
|
|
20621
21560
|
return [
|
|
20622
21561
|
...CLAUDE_MANAGED[platform] ? [CLAUDE_MANAGED[platform]] : [],
|
|
20623
|
-
|
|
20624
|
-
|
|
20625
|
-
|
|
21562
|
+
path29.join(user, "settings.json"),
|
|
21563
|
+
path29.join(project, ".claude", "settings.json"),
|
|
21564
|
+
path29.join(project, ".claude", "settings.local.json")
|
|
20626
21565
|
];
|
|
20627
21566
|
}
|
|
20628
21567
|
var bashPattern = (entry) => {
|
|
@@ -20712,10 +21651,10 @@ var opencodeConfig = (value, rules) => {
|
|
|
20712
21651
|
function opencodeRules(cwd, env = process.env) {
|
|
20713
21652
|
const rules = EMPTY2();
|
|
20714
21653
|
try {
|
|
20715
|
-
const config = env.XDG_CONFIG_HOME ||
|
|
21654
|
+
const config = env.XDG_CONFIG_HOME || path29.join(homeOf(env), ".config");
|
|
20716
21655
|
const files = [
|
|
20717
|
-
...OPENCODE_NAMES.map((name) =>
|
|
20718
|
-
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [
|
|
21656
|
+
...OPENCODE_NAMES.map((name) => path29.join(config, "opencode", name)),
|
|
21657
|
+
...projectDirs(cwd).flatMap((dir) => OPENCODE_NAMES.flatMap((name) => [path29.join(dir, name), path29.join(dir, ".opencode", name)])),
|
|
20719
21658
|
...env.OPENCODE_CONFIG ? [env.OPENCODE_CONFIG] : []
|
|
20720
21659
|
];
|
|
20721
21660
|
for (const file of files) {
|
|
@@ -20759,9 +21698,9 @@ var codexRule = (body) => {
|
|
|
20759
21698
|
function codexRules(cwd, env = process.env) {
|
|
20760
21699
|
const rules = EMPTY2();
|
|
20761
21700
|
try {
|
|
20762
|
-
const home = env.CODEX_HOME ||
|
|
21701
|
+
const home = env.CODEX_HOME || path29.join(homeOf(env), ".codex");
|
|
20763
21702
|
const top = gitTop(cwd) ?? cwd;
|
|
20764
|
-
for (const dir of [
|
|
21703
|
+
for (const dir of [path29.join(home, "rules"), path29.join(top, ".codex", "rules")]) {
|
|
20765
21704
|
let files;
|
|
20766
21705
|
try {
|
|
20767
21706
|
files = fs19.readdirSync(dir).filter((name) => name.endsWith(".rules"));
|
|
@@ -20771,7 +21710,7 @@ function codexRules(cwd, env = process.env) {
|
|
|
20771
21710
|
return "unreadable";
|
|
20772
21711
|
}
|
|
20773
21712
|
for (const name of files) {
|
|
20774
|
-
const text = readText2(
|
|
21713
|
+
const text = readText2(path29.join(dir, name)) ?? "";
|
|
20775
21714
|
for (const part of text.split(/\bprefix_rule\s*\(/u).slice(1)) {
|
|
20776
21715
|
const rule = codexRule(part);
|
|
20777
21716
|
if (!rule)
|
|
@@ -20790,8 +21729,8 @@ function cursorRules(cwd, env = process.env) {
|
|
|
20790
21729
|
try {
|
|
20791
21730
|
const top = gitTop(cwd) ?? cwd;
|
|
20792
21731
|
for (const file of [
|
|
20793
|
-
|
|
20794
|
-
|
|
21732
|
+
path29.join(homeOf(env), ".cursor", "cli-config.json"),
|
|
21733
|
+
path29.join(top, ".cursor", "cli.json")
|
|
20795
21734
|
]) {
|
|
20796
21735
|
const text = readText2(file);
|
|
20797
21736
|
if (text === null)
|
|
@@ -20867,21 +21806,21 @@ function wrapAllowed(read, words, command, options = {}) {
|
|
|
20867
21806
|
// packages/workit-core/src/core/heavy.ts
|
|
20868
21807
|
import fs20 from "node:fs";
|
|
20869
21808
|
import os7 from "node:os";
|
|
20870
|
-
import
|
|
21809
|
+
import path30 from "node:path";
|
|
20871
21810
|
function stateDir(env, platform = process.platform) {
|
|
20872
21811
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
20873
21812
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
20874
21813
|
if (env.XDG_STATE_HOME)
|
|
20875
|
-
return
|
|
21814
|
+
return path30.join(env.XDG_STATE_HOME, "workit");
|
|
20876
21815
|
const home = env.HOME || os7.homedir();
|
|
20877
21816
|
if (platform === "darwin")
|
|
20878
|
-
return
|
|
21817
|
+
return path30.join(home, "Library", "Application Support", "workit");
|
|
20879
21818
|
if (platform === "win32")
|
|
20880
|
-
return
|
|
20881
|
-
return
|
|
21819
|
+
return path30.join(env.LOCALAPPDATA ?? path30.join(home, "AppData", "Local"), "workit");
|
|
21820
|
+
return path30.join(home, ".local", "state", "workit");
|
|
20882
21821
|
}
|
|
20883
21822
|
var HEAVY_COMMANDS_FILE = "heavy-commands.json";
|
|
20884
|
-
var heavyCommandsFile = (env) =>
|
|
21823
|
+
var heavyCommandsFile = (env) => path30.join(stateDir(env), HEAVY_COMMANDS_FILE);
|
|
20885
21824
|
var HEAVY_SCRIPT = /^(?:test|tests|t|tst|unit|integration|e2e|coverage|cov|lint|eslint|typecheck|type-check|types|tsc|check|verify|validate|build|compile|bundle|ci|sonar|cypress|playwright|jest|vitest|mocha|bench|benchmark|all)(?:$|[:_.-])/iu;
|
|
20886
21825
|
var INFO_FLAGS = new Set(["--version", "-v", "-V", "--help", "-h", "help", "version"]);
|
|
20887
21826
|
var WATCH_FLAG = /^(?:--watch(?:All)?(?:=.*)?|-w|--serve)$/u;
|
|
@@ -21156,7 +22095,7 @@ function machinePatterns(env) {
|
|
|
21156
22095
|
return isRecord5(value) ? stringList(value.patterns) : [];
|
|
21157
22096
|
}
|
|
21158
22097
|
function repoPatterns(cwd) {
|
|
21159
|
-
return repoHeavyCommands(gitTop(cwd) ??
|
|
22098
|
+
return repoHeavyCommands(gitTop(cwd) ?? path30.resolve(cwd));
|
|
21160
22099
|
}
|
|
21161
22100
|
function heavyLines(lines, options) {
|
|
21162
22101
|
const found = [];
|
|
@@ -21234,12 +22173,12 @@ function heavyPlan(command, options) {
|
|
|
21234
22173
|
}
|
|
21235
22174
|
function workitOnPath2(env = process.env) {
|
|
21236
22175
|
const names = process.platform === "win32" ? ["workit.exe", "workit.cmd", "workit"] : ["workit"];
|
|
21237
|
-
for (const dir of (env.PATH ?? "").split(
|
|
22176
|
+
for (const dir of (env.PATH ?? "").split(path30.delimiter))
|
|
21238
22177
|
for (const name of names) {
|
|
21239
22178
|
if (!dir)
|
|
21240
22179
|
continue;
|
|
21241
22180
|
try {
|
|
21242
|
-
fs20.accessSync(
|
|
22181
|
+
fs20.accessSync(path30.join(dir, name), fs20.constants.X_OK);
|
|
21243
22182
|
return true;
|
|
21244
22183
|
} catch {}
|
|
21245
22184
|
}
|
|
@@ -21258,20 +22197,20 @@ function leaseRewrite(command) {
|
|
|
21258
22197
|
// packages/workit-core/src/core/heavy-words.ts
|
|
21259
22198
|
import fs21 from "node:fs";
|
|
21260
22199
|
import os8 from "node:os";
|
|
21261
|
-
import
|
|
22200
|
+
import path31 from "node:path";
|
|
21262
22201
|
var HEAVY_WORDS = new Set(("npm npx pnpm pnpx yarn bun bunx deno corepack make gmake docker podman nerdctl " + "python python3 py uv poetry pdm pipenv hatch rye bundle rake nx " + "jest vitest mocha ava tap karma jasmine nyc c8 tsc vue-tsc svelte-check eslint stylelint " + "tslint webpack webpack-cli rollup parcel tsup vite next nuxt nuxi astro ng react-scripts " + "playwright cypress wdio testcafe turbo lerna sonar-scanner sonarless sonarless-mcp go " + "golangci-lint gotestsum staticcheck cargo mvn mvnw gradle gradlew ant sbt bazel bazelisk " + "buck2 dotnet msbuild pytest py.test tox nox mypy pyright pylint coverage maturin pip pip3 " + "rspec rails phpunit pest phpstan psalm composer mix swift xcodebuild flutter dart pod cmake " + "ninja meson ctest scons docker-compose buildah earthly").split(" "));
|
|
21263
22202
|
var wordsOf = (command) => command.split(/[\s;&|()<>`'"]+/u).map((word) => (word.split(/[\\/]/u).at(-1) ?? "").toLowerCase().replace(/\.(?:exe|cmd|bat)$/u, "")).filter(Boolean);
|
|
21264
22203
|
var stateDir2 = (env) => {
|
|
21265
22204
|
if (env.WORKFLOW_TOOLKIT_STATE)
|
|
21266
22205
|
return env.WORKFLOW_TOOLKIT_STATE;
|
|
21267
22206
|
if (env.XDG_STATE_HOME)
|
|
21268
|
-
return
|
|
22207
|
+
return path31.join(env.XDG_STATE_HOME, "workit");
|
|
21269
22208
|
const home = env.HOME || os8.homedir();
|
|
21270
22209
|
if (process.platform === "darwin")
|
|
21271
|
-
return
|
|
22210
|
+
return path31.join(home, "Library", "Application Support", "workit");
|
|
21272
22211
|
if (process.platform === "win32")
|
|
21273
|
-
return
|
|
21274
|
-
return
|
|
22212
|
+
return path31.join(env.LOCALAPPDATA ?? path31.join(home, "AppData", "Local"), "workit");
|
|
22213
|
+
return path31.join(home, ".local", "state", "workit");
|
|
21275
22214
|
};
|
|
21276
22215
|
var readJson2 = (file) => {
|
|
21277
22216
|
try {
|
|
@@ -21282,15 +22221,15 @@ var readJson2 = (file) => {
|
|
|
21282
22221
|
};
|
|
21283
22222
|
var patternWords = (cwd, env) => {
|
|
21284
22223
|
const patterns = [];
|
|
21285
|
-
const machine = readJson2(
|
|
22224
|
+
const machine = readJson2(path31.join(stateDir2(env), "heavy-commands.json"));
|
|
21286
22225
|
if (Array.isArray(machine?.patterns))
|
|
21287
22226
|
patterns.push(...machine.patterns);
|
|
21288
|
-
for (let dir =
|
|
21289
|
-
if (fs21.existsSync(
|
|
22227
|
+
for (let dir = path31.resolve(cwd);; dir = path31.dirname(dir)) {
|
|
22228
|
+
if (fs21.existsSync(path31.join(dir, ".git"))) {
|
|
21290
22229
|
patterns.push(...repoHeavyCommands(dir));
|
|
21291
22230
|
break;
|
|
21292
22231
|
}
|
|
21293
|
-
if (
|
|
22232
|
+
if (path31.dirname(dir) === dir)
|
|
21294
22233
|
break;
|
|
21295
22234
|
}
|
|
21296
22235
|
const words = new Set;
|
|
@@ -21349,21 +22288,21 @@ function heavyGate(cwd, command, options) {
|
|
|
21349
22288
|
}
|
|
21350
22289
|
}
|
|
21351
22290
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
21352
|
-
import { realpathSync as realpathSync6 } from "node:fs";
|
|
22291
|
+
import { existsSync as existsSync11, realpathSync as realpathSync6 } from "node:fs";
|
|
21353
22292
|
import os10 from "node:os";
|
|
21354
|
-
import
|
|
22293
|
+
import path33 from "node:path";
|
|
21355
22294
|
|
|
21356
22295
|
// packages/workit-core/src/core/live-session.ts
|
|
21357
|
-
import { spawnSync as
|
|
22296
|
+
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
21358
22297
|
import { createHash as createHash15 } from "node:crypto";
|
|
21359
22298
|
import fs22 from "node:fs";
|
|
21360
22299
|
import os9 from "node:os";
|
|
21361
|
-
import
|
|
22300
|
+
import path32 from "node:path";
|
|
21362
22301
|
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
21363
22302
|
var HEARTBEAT_EVERY_MS = 30000;
|
|
21364
22303
|
var liveProbe = {
|
|
21365
22304
|
platform: process.platform,
|
|
21366
|
-
run: (args) =>
|
|
22305
|
+
run: (args) => spawnSync18("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
|
|
21367
22306
|
start: (pid) => processStartOf(pid)
|
|
21368
22307
|
};
|
|
21369
22308
|
var leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session;
|
|
@@ -21432,15 +22371,15 @@ var liveOwner = (input, transport, env = process.env) => {
|
|
|
21432
22371
|
};
|
|
21433
22372
|
};
|
|
21434
22373
|
var checkoutOf = (target) => {
|
|
21435
|
-
let dir =
|
|
21436
|
-
while (!fs22.existsSync(dir) &&
|
|
21437
|
-
dir =
|
|
22374
|
+
let dir = path32.resolve(target);
|
|
22375
|
+
while (!fs22.existsSync(dir) && path32.dirname(dir) !== dir)
|
|
22376
|
+
dir = path32.dirname(dir);
|
|
21438
22377
|
if (fs22.existsSync(dir) && !fs22.statSync(dir).isDirectory())
|
|
21439
|
-
dir =
|
|
21440
|
-
const parts = dir.split(
|
|
22378
|
+
dir = path32.dirname(dir);
|
|
22379
|
+
const parts = dir.split(path32.sep);
|
|
21441
22380
|
const dotGit = parts.lastIndexOf(".git");
|
|
21442
22381
|
if (dotGit > 0)
|
|
21443
|
-
dir = parts.slice(0, dotGit).join(
|
|
22382
|
+
dir = parts.slice(0, dotGit).join(path32.sep) || path32.sep;
|
|
21444
22383
|
const location = resolveStore(dir);
|
|
21445
22384
|
if (location instanceof Error || !location.shared || !location.top)
|
|
21446
22385
|
return null;
|
|
@@ -21450,9 +22389,9 @@ var checkoutOf = (target) => {
|
|
|
21450
22389
|
} catch {}
|
|
21451
22390
|
return top;
|
|
21452
22391
|
};
|
|
21453
|
-
var markerFile2 = (top) =>
|
|
22392
|
+
var markerFile2 = (top) => path32.join(resolveStateDir(), "live-sessions", `${createHash15("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
|
|
21454
22393
|
var checkoutKey = (top, platform = process.platform) => {
|
|
21455
|
-
let key =
|
|
22394
|
+
let key = path32.resolve(top);
|
|
21456
22395
|
try {
|
|
21457
22396
|
key = fs22.realpathSync.native(key);
|
|
21458
22397
|
} catch {}
|
|
@@ -21547,7 +22486,7 @@ var claimLiveSession = (target, owner, deps = {}) => {
|
|
|
21547
22486
|
checkout,
|
|
21548
22487
|
heartbeatAt: now.toISOString()
|
|
21549
22488
|
};
|
|
21550
|
-
fs22.mkdirSync(
|
|
22489
|
+
fs22.mkdirSync(path32.dirname(file), { recursive: true });
|
|
21551
22490
|
const temp = `${file}.${process.pid}.tmp`;
|
|
21552
22491
|
fs22.writeFileSync(temp, JSON.stringify(marker));
|
|
21553
22492
|
fs22.renameSync(temp, file);
|
|
@@ -21559,26 +22498,26 @@ var NONE3 = { kind: "none" };
|
|
|
21559
22498
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
21560
22499
|
var canonical2 = (file) => {
|
|
21561
22500
|
let rest = "";
|
|
21562
|
-
for (let dir =
|
|
22501
|
+
for (let dir = path33.resolve(file);; dir = path33.dirname(dir)) {
|
|
21563
22502
|
try {
|
|
21564
|
-
return
|
|
22503
|
+
return path33.join(realpathSync6.native(dir), rest);
|
|
21565
22504
|
} catch {
|
|
21566
|
-
if (
|
|
21567
|
-
return
|
|
21568
|
-
rest = rest ?
|
|
22505
|
+
if (path33.dirname(dir) === dir)
|
|
22506
|
+
return path33.resolve(file);
|
|
22507
|
+
rest = rest ? path33.join(path33.basename(dir), rest) : path33.basename(dir);
|
|
21569
22508
|
}
|
|
21570
22509
|
}
|
|
21571
22510
|
};
|
|
21572
22511
|
var exempt = (cwd, root, file, plans) => {
|
|
21573
22512
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
21574
|
-
const relative =
|
|
21575
|
-
if (relative.startsWith("..") ||
|
|
22513
|
+
const relative = path33.relative(fold(canonical2(root)), fold(canonical2(path33.resolve(cwd, file))));
|
|
22514
|
+
if (relative.startsWith("..") || path33.isAbsolute(relative))
|
|
21576
22515
|
return true;
|
|
21577
|
-
const posix = relative.split(
|
|
22516
|
+
const posix = relative.split(path33.sep).join("/");
|
|
21578
22517
|
const segments = posix.split("/");
|
|
21579
22518
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
21580
22519
|
};
|
|
21581
|
-
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
22520
|
+
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs", "busybox"]);
|
|
21582
22521
|
var operands = (words) => words.filter((item) => !item.startsWith("-"));
|
|
21583
22522
|
var SCRIPT_SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
21584
22523
|
var wrappedScript = (input) => {
|
|
@@ -21590,9 +22529,9 @@ var wrappedScript = (input) => {
|
|
|
21590
22529
|
return null;
|
|
21591
22530
|
if ("script" in tail)
|
|
21592
22531
|
return tail.script;
|
|
21593
|
-
const
|
|
21594
|
-
if (SCRIPT_SHELLS.has((
|
|
21595
|
-
return script
|
|
22532
|
+
const script = shellScript2(tail.argv);
|
|
22533
|
+
if (SCRIPT_SHELLS.has((tail.argv[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()) && script)
|
|
22534
|
+
return script;
|
|
21596
22535
|
return tail.argv.map(quoteWord2).join(" ");
|
|
21597
22536
|
};
|
|
21598
22537
|
var editsOf = (input) => {
|
|
@@ -21671,6 +22610,94 @@ var editsOf = (input) => {
|
|
|
21671
22610
|
return [];
|
|
21672
22611
|
}
|
|
21673
22612
|
};
|
|
22613
|
+
var shellScript2 = (words) => {
|
|
22614
|
+
if (!SCRIPT_SHELLS.has((words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase()))
|
|
22615
|
+
return null;
|
|
22616
|
+
for (let index = 1;index < words.length; index++) {
|
|
22617
|
+
const word = words[index];
|
|
22618
|
+
if (/^-[a-z]*c$/.test(word))
|
|
22619
|
+
return words[index + 1] ?? null;
|
|
22620
|
+
if (/^[-+][oO]$/.test(word))
|
|
22621
|
+
index++;
|
|
22622
|
+
else if (!/^[-+]/.test(word))
|
|
22623
|
+
return null;
|
|
22624
|
+
}
|
|
22625
|
+
return null;
|
|
22626
|
+
};
|
|
22627
|
+
var XARGS_VALUE = /^-[IinLPdsEa]$/;
|
|
22628
|
+
var runsWriter = (words) => {
|
|
22629
|
+
const filled = [...words.filter((word) => word !== "{}" && word !== ";" && word !== "+"), "x"];
|
|
22630
|
+
const edits = editsOf(filled);
|
|
22631
|
+
return edits === null || edits.length > 0 || opaqueWrites(filled) !== null;
|
|
22632
|
+
};
|
|
22633
|
+
var opaqueWrites = (input) => {
|
|
22634
|
+
let words = input;
|
|
22635
|
+
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0]))) {
|
|
22636
|
+
if (words[0] === "xargs") {
|
|
22637
|
+
let at = 1;
|
|
22638
|
+
while (words[at]?.startsWith("-"))
|
|
22639
|
+
at += XARGS_VALUE.test(words[at]) ? 2 : 1;
|
|
22640
|
+
return runsWriter(words.slice(at)) ? ["."] : null;
|
|
22641
|
+
}
|
|
22642
|
+
words = words.slice(1);
|
|
22643
|
+
}
|
|
22644
|
+
const [verb, ...args] = words;
|
|
22645
|
+
const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
22646
|
+
const files = operands(args);
|
|
22647
|
+
const has = (pattern) => args.some((arg) => pattern.test(arg));
|
|
22648
|
+
const listed = (named) => named.length ? named : ["."];
|
|
22649
|
+
if (name === "unlink" || name === "shred")
|
|
22650
|
+
return listed(files);
|
|
22651
|
+
if (/^(?:chmod|chown|chgrp|chattr)$/.test(name))
|
|
22652
|
+
return listed(has(/^--reference/) ? files : files.slice(1));
|
|
22653
|
+
if (name === "rsync") {
|
|
22654
|
+
const dest = files.at(-1);
|
|
22655
|
+
const remote = /^[\w.@-]*:/.test(dest ?? "") && !/^[A-Za-z]:[\\/]/.test(dest ?? "");
|
|
22656
|
+
return dest === undefined ? ["."] : remote ? [] : [dest];
|
|
22657
|
+
}
|
|
22658
|
+
if (name === "find") {
|
|
22659
|
+
const exec = args.findIndex((arg) => /^-(?:exec|execdir|ok|okdir)$/.test(arg));
|
|
22660
|
+
const writes = has(/^-(?:delete|fprint\w*|fls)$/) || exec >= 0 && runsWriter(args.slice(exec + 1));
|
|
22661
|
+
const end = args.findIndex((arg) => /^[-(!]/.test(arg));
|
|
22662
|
+
return writes ? listed(end < 0 ? args : args.slice(0, end)) : null;
|
|
22663
|
+
}
|
|
22664
|
+
if (/^g?awk$/.test(name)) {
|
|
22665
|
+
if (!has(/^-i|^--include/))
|
|
22666
|
+
return null;
|
|
22667
|
+
const named = [];
|
|
22668
|
+
let program = !has(/^-f$|^--file/);
|
|
22669
|
+
for (let index = 0;index < args.length; index++)
|
|
22670
|
+
if (/^(?:-[ifvF]|--(?:include|file|assign|field-separator))$/.test(args[index]))
|
|
22671
|
+
index++;
|
|
22672
|
+
else if (args[index].startsWith("-"))
|
|
22673
|
+
continue;
|
|
22674
|
+
else if (program)
|
|
22675
|
+
program = false;
|
|
22676
|
+
else
|
|
22677
|
+
named.push(args[index]);
|
|
22678
|
+
return listed(named);
|
|
22679
|
+
}
|
|
22680
|
+
if (name === "perl")
|
|
22681
|
+
return has(/^-[a-zA-Z]*[eE]/) ? ["."] : null;
|
|
22682
|
+
if (/^(?:python[\d.]*|node|bun|deno|ruby|php)$/.test(name))
|
|
22683
|
+
return has(/^-[a-zA-Z]*[ce]$|^--eval$|^-p$/) ? ["."] : null;
|
|
22684
|
+
return shellScript2(words) === null ? null : ["."];
|
|
22685
|
+
};
|
|
22686
|
+
var linkSources = (args, at) => {
|
|
22687
|
+
const names = operands([...args]);
|
|
22688
|
+
const into = args.indexOf("-t");
|
|
22689
|
+
const dest = into >= 0 ? args[into + 1] : names.length > 1 ? names.at(-1) : ".";
|
|
22690
|
+
const sources = names.filter((name, index) => into >= 0 ? name !== dest : index < names.length - 1 || names.length === 1);
|
|
22691
|
+
const target = dest === undefined ? null : at(dest);
|
|
22692
|
+
const symbolic = args.some((arg) => /^-[a-zA-Z]*s|^--symbolic$/.test(arg));
|
|
22693
|
+
const base = target === null ? null : into >= 0 || sources.length > 1 || existsSync11(target) || /[\\/]$/.test(dest ?? "") ? target : path33.dirname(target);
|
|
22694
|
+
return sources.flatMap((source) => {
|
|
22695
|
+
if (symbolic && base !== null && !path33.isAbsolute(homeExpanded(source)))
|
|
22696
|
+
return [path33.resolve(base, source)];
|
|
22697
|
+
const resolved = at(source);
|
|
22698
|
+
return resolved ? [resolved] : [];
|
|
22699
|
+
});
|
|
22700
|
+
};
|
|
21674
22701
|
var shellWrites = (command, depth = 0) => {
|
|
21675
22702
|
const targets = [];
|
|
21676
22703
|
let unknown = false;
|
|
@@ -21695,7 +22722,7 @@ var shellWrites = (command, depth = 0) => {
|
|
|
21695
22722
|
return { writes: true, targets: unknown ? null : targets };
|
|
21696
22723
|
};
|
|
21697
22724
|
var cache2 = new Map;
|
|
21698
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
22725
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path33.resolve(root, file)) ?? "-").join("\x00");
|
|
21699
22726
|
var gateFor = (cwd) => {
|
|
21700
22727
|
const store = new TaskStore(cwd);
|
|
21701
22728
|
const listed = store.listTaskIndex();
|
|
@@ -21774,32 +22801,36 @@ var gitMutationDirs = (cwd, command, dialect) => {
|
|
|
21774
22801
|
return [];
|
|
21775
22802
|
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
21776
22803
|
return [];
|
|
21777
|
-
return [
|
|
22804
|
+
return [path33.resolve(cwd, invocation.dir ?? ".")];
|
|
21778
22805
|
});
|
|
21779
22806
|
};
|
|
21780
22807
|
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
21781
22808
|
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
21782
22809
|
var POPD2 = new Set(["popd", "pop-location"]);
|
|
21783
|
-
var
|
|
22810
|
+
var UNKNOWN_DIR = path33.resolve(path33.parse(process.cwd()).root, ".workit-unknown-dir");
|
|
22811
|
+
var cdInto = (dir, words, strict = false) => {
|
|
22812
|
+
const lost = strict ? UNKNOWN_DIR : undefined;
|
|
21784
22813
|
const named = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
21785
22814
|
const target = named === undefined ? undefined : homeExpanded(named);
|
|
21786
|
-
if (
|
|
21787
|
-
return;
|
|
21788
|
-
|
|
22815
|
+
if (target === "-" || target && /^~|[$`*?]/.test(target))
|
|
22816
|
+
return lost;
|
|
22817
|
+
if (dir === undefined)
|
|
22818
|
+
return lost;
|
|
22819
|
+
return target === undefined ? os10.homedir() : path33.resolve(dir, target);
|
|
21789
22820
|
};
|
|
21790
22821
|
var quoteWord2 = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
21791
22822
|
var homeExpanded = (file) => {
|
|
21792
22823
|
const match = /^(?:~|\$HOME|\$\{HOME\})(?=\/|$)/.exec(file);
|
|
21793
22824
|
return match ? (process.env.HOME || os10.homedir()) + file.slice(match[0].length) : file;
|
|
21794
22825
|
};
|
|
21795
|
-
var segmentTargets = (cwd, command, dialect, depth = 0) => {
|
|
22826
|
+
var segmentTargets = (cwd, command, dialect, depth = 0, strict = false) => {
|
|
21796
22827
|
const targets = [];
|
|
21797
22828
|
let dir = cwd;
|
|
21798
22829
|
let stack = [];
|
|
21799
22830
|
const groups = [];
|
|
21800
22831
|
const at = (file) => {
|
|
21801
22832
|
const home = homeExpanded(file);
|
|
21802
|
-
return
|
|
22833
|
+
return path33.isAbsolute(home) ? home : dir === undefined ? null : path33.resolve(dir, home);
|
|
21803
22834
|
};
|
|
21804
22835
|
for (const segment of segmentsOf(command, dialect)) {
|
|
21805
22836
|
if (segment.group === "open") {
|
|
@@ -21816,21 +22847,35 @@ var segmentTargets = (cwd, command, dialect, depth = 0) => {
|
|
|
21816
22847
|
const words = lead < 0 ? [] : segment.words.slice(lead);
|
|
21817
22848
|
const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
21818
22849
|
if (CD2.has(name)) {
|
|
21819
|
-
dir = cdInto(dir, words);
|
|
22850
|
+
dir = cdInto(dir, words, strict);
|
|
21820
22851
|
continue;
|
|
21821
22852
|
}
|
|
21822
22853
|
if (PUSHD2.has(name)) {
|
|
21823
22854
|
stack.push(dir);
|
|
21824
|
-
dir = cdInto(dir, words);
|
|
22855
|
+
dir = cdInto(dir, words, strict);
|
|
21825
22856
|
continue;
|
|
21826
22857
|
}
|
|
21827
22858
|
if (POPD2.has(name)) {
|
|
21828
|
-
dir = stack.length ? stack.pop() : undefined;
|
|
22859
|
+
dir = stack.length ? stack.pop() : strict ? UNKNOWN_DIR : undefined;
|
|
21829
22860
|
continue;
|
|
21830
22861
|
}
|
|
21831
|
-
const inner = depth < 4 ? wrappedScript(words) : null;
|
|
22862
|
+
const inner = depth < 4 ? wrappedScript(words) ?? (strict ? shellScript2(words) : null) : null;
|
|
21832
22863
|
if (inner !== null && dir !== undefined)
|
|
21833
|
-
targets.push(...segmentTargets(dir, inner, "posix", depth + 1));
|
|
22864
|
+
targets.push(...segmentTargets(dir, inner, "posix", depth + 1, strict));
|
|
22865
|
+
if (strict && dir !== undefined) {
|
|
22866
|
+
const runIn = dir;
|
|
22867
|
+
for (const sub of depth < 4 ? segment.subs ?? [] : [])
|
|
22868
|
+
targets.push(...segmentTargets(runIn, sub, dialect, depth + 1, true));
|
|
22869
|
+
const opaque = segment.unanalyzable ? ["."] : inner === null ? opaqueWrites(words) : null;
|
|
22870
|
+
for (const file of opaque ?? []) {
|
|
22871
|
+
const resolved = at(file);
|
|
22872
|
+
if (resolved)
|
|
22873
|
+
targets.push(file === "." ? path33.join(resolved, DIR_TARGET) : resolved);
|
|
22874
|
+
}
|
|
22875
|
+
const ln = words.findIndex((word) => /^ln$/i.test(word.split(/[\\/]/).at(-1) ?? ""));
|
|
22876
|
+
if (ln >= 0 && words.slice(0, ln).every((word) => PREFIXES2.has(word)))
|
|
22877
|
+
targets.push(...linkSources(words.slice(ln + 1), at));
|
|
22878
|
+
}
|
|
21834
22879
|
for (const file of segment.redirects)
|
|
21835
22880
|
if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
|
|
21836
22881
|
const resolved = at(file);
|
|
@@ -21840,18 +22885,36 @@ var segmentTargets = (cwd, command, dialect, depth = 0) => {
|
|
|
21840
22885
|
const edits = editsOf(segment.words);
|
|
21841
22886
|
if (edits === null) {
|
|
21842
22887
|
if (dir !== undefined)
|
|
21843
|
-
targets.push(
|
|
22888
|
+
targets.push(path33.join(dir, DIR_TARGET));
|
|
21844
22889
|
} else
|
|
21845
22890
|
for (const file of edits) {
|
|
21846
22891
|
const resolved = at(file);
|
|
21847
22892
|
if (resolved)
|
|
21848
22893
|
targets.push(resolved);
|
|
21849
22894
|
}
|
|
21850
|
-
if (dir !== undefined
|
|
21851
|
-
|
|
22895
|
+
if (dir !== undefined)
|
|
22896
|
+
for (const invocation of workitInvocations(segment.words.map(quoteWord2).join(" "), "posix")) {
|
|
22897
|
+
const { at: runsIn, args } = workitCwd(dir, invocation);
|
|
22898
|
+
if (args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), ""))
|
|
22899
|
+
targets.push(path33.join(runsIn, DIR_TARGET));
|
|
22900
|
+
}
|
|
21852
22901
|
}
|
|
21853
22902
|
return targets;
|
|
21854
22903
|
};
|
|
22904
|
+
var workitCwd = (dir, args) => {
|
|
22905
|
+
const { rest, cwd } = skipWorkitGlobals(args);
|
|
22906
|
+
return { at: cwd === null ? dir : path33.resolve(dir, homeExpanded(cwd)), args: rest };
|
|
22907
|
+
};
|
|
22908
|
+
var shellMutationTargets = (cwd, command, dialect = "posix", strict = false) => {
|
|
22909
|
+
try {
|
|
22910
|
+
return [
|
|
22911
|
+
...segmentTargets(cwd, command, dialect, 0, strict),
|
|
22912
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path33.join(dir, DIR_TARGET))
|
|
22913
|
+
];
|
|
22914
|
+
} catch {
|
|
22915
|
+
return [];
|
|
22916
|
+
}
|
|
22917
|
+
};
|
|
21855
22918
|
var overridden = (command, dialect) => {
|
|
21856
22919
|
const segments = segmentsOf(command, dialect);
|
|
21857
22920
|
if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
|
|
@@ -21883,7 +22946,7 @@ var liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
|
21883
22946
|
var guarded = (targets) => {
|
|
21884
22947
|
const seen = new Set;
|
|
21885
22948
|
return targets.filter((target) => {
|
|
21886
|
-
const dir =
|
|
22949
|
+
const dir = path33.dirname(target);
|
|
21887
22950
|
if (seen.has(dir))
|
|
21888
22951
|
return false;
|
|
21889
22952
|
seen.add(dir);
|
|
@@ -21904,7 +22967,7 @@ var claimAll = (owner, targets, deps) => {
|
|
|
21904
22967
|
for (const target of guarded(targets))
|
|
21905
22968
|
claimLiveSession(target, owner, deps);
|
|
21906
22969
|
};
|
|
21907
|
-
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) =>
|
|
22970
|
+
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path33.resolve(cwd, file)) : [path33.join(cwd, DIR_TARGET)];
|
|
21908
22971
|
var issuedTarget = (cwd, targets) => {
|
|
21909
22972
|
const state = resolveStateDir();
|
|
21910
22973
|
const found = targets.find((target) => underIssued(state, cwd, homeExpanded(target), process.platform, canonical2));
|
|
@@ -21912,7 +22975,7 @@ var issuedTarget = (cwd, targets) => {
|
|
|
21912
22975
|
};
|
|
21913
22976
|
var underIssued = (state, cwd, target, platform = process.platform, real = (file) => file) => {
|
|
21914
22977
|
const win = platform === "win32";
|
|
21915
|
-
const p = win ?
|
|
22978
|
+
const p = win ? path33.win32 : path33.posix;
|
|
21916
22979
|
const fold = (value) => win || platform === "darwin" ? value.toLowerCase() : value;
|
|
21917
22980
|
const named = win ? target.replace(/^\/([a-zA-Z])(?=\/|$)/, "$1:") : target;
|
|
21918
22981
|
const relative = p.relative(fold(real(p.resolve(state))), fold(real(p.resolve(cwd, named))));
|
|
@@ -21957,16 +23020,8 @@ function shellGate(cwd, command, owner, dialect = "posix", deps) {
|
|
|
21957
23020
|
} catch {}
|
|
21958
23021
|
const writes = shellWrites(command);
|
|
21959
23022
|
let targets = [];
|
|
21960
|
-
if (owner)
|
|
21961
|
-
|
|
21962
|
-
targets = [
|
|
21963
|
-
...segmentTargets(cwd, command, dialect),
|
|
21964
|
-
...gitMutationDirs(cwd, command, dialect).map((dir) => path31.join(dir, DIR_TARGET))
|
|
21965
|
-
];
|
|
21966
|
-
} catch {
|
|
21967
|
-
targets = [];
|
|
21968
|
-
}
|
|
21969
|
-
}
|
|
23023
|
+
if (owner)
|
|
23024
|
+
targets = shellMutationTargets(cwd, command, dialect);
|
|
21970
23025
|
if (owner && targets.length) {
|
|
21971
23026
|
const live = liveGate(owner, targets, deps);
|
|
21972
23027
|
if (live.kind !== "none") {
|
|
@@ -22010,7 +23065,25 @@ function beforeWriteGate(cwd, paths) {
|
|
|
22010
23065
|
}
|
|
22011
23066
|
}
|
|
22012
23067
|
// packages/workit-core/src/hooks/stop.ts
|
|
22013
|
-
import { spawnSync as
|
|
23068
|
+
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
23069
|
+
|
|
23070
|
+
// packages/workit-core/src/core/policy/current-judgment.ts
|
|
23071
|
+
function currentJudgment(cwd) {
|
|
23072
|
+
try {
|
|
23073
|
+
const store = new TaskStore(cwd);
|
|
23074
|
+
const listed = store.listTaskIndex();
|
|
23075
|
+
const key = store.currentKey();
|
|
23076
|
+
if (!listed.ok || !key.ok)
|
|
23077
|
+
return null;
|
|
23078
|
+
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
23079
|
+
const task = entry ? store.readTask(entry.id) : null;
|
|
23080
|
+
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
23081
|
+
} catch {
|
|
23082
|
+
return null;
|
|
23083
|
+
}
|
|
23084
|
+
}
|
|
23085
|
+
|
|
23086
|
+
// packages/workit-core/src/hooks/stop.ts
|
|
22014
23087
|
var NONE4 = { kind: "none" };
|
|
22015
23088
|
var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
|
|
22016
23089
|
var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
|
|
@@ -22031,7 +23104,7 @@ var asksUser = (message) => {
|
|
|
22031
23104
|
};
|
|
22032
23105
|
var short = (sha) => (sha ?? "?").slice(0, 12);
|
|
22033
23106
|
var gitList = (cwd, args) => {
|
|
22034
|
-
const run =
|
|
23107
|
+
const run = spawnSync19("git", args, {
|
|
22035
23108
|
cwd,
|
|
22036
23109
|
encoding: "utf8",
|
|
22037
23110
|
timeout: 3000,
|
|
@@ -22103,29 +23176,32 @@ var redChecks = (rows, pr, head, endpoint) => {
|
|
|
22103
23176
|
reason: `PR #${pr}'s checks were last seen ${state} at ${short(head)}, and this workspace's endpoint is \`${endpoint}\`: run \`workit ci wait --pr ${pr}\`${state === "failing" ? " after fixing the failures `workit pr status` lists (then push)" : ""}.`
|
|
22104
23177
|
};
|
|
22105
23178
|
};
|
|
22106
|
-
var
|
|
22107
|
-
|
|
22108
|
-
|
|
22109
|
-
const listed = store.listTaskIndex();
|
|
22110
|
-
const key = store.currentKey();
|
|
22111
|
-
if (!listed.ok || !key.ok)
|
|
22112
|
-
return null;
|
|
22113
|
-
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
22114
|
-
const task = entry ? store.readTask(entry.id) : null;
|
|
22115
|
-
return task?.ok === true ? latestJudgment(task.data) : null;
|
|
22116
|
-
} catch {
|
|
22117
|
-
return null;
|
|
22118
|
-
}
|
|
23179
|
+
var prBase = (rows) => {
|
|
23180
|
+
const seen = rows.findLast((row) => (row.type === "pr.status" || row.type === "pr.created") && row.observer === "workit_cli" && typeof row.base === "string");
|
|
23181
|
+
return typeof seen?.base === "string" ? seen.base : null;
|
|
22119
23182
|
};
|
|
22120
|
-
var unverified = (cwd, branch, all, descriptor, verification) => {
|
|
23183
|
+
var unverified = (cwd, branch, all, descriptor, verification, head) => {
|
|
22121
23184
|
const check = checkVerdicts(cwd, branch, all);
|
|
22122
23185
|
if (check.accepted.accepted || check.accepted.reasons.includes("failing_verdict"))
|
|
22123
23186
|
return null;
|
|
22124
|
-
const judgment =
|
|
23187
|
+
const judgment = currentJudgment(cwd);
|
|
22125
23188
|
const high = judgment?.riskTier === "high";
|
|
22126
|
-
|
|
22127
|
-
|
|
22128
|
-
|
|
23189
|
+
let independent;
|
|
23190
|
+
if (verification === "proportional") {
|
|
23191
|
+
const lane = branchLane(cwd, {
|
|
23192
|
+
base: prBase(all.filter((row) => row.branch === branch)),
|
|
23193
|
+
head,
|
|
23194
|
+
judgment
|
|
23195
|
+
});
|
|
23196
|
+
const need = laneNeed(lane.lane);
|
|
23197
|
+
if (need === "none")
|
|
23198
|
+
return null;
|
|
23199
|
+
independent = need === "non-author";
|
|
23200
|
+
} else {
|
|
23201
|
+
if (judgment && !judgment.behaviorChange && !high)
|
|
23202
|
+
return null;
|
|
23203
|
+
independent = verification === "independent" || high;
|
|
23204
|
+
}
|
|
22129
23205
|
if (!independent && check.review === "self-reviewed")
|
|
22130
23206
|
return null;
|
|
22131
23207
|
const has = check.review === "self-reviewed" ? "only a self verdict" : "no current verdict";
|
|
@@ -22172,7 +23248,7 @@ function stopObligation(input, descriptor) {
|
|
|
22172
23248
|
}
|
|
22173
23249
|
if (!delivered || endpoint !== "green" && endpoint !== "merged")
|
|
22174
23250
|
return null;
|
|
22175
|
-
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification);
|
|
23251
|
+
const left = unverified(input.cwd, branch, all, descriptor, autonomy.verification, head);
|
|
22176
23252
|
return left ? { ...left, head } : null;
|
|
22177
23253
|
} catch {
|
|
22178
23254
|
return null;
|
|
@@ -22442,9 +23518,9 @@ var WRITE_TOOLS = new Set([
|
|
|
22442
23518
|
]);
|
|
22443
23519
|
// packages/workit-core/src/hooks/standing.ts
|
|
22444
23520
|
import fs23 from "node:fs";
|
|
22445
|
-
import
|
|
23521
|
+
import path34 from "node:path";
|
|
22446
23522
|
var readPlans = (store) => {
|
|
22447
|
-
const dir =
|
|
23523
|
+
const dir = path34.join(store, "fanouts");
|
|
22448
23524
|
let names;
|
|
22449
23525
|
try {
|
|
22450
23526
|
names = fs23.readdirSync(dir).filter((name) => name.endsWith(".json"));
|
|
@@ -22454,7 +23530,7 @@ var readPlans = (store) => {
|
|
|
22454
23530
|
const plans = [];
|
|
22455
23531
|
for (const name of names) {
|
|
22456
23532
|
try {
|
|
22457
|
-
const raw = JSON.parse(fs23.readFileSync(
|
|
23533
|
+
const raw = JSON.parse(fs23.readFileSync(path34.join(dir, name), "utf8"));
|
|
22458
23534
|
if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
|
|
22459
23535
|
continue;
|
|
22460
23536
|
const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
|
|
@@ -22544,6 +23620,19 @@ var standingSinceStart = (input) => {
|
|
|
22544
23620
|
// packages/workit-core/src/hooks/cwd-normalize.ts
|
|
22545
23621
|
var FILTERS = new Set(["tail", "head", "grep"]);
|
|
22546
23622
|
|
|
23623
|
+
// packages/workit-core/src/hooks/worktree-confine.ts
|
|
23624
|
+
var CASE_INSENSITIVE2 = process.platform === "darwin" || process.platform === "win32";
|
|
23625
|
+
var MUTATING_REMOTE = new Set([
|
|
23626
|
+
"add",
|
|
23627
|
+
"remove",
|
|
23628
|
+
"rm",
|
|
23629
|
+
"rename",
|
|
23630
|
+
"set-url",
|
|
23631
|
+
"set-head",
|
|
23632
|
+
"set-branches",
|
|
23633
|
+
"prune"
|
|
23634
|
+
]);
|
|
23635
|
+
|
|
22547
23636
|
// packages/workit-core/src/hooks/handle.ts
|
|
22548
23637
|
var offered = new Set;
|
|
22549
23638
|
var PRE_TOOL = new Set(["shell.pre", "write.pre", "tool.pre"]);
|
|
@@ -22649,11 +23738,11 @@ var OPENCODE_DESCRIPTOR = {
|
|
|
22649
23738
|
]
|
|
22650
23739
|
};
|
|
22651
23740
|
// packages/workit-opencode/src/shared/init-apply.ts
|
|
22652
|
-
import
|
|
23741
|
+
import path39 from "node:path";
|
|
22653
23742
|
|
|
22654
23743
|
// packages/workit-core/src/core/gitignore.ts
|
|
22655
|
-
import { existsSync as
|
|
22656
|
-
import
|
|
23744
|
+
import { existsSync as existsSync12, readFileSync as readFileSync8, writeFileSync as writeFileSync4 } from "node:fs";
|
|
23745
|
+
import path35 from "node:path";
|
|
22657
23746
|
var GITIGNORE_ENTRIES = [
|
|
22658
23747
|
"# workit: SDD working state (never commit)",
|
|
22659
23748
|
"docs/*/sdd/",
|
|
@@ -22673,8 +23762,8 @@ var GITIGNORE_ENTRIES = [
|
|
|
22673
23762
|
var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
22674
23763
|
if (!confirmed)
|
|
22675
23764
|
return { ok: false, error: "confirmed: true required" };
|
|
22676
|
-
const file =
|
|
22677
|
-
const existing =
|
|
23765
|
+
const file = path35.join(workspaceRoot, ".gitignore");
|
|
23766
|
+
const existing = existsSync12(file) ? readFileSync8(file, "utf8") : "";
|
|
22678
23767
|
const existingLines = new Set(existing.split(`
|
|
22679
23768
|
`).map((l) => l.trim()).filter(Boolean));
|
|
22680
23769
|
const added = [];
|
|
@@ -22693,33 +23782,33 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
|
22693
23782
|
` : "") + append.join(`
|
|
22694
23783
|
`) + `
|
|
22695
23784
|
`, "utf8");
|
|
22696
|
-
} else if (!
|
|
23785
|
+
} else if (!existsSync12(file)) {
|
|
22697
23786
|
writeFileSync4(file, "", "utf8");
|
|
22698
23787
|
}
|
|
22699
23788
|
return { ok: true, path: file, added };
|
|
22700
23789
|
};
|
|
22701
23790
|
|
|
22702
23791
|
// packages/workit-core/src/core/hygiene.ts
|
|
22703
|
-
import { existsSync as
|
|
22704
|
-
import
|
|
23792
|
+
import { existsSync as existsSync13, readFileSync as readFileSync9, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
23793
|
+
import path36 from "node:path";
|
|
22705
23794
|
var repoRoot3 = assetRoot();
|
|
22706
|
-
var templatesDir = () =>
|
|
23795
|
+
var templatesDir = () => path36.join(repoRoot3, "templates", "hygiene");
|
|
22707
23796
|
var packageJson = (root) => {
|
|
22708
|
-
if (!
|
|
23797
|
+
if (!existsSync13(path36.join(root, "package.json")))
|
|
22709
23798
|
return null;
|
|
22710
23799
|
try {
|
|
22711
|
-
return JSON.parse(
|
|
23800
|
+
return JSON.parse(readFileSync9(path36.join(root, "package.json"), "utf8"));
|
|
22712
23801
|
} catch {
|
|
22713
23802
|
return null;
|
|
22714
23803
|
}
|
|
22715
23804
|
};
|
|
22716
23805
|
var isOpenSource = (root) => {
|
|
22717
|
-
if (
|
|
23806
|
+
if (existsSync13(path36.join(root, "LICENSE")))
|
|
22718
23807
|
return true;
|
|
22719
23808
|
const pkg = packageJson(root);
|
|
22720
23809
|
if (pkg && !pkg.private)
|
|
22721
23810
|
return true;
|
|
22722
|
-
return
|
|
23811
|
+
return path36.basename(root) === "workflow-toolkit";
|
|
22723
23812
|
};
|
|
22724
23813
|
var licenseHolder = (root) => {
|
|
22725
23814
|
const pkg = packageJson(root);
|
|
@@ -22742,13 +23831,13 @@ var planHygieneFiles = (root, opts = {}) => {
|
|
|
22742
23831
|
files.push("LICENSE", "CONTRIBUTING.md");
|
|
22743
23832
|
const planned = [];
|
|
22744
23833
|
for (const file of files) {
|
|
22745
|
-
if (
|
|
23834
|
+
if (existsSync13(path36.join(root, file)))
|
|
22746
23835
|
continue;
|
|
22747
|
-
const tpl =
|
|
22748
|
-
if (!
|
|
23836
|
+
const tpl = path36.join(templatesDir(), file);
|
|
23837
|
+
if (!existsSync13(tpl))
|
|
22749
23838
|
continue;
|
|
22750
|
-
const content =
|
|
22751
|
-
planned.push({ path:
|
|
23839
|
+
const content = readFileSync9(tpl, "utf8").replace(/<PROJECT>/g, path36.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
|
|
23840
|
+
planned.push({ path: path36.join(root, file), content });
|
|
22752
23841
|
}
|
|
22753
23842
|
return planned;
|
|
22754
23843
|
};
|
|
@@ -22758,30 +23847,30 @@ var ensureHygieneFiles = (root, opts) => {
|
|
|
22758
23847
|
const created = [];
|
|
22759
23848
|
for (const planned of planHygieneFiles(root, opts)) {
|
|
22760
23849
|
writeFileSync5(planned.path, planned.content, "utf8");
|
|
22761
|
-
created.push(
|
|
23850
|
+
created.push(path36.basename(planned.path));
|
|
22762
23851
|
}
|
|
22763
23852
|
return { ok: true, created };
|
|
22764
23853
|
};
|
|
22765
23854
|
|
|
22766
23855
|
// packages/workit-core/src/core/init.ts
|
|
22767
23856
|
import fs24 from "node:fs";
|
|
22768
|
-
import
|
|
22769
|
-
import { spawnSync as
|
|
23857
|
+
import path38 from "node:path";
|
|
23858
|
+
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
22770
23859
|
|
|
22771
23860
|
// packages/workit-core/src/core/workspace-branch-policy.ts
|
|
22772
|
-
import { mkdirSync as mkdirSync4, readFileSync as
|
|
22773
|
-
import
|
|
23861
|
+
import { mkdirSync as mkdirSync4, readFileSync as readFileSync10, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
23862
|
+
import path37 from "node:path";
|
|
22774
23863
|
import { isDeepStrictEqual } from "node:util";
|
|
22775
23864
|
var readFileSafe = (p) => {
|
|
22776
23865
|
try {
|
|
22777
|
-
return
|
|
23866
|
+
return readFileSync10(p, "utf8");
|
|
22778
23867
|
} catch {
|
|
22779
23868
|
return null;
|
|
22780
23869
|
}
|
|
22781
23870
|
};
|
|
22782
23871
|
function applyWorkspaceBranchPolicy(opts) {
|
|
22783
23872
|
const { workspace_root, env = process.env } = opts;
|
|
22784
|
-
const dir =
|
|
23873
|
+
const dir = path37.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
22785
23874
|
const {
|
|
22786
23875
|
status,
|
|
22787
23876
|
path: wsPath,
|
|
@@ -22793,7 +23882,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22793
23882
|
if (status === "malformed" || status === "invalid")
|
|
22794
23883
|
return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
|
|
22795
23884
|
const detection = detectBranchPolicy(workspace_root);
|
|
22796
|
-
const name = String(env.WORKFLOW_BP_NAME ??
|
|
23885
|
+
const name = String(env.WORKFLOW_BP_NAME ?? path37.basename(workspace_root));
|
|
22797
23886
|
const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
|
|
22798
23887
|
const policy = {
|
|
22799
23888
|
preset: detection.preset,
|
|
@@ -22842,7 +23931,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22842
23931
|
ok: false,
|
|
22843
23932
|
error: `workspaces.json changed while applying the branch policy: ${wsPath}`
|
|
22844
23933
|
};
|
|
22845
|
-
mkdirSync4(
|
|
23934
|
+
mkdirSync4(path37.dirname(wsPath), { recursive: true });
|
|
22846
23935
|
const tmp = `${wsPath}.${process.pid}.tmp`;
|
|
22847
23936
|
try {
|
|
22848
23937
|
writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
|
|
@@ -22889,7 +23978,7 @@ var resolveInitProvider = (cwd) => {
|
|
|
22889
23978
|
return env.toLowerCase();
|
|
22890
23979
|
const root = cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
22891
23980
|
try {
|
|
22892
|
-
const out =
|
|
23981
|
+
const out = spawnSync20("git", ["remote", "get-url", "origin"], {
|
|
22893
23982
|
cwd: root,
|
|
22894
23983
|
encoding: "utf8"
|
|
22895
23984
|
});
|
|
@@ -22907,7 +23996,7 @@ var youtrackJsonContent = (dir) => {
|
|
|
22907
23996
|
const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
|
|
22908
23997
|
return {
|
|
22909
23998
|
baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
|
|
22910
|
-
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ??
|
|
23999
|
+
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path38.join(dir, "youtrack.token"),
|
|
22911
24000
|
...meetingIssue ? {
|
|
22912
24001
|
meetingIssue,
|
|
22913
24002
|
meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
|
|
@@ -22940,16 +24029,16 @@ function initApplyData(action, env = process.env) {
|
|
|
22940
24029
|
fs24.mkdirSync(dir, { recursive: true });
|
|
22941
24030
|
switch (action) {
|
|
22942
24031
|
case "youtrack_json": {
|
|
22943
|
-
const out =
|
|
24032
|
+
const out = path38.join(dir, "youtrack.json");
|
|
22944
24033
|
fs24.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
22945
24034
|
`, "utf8");
|
|
22946
24035
|
return { action, ok: true, path: out };
|
|
22947
24036
|
}
|
|
22948
24037
|
case "youtrack_token_placeholder": {
|
|
22949
|
-
const p =
|
|
24038
|
+
const p = path38.join(dir, "youtrack.token");
|
|
22950
24039
|
const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
|
|
22951
24040
|
`, 384) === "preserved";
|
|
22952
|
-
const abs =
|
|
24041
|
+
const abs = path38.resolve(p);
|
|
22953
24042
|
return {
|
|
22954
24043
|
action,
|
|
22955
24044
|
ok: true,
|
|
@@ -22961,14 +24050,14 @@ function initApplyData(action, env = process.env) {
|
|
|
22961
24050
|
};
|
|
22962
24051
|
}
|
|
22963
24052
|
case "youtrack_scaffold": {
|
|
22964
|
-
const jsonOut =
|
|
22965
|
-
const tokenOut =
|
|
24053
|
+
const jsonOut = path38.join(dir, "youtrack.json");
|
|
24054
|
+
const tokenOut = path38.join(dir, "youtrack.token");
|
|
22966
24055
|
fs24.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
22967
24056
|
`, "utf8");
|
|
22968
24057
|
const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
|
|
22969
24058
|
`, 384) === "preserved";
|
|
22970
|
-
const configPath =
|
|
22971
|
-
const tokenPath =
|
|
24059
|
+
const configPath = path38.resolve(jsonOut);
|
|
24060
|
+
const tokenPath = path38.resolve(tokenOut);
|
|
22972
24061
|
const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
|
|
22973
24062
|
process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
|
|
22974
24063
|
let tokenCreate = {};
|
|
@@ -23020,10 +24109,10 @@ function initApplyData(action, env = process.env) {
|
|
|
23020
24109
|
}
|
|
23021
24110
|
}
|
|
23022
24111
|
case "vcs_scaffold": {
|
|
23023
|
-
const jsonOut =
|
|
24112
|
+
const jsonOut = path38.join(dir, "vcs.json");
|
|
23024
24113
|
fs24.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
|
|
23025
24114
|
`, "utf8");
|
|
23026
|
-
const configPath =
|
|
24115
|
+
const configPath = path38.resolve(jsonOut);
|
|
23027
24116
|
const prev = process.env.WORKFLOW_VCS_CONFIG;
|
|
23028
24117
|
process.env.WORKFLOW_VCS_CONFIG = configPath;
|
|
23029
24118
|
try {
|
|
@@ -23142,7 +24231,7 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
23142
24231
|
protectedNames: args.branch_policy_protected
|
|
23143
24232
|
}, current);
|
|
23144
24233
|
writeConfig(next);
|
|
23145
|
-
return output(ok({ action: "config", path:
|
|
24234
|
+
return output(ok({ action: "config", path: path39.join(configDir(), "config.json"), ...next }));
|
|
23146
24235
|
}
|
|
23147
24236
|
const env = Object.fromEntries(Object.entries({
|
|
23148
24237
|
WORKFLOW_YT_BASE_URL: args.base_url,
|
|
@@ -23159,14 +24248,14 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
23159
24248
|
|
|
23160
24249
|
// packages/workit-opencode/src/shared/session.ts
|
|
23161
24250
|
import { realpathSync as realpathSync7 } from "node:fs";
|
|
23162
|
-
import
|
|
24251
|
+
import path40 from "node:path";
|
|
23163
24252
|
var sameWorkspace = (expected, observed) => {
|
|
23164
24253
|
if (typeof observed !== "string" || !observed)
|
|
23165
24254
|
return false;
|
|
23166
24255
|
try {
|
|
23167
24256
|
return realpathSync7(expected) === realpathSync7(observed);
|
|
23168
24257
|
} catch {
|
|
23169
|
-
return
|
|
24258
|
+
return path40.resolve(expected) === path40.resolve(observed);
|
|
23170
24259
|
}
|
|
23171
24260
|
};
|
|
23172
24261
|
|
|
@@ -23238,7 +24327,7 @@ var workitFamilyOf = (toolName) => {
|
|
|
23238
24327
|
|
|
23239
24328
|
// packages/workit-opencode/src/tools/context.ts
|
|
23240
24329
|
import { realpathSync as realpathSync8 } from "node:fs";
|
|
23241
|
-
import
|
|
24330
|
+
import path41 from "node:path";
|
|
23242
24331
|
var createContextTool = () => ({
|
|
23243
24332
|
description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
|
|
23244
24333
|
execute: async (args, context) => {
|
|
@@ -23248,7 +24337,7 @@ var createContextTool = () => ({
|
|
|
23248
24337
|
let root;
|
|
23249
24338
|
try {
|
|
23250
24339
|
const cwd = parsed.data.cwd;
|
|
23251
|
-
root = realpathSync8(cwd ?
|
|
24340
|
+
root = realpathSync8(cwd ? path41.resolve(context.directory, cwd) : context.directory);
|
|
23252
24341
|
} catch {
|
|
23253
24342
|
return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
|
|
23254
24343
|
}
|
|
@@ -23280,118 +24369,13 @@ var workerCoordinatorFor = (task, worker) => {
|
|
|
23280
24369
|
// packages/workit-core/src/fanout.ts
|
|
23281
24370
|
import { createHash as createHash17 } from "node:crypto";
|
|
23282
24371
|
import fs26 from "node:fs";
|
|
23283
|
-
import
|
|
24372
|
+
import path43 from "node:path";
|
|
23284
24373
|
|
|
23285
24374
|
// packages/workit-core/src/stack.ts
|
|
23286
24375
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
23287
24376
|
import { createHash as createHash16, randomBytes as randomBytes4 } from "node:crypto";
|
|
23288
24377
|
import fs25 from "node:fs";
|
|
23289
|
-
import
|
|
23290
|
-
|
|
23291
|
-
// packages/workit-core/src/git/ops.ts
|
|
23292
|
-
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
23293
|
-
import { existsSync as existsSync11, readFileSync as readFileSync9 } from "node:fs";
|
|
23294
|
-
import path40 from "node:path";
|
|
23295
|
-
|
|
23296
|
-
// packages/workit-core/src/forge/redact.ts
|
|
23297
|
-
var KEY = String.raw`[A-Za-z0-9_.-]*?(?:token|secret|password|passwd|pwd|api[_-]?key|access[_-]?key|account[_-]?key|private[_-]?key|client[_-]?secret|credentials?|authorization|cookie|signature)[A-Za-z0-9_.-]*`;
|
|
23298
|
-
var ASSIGNMENT2 = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
|
|
23299
|
-
var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
|
|
23300
|
-
var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
|
|
23301
|
-
|
|
23302
|
-
// packages/workit-core/src/forge/types.ts
|
|
23303
|
-
var success2 = (data) => ({ ok: true, data });
|
|
23304
|
-
var failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
|
|
23305
|
-
|
|
23306
|
-
// packages/workit-core/src/git/ops.ts
|
|
23307
|
-
var COMMIT_TIMEOUT_MS = 10 * 60000;
|
|
23308
|
-
var git5 = (cwd, args, options = {}) => {
|
|
23309
|
-
const result = spawnSync20("git", args, {
|
|
23310
|
-
cwd,
|
|
23311
|
-
encoding: "utf8",
|
|
23312
|
-
env: { ...options.env ?? process.env, GIT_TERMINAL_PROMPT: "0" },
|
|
23313
|
-
maxBuffer: 64 * 1024 * 1024,
|
|
23314
|
-
timeout: options.timeoutMs ?? GIT_TIMEOUTS.local,
|
|
23315
|
-
killSignal: "SIGKILL",
|
|
23316
|
-
windowsHide: true,
|
|
23317
|
-
...options.input === undefined ? { stdio: ["ignore", "pipe", "pipe"] } : { input: options.input, stdio: ["pipe", "pipe", "pipe"] }
|
|
23318
|
-
});
|
|
23319
|
-
return {
|
|
23320
|
-
ok: result.status === 0,
|
|
23321
|
-
status: result.status,
|
|
23322
|
-
stdout: result.stdout ?? "",
|
|
23323
|
-
stderr: result.stderr ?? ""
|
|
23324
|
-
};
|
|
23325
|
-
};
|
|
23326
|
-
function operationInProgress(cwd) {
|
|
23327
|
-
const markers = [
|
|
23328
|
-
["rebase-merge", "rebase"],
|
|
23329
|
-
["rebase-apply", "rebase"],
|
|
23330
|
-
["MERGE_HEAD", "merge"],
|
|
23331
|
-
["CHERRY_PICK_HEAD", "cherry-pick"],
|
|
23332
|
-
["REVERT_HEAD", "revert"]
|
|
23333
|
-
];
|
|
23334
|
-
const run = git5(cwd, ["rev-parse", ...markers.flatMap(([name]) => ["--git-path", name])]);
|
|
23335
|
-
if (!run.ok)
|
|
23336
|
-
return null;
|
|
23337
|
-
const found = run.stdout.split(`
|
|
23338
|
-
`).map((line) => path40.resolve(cwd, line.trim()));
|
|
23339
|
-
const conflicted = () => git5(cwd, ["ls-files", "-u"]).stdout.trim() !== "";
|
|
23340
|
-
for (const [index, [, operation]] of markers.entries()) {
|
|
23341
|
-
const marker = found[index];
|
|
23342
|
-
if (!marker || !existsSync11(marker))
|
|
23343
|
-
continue;
|
|
23344
|
-
if (index === 1 && existsSync11(path40.join(marker, "applying")))
|
|
23345
|
-
return { operation: "am", commitAllowed: false, branch: null };
|
|
23346
|
-
if (index === 0 && existsSync11(path40.join(marker, "amend")) && !conflicted()) {
|
|
23347
|
-
let headName = "";
|
|
23348
|
-
try {
|
|
23349
|
-
headName = readFileSync9(path40.join(marker, "head-name"), "utf8").trim();
|
|
23350
|
-
} catch {}
|
|
23351
|
-
const branch = headName.startsWith("refs/heads/") ? headName.slice("refs/heads/".length) : null;
|
|
23352
|
-
return { operation, commitAllowed: branch !== null, branch };
|
|
23353
|
-
}
|
|
23354
|
-
if ((operation === "revert" || operation === "merge") && !conflicted())
|
|
23355
|
-
return { operation, commitAllowed: true, branch: null };
|
|
23356
|
-
return { operation, commitAllowed: false, branch: null };
|
|
23357
|
-
}
|
|
23358
|
-
return null;
|
|
23359
|
-
}
|
|
23360
|
-
function inProgressFailure(operation, verb) {
|
|
23361
|
-
const command = operation === "am" ? "git am" : `git ${operation}`;
|
|
23362
|
-
return failure3("blocked", `${operation.replace("-", "_")}_in_progress: a ${operation === "am" ? "git am" : operation} is in progress; finish or abort it before workit can ${verb}`, `resolve the conflicts, git add <files>, then ${command} --continue # or ${command} --abort to return to where you started`);
|
|
23363
|
-
}
|
|
23364
|
-
function pushPreflight(cwd, options = {}) {
|
|
23365
|
-
const progress = operationInProgress(cwd);
|
|
23366
|
-
if (progress)
|
|
23367
|
-
return inProgressFailure(progress.operation, "push");
|
|
23368
|
-
const branch = options.branch ?? currentBranch(cwd);
|
|
23369
|
-
if (!branch)
|
|
23370
|
-
return failure3("invalid_input", "HEAD is detached; push a branch");
|
|
23371
|
-
if (isProtectedTarget(cwd, branch))
|
|
23372
|
-
return failure3("blocked", `protected_branch: ${branch} is protected by the workspace branch policy; workit git push never pushes to it`, releaseBranch(cwd, branch) ? `workit git push --release ${branch} # finishing a release or hotfix; needs the release grant` : "workit git branch <feature/name> # push a feature branch and open a PR");
|
|
23373
|
-
const sha = resolveRef(cwd, `refs/heads/${branch}`);
|
|
23374
|
-
if (!sha)
|
|
23375
|
-
return failure3("not_found", `branch ${branch} has no commit to push`);
|
|
23376
|
-
const remote = pushRemoteName(cwd, branch);
|
|
23377
|
-
if (!remote)
|
|
23378
|
-
return failure3("not_found", "no push remote is configured for this branch", "git remote add origin <url>");
|
|
23379
|
-
const raw = pushUrl(cwd, remote);
|
|
23380
|
-
if (!raw)
|
|
23381
|
-
return failure3("blocked", `remote "${remote}" has no single push URL`, `git remote get-url --push --all ${remote} # keep exactly one push URL`);
|
|
23382
|
-
const parsed = parseRemoteUrl(raw);
|
|
23383
|
-
const local = parsed?.protocol === "file";
|
|
23384
|
-
if (!local && !pushTargetIsStable(cwd, raw))
|
|
23385
|
-
return failure3("blocked", `push_target_rewritten: the push URL of "${remote}" is rewritten by a url.*.insteadOf rule, so the destination is not the one checked`, "git config --get-regexp '^url\\.' # remove the rewrite for this remote");
|
|
23386
|
-
return success2({
|
|
23387
|
-
branch,
|
|
23388
|
-
sha,
|
|
23389
|
-
remote,
|
|
23390
|
-
url: redactRemote(raw),
|
|
23391
|
-
rawUrl: raw,
|
|
23392
|
-
local
|
|
23393
|
-
});
|
|
23394
|
-
}
|
|
24378
|
+
import path42 from "node:path";
|
|
23395
24379
|
|
|
23396
24380
|
// packages/workit-core/src/forge/report.ts
|
|
23397
24381
|
var GITLAB_MERGEABLE = new Set(["mergeable"]);
|
|
@@ -23473,7 +24457,7 @@ function rebaseInProgress(dir) {
|
|
|
23473
24457
|
}
|
|
23474
24458
|
var scratchDir = (cwd) => {
|
|
23475
24459
|
const dir = stacksDir(cwd);
|
|
23476
|
-
return dir.ok ?
|
|
24460
|
+
return dir.ok ? path42.join(dir.data, "worktrees") : null;
|
|
23477
24461
|
};
|
|
23478
24462
|
function gitRestack(cwd, input) {
|
|
23479
24463
|
const owner = worktreeOf(cwd, input.branch);
|
|
@@ -23483,7 +24467,7 @@ function gitRestack(cwd, input) {
|
|
|
23483
24467
|
if (!scratch)
|
|
23484
24468
|
return { ok: false, conflict: false, worktree: cwd, error: "no store root" };
|
|
23485
24469
|
fs25.mkdirSync(scratch, { recursive: true });
|
|
23486
|
-
dir =
|
|
24470
|
+
dir = path42.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
|
|
23487
24471
|
const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
|
|
23488
24472
|
if (!added.ok)
|
|
23489
24473
|
return { ok: false, conflict: false, worktree: cwd, error: firstLine(added) };
|
|
@@ -23527,7 +24511,7 @@ function stacksDir(cwd) {
|
|
|
23527
24511
|
const root = storeRoot(cwd);
|
|
23528
24512
|
if (!root.ok)
|
|
23529
24513
|
return stackFail(root.code, root.error, root.unblock);
|
|
23530
|
-
return { ok: true, data:
|
|
24514
|
+
return { ok: true, data: path42.join(root.value.root, "stacks") };
|
|
23531
24515
|
}
|
|
23532
24516
|
var HEARTBEAT_STALE_MS = 2 * 60000;
|
|
23533
24517
|
|
|
@@ -23638,7 +24622,7 @@ function fanoutsDir(cwd) {
|
|
|
23638
24622
|
const root = storeRoot(cwd);
|
|
23639
24623
|
if (!root.ok)
|
|
23640
24624
|
return fanoutFail(root.code, root.error, root.unblock);
|
|
23641
|
-
return { ok: true, data:
|
|
24625
|
+
return { ok: true, data: path43.join(root.value.root, "fanouts") };
|
|
23642
24626
|
}
|
|
23643
24627
|
function parseStored(raw) {
|
|
23644
24628
|
if (!isRecord7(raw) || typeof raw.name !== "string" || typeof raw.trunk !== "string")
|
|
@@ -23706,7 +24690,7 @@ function readFanout(cwd, name) {
|
|
|
23706
24690
|
const dir = fanoutsDir(cwd);
|
|
23707
24691
|
if (!dir.ok)
|
|
23708
24692
|
return dir;
|
|
23709
|
-
const file =
|
|
24693
|
+
const file = path43.join(dir.data, stackFileName(name));
|
|
23710
24694
|
let parsed = null;
|
|
23711
24695
|
try {
|
|
23712
24696
|
parsed = parseStored(JSON.parse(fs26.readFileSync(file, "utf8")));
|
|
@@ -23731,7 +24715,7 @@ function listFanouts(cwd) {
|
|
|
23731
24715
|
for (const entry of names) {
|
|
23732
24716
|
let parsed = null;
|
|
23733
24717
|
try {
|
|
23734
|
-
parsed = parseStored(JSON.parse(fs26.readFileSync(
|
|
24718
|
+
parsed = parseStored(JSON.parse(fs26.readFileSync(path43.join(dir.data, entry), "utf8")));
|
|
23735
24719
|
} catch {
|
|
23736
24720
|
parsed = null;
|
|
23737
24721
|
}
|
|
@@ -24423,7 +25407,7 @@ var childTaskMissing = (store, input) => {
|
|
|
24423
25407
|
var childDenial = (identity) => `OpenCode child session has no validated Workit worker: as ${identity} it may only inspect the task summary (workit_task action "inspect") or record evidence or findings (workit_evidence/workit_finding action "record") on an existing task; for anything else the lead assigns it a worker with workit_worker before launching it`;
|
|
24424
25408
|
|
|
24425
25409
|
// packages/workit-opencode/src/v2/shell.ts
|
|
24426
|
-
import
|
|
25410
|
+
import path44 from "node:path";
|
|
24427
25411
|
var commandOf = (input) => {
|
|
24428
25412
|
if (input === null || typeof input !== "object")
|
|
24429
25413
|
return null;
|
|
@@ -24432,7 +25416,7 @@ var commandOf = (input) => {
|
|
|
24432
25416
|
};
|
|
24433
25417
|
var hookInput = (root, sessionID, id, shell) => ({
|
|
24434
25418
|
host: "opencode",
|
|
24435
|
-
cwd: shell.workdir ?
|
|
25419
|
+
cwd: shell.workdir ? path44.resolve(root, shell.workdir) : root,
|
|
24436
25420
|
session: { id: sessionID, agentId: null, agentType: null, parentId: null },
|
|
24437
25421
|
permissionMode: null,
|
|
24438
25422
|
transcriptPath: null,
|
|
@@ -24638,38 +25622,38 @@ var createStopControl = (root, port) => {
|
|
|
24638
25622
|
};
|
|
24639
25623
|
|
|
24640
25624
|
// packages/workit-opencode/src/v2/registry.ts
|
|
24641
|
-
import { existsSync as
|
|
24642
|
-
import
|
|
25625
|
+
import { existsSync as existsSync15, readFileSync as readFileSync11 } from "node:fs";
|
|
25626
|
+
import path46 from "node:path";
|
|
24643
25627
|
|
|
24644
25628
|
// packages/workit-opencode/src/shared/assets.ts
|
|
24645
|
-
import { existsSync as
|
|
24646
|
-
import
|
|
25629
|
+
import { existsSync as existsSync14, realpathSync as realpathSync9 } from "node:fs";
|
|
25630
|
+
import path45 from "node:path";
|
|
24647
25631
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
24648
25632
|
var moduleDir = () => {
|
|
24649
25633
|
try {
|
|
24650
|
-
return realpathSync9(
|
|
25634
|
+
return realpathSync9(path45.dirname(fileURLToPath2(import.meta.url)));
|
|
24651
25635
|
} catch {
|
|
24652
|
-
return
|
|
25636
|
+
return path45.dirname(fileURLToPath2(import.meta.url));
|
|
24653
25637
|
}
|
|
24654
25638
|
};
|
|
24655
25639
|
var packageRoot2 = () => {
|
|
24656
25640
|
const dir = moduleDir();
|
|
24657
25641
|
for (const candidate of [
|
|
24658
|
-
|
|
24659
|
-
|
|
24660
|
-
|
|
25642
|
+
path45.resolve(dir, ".."),
|
|
25643
|
+
path45.resolve(dir, "..", ".."),
|
|
25644
|
+
path45.resolve(dir, "..", "..", "..")
|
|
24661
25645
|
]) {
|
|
24662
|
-
if (
|
|
25646
|
+
if (existsSync14(path45.join(candidate, "package.json")))
|
|
24663
25647
|
return candidate;
|
|
24664
25648
|
}
|
|
24665
|
-
return
|
|
25649
|
+
return path45.resolve(dir, "..");
|
|
24666
25650
|
};
|
|
24667
25651
|
var skillsRoot = () => {
|
|
24668
25652
|
const root = packageRoot2();
|
|
24669
|
-
const canonical =
|
|
24670
|
-
if (
|
|
25653
|
+
const canonical = path45.resolve(root, "..", "workit-core", "skills");
|
|
25654
|
+
if (existsSync14(path45.join(root, "src")) && existsSync14(canonical))
|
|
24671
25655
|
return canonical;
|
|
24672
|
-
return
|
|
25656
|
+
return path45.join(root, "assets", "skills");
|
|
24673
25657
|
};
|
|
24674
25658
|
|
|
24675
25659
|
// packages/workit-opencode/src/v2/registry.ts
|
|
@@ -24693,15 +25677,15 @@ var parseFrontmatter = (markdown) => {
|
|
|
24693
25677
|
};
|
|
24694
25678
|
var registerSkills = async (ctx) => {
|
|
24695
25679
|
const skillsDir = skillsRoot();
|
|
24696
|
-
if (!
|
|
25680
|
+
if (!existsSync15(skillsDir))
|
|
24697
25681
|
return new Set;
|
|
24698
25682
|
const listed = await ctx.skill.list().catch(() => ({ data: [] }));
|
|
24699
|
-
const own = (skill) => typeof skill.path === "string" &&
|
|
25683
|
+
const own = (skill) => typeof skill.path === "string" && path46.resolve(skill.path).startsWith(skillsDir + path46.sep);
|
|
24700
25684
|
const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
|
|
24701
25685
|
const packaged = new Map;
|
|
24702
25686
|
for (const id of WORKIT_METHOD_SKILLS) {
|
|
24703
|
-
const file =
|
|
24704
|
-
if (!userSkills.has(id) &&
|
|
25687
|
+
const file = path46.join(skillsDir, id, "SKILL.md");
|
|
25688
|
+
if (!userSkills.has(id) && existsSync15(file))
|
|
24705
25689
|
packaged.set(id, file);
|
|
24706
25690
|
}
|
|
24707
25691
|
await ctx.skill.transform((editor) => {
|
|
@@ -24709,7 +25693,7 @@ var registerSkills = async (ctx) => {
|
|
|
24709
25693
|
for (const [id, file] of packaged) {
|
|
24710
25694
|
if (existing.has(id))
|
|
24711
25695
|
continue;
|
|
24712
|
-
const parsed = parseFrontmatter(
|
|
25696
|
+
const parsed = parseFrontmatter(readFileSync11(file, "utf8"));
|
|
24713
25697
|
editor.add({
|
|
24714
25698
|
id,
|
|
24715
25699
|
name: parsed.name || id,
|
|
@@ -24747,25 +25731,25 @@ var registerCommands = async (ctx, registeredSkills) => {
|
|
|
24747
25731
|
};
|
|
24748
25732
|
|
|
24749
25733
|
// packages/workit-opencode/src/stale-sources.ts
|
|
24750
|
-
import { existsSync as
|
|
24751
|
-
import
|
|
25734
|
+
import { existsSync as existsSync16, readdirSync as readdirSync6 } from "node:fs";
|
|
25735
|
+
import path47 from "node:path";
|
|
24752
25736
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
24753
25737
|
var typescriptFiles = (directory) => {
|
|
24754
|
-
if (!
|
|
25738
|
+
if (!existsSync16(directory))
|
|
24755
25739
|
return [];
|
|
24756
25740
|
return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
24757
|
-
const file =
|
|
25741
|
+
const file = path47.join(directory, entry.name);
|
|
24758
25742
|
return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
|
|
24759
25743
|
});
|
|
24760
25744
|
};
|
|
24761
25745
|
var pluginSourceFiles = [
|
|
24762
25746
|
...new Set([
|
|
24763
25747
|
fileURLToPath3(import.meta.url),
|
|
24764
|
-
...typescriptFiles(
|
|
24765
|
-
|
|
24766
|
-
...typescriptFiles(
|
|
25748
|
+
...typescriptFiles(path47.join(packageRoot2(), "src")),
|
|
25749
|
+
path47.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
|
|
25750
|
+
...typescriptFiles(path47.join(packageRoot2(), "..", "workit-core", "src", "core"))
|
|
24767
25751
|
])
|
|
24768
|
-
].filter((file) =>
|
|
25752
|
+
].filter((file) => existsSync16(file)).toSorted();
|
|
24769
25753
|
|
|
24770
25754
|
// packages/workit-opencode/src/v2/plugin.ts
|
|
24771
25755
|
var sessionFacts = async (ctx, sessionID) => {
|