@brainervirus/workit-cursor 8.10.0 → 8.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +553 -278
- package/dist/mcp-server.js +66 -57
- package/dist/workit-hook.js +569 -277
- package/package.json +3 -3
package/dist/workit-hook.js
CHANGED
|
@@ -8792,6 +8792,7 @@ var branchNames = (workspaceRoot) => {
|
|
|
8792
8792
|
return new Set((r.stdout ?? "").split(`
|
|
8793
8793
|
`).filter(Boolean).map((ref) => ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref.slice("refs/remotes/origin/".length)));
|
|
8794
8794
|
};
|
|
8795
|
+
var productionBranchOf = (names) => names.has("main") ? "main" : names.has("master") ? "master" : "main";
|
|
8795
8796
|
var detectBranchPolicy = (workspaceRoot) => {
|
|
8796
8797
|
const names = branchNames(workspaceRoot);
|
|
8797
8798
|
const develop = names.has("develop");
|
|
@@ -17666,6 +17667,18 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
|
|
|
17666
17667
|
else
|
|
17667
17668
|
clearTurnDigest(file);
|
|
17668
17669
|
};
|
|
17670
|
+
// packages/workit-core/src/core/route-intent.ts
|
|
17671
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
17672
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
17673
|
+
import path20 from "node:path";
|
|
17674
|
+
|
|
17675
|
+
// packages/workit-core/src/hooks/raw-git.ts
|
|
17676
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17677
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
17678
|
+
import fs16 from "node:fs";
|
|
17679
|
+
import os4 from "node:os";
|
|
17680
|
+
import path19 from "node:path";
|
|
17681
|
+
|
|
17669
17682
|
// packages/workit-core/src/core/branch.ts
|
|
17670
17683
|
import { createHash as createHash12 } from "node:crypto";
|
|
17671
17684
|
var withReleaseTracks = (workspaceRoot, policy) => {
|
|
@@ -17703,10 +17716,72 @@ function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
|
|
|
17703
17716
|
provenance: { commitPolicy: selected.source }
|
|
17704
17717
|
};
|
|
17705
17718
|
}
|
|
17719
|
+
var KIND_SYNONYMS = {
|
|
17720
|
+
fix: "bugfix",
|
|
17721
|
+
fixes: "bugfix",
|
|
17722
|
+
bug: "bugfix",
|
|
17723
|
+
bugs: "bugfix",
|
|
17724
|
+
bugfixes: "bugfix",
|
|
17725
|
+
feat: "feature",
|
|
17726
|
+
features: "feature",
|
|
17727
|
+
chore: "feature",
|
|
17728
|
+
docs: "feature",
|
|
17729
|
+
doc: "feature",
|
|
17730
|
+
refactor: "feature",
|
|
17731
|
+
test: "feature",
|
|
17732
|
+
tests: "feature",
|
|
17733
|
+
ci: "feature",
|
|
17734
|
+
build: "feature",
|
|
17735
|
+
perf: "feature",
|
|
17736
|
+
style: "feature",
|
|
17737
|
+
deps: "feature",
|
|
17738
|
+
task: "feature",
|
|
17739
|
+
enhancement: "feature",
|
|
17740
|
+
hot: "hotfix",
|
|
17741
|
+
hotfixes: "hotfix",
|
|
17742
|
+
releases: "release",
|
|
17743
|
+
rel: "release"
|
|
17744
|
+
};
|
|
17745
|
+
var allowedKinds = (allowed) => [
|
|
17746
|
+
...new Set(allowed.map((pattern) => /^\^([\w.-]+)\\?\/\.\*\$$/u.exec(pattern.source)?.[1]?.toLowerCase()).filter((kind) => Boolean(kind)))
|
|
17747
|
+
];
|
|
17748
|
+
var editDistance = (a, b) => {
|
|
17749
|
+
const row = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
17750
|
+
for (let i = 1;i <= a.length; i++) {
|
|
17751
|
+
let previous = row[0];
|
|
17752
|
+
row[0] = i;
|
|
17753
|
+
for (let j = 1;j <= b.length; j++) {
|
|
17754
|
+
const saved = row[j];
|
|
17755
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
17756
|
+
previous = saved;
|
|
17757
|
+
}
|
|
17758
|
+
}
|
|
17759
|
+
return row[b.length];
|
|
17760
|
+
};
|
|
17761
|
+
var suggestBranchName = (name, kinds) => {
|
|
17762
|
+
if (!kinds.length)
|
|
17763
|
+
return null;
|
|
17764
|
+
const slash = name.indexOf("/");
|
|
17765
|
+
const prefix = slash > 0 ? name.slice(0, slash).toLowerCase() : "";
|
|
17766
|
+
const rest = (slash > 0 ? name.slice(slash + 1) : name) || "name";
|
|
17767
|
+
const synonym = KIND_SYNONYMS[prefix];
|
|
17768
|
+
const near = kinds.map((kind) => ({ kind, distance: editDistance(prefix, kind) })).filter((entry) => prefix && entry.distance <= 2).toSorted((a, b) => a.distance - b.distance)[0]?.kind;
|
|
17769
|
+
const kind = (synonym && kinds.includes(synonym) ? synonym : null) ?? near ?? (kinds.includes("feature") ? "feature" : kinds[0]);
|
|
17770
|
+
return `${kind}/${rest}`;
|
|
17771
|
+
};
|
|
17772
|
+
var kindList = (kinds) => kinds.map((kind) => `${kind}/`).join(", ");
|
|
17773
|
+
var aliasNote = (kinds) => {
|
|
17774
|
+
const notes = [
|
|
17775
|
+
["fix", "bugfix"],
|
|
17776
|
+
["chore", "feature"]
|
|
17777
|
+
].filter(([, kind]) => kinds.includes(kind)).map(([alias, kind]) => `${alias}/ is ${kind}/`);
|
|
17778
|
+
return notes.length ? ` (${notes.join(", ")})` : "";
|
|
17779
|
+
};
|
|
17706
17780
|
var validateBranchNamePolicy = (name, policy, source) => {
|
|
17707
17781
|
const allowed = policy.allowed.map((pattern) => `/${pattern.source}/${pattern.flags}`);
|
|
17782
|
+
const kinds = allowedKinds(policy.allowed);
|
|
17708
17783
|
if (policy.protected.has(name.toLowerCase())) {
|
|
17709
|
-
const correction = `choose a non-protected branch matching one of: ${allowed.join(", ")}`;
|
|
17784
|
+
const correction = kinds.length ? `choose a non-protected branch of an allowed kind (${kindList(kinds)}), e.g. ${kinds[0]}/<slug>: workit git branch --kind ${kinds[0]} --slug <slug>` : `choose a non-protected branch matching one of: ${allowed.join(", ")}`;
|
|
17710
17785
|
return {
|
|
17711
17786
|
ok: false,
|
|
17712
17787
|
rule: "protected_ref",
|
|
@@ -17717,7 +17792,8 @@ var validateBranchNamePolicy = (name, policy, source) => {
|
|
|
17717
17792
|
};
|
|
17718
17793
|
}
|
|
17719
17794
|
if (!policy.allowed.some((pattern) => pattern.test(name))) {
|
|
17720
|
-
const
|
|
17795
|
+
const suggestion = suggestBranchName(name, kinds);
|
|
17796
|
+
const correction = suggestion && policy.allowed.some((pattern) => pattern.test(suggestion)) ? `use ${JSON.stringify(suggestion)}; the allowed kinds are ${kindList(kinds)} (workit git branch ${suggestion})` : `choose a branch matching one of: ${allowed.join(", ")}`;
|
|
17721
17797
|
return {
|
|
17722
17798
|
ok: false,
|
|
17723
17799
|
rule: "allowed_pattern",
|
|
@@ -17760,31 +17836,87 @@ var validateBranchNameFor = (workspaceRoot, name) => {
|
|
|
17760
17836
|
}
|
|
17761
17837
|
};
|
|
17762
17838
|
var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
|
|
17763
|
-
|
|
17764
|
-
|
|
17765
|
-
|
|
17766
|
-
|
|
17767
|
-
|
|
17768
|
-
|
|
17769
|
-
|
|
17839
|
+
var gitFlowOf = (root, integration) => {
|
|
17840
|
+
try {
|
|
17841
|
+
if (resolveBranchPolicyFor(root).preset !== "gitflow")
|
|
17842
|
+
return null;
|
|
17843
|
+
const selected = resolveRuntimeWorkspacePolicy(root, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined).policy;
|
|
17844
|
+
const names = branchNames(root);
|
|
17845
|
+
const production = productionBranchOf(names);
|
|
17846
|
+
const develop = selected?.developBranch?.trim() || (integration && integration.toLowerCase() !== production ? integration : "develop");
|
|
17847
|
+
return { develop, production, developExists: names.has(develop) };
|
|
17848
|
+
} catch {
|
|
17770
17849
|
return null;
|
|
17771
|
-
|
|
17772
|
-
const target = args[1] === "switch" && ["-c", "--create", "-C", "--force-create"].includes(option ?? "") && (args.length === 4 || args.length === 5) ? args[3] : args[1] === "checkout" && ["-b", "-B"].includes(option ?? "") && (args.length === 4 || args.length === 5) ? args[3] : args[1] === "branch" && (args.length === 3 || args.length === 4) ? args[2] : null;
|
|
17773
|
-
return target && !target.startsWith("-") ? target : null;
|
|
17850
|
+
}
|
|
17774
17851
|
};
|
|
17775
|
-
var
|
|
17776
|
-
|
|
17777
|
-
|
|
17852
|
+
var releaseBranchesOf = (cwd) => {
|
|
17853
|
+
try {
|
|
17854
|
+
const resolved = resolveBranchPolicyFor(cwd);
|
|
17855
|
+
const flow = resolved.preset === "gitflow" ? gitFlowOf(cwd) : null;
|
|
17856
|
+
return {
|
|
17857
|
+
preset: resolved.preset,
|
|
17858
|
+
branches: flow ? [flow.production, flow.develop] : [],
|
|
17859
|
+
production: flow ? flow.production : resolved.defaultTargetBranch
|
|
17860
|
+
};
|
|
17861
|
+
} catch {
|
|
17862
|
+
return { preset: "unknown", branches: [], production: null };
|
|
17863
|
+
}
|
|
17778
17864
|
};
|
|
17779
|
-
|
|
17780
|
-
|
|
17781
|
-
|
|
17782
|
-
|
|
17783
|
-
|
|
17865
|
+
var releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase());
|
|
17866
|
+
var branchGuidance = (cwd) => {
|
|
17867
|
+
try {
|
|
17868
|
+
const patterns = branchPolicySnapshotFor(cwd).namePolicy.allowed;
|
|
17869
|
+
const kinds = allowedKinds(patterns);
|
|
17870
|
+
const kindShaped = new Set(kinds.map((kind) => branchGlobPattern(`${kind}/*`).source));
|
|
17871
|
+
const anyName = patterns.some((pattern) => pattern.source === "^.*$");
|
|
17872
|
+
const others = patterns.filter((pattern) => !kindShaped.has(pattern.source)).map((pattern) => globOf(pattern));
|
|
17873
|
+
const allowed = anyName ? "Any non-protected branch name is allowed here." : `${kinds.length ? `Allowed branch kinds here: ${kindList(kinds)}${aliasNote(kinds)}` : "Allowed branch names here:"}${others.length ? `${kinds.length ? "; also" : ""} names matching ${others.join(", ")}` : ""}; anything else is denied.`;
|
|
17874
|
+
const resolved = vcsConfig("resolve", cwd);
|
|
17875
|
+
const integration = resolved.ok === false ? null : String(resolved.defaultTargetBranch ?? "") || null;
|
|
17876
|
+
const flow = resolved.releaseTrack?.track ? null : gitFlowOf(cwd, integration);
|
|
17877
|
+
if (flow?.developExists)
|
|
17878
|
+
return `${allowed} git-flow: feature/, bugfix/ and release/ start from origin/${flow.develop}, hotfix/ from origin/${flow.production}; feature/ and bugfix/ land on ${flow.develop}, release/ and hotfix/ on ${flow.production}.`;
|
|
17879
|
+
const names = branchNames(cwd);
|
|
17880
|
+
const configured = String(resolved.baseBranch ?? integration ?? "") || null;
|
|
17881
|
+
const trunk = productionBranchOf(names);
|
|
17882
|
+
const base = configured && names.has(configured) ? `Branch from origin/${configured}.` : names.has(trunk) ? `Branch from origin/${trunk}${configured ? ` (the default target ${configured} does not exist here)` : ""}.` : "No base branch exists here yet; pass the base explicitly.";
|
|
17883
|
+
return `${allowed} ${base}`;
|
|
17884
|
+
} catch {
|
|
17885
|
+
return null;
|
|
17886
|
+
}
|
|
17784
17887
|
};
|
|
17785
|
-
|
|
17786
|
-
|
|
17888
|
+
var globOf = (pattern) => pattern.source.replace(/^\^|\$$/gu, "").replaceAll(".*", "*").replace(/\\(.)/gu, "$1");
|
|
17889
|
+
|
|
17890
|
+
// packages/workit-core/src/hooks/session-marker.ts
|
|
17891
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
17892
|
+
import fs15 from "node:fs";
|
|
17787
17893
|
import path18 from "node:path";
|
|
17894
|
+
var sessionMarkerFile = (input, session, store) => {
|
|
17895
|
+
let dir = store;
|
|
17896
|
+
if (dir === undefined) {
|
|
17897
|
+
const location = resolveStore(input.cwd);
|
|
17898
|
+
if (location instanceof Error || !location.shared)
|
|
17899
|
+
return null;
|
|
17900
|
+
dir = location.dir;
|
|
17901
|
+
}
|
|
17902
|
+
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
17903
|
+
return path18.join(dir, "hooks", `skills-${name}.json`);
|
|
17904
|
+
};
|
|
17905
|
+
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
17906
|
+
var readSessionMarker = (file) => {
|
|
17907
|
+
try {
|
|
17908
|
+
const value = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
17909
|
+
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
17910
|
+
} catch {
|
|
17911
|
+
return { nudged: [], loaded: [], raw: [] };
|
|
17912
|
+
}
|
|
17913
|
+
};
|
|
17914
|
+
var writeSessionMarker = (file, marker) => {
|
|
17915
|
+
try {
|
|
17916
|
+
fs15.mkdirSync(path18.dirname(file), { recursive: true });
|
|
17917
|
+
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
17918
|
+
} catch {}
|
|
17919
|
+
};
|
|
17788
17920
|
|
|
17789
17921
|
// packages/workit-core/src/hooks/shell-words.ts
|
|
17790
17922
|
var closingQuote = (command, start, dialect) => {
|
|
@@ -17909,224 +18041,10 @@ var segmentsOf = (command, dialect = "posix") => {
|
|
|
17909
18041
|
return segments;
|
|
17910
18042
|
};
|
|
17911
18043
|
|
|
17912
|
-
// packages/workit-core/src/hooks/write-gate.ts
|
|
17913
|
-
var NONE2 = { kind: "none" };
|
|
17914
|
-
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
17915
|
-
var canonical2 = (file) => {
|
|
17916
|
-
let rest = "";
|
|
17917
|
-
for (let dir = path18.resolve(file);; dir = path18.dirname(dir)) {
|
|
17918
|
-
try {
|
|
17919
|
-
return path18.join(realpathSync5(dir), rest);
|
|
17920
|
-
} catch {
|
|
17921
|
-
if (path18.dirname(dir) === dir)
|
|
17922
|
-
return path18.resolve(file);
|
|
17923
|
-
rest = rest ? path18.join(path18.basename(dir), rest) : path18.basename(dir);
|
|
17924
|
-
}
|
|
17925
|
-
}
|
|
17926
|
-
};
|
|
17927
|
-
var exempt = (cwd, root, file, plans) => {
|
|
17928
|
-
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
17929
|
-
const relative = path18.relative(fold(canonical2(root)), fold(canonical2(path18.resolve(cwd, file))));
|
|
17930
|
-
if (relative.startsWith("..") || path18.isAbsolute(relative))
|
|
17931
|
-
return true;
|
|
17932
|
-
const posix = relative.split(path18.sep).join("/");
|
|
17933
|
-
const segments = posix.split("/");
|
|
17934
|
-
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
17935
|
-
};
|
|
17936
|
-
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
17937
|
-
var operands = (words) => words.filter((item) => !item.startsWith("-"));
|
|
17938
|
-
var editsOf = (input) => {
|
|
17939
|
-
let words = input;
|
|
17940
|
-
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
|
|
17941
|
-
words = words.slice(1);
|
|
17942
|
-
const [verb, ...args] = words;
|
|
17943
|
-
const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
17944
|
-
if (!name || name === "workit" || name === "npx" && args.some((item) => /workit/.test(item)))
|
|
17945
|
-
return [];
|
|
17946
|
-
switch (name) {
|
|
17947
|
-
case "touch":
|
|
17948
|
-
case "mkdir":
|
|
17949
|
-
case "rm":
|
|
17950
|
-
case "rmdir":
|
|
17951
|
-
case "tee":
|
|
17952
|
-
case "mv":
|
|
17953
|
-
case "truncate":
|
|
17954
|
-
return operands(args);
|
|
17955
|
-
case "cp":
|
|
17956
|
-
case "ln":
|
|
17957
|
-
case "install": {
|
|
17958
|
-
const target = args.indexOf("-t");
|
|
17959
|
-
if (target >= 0)
|
|
17960
|
-
return args[target + 1] ? [args[target + 1]] : null;
|
|
17961
|
-
const last = operands(args).at(-1);
|
|
17962
|
-
return last ? [last] : null;
|
|
17963
|
-
}
|
|
17964
|
-
case "dd":
|
|
17965
|
-
return args.filter((item) => item.startsWith("of=")).map((item) => item.slice(3));
|
|
17966
|
-
case "sed":
|
|
17967
|
-
case "perl": {
|
|
17968
|
-
if (!args.some((item) => /^-[a-zA-Z]*i|^--in-place/.test(item)))
|
|
17969
|
-
return [];
|
|
17970
|
-
const scripted = args.some((item) => item === "-e" || item === "-f");
|
|
17971
|
-
const files = [];
|
|
17972
|
-
for (let index = 0;index < args.length; index++) {
|
|
17973
|
-
if (args[index] === "-e" || args[index] === "-f")
|
|
17974
|
-
index++;
|
|
17975
|
-
else if (!args[index].startsWith("-"))
|
|
17976
|
-
files.push(args[index]);
|
|
17977
|
-
}
|
|
17978
|
-
return scripted ? files : files.slice(1);
|
|
17979
|
-
}
|
|
17980
|
-
case "patch":
|
|
17981
|
-
return null;
|
|
17982
|
-
case "set-content":
|
|
17983
|
-
case "add-content":
|
|
17984
|
-
case "out-file":
|
|
17985
|
-
case "new-item":
|
|
17986
|
-
case "ni":
|
|
17987
|
-
case "remove-item":
|
|
17988
|
-
case "del":
|
|
17989
|
-
case "copy-item":
|
|
17990
|
-
case "move-item": {
|
|
17991
|
-
const flag = args.findIndex((item) => /^-(?:path|filepath|literalpath|destination)$/i.test(item));
|
|
17992
|
-
if (flag >= 0)
|
|
17993
|
-
return args[flag + 1] ? [args[flag + 1]] : null;
|
|
17994
|
-
const files = operands(args);
|
|
17995
|
-
if (name === "move-item")
|
|
17996
|
-
return files.length ? files : null;
|
|
17997
|
-
const file = name === "copy-item" ? files.at(-1) : files[0];
|
|
17998
|
-
return file ? [file] : null;
|
|
17999
|
-
}
|
|
18000
|
-
case "git": {
|
|
18001
|
-
const sub = operands(args)[0];
|
|
18002
|
-
if (sub === "apply")
|
|
18003
|
-
return null;
|
|
18004
|
-
if (sub === "restore" || sub === "mv" || sub === "rm")
|
|
18005
|
-
return operands(args).slice(1);
|
|
18006
|
-
if (sub === "checkout" && args.includes("--"))
|
|
18007
|
-
return args.slice(args.indexOf("--") + 1);
|
|
18008
|
-
return [];
|
|
18009
|
-
}
|
|
18010
|
-
default:
|
|
18011
|
-
return [];
|
|
18012
|
-
}
|
|
18013
|
-
};
|
|
18014
|
-
var shellWrites = (command) => {
|
|
18015
|
-
const targets = [];
|
|
18016
|
-
let unknown = false;
|
|
18017
|
-
for (const segment of segmentsOf(command)) {
|
|
18018
|
-
targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
|
|
18019
|
-
const edits = editsOf(segment.words);
|
|
18020
|
-
if (edits === null)
|
|
18021
|
-
unknown = true;
|
|
18022
|
-
else
|
|
18023
|
-
targets.push(...edits);
|
|
18024
|
-
}
|
|
18025
|
-
if (!unknown && targets.length === 0)
|
|
18026
|
-
return { writes: false, targets: null };
|
|
18027
|
-
return { writes: true, targets: unknown ? null : targets };
|
|
18028
|
-
};
|
|
18029
|
-
var cache2 = new Map;
|
|
18030
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path18.resolve(root, file)) ?? "-").join("\x00");
|
|
18031
|
-
var gateFor = (cwd) => {
|
|
18032
|
-
const store = new TaskStore(cwd);
|
|
18033
|
-
const listed = store.listTaskIndex();
|
|
18034
|
-
if (!listed.ok)
|
|
18035
|
-
return null;
|
|
18036
|
-
const key = store.currentKey();
|
|
18037
|
-
if (!key.ok)
|
|
18038
|
-
return null;
|
|
18039
|
-
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
18040
|
-
if (!entry)
|
|
18041
|
-
return { blockers: [], root: store.root, plans: new Set };
|
|
18042
|
-
const ledger = ledgerPath(store.root);
|
|
18043
|
-
const cacheKey = `${entry.id}\x00${entry.revision}\x00${ledger.ok && fileSignature(ledger.value) || "-"}`;
|
|
18044
|
-
const hit = cache2.get(store.root);
|
|
18045
|
-
if (hit && hit.key === cacheKey && hit.planKey === planKeyOf(store.root, hit.plans))
|
|
18046
|
-
return hit;
|
|
18047
|
-
const task = store.readTask(entry.id);
|
|
18048
|
-
if (!task.ok)
|
|
18049
|
-
return null;
|
|
18050
|
-
const workspace = store.readWorkspace();
|
|
18051
|
-
const plans = new Set((latestJudgment(task.data)?.refs ?? []).flatMap((ref) => ref.kind === "file" ? [ref.path] : []));
|
|
18052
|
-
const blockers = writeBlockers(task.data, store.root).filter((blocker) => {
|
|
18053
|
-
const requirement = task.data.policy?.requirements.find((item) => item.ruleId === blocker.ruleId && item.before === "write");
|
|
18054
|
-
return !(requirement?.acceptanceAllowed && workspace.ok && workspace.data && applicableDecision(task.data, workspace.data, requirement, store.root).length > 0);
|
|
18055
|
-
});
|
|
18056
|
-
const gate = {
|
|
18057
|
-
blockers,
|
|
18058
|
-
root: store.root,
|
|
18059
|
-
plans,
|
|
18060
|
-
key: cacheKey,
|
|
18061
|
-
planKey: planKeyOf(store.root, plans)
|
|
18062
|
-
};
|
|
18063
|
-
cache2.set(store.root, gate);
|
|
18064
|
-
if (cache2.size > 32)
|
|
18065
|
-
cache2.delete(cache2.keys().next().value);
|
|
18066
|
-
return gate;
|
|
18067
|
-
};
|
|
18068
|
-
function writeGate(cwd, paths) {
|
|
18069
|
-
try {
|
|
18070
|
-
const gate = gateFor(cwd);
|
|
18071
|
-
if (!gate || gate.blockers.length === 0)
|
|
18072
|
-
return NONE2;
|
|
18073
|
-
if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
18074
|
-
return NONE2;
|
|
18075
|
-
const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
|
|
18076
|
-
return {
|
|
18077
|
-
kind: "deny",
|
|
18078
|
-
reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
|
|
18079
|
-
unblock: gate.blockers[0].reason
|
|
18080
|
-
};
|
|
18081
|
-
} catch {
|
|
18082
|
-
return NONE2;
|
|
18083
|
-
}
|
|
18084
|
-
}
|
|
18085
|
-
// packages/workit-core/src/hooks/stop.ts
|
|
18086
|
-
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
18087
|
-
|
|
18088
18044
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
18089
|
-
|
|
18090
|
-
import { createHash as createHash14 } from "node:crypto";
|
|
18091
|
-
import fs16 from "node:fs";
|
|
18092
|
-
import os4 from "node:os";
|
|
18093
|
-
import path20 from "node:path";
|
|
18094
|
-
|
|
18095
|
-
// packages/workit-core/src/hooks/session-marker.ts
|
|
18096
|
-
import { createHash as createHash13 } from "node:crypto";
|
|
18097
|
-
import fs15 from "node:fs";
|
|
18098
|
-
import path19 from "node:path";
|
|
18099
|
-
var sessionMarkerFile = (input, session, store) => {
|
|
18100
|
-
let dir = store;
|
|
18101
|
-
if (dir === undefined) {
|
|
18102
|
-
const location = resolveStore(input.cwd);
|
|
18103
|
-
if (location instanceof Error || !location.shared)
|
|
18104
|
-
return null;
|
|
18105
|
-
dir = location.dir;
|
|
18106
|
-
}
|
|
18107
|
-
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
18108
|
-
return path19.join(dir, "hooks", `skills-${name}.json`);
|
|
18109
|
-
};
|
|
18110
|
-
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
18111
|
-
var readSessionMarker = (file) => {
|
|
18112
|
-
try {
|
|
18113
|
-
const value = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
18114
|
-
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
18115
|
-
} catch {
|
|
18116
|
-
return { nudged: [], loaded: [], raw: [] };
|
|
18117
|
-
}
|
|
18118
|
-
};
|
|
18119
|
-
var writeSessionMarker = (file, marker) => {
|
|
18120
|
-
try {
|
|
18121
|
-
fs15.mkdirSync(path19.dirname(file), { recursive: true });
|
|
18122
|
-
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
18123
|
-
} catch {}
|
|
18124
|
-
};
|
|
18125
|
-
|
|
18126
|
-
// packages/workit-core/src/hooks/raw-git.ts
|
|
18127
|
-
var NONE3 = { kind: "none" };
|
|
18045
|
+
var NONE2 = { kind: "none" };
|
|
18128
18046
|
var SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
18129
|
-
var
|
|
18047
|
+
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
|
|
18130
18048
|
var SCRIPT_FLAG = /^-[a-zA-Z]*c[a-zA-Z]*$/;
|
|
18131
18049
|
var CD = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
18132
18050
|
var PUSHD = new Set(["pushd", "push-location"]);
|
|
@@ -18140,12 +18058,12 @@ var GIT_VALUE_OPTIONS = new Set([
|
|
|
18140
18058
|
"--config-env",
|
|
18141
18059
|
"--super-prefix"
|
|
18142
18060
|
]);
|
|
18143
|
-
var isAbsolute = (value) =>
|
|
18061
|
+
var isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
18144
18062
|
var joinDir = (base, next) => {
|
|
18145
18063
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
18146
18064
|
return;
|
|
18147
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
18148
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
18065
|
+
const expanded = next === "~" || next.startsWith("~/") ? path19.join(os4.homedir(), next.slice(1)) : next;
|
|
18066
|
+
return base === null || isAbsolute(expanded) ? expanded : path19.join(base, expanded);
|
|
18149
18067
|
};
|
|
18150
18068
|
var cdTarget = (words) => {
|
|
18151
18069
|
const args = words.slice(1);
|
|
@@ -18162,7 +18080,7 @@ var stripPrefixes = (input) => {
|
|
|
18162
18080
|
return words;
|
|
18163
18081
|
if (/^\w+=/.test(first))
|
|
18164
18082
|
words = words.slice(1);
|
|
18165
|
-
else if (
|
|
18083
|
+
else if (PREFIXES2.has(first)) {
|
|
18166
18084
|
words = words.slice(1);
|
|
18167
18085
|
while (words[0]?.startsWith("-"))
|
|
18168
18086
|
words = words.slice(words[0] === "-u" ? 2 : 1);
|
|
@@ -18277,6 +18195,10 @@ var pushAction = (args) => {
|
|
|
18277
18195
|
let force = false;
|
|
18278
18196
|
let lease = false;
|
|
18279
18197
|
let wholesale = false;
|
|
18198
|
+
let allTags = false;
|
|
18199
|
+
let deletes = false;
|
|
18200
|
+
let allBranches = false;
|
|
18201
|
+
let mirror = false;
|
|
18280
18202
|
let remote = null;
|
|
18281
18203
|
const positionals = [];
|
|
18282
18204
|
for (let index = 1;index < args.length; index++) {
|
|
@@ -18297,14 +18219,22 @@ var pushAction = (args) => {
|
|
|
18297
18219
|
force = true;
|
|
18298
18220
|
else if (arg.startsWith("--force-with-lease") || arg === "--force-if-includes")
|
|
18299
18221
|
lease = true;
|
|
18300
|
-
else if (
|
|
18301
|
-
|
|
18222
|
+
else if (arg === "--delete" || arg === "-d")
|
|
18223
|
+
deletes = true;
|
|
18224
|
+
else if (arg === "--tags")
|
|
18225
|
+
allTags = wholesale = true;
|
|
18226
|
+
else if (arg === "--all" || arg === "--branches")
|
|
18227
|
+
allBranches = wholesale = true;
|
|
18228
|
+
else if (arg === "--mirror")
|
|
18229
|
+
mirror = wholesale = true;
|
|
18302
18230
|
else if (!arg.startsWith("-"))
|
|
18303
18231
|
positionals.push(arg);
|
|
18304
18232
|
}
|
|
18305
18233
|
const specs = remote === null ? positionals.slice(1) : positionals;
|
|
18306
18234
|
remote ??= positionals[0] ?? null;
|
|
18307
18235
|
const targets = [];
|
|
18236
|
+
const tags = [];
|
|
18237
|
+
const deleted = [];
|
|
18308
18238
|
for (const raw of specs) {
|
|
18309
18239
|
if (raw.startsWith("+"))
|
|
18310
18240
|
force = true;
|
|
@@ -18313,16 +18243,29 @@ var pushAction = (args) => {
|
|
|
18313
18243
|
const destination = colon >= 0 ? spec.slice(colon + 1) : spec;
|
|
18314
18244
|
if (!destination)
|
|
18315
18245
|
continue;
|
|
18316
|
-
|
|
18317
|
-
|
|
18318
|
-
|
|
18319
|
-
|
|
18320
|
-
|
|
18321
|
-
|
|
18246
|
+
const name = destination === "HEAD" ? null : destination.startsWith("refs/heads/") ? destination.slice(11) : destination.startsWith("refs/") ? undefined : destination;
|
|
18247
|
+
if (destination.startsWith("refs/tags/"))
|
|
18248
|
+
tags.push(destination.slice(10));
|
|
18249
|
+
if (name === undefined)
|
|
18250
|
+
continue;
|
|
18251
|
+
targets.push(name);
|
|
18252
|
+
if (colon === 0 && name !== null)
|
|
18253
|
+
deleted.push(name);
|
|
18322
18254
|
}
|
|
18323
18255
|
if (specs.length === 0 && !wholesale)
|
|
18324
18256
|
targets.push(null);
|
|
18325
|
-
return {
|
|
18257
|
+
return {
|
|
18258
|
+
kind: "push",
|
|
18259
|
+
targets,
|
|
18260
|
+
blindForce: force && !lease,
|
|
18261
|
+
remote,
|
|
18262
|
+
...tags.length ? { tags } : {},
|
|
18263
|
+
...allTags ? { allTags: true } : {},
|
|
18264
|
+
...deletes ? { deletes: true } : {},
|
|
18265
|
+
...deleted.length ? { deleted } : {},
|
|
18266
|
+
...allBranches ? { allBranches: true } : {},
|
|
18267
|
+
...mirror ? { mirror: true } : {}
|
|
18268
|
+
};
|
|
18326
18269
|
};
|
|
18327
18270
|
var VALUE_OPTIONS = {
|
|
18328
18271
|
git: new Set([
|
|
@@ -18429,28 +18372,80 @@ var headSha2 = (repo) => {
|
|
|
18429
18372
|
if (!git || git.reftable)
|
|
18430
18373
|
return null;
|
|
18431
18374
|
try {
|
|
18432
|
-
const head = fs16.readFileSync(
|
|
18375
|
+
const head = fs16.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18433
18376
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18434
18377
|
return head;
|
|
18435
18378
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
18436
18379
|
if (!ref)
|
|
18437
18380
|
return null;
|
|
18438
|
-
const common =
|
|
18381
|
+
const common = path19.dirname(repo.location.dir);
|
|
18439
18382
|
try {
|
|
18440
|
-
return fs16.readFileSync(
|
|
18383
|
+
return fs16.readFileSync(path19.join(common, ref), "utf8").trim() || null;
|
|
18441
18384
|
} catch {}
|
|
18442
|
-
const packed = fs16.readFileSync(
|
|
18385
|
+
const packed = fs16.readFileSync(path19.join(common, "packed-refs"), "utf8");
|
|
18443
18386
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18444
18387
|
} catch {
|
|
18445
18388
|
return null;
|
|
18446
18389
|
}
|
|
18447
18390
|
};
|
|
18448
18391
|
var quote = (text) => `\`${text}\``;
|
|
18449
|
-
var refusal = (action, branch) => {
|
|
18392
|
+
var refusal = (action, branch, dir) => {
|
|
18450
18393
|
if (action.kind === "merge")
|
|
18451
18394
|
return `workit: raw ${quote(action.forge === "gh" ? "gh pr merge" : "glab mr merge")} bypasses the merge grant and the verdict gate. Run ${quote("workit pr merge")} instead (it checks both; ${quote("workit help pr")}).`;
|
|
18395
|
+
const plainPush = action.kind === "push" && !action.blindForce && !action.deletes;
|
|
18396
|
+
if (dir && branch && plainPush && releaseBranch(dir, branch))
|
|
18397
|
+
return `workit: raw ${quote("git push")} onto ${branch}, a protected release branch, bypasses workit's protected-branch check. To finish a release or hotfix, push it with ${quote(`workit git push --release ${branch} [--release v<version>]`)}: it pushes ${branch} (fast-forward only) and the release tag, verifies the remote and records it, under the ${quote("release")} grant (if refused, ask the user to run ${quote("workit grant set <workspace> release=true")}). Feature work goes through ${quote("workit git push")} and ${quote("workit pr merge")}.`;
|
|
18452
18398
|
return `workit: raw ${quote("git push")}${action.kind === "push" && action.blindForce ? " --force" : ""} onto ${branch}, a protected branch or the default target, bypasses workit's protected-branch check. Push your feature branch with ${quote("workit git push")} (${quote("workit git push --force-with-lease")} to rewrite it) and land it with ${quote("workit pr merge")}.`;
|
|
18453
18399
|
};
|
|
18400
|
+
var refFacts = (repo, remote) => {
|
|
18401
|
+
const facts = { heads: new Set, tags: new Set, remoteHeads: new Set };
|
|
18402
|
+
const listed = spawnSync11("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/tags", `refs/remotes/${remote}`], { cwd: repo.dir, encoding: "utf8", timeout: 5000 });
|
|
18403
|
+
const remotePrefix = `refs/remotes/${remote}/`;
|
|
18404
|
+
for (const ref of (listed.stdout ?? "").split(`
|
|
18405
|
+
`)) {
|
|
18406
|
+
if (ref.startsWith("refs/heads/"))
|
|
18407
|
+
facts.heads.add(ref.slice(11));
|
|
18408
|
+
else if (ref.startsWith("refs/tags/"))
|
|
18409
|
+
facts.tags.add(ref.slice(10));
|
|
18410
|
+
else if (ref.startsWith(remotePrefix))
|
|
18411
|
+
facts.remoteHeads.add(ref.slice(remotePrefix.length));
|
|
18412
|
+
}
|
|
18413
|
+
return facts;
|
|
18414
|
+
};
|
|
18415
|
+
var isTag = (facts, name) => facts.tags.has(name) && !facts.heads.has(name);
|
|
18416
|
+
var tagRefusal = (action, facts) => {
|
|
18417
|
+
const tags = [
|
|
18418
|
+
...action.tags ?? [],
|
|
18419
|
+
...action.targets.filter((name) => name !== null && isTag(facts, name))
|
|
18420
|
+
];
|
|
18421
|
+
if (!tags.length && !action.allTags)
|
|
18422
|
+
return null;
|
|
18423
|
+
const named = tags.length ? tags.join(" --release ") : "v<version>";
|
|
18424
|
+
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}`)} (v<version> 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")}).`;
|
|
18425
|
+
};
|
|
18426
|
+
var remoteHas = (repo, remote, branch) => {
|
|
18427
|
+
const listed = spawnSync11("git", ["ls-remote", "--heads", remote, `refs/heads/${branch}`], {
|
|
18428
|
+
cwd: repo.dir,
|
|
18429
|
+
encoding: "utf8",
|
|
18430
|
+
timeout: 3000,
|
|
18431
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
18432
|
+
});
|
|
18433
|
+
return listed.status !== 0 || (listed.stdout ?? "").trim() !== "";
|
|
18434
|
+
};
|
|
18435
|
+
var offPolicyPush = (action, repo, facts, remote) => {
|
|
18436
|
+
const names = action.allBranches ? [...facts.heads] : action.targets.map((target) => target ?? repo.branch);
|
|
18437
|
+
for (const branch of names) {
|
|
18438
|
+
if (!branch || action.deleted?.includes(branch) || isTag(facts, branch))
|
|
18439
|
+
continue;
|
|
18440
|
+
if (facts.remoteHeads.has(branch))
|
|
18441
|
+
continue;
|
|
18442
|
+
const check = validateBranchNameFor(repo.dir, branch);
|
|
18443
|
+
if (check.ok || check.rule !== "allowed_pattern" || remoteHas(repo, remote, branch))
|
|
18444
|
+
continue;
|
|
18445
|
+
return `workit: raw ${quote("git push")} onto ${branch}: ${check.error}`;
|
|
18446
|
+
}
|
|
18447
|
+
return null;
|
|
18448
|
+
};
|
|
18454
18449
|
var nudgeFor = (action, host, session) => {
|
|
18455
18450
|
switch (action.kind) {
|
|
18456
18451
|
case "commit": {
|
|
@@ -18476,7 +18471,7 @@ var actionsIn = (cwd, command, dialect) => {
|
|
|
18476
18471
|
const action = rawAction(invocation);
|
|
18477
18472
|
if (!action)
|
|
18478
18473
|
continue;
|
|
18479
|
-
const dir =
|
|
18474
|
+
const dir = path19.resolve(cwd, invocation.dir ?? ".");
|
|
18480
18475
|
if (!repos.has(dir)) {
|
|
18481
18476
|
const repo = repoAt(dir);
|
|
18482
18477
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -18513,7 +18508,7 @@ var unseenNudges = (input, nudges, store) => {
|
|
|
18513
18508
|
function rawGitPre(input, command, options = {}) {
|
|
18514
18509
|
try {
|
|
18515
18510
|
if (!mentionsRawTool(command))
|
|
18516
|
-
return
|
|
18511
|
+
return NONE2;
|
|
18517
18512
|
const dialect = dialectOf(input);
|
|
18518
18513
|
const nudges = new Map;
|
|
18519
18514
|
let store = null;
|
|
@@ -18521,31 +18516,42 @@ function rawGitPre(input, command, options = {}) {
|
|
|
18521
18516
|
store ??= repo.location.dir;
|
|
18522
18517
|
if (action.kind === "merge")
|
|
18523
18518
|
return deny(refusal(action, null));
|
|
18524
|
-
if (action.kind === "push")
|
|
18519
|
+
if (action.kind === "push" && toPushRemote(repo, action.remote)) {
|
|
18520
|
+
if (action.mirror)
|
|
18521
|
+
return deny(`workit: raw ${quote("git push --mirror")} rewrites and deletes every remote ref, protected branches and tags included. Push one branch with ${quote("workit git push")}.`);
|
|
18525
18522
|
for (const target of action.targets) {
|
|
18526
18523
|
const branch = target ?? repo.branch;
|
|
18527
|
-
if (branch && guardedBranch(repo.dir, branch)
|
|
18528
|
-
return deny(refusal(action, branch));
|
|
18524
|
+
if (branch && guardedBranch(repo.dir, branch))
|
|
18525
|
+
return deny(refusal(action, branch, repo.dir));
|
|
18529
18526
|
}
|
|
18527
|
+
const remote = action.remote ?? pushRemoteName(repo.dir, repo.branch) ?? "origin";
|
|
18528
|
+
const facts = refFacts(repo, remote);
|
|
18529
|
+
const tagDeny = tagRefusal(action, facts);
|
|
18530
|
+
if (tagDeny)
|
|
18531
|
+
return deny(tagDeny);
|
|
18532
|
+
const nameDeny = action.deletes ? null : offPolicyPush(action, repo, facts, remote);
|
|
18533
|
+
if (nameDeny)
|
|
18534
|
+
return deny(nameDeny);
|
|
18535
|
+
}
|
|
18530
18536
|
const nudge = nudgeFor(action, input.host, input.session.id);
|
|
18531
18537
|
if (nudge && !nudges.has(nudgeKind(action)))
|
|
18532
18538
|
nudges.set(nudgeKind(action), nudge);
|
|
18533
18539
|
}
|
|
18534
18540
|
if (options.nudge === false || !store || nudges.size === 0)
|
|
18535
|
-
return
|
|
18541
|
+
return NONE2;
|
|
18536
18542
|
if (workitInvocations(command, dialect).length > 0)
|
|
18537
|
-
return
|
|
18543
|
+
return NONE2;
|
|
18538
18544
|
const shown = unseenNudges(input, nudges, store);
|
|
18539
18545
|
return shown.length ? { kind: "context", text: shown.join(`
|
|
18540
|
-
`) } :
|
|
18546
|
+
`) } : NONE2;
|
|
18541
18547
|
} catch {
|
|
18542
|
-
return
|
|
18548
|
+
return NONE2;
|
|
18543
18549
|
}
|
|
18544
18550
|
}
|
|
18545
18551
|
var deny = (reason) => ({ kind: "deny", reason, unblock: null });
|
|
18546
18552
|
var NEXT_COMMAND = "next";
|
|
18547
18553
|
var MARKER_TTL_SECONDS = 3600;
|
|
18548
|
-
var markerFile = (repo, session, key) =>
|
|
18554
|
+
var markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
18549
18555
|
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
|
|
18550
18556
|
function noteRawCommit(input, command, key) {
|
|
18551
18557
|
try {
|
|
@@ -18556,7 +18562,7 @@ function noteRawCommit(input, command, key) {
|
|
|
18556
18562
|
if (action.kind !== "commit")
|
|
18557
18563
|
continue;
|
|
18558
18564
|
const file = markerFile(repo, input.session.id, key);
|
|
18559
|
-
fs16.mkdirSync(
|
|
18565
|
+
fs16.mkdirSync(path19.dirname(file), { recursive: true });
|
|
18560
18566
|
fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
18561
18567
|
`);
|
|
18562
18568
|
}
|
|
@@ -18638,7 +18644,292 @@ function settlePendingCommit(input) {
|
|
|
18638
18644
|
} catch {}
|
|
18639
18645
|
}
|
|
18640
18646
|
|
|
18647
|
+
// packages/workit-core/src/core/route-intent.ts
|
|
18648
|
+
var UNKNOWABLE = /[$`*?[\]{}~]/u;
|
|
18649
|
+
var SWITCH_CREATE = new Set(["-c", "--create", "-C", "--force-create", "--orphan"]);
|
|
18650
|
+
var CHECKOUT_CREATE = new Set(["-b", "-B", "--orphan"]);
|
|
18651
|
+
var WORKTREE_CREATE = new Set(["-b", "-B"]);
|
|
18652
|
+
var BRANCH_NO_CREATE = new Set(["-d", "-D", "--delete", "-l", "--list", "-a", "--all", "-r", "--remotes", "--show-current"].concat(["-v", "-vv", "--verbose", "--contains", "--no-contains", "--merged", "--no-merged"]).concat(["--points-at", "--sort", "--format", "--column", "--no-column", "-u"]).concat(["--set-upstream-to", "--unset-upstream", "--edit-description", "-i", "--ignore-case"]));
|
|
18653
|
+
var BRANCH_RENAME = new Set(["-m", "-M", "--move", "-c", "-C", "--copy"]);
|
|
18654
|
+
var flagValue = (args, flags) => {
|
|
18655
|
+
for (let index = 1;index < args.length; index++) {
|
|
18656
|
+
const arg = args[index];
|
|
18657
|
+
if (arg === "--")
|
|
18658
|
+
return null;
|
|
18659
|
+
if (flags.has(arg))
|
|
18660
|
+
return args[index + 1] ?? null;
|
|
18661
|
+
const eq = arg.indexOf("=");
|
|
18662
|
+
if (arg.startsWith("--") && eq > 0 && flags.has(arg.slice(0, eq)))
|
|
18663
|
+
return arg.slice(eq + 1);
|
|
18664
|
+
if (!arg.startsWith("--") && arg.length > 2 && flags.has(arg.slice(0, 2)))
|
|
18665
|
+
return arg.slice(2);
|
|
18666
|
+
}
|
|
18667
|
+
return null;
|
|
18668
|
+
};
|
|
18669
|
+
var WORKTREE_VALUES = new Set(["--reason"]);
|
|
18670
|
+
var worktreeTarget = (args) => {
|
|
18671
|
+
const named = flagValue(args, WORKTREE_CREATE);
|
|
18672
|
+
if (named !== null)
|
|
18673
|
+
return named;
|
|
18674
|
+
const positionals = [];
|
|
18675
|
+
for (let index = 1;index < args.length; index++) {
|
|
18676
|
+
const arg = args[index];
|
|
18677
|
+
if (arg === "--detach" || arg === "-d")
|
|
18678
|
+
return null;
|
|
18679
|
+
if (WORKTREE_VALUES.has(arg))
|
|
18680
|
+
index++;
|
|
18681
|
+
else if (!arg.startsWith("-"))
|
|
18682
|
+
positionals.push(arg);
|
|
18683
|
+
}
|
|
18684
|
+
if (positionals.length !== 1)
|
|
18685
|
+
return null;
|
|
18686
|
+
const base = positionals[0].split(/[\\/]/u).findLast(Boolean) ?? "";
|
|
18687
|
+
return base && base !== "." && base !== ".." ? base : null;
|
|
18688
|
+
};
|
|
18689
|
+
var branchCommandTarget = (args) => {
|
|
18690
|
+
let rename = false;
|
|
18691
|
+
const positionals = [];
|
|
18692
|
+
for (let index = 1;index < args.length; index++) {
|
|
18693
|
+
const arg = args[index];
|
|
18694
|
+
if (arg === "--") {
|
|
18695
|
+
positionals.push(...args.slice(index + 1));
|
|
18696
|
+
break;
|
|
18697
|
+
}
|
|
18698
|
+
const flag = arg.split("=", 1)[0];
|
|
18699
|
+
if (BRANCH_NO_CREATE.has(flag))
|
|
18700
|
+
return null;
|
|
18701
|
+
if (BRANCH_RENAME.has(flag))
|
|
18702
|
+
rename = true;
|
|
18703
|
+
else if (!arg.startsWith("-"))
|
|
18704
|
+
positionals.push(arg);
|
|
18705
|
+
}
|
|
18706
|
+
if (positionals.length < 1 || positionals.length > 2)
|
|
18707
|
+
return null;
|
|
18708
|
+
return rename ? positionals.at(-1) ?? null : positionals[0];
|
|
18709
|
+
};
|
|
18710
|
+
var gitBranchCreation = (args) => {
|
|
18711
|
+
const [sub, second] = args;
|
|
18712
|
+
const target = sub === "switch" ? flagValue(args, SWITCH_CREATE) : sub === "checkout" ? flagValue(args, CHECKOUT_CREATE) : sub === "branch" ? branchCommandTarget(args) : sub === "worktree" && second === "add" ? worktreeTarget(args.slice(1)) : null;
|
|
18713
|
+
return target && !target.startsWith("-") && !UNKNOWABLE.test(target) ? target : null;
|
|
18714
|
+
};
|
|
18715
|
+
var shellBranchTargets = (command, dialect = "posix") => {
|
|
18716
|
+
if (!/\bgit\b/u.test(command))
|
|
18717
|
+
return [];
|
|
18718
|
+
const found = [];
|
|
18719
|
+
for (const invocation of rawInvocations(command, dialect)) {
|
|
18720
|
+
if (invocation.tool !== "git")
|
|
18721
|
+
continue;
|
|
18722
|
+
const branch = gitBranchCreation(invocation.args);
|
|
18723
|
+
if (branch)
|
|
18724
|
+
found.push({ branch, dir: invocation.dir });
|
|
18725
|
+
}
|
|
18726
|
+
return found;
|
|
18727
|
+
};
|
|
18728
|
+
var localBranchExists = (dir, branch) => spawnSync12("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
|
|
18729
|
+
cwd: dir,
|
|
18730
|
+
stdio: "ignore",
|
|
18731
|
+
timeout: 5000
|
|
18732
|
+
}).status === 0;
|
|
18733
|
+
var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
18734
|
+
let judged = false;
|
|
18735
|
+
for (const target of shellBranchTargets(command, dialect)) {
|
|
18736
|
+
if (target.dir === undefined)
|
|
18737
|
+
continue;
|
|
18738
|
+
const dir = target.dir === null ? root : path20.resolve(root, target.dir);
|
|
18739
|
+
if (target.dir !== null && (!existsSync6(dir) || !inWorkitWorkspace(dir)))
|
|
18740
|
+
continue;
|
|
18741
|
+
if (localBranchExists(dir, target.branch))
|
|
18742
|
+
continue;
|
|
18743
|
+
judged = true;
|
|
18744
|
+
const check = validateBranchNameFor(dir, target.branch);
|
|
18745
|
+
if (!check.ok)
|
|
18746
|
+
return check;
|
|
18747
|
+
}
|
|
18748
|
+
return judged ? { ok: true } : null;
|
|
18749
|
+
};
|
|
18750
|
+
|
|
18751
|
+
// packages/workit-core/src/hooks/policy.ts
|
|
18752
|
+
var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
18753
|
+
const policy = shellBranchPolicyViolation(cwd, command, dialect);
|
|
18754
|
+
return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
|
|
18755
|
+
};
|
|
18756
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
18757
|
+
import { realpathSync as realpathSync5 } from "node:fs";
|
|
18758
|
+
import path21 from "node:path";
|
|
18759
|
+
var NONE3 = { kind: "none" };
|
|
18760
|
+
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
18761
|
+
var canonical2 = (file) => {
|
|
18762
|
+
let rest = "";
|
|
18763
|
+
for (let dir = path21.resolve(file);; dir = path21.dirname(dir)) {
|
|
18764
|
+
try {
|
|
18765
|
+
return path21.join(realpathSync5(dir), rest);
|
|
18766
|
+
} catch {
|
|
18767
|
+
if (path21.dirname(dir) === dir)
|
|
18768
|
+
return path21.resolve(file);
|
|
18769
|
+
rest = rest ? path21.join(path21.basename(dir), rest) : path21.basename(dir);
|
|
18770
|
+
}
|
|
18771
|
+
}
|
|
18772
|
+
};
|
|
18773
|
+
var exempt = (cwd, root, file, plans) => {
|
|
18774
|
+
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
18775
|
+
const relative = path21.relative(fold(canonical2(root)), fold(canonical2(path21.resolve(cwd, file))));
|
|
18776
|
+
if (relative.startsWith("..") || path21.isAbsolute(relative))
|
|
18777
|
+
return true;
|
|
18778
|
+
const posix = relative.split(path21.sep).join("/");
|
|
18779
|
+
const segments = posix.split("/");
|
|
18780
|
+
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
18781
|
+
};
|
|
18782
|
+
var PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
18783
|
+
var operands = (words) => words.filter((item) => !item.startsWith("-"));
|
|
18784
|
+
var editsOf = (input) => {
|
|
18785
|
+
let words = input;
|
|
18786
|
+
while (words.length && (PREFIXES3.has(words[0]) || /^\w+=/.test(words[0])))
|
|
18787
|
+
words = words.slice(1);
|
|
18788
|
+
const [verb, ...args] = words;
|
|
18789
|
+
const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
18790
|
+
if (!name || name === "workit" || name === "npx" && args.some((item) => /workit/.test(item)))
|
|
18791
|
+
return [];
|
|
18792
|
+
switch (name) {
|
|
18793
|
+
case "touch":
|
|
18794
|
+
case "mkdir":
|
|
18795
|
+
case "rm":
|
|
18796
|
+
case "rmdir":
|
|
18797
|
+
case "tee":
|
|
18798
|
+
case "mv":
|
|
18799
|
+
case "truncate":
|
|
18800
|
+
return operands(args);
|
|
18801
|
+
case "cp":
|
|
18802
|
+
case "ln":
|
|
18803
|
+
case "install": {
|
|
18804
|
+
const target = args.indexOf("-t");
|
|
18805
|
+
if (target >= 0)
|
|
18806
|
+
return args[target + 1] ? [args[target + 1]] : null;
|
|
18807
|
+
const last = operands(args).at(-1);
|
|
18808
|
+
return last ? [last] : null;
|
|
18809
|
+
}
|
|
18810
|
+
case "dd":
|
|
18811
|
+
return args.filter((item) => item.startsWith("of=")).map((item) => item.slice(3));
|
|
18812
|
+
case "sed":
|
|
18813
|
+
case "perl": {
|
|
18814
|
+
if (!args.some((item) => /^-[a-zA-Z]*i|^--in-place/.test(item)))
|
|
18815
|
+
return [];
|
|
18816
|
+
const scripted = args.some((item) => item === "-e" || item === "-f");
|
|
18817
|
+
const files = [];
|
|
18818
|
+
for (let index = 0;index < args.length; index++) {
|
|
18819
|
+
if (args[index] === "-e" || args[index] === "-f")
|
|
18820
|
+
index++;
|
|
18821
|
+
else if (!args[index].startsWith("-"))
|
|
18822
|
+
files.push(args[index]);
|
|
18823
|
+
}
|
|
18824
|
+
return scripted ? files : files.slice(1);
|
|
18825
|
+
}
|
|
18826
|
+
case "patch":
|
|
18827
|
+
return null;
|
|
18828
|
+
case "set-content":
|
|
18829
|
+
case "add-content":
|
|
18830
|
+
case "out-file":
|
|
18831
|
+
case "new-item":
|
|
18832
|
+
case "ni":
|
|
18833
|
+
case "remove-item":
|
|
18834
|
+
case "del":
|
|
18835
|
+
case "copy-item":
|
|
18836
|
+
case "move-item": {
|
|
18837
|
+
const flag = args.findIndex((item) => /^-(?:path|filepath|literalpath|destination)$/i.test(item));
|
|
18838
|
+
if (flag >= 0)
|
|
18839
|
+
return args[flag + 1] ? [args[flag + 1]] : null;
|
|
18840
|
+
const files = operands(args);
|
|
18841
|
+
if (name === "move-item")
|
|
18842
|
+
return files.length ? files : null;
|
|
18843
|
+
const file = name === "copy-item" ? files.at(-1) : files[0];
|
|
18844
|
+
return file ? [file] : null;
|
|
18845
|
+
}
|
|
18846
|
+
case "git": {
|
|
18847
|
+
const sub = operands(args)[0];
|
|
18848
|
+
if (sub === "apply")
|
|
18849
|
+
return null;
|
|
18850
|
+
if (sub === "restore" || sub === "mv" || sub === "rm")
|
|
18851
|
+
return operands(args).slice(1);
|
|
18852
|
+
if (sub === "checkout" && args.includes("--"))
|
|
18853
|
+
return args.slice(args.indexOf("--") + 1);
|
|
18854
|
+
return [];
|
|
18855
|
+
}
|
|
18856
|
+
default:
|
|
18857
|
+
return [];
|
|
18858
|
+
}
|
|
18859
|
+
};
|
|
18860
|
+
var shellWrites = (command) => {
|
|
18861
|
+
const targets = [];
|
|
18862
|
+
let unknown = false;
|
|
18863
|
+
for (const segment of segmentsOf(command)) {
|
|
18864
|
+
targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
|
|
18865
|
+
const edits = editsOf(segment.words);
|
|
18866
|
+
if (edits === null)
|
|
18867
|
+
unknown = true;
|
|
18868
|
+
else
|
|
18869
|
+
targets.push(...edits);
|
|
18870
|
+
}
|
|
18871
|
+
if (!unknown && targets.length === 0)
|
|
18872
|
+
return { writes: false, targets: null };
|
|
18873
|
+
return { writes: true, targets: unknown ? null : targets };
|
|
18874
|
+
};
|
|
18875
|
+
var cache2 = new Map;
|
|
18876
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path21.resolve(root, file)) ?? "-").join("\x00");
|
|
18877
|
+
var gateFor = (cwd) => {
|
|
18878
|
+
const store = new TaskStore(cwd);
|
|
18879
|
+
const listed = store.listTaskIndex();
|
|
18880
|
+
if (!listed.ok)
|
|
18881
|
+
return null;
|
|
18882
|
+
const key = store.currentKey();
|
|
18883
|
+
if (!key.ok)
|
|
18884
|
+
return null;
|
|
18885
|
+
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
18886
|
+
if (!entry)
|
|
18887
|
+
return { blockers: [], root: store.root, plans: new Set };
|
|
18888
|
+
const ledger = ledgerPath(store.root);
|
|
18889
|
+
const cacheKey = `${entry.id}\x00${entry.revision}\x00${ledger.ok && fileSignature(ledger.value) || "-"}`;
|
|
18890
|
+
const hit = cache2.get(store.root);
|
|
18891
|
+
if (hit && hit.key === cacheKey && hit.planKey === planKeyOf(store.root, hit.plans))
|
|
18892
|
+
return hit;
|
|
18893
|
+
const task = store.readTask(entry.id);
|
|
18894
|
+
if (!task.ok)
|
|
18895
|
+
return null;
|
|
18896
|
+
const workspace = store.readWorkspace();
|
|
18897
|
+
const plans = new Set((latestJudgment(task.data)?.refs ?? []).flatMap((ref) => ref.kind === "file" ? [ref.path] : []));
|
|
18898
|
+
const blockers = writeBlockers(task.data, store.root).filter((blocker) => {
|
|
18899
|
+
const requirement = task.data.policy?.requirements.find((item) => item.ruleId === blocker.ruleId && item.before === "write");
|
|
18900
|
+
return !(requirement?.acceptanceAllowed && workspace.ok && workspace.data && applicableDecision(task.data, workspace.data, requirement, store.root).length > 0);
|
|
18901
|
+
});
|
|
18902
|
+
const gate = {
|
|
18903
|
+
blockers,
|
|
18904
|
+
root: store.root,
|
|
18905
|
+
plans,
|
|
18906
|
+
key: cacheKey,
|
|
18907
|
+
planKey: planKeyOf(store.root, plans)
|
|
18908
|
+
};
|
|
18909
|
+
cache2.set(store.root, gate);
|
|
18910
|
+
if (cache2.size > 32)
|
|
18911
|
+
cache2.delete(cache2.keys().next().value);
|
|
18912
|
+
return gate;
|
|
18913
|
+
};
|
|
18914
|
+
function writeGate(cwd, paths) {
|
|
18915
|
+
try {
|
|
18916
|
+
const gate = gateFor(cwd);
|
|
18917
|
+
if (!gate || gate.blockers.length === 0)
|
|
18918
|
+
return NONE3;
|
|
18919
|
+
if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
18920
|
+
return NONE3;
|
|
18921
|
+
const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
|
|
18922
|
+
return {
|
|
18923
|
+
kind: "deny",
|
|
18924
|
+
reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
|
|
18925
|
+
unblock: gate.blockers[0].reason
|
|
18926
|
+
};
|
|
18927
|
+
} catch {
|
|
18928
|
+
return NONE3;
|
|
18929
|
+
}
|
|
18930
|
+
}
|
|
18641
18931
|
// packages/workit-core/src/hooks/stop.ts
|
|
18932
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
18642
18933
|
var NONE4 = { kind: "none" };
|
|
18643
18934
|
var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
|
|
18644
18935
|
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;
|
|
@@ -18659,7 +18950,7 @@ var asksUser = (message) => {
|
|
|
18659
18950
|
};
|
|
18660
18951
|
var short = (sha) => (sha ?? "?").slice(0, 12);
|
|
18661
18952
|
var gitList = (cwd, args) => {
|
|
18662
|
-
const run =
|
|
18953
|
+
const run = spawnSync13("git", args, {
|
|
18663
18954
|
cwd,
|
|
18664
18955
|
encoding: "utf8",
|
|
18665
18956
|
timeout: 3000,
|
|
@@ -19022,13 +19313,13 @@ ${line}` };
|
|
|
19022
19313
|
return decision;
|
|
19023
19314
|
};
|
|
19024
19315
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
19025
|
-
import { existsSync as
|
|
19026
|
-
import
|
|
19316
|
+
import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
19317
|
+
import path22 from "node:path";
|
|
19027
19318
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
19028
19319
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
19029
19320
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
19030
19321
|
var existingDirectory = (value) => {
|
|
19031
|
-
if (!nonEmpty2(value) || !
|
|
19322
|
+
if (!nonEmpty2(value) || !path22.isAbsolute(value) || !existsSync7(value))
|
|
19032
19323
|
return null;
|
|
19033
19324
|
try {
|
|
19034
19325
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -19117,13 +19408,14 @@ var subagentStartText = (input, descriptor, event) => {
|
|
|
19117
19408
|
const role = workitRole(descriptor, event.agentType);
|
|
19118
19409
|
const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
|
|
19119
19410
|
const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer" || CLAUDE_WORKTREE.test(input.cwd));
|
|
19411
|
+
const guide = role === "implementer" ? branchGuidance(input.cwd) : null;
|
|
19120
19412
|
if (role === "implementer" && isolated)
|
|
19121
|
-
return `Workit observed ${who} working in its own git worktree: it may edit and commit there, within its brief's scope. ${ISOLATED_GIT}
|
|
19413
|
+
return `Workit observed ${who} working in its own git worktree: it may edit and commit there, within its brief's scope. ${ISOLATED_GIT} Plain git is expected here, and the branch name is still policy-checked: a raw \`git switch -c\`, \`git checkout -b\`, \`git branch\` or \`git worktree add -b\` onto a protected or non-compliant name is denied, so use a policy-compliant branch.${guide ? ` ${guide}` : " (e.g. feature/<slug>)"} Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
|
|
19122
19414
|
if (isolated)
|
|
19123
19415
|
return `${ISOLATED_GIT}
|
|
19124
19416
|
${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
19125
19417
|
if (role === "implementer")
|
|
19126
|
-
return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree (the lead makes one with \`workit fanout worktree create <slice>\`), never in the lead's checkout, within its brief's scope. Before the first commit, switch to a policy-compliant branch with \`workit git branch <branch> --base <base>\` (
|
|
19418
|
+
return `Workit observed ${who} as an implementer: it edits and commits only in its own git worktree (the lead makes one with \`workit fanout worktree create <slice>\`), never in the lead's checkout, within its brief's scope. Before the first commit, switch to a policy-compliant branch with \`workit git branch <branch> --base <base>\` (or \`--kind <kind> --slug <slug>\`, which picks the base); branch policy hooks still deny protected or non-compliant branches, raw git included.${guide ? ` ${guide}` : " (e.g. feature/<slug>)"} Never record a verdict on your own work. Never push, open a PR, or merge unless the brief asks for it.`;
|
|
19127
19419
|
const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
|
|
19128
19420
|
if (role !== "judge" || !event.agentId)
|
|
19129
19421
|
return readOnly;
|
|
@@ -19163,7 +19455,7 @@ function handleHook(input, deps) {
|
|
|
19163
19455
|
if (skillRead)
|
|
19164
19456
|
recordSkillLoad(input, skillRead, "read");
|
|
19165
19457
|
const canDeny = usable2(descriptor.shellPolicy.deny);
|
|
19166
|
-
const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE5;
|
|
19458
|
+
const policy = canDeny ? shellPolicy(input.cwd, event.command, event.dialect) : NONE5;
|
|
19167
19459
|
if (policy.kind !== "none")
|
|
19168
19460
|
return policy;
|
|
19169
19461
|
const postTool = usable2(descriptor.events["shell.post"].support);
|
|
@@ -19766,7 +20058,7 @@ var codexAdapter = {
|
|
|
19766
20058
|
|
|
19767
20059
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
19768
20060
|
import fs17 from "node:fs";
|
|
19769
|
-
import
|
|
20061
|
+
import path23 from "node:path";
|
|
19770
20062
|
var undocumented = { support: "undocumented", native: null };
|
|
19771
20063
|
var none = { support: "none", native: null };
|
|
19772
20064
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -19963,7 +20255,7 @@ var protocolEvent2 = (input) => {
|
|
|
19963
20255
|
};
|
|
19964
20256
|
var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
|
|
19965
20257
|
var lastAssistantText = (file) => {
|
|
19966
|
-
if (!file || !
|
|
20258
|
+
if (!file || !path23.isAbsolute(file))
|
|
19967
20259
|
return null;
|
|
19968
20260
|
try {
|
|
19969
20261
|
const size = fs17.statSync(file).size;
|
|
@@ -20000,7 +20292,7 @@ var lastAssistantText = (file) => {
|
|
|
20000
20292
|
return null;
|
|
20001
20293
|
}
|
|
20002
20294
|
};
|
|
20003
|
-
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
20295
|
+
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path23.sep) ? root : `${root}${path23.sep}`);
|
|
20004
20296
|
var hookCwd = (input) => {
|
|
20005
20297
|
const cwd = existingDirectory(input.cwd);
|
|
20006
20298
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|