@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
|
@@ -8834,7 +8834,7 @@ var PREFIXES, branchNames = (workspaceRoot) => {
|
|
|
8834
8834
|
return new Set;
|
|
8835
8835
|
return new Set((r.stdout ?? "").split(`
|
|
8836
8836
|
`).filter(Boolean).map((ref) => ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref.slice("refs/remotes/origin/".length)));
|
|
8837
|
-
}, detectBranchPolicy = (workspaceRoot) => {
|
|
8837
|
+
}, productionBranchOf = (names) => names.has("main") ? "main" : names.has("master") ? "master" : "main", detectBranchPolicy = (workspaceRoot) => {
|
|
8838
8838
|
const names = branchNames(workspaceRoot);
|
|
8839
8839
|
const develop = names.has("develop");
|
|
8840
8840
|
const main = names.has("main");
|
|
@@ -17647,10 +17647,41 @@ var withReleaseTracks = (workspaceRoot, policy) => {
|
|
|
17647
17647
|
...[...trackBranches(read.tracks), ...read.protectedBranches].map((name) => name.toLowerCase())
|
|
17648
17648
|
])
|
|
17649
17649
|
};
|
|
17650
|
+
}, KIND_SYNONYMS, allowedKinds = (allowed) => [
|
|
17651
|
+
...new Set(allowed.map((pattern) => /^\^([\w.-]+)\\?\/\.\*\$$/u.exec(pattern.source)?.[1]?.toLowerCase()).filter((kind) => Boolean(kind)))
|
|
17652
|
+
], editDistance = (a, b) => {
|
|
17653
|
+
const row = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
17654
|
+
for (let i = 1;i <= a.length; i++) {
|
|
17655
|
+
let previous = row[0];
|
|
17656
|
+
row[0] = i;
|
|
17657
|
+
for (let j = 1;j <= b.length; j++) {
|
|
17658
|
+
const saved = row[j];
|
|
17659
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
17660
|
+
previous = saved;
|
|
17661
|
+
}
|
|
17662
|
+
}
|
|
17663
|
+
return row[b.length];
|
|
17664
|
+
}, suggestBranchName = (name, kinds) => {
|
|
17665
|
+
if (!kinds.length)
|
|
17666
|
+
return null;
|
|
17667
|
+
const slash = name.indexOf("/");
|
|
17668
|
+
const prefix = slash > 0 ? name.slice(0, slash).toLowerCase() : "";
|
|
17669
|
+
const rest = (slash > 0 ? name.slice(slash + 1) : name) || "name";
|
|
17670
|
+
const synonym = KIND_SYNONYMS[prefix];
|
|
17671
|
+
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;
|
|
17672
|
+
const kind = (synonym && kinds.includes(synonym) ? synonym : null) ?? near ?? (kinds.includes("feature") ? "feature" : kinds[0]);
|
|
17673
|
+
return `${kind}/${rest}`;
|
|
17674
|
+
}, kindList = (kinds) => kinds.map((kind) => `${kind}/`).join(", "), aliasNote = (kinds) => {
|
|
17675
|
+
const notes = [
|
|
17676
|
+
["fix", "bugfix"],
|
|
17677
|
+
["chore", "feature"]
|
|
17678
|
+
].filter(([, kind]) => kinds.includes(kind)).map(([alias, kind]) => `${alias}/ is ${kind}/`);
|
|
17679
|
+
return notes.length ? ` (${notes.join(", ")})` : "";
|
|
17650
17680
|
}, validateBranchNamePolicy = (name, policy, source) => {
|
|
17651
17681
|
const allowed = policy.allowed.map((pattern) => `/${pattern.source}/${pattern.flags}`);
|
|
17682
|
+
const kinds = allowedKinds(policy.allowed);
|
|
17652
17683
|
if (policy.protected.has(name.toLowerCase())) {
|
|
17653
|
-
const correction = `choose a non-protected branch matching one of: ${allowed.join(", ")}`;
|
|
17684
|
+
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(", ")}`;
|
|
17654
17685
|
return {
|
|
17655
17686
|
ok: false,
|
|
17656
17687
|
rule: "protected_ref",
|
|
@@ -17661,7 +17692,8 @@ var withReleaseTracks = (workspaceRoot, policy) => {
|
|
|
17661
17692
|
};
|
|
17662
17693
|
}
|
|
17663
17694
|
if (!policy.allowed.some((pattern) => pattern.test(name))) {
|
|
17664
|
-
const
|
|
17695
|
+
const suggestion = suggestBranchName(name, kinds);
|
|
17696
|
+
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(", ")}`;
|
|
17665
17697
|
return {
|
|
17666
17698
|
ok: false,
|
|
17667
17699
|
rule: "allowed_pattern",
|
|
@@ -17700,7 +17732,52 @@ var withReleaseTracks = (workspaceRoot, policy) => {
|
|
|
17700
17732
|
error: `policy_unavailable: cannot validate branch ${JSON.stringify(name)} because ${error instanceof Error ? error.message : String(error)}; ${correction}`
|
|
17701
17733
|
};
|
|
17702
17734
|
}
|
|
17703
|
-
}, resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy
|
|
17735
|
+
}, resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy, gitFlowOf = (root, integration) => {
|
|
17736
|
+
try {
|
|
17737
|
+
if (resolveBranchPolicyFor(root).preset !== "gitflow")
|
|
17738
|
+
return null;
|
|
17739
|
+
const selected = resolveRuntimeWorkspacePolicy(root, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined).policy;
|
|
17740
|
+
const names = branchNames(root);
|
|
17741
|
+
const production = productionBranchOf(names);
|
|
17742
|
+
const develop = selected?.developBranch?.trim() || (integration && integration.toLowerCase() !== production ? integration : "develop");
|
|
17743
|
+
return { develop, production, developExists: names.has(develop) };
|
|
17744
|
+
} catch {
|
|
17745
|
+
return null;
|
|
17746
|
+
}
|
|
17747
|
+
}, releaseBranchesOf = (cwd) => {
|
|
17748
|
+
try {
|
|
17749
|
+
const resolved = resolveBranchPolicyFor(cwd);
|
|
17750
|
+
const flow = resolved.preset === "gitflow" ? gitFlowOf(cwd) : null;
|
|
17751
|
+
return {
|
|
17752
|
+
preset: resolved.preset,
|
|
17753
|
+
branches: flow ? [flow.production, flow.develop] : [],
|
|
17754
|
+
production: flow ? flow.production : resolved.defaultTargetBranch
|
|
17755
|
+
};
|
|
17756
|
+
} catch {
|
|
17757
|
+
return { preset: "unknown", branches: [], production: null };
|
|
17758
|
+
}
|
|
17759
|
+
}, releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase()), branchGuidance = (cwd) => {
|
|
17760
|
+
try {
|
|
17761
|
+
const patterns = branchPolicySnapshotFor(cwd).namePolicy.allowed;
|
|
17762
|
+
const kinds = allowedKinds(patterns);
|
|
17763
|
+
const kindShaped = new Set(kinds.map((kind) => branchGlobPattern(`${kind}/*`).source));
|
|
17764
|
+
const anyName = patterns.some((pattern) => pattern.source === "^.*$");
|
|
17765
|
+
const others = patterns.filter((pattern) => !kindShaped.has(pattern.source)).map((pattern) => globOf(pattern));
|
|
17766
|
+
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.`;
|
|
17767
|
+
const resolved = vcsConfig("resolve", cwd);
|
|
17768
|
+
const integration = resolved.ok === false ? null : String(resolved.defaultTargetBranch ?? "") || null;
|
|
17769
|
+
const flow = resolved.releaseTrack?.track ? null : gitFlowOf(cwd, integration);
|
|
17770
|
+
if (flow?.developExists)
|
|
17771
|
+
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}.`;
|
|
17772
|
+
const names = branchNames(cwd);
|
|
17773
|
+
const configured = String(resolved.baseBranch ?? integration ?? "") || null;
|
|
17774
|
+
const trunk = productionBranchOf(names);
|
|
17775
|
+
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.";
|
|
17776
|
+
return `${allowed} ${base}`;
|
|
17777
|
+
} catch {
|
|
17778
|
+
return null;
|
|
17779
|
+
}
|
|
17780
|
+
}, globOf = (pattern) => pattern.source.replace(/^\^|\$$/gu, "").replaceAll(".*", "*").replace(/\\(.)/gu, "$1");
|
|
17704
17781
|
var init_branch = __esm(() => {
|
|
17705
17782
|
init_branch_policy();
|
|
17706
17783
|
init_commit_flavors();
|
|
@@ -17709,33 +17786,63 @@ var init_branch = __esm(() => {
|
|
|
17709
17786
|
init_workspaces();
|
|
17710
17787
|
init_vcs_config();
|
|
17711
17788
|
init_release_tracks();
|
|
17789
|
+
KIND_SYNONYMS = {
|
|
17790
|
+
fix: "bugfix",
|
|
17791
|
+
fixes: "bugfix",
|
|
17792
|
+
bug: "bugfix",
|
|
17793
|
+
bugs: "bugfix",
|
|
17794
|
+
bugfixes: "bugfix",
|
|
17795
|
+
feat: "feature",
|
|
17796
|
+
features: "feature",
|
|
17797
|
+
chore: "feature",
|
|
17798
|
+
docs: "feature",
|
|
17799
|
+
doc: "feature",
|
|
17800
|
+
refactor: "feature",
|
|
17801
|
+
test: "feature",
|
|
17802
|
+
tests: "feature",
|
|
17803
|
+
ci: "feature",
|
|
17804
|
+
build: "feature",
|
|
17805
|
+
perf: "feature",
|
|
17806
|
+
style: "feature",
|
|
17807
|
+
deps: "feature",
|
|
17808
|
+
task: "feature",
|
|
17809
|
+
enhancement: "feature",
|
|
17810
|
+
hot: "hotfix",
|
|
17811
|
+
hotfixes: "hotfix",
|
|
17812
|
+
releases: "release",
|
|
17813
|
+
rel: "release"
|
|
17814
|
+
};
|
|
17712
17815
|
});
|
|
17713
17816
|
|
|
17714
|
-
// packages/workit-core/src/
|
|
17715
|
-
|
|
17716
|
-
|
|
17717
|
-
|
|
17718
|
-
|
|
17719
|
-
|
|
17720
|
-
|
|
17721
|
-
|
|
17722
|
-
|
|
17723
|
-
|
|
17724
|
-
|
|
17725
|
-
|
|
17726
|
-
|
|
17727
|
-
};
|
|
17728
|
-
|
|
17729
|
-
|
|
17730
|
-
|
|
17731
|
-
|
|
17732
|
-
|
|
17733
|
-
|
|
17734
|
-
|
|
17735
|
-
|
|
17817
|
+
// packages/workit-core/src/hooks/session-marker.ts
|
|
17818
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
17819
|
+
import fs15 from "node:fs";
|
|
17820
|
+
import path18 from "node:path";
|
|
17821
|
+
var sessionMarkerFile = (input, session, store) => {
|
|
17822
|
+
let dir = store;
|
|
17823
|
+
if (dir === undefined) {
|
|
17824
|
+
const location = resolveStore(input.cwd);
|
|
17825
|
+
if (location instanceof Error || !location.shared)
|
|
17826
|
+
return null;
|
|
17827
|
+
dir = location.dir;
|
|
17828
|
+
}
|
|
17829
|
+
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
17830
|
+
return path18.join(dir, "hooks", `skills-${name}.json`);
|
|
17831
|
+
}, list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], readSessionMarker = (file) => {
|
|
17832
|
+
try {
|
|
17833
|
+
const value = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
17834
|
+
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
17835
|
+
} catch {
|
|
17836
|
+
return { nudged: [], loaded: [], raw: [] };
|
|
17837
|
+
}
|
|
17838
|
+
}, writeSessionMarker = (file, marker) => {
|
|
17839
|
+
try {
|
|
17840
|
+
fs15.mkdirSync(path18.dirname(file), { recursive: true });
|
|
17841
|
+
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
17842
|
+
} catch {}
|
|
17736
17843
|
};
|
|
17737
|
-
var
|
|
17738
|
-
|
|
17844
|
+
var init_session_marker = __esm(() => {
|
|
17845
|
+
init_paths();
|
|
17739
17846
|
});
|
|
17740
17847
|
|
|
17741
17848
|
// packages/workit-core/src/hooks/shell-words.ts
|
|
@@ -17859,219 +17966,12 @@ var closingQuote = (command, start, dialect) => {
|
|
|
17859
17966
|
return segments;
|
|
17860
17967
|
};
|
|
17861
17968
|
|
|
17862
|
-
// packages/workit-core/src/hooks/write-gate.ts
|
|
17863
|
-
import { realpathSync as realpathSync5 } from "node:fs";
|
|
17864
|
-
import path18 from "node:path";
|
|
17865
|
-
function writeGate(cwd, paths) {
|
|
17866
|
-
try {
|
|
17867
|
-
const gate = gateFor(cwd);
|
|
17868
|
-
if (!gate || gate.blockers.length === 0)
|
|
17869
|
-
return NONE2;
|
|
17870
|
-
if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
17871
|
-
return NONE2;
|
|
17872
|
-
const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
|
|
17873
|
-
return {
|
|
17874
|
-
kind: "deny",
|
|
17875
|
-
reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
|
|
17876
|
-
unblock: gate.blockers[0].reason
|
|
17877
|
-
};
|
|
17878
|
-
} catch {
|
|
17879
|
-
return NONE2;
|
|
17880
|
-
}
|
|
17881
|
-
}
|
|
17882
|
-
var NONE2, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
17883
|
-
let rest = "";
|
|
17884
|
-
for (let dir = path18.resolve(file);; dir = path18.dirname(dir)) {
|
|
17885
|
-
try {
|
|
17886
|
-
return path18.join(realpathSync5(dir), rest);
|
|
17887
|
-
} catch {
|
|
17888
|
-
if (path18.dirname(dir) === dir)
|
|
17889
|
-
return path18.resolve(file);
|
|
17890
|
-
rest = rest ? path18.join(path18.basename(dir), rest) : path18.basename(dir);
|
|
17891
|
-
}
|
|
17892
|
-
}
|
|
17893
|
-
}, exempt = (cwd, root, file, plans) => {
|
|
17894
|
-
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
17895
|
-
const relative = path18.relative(fold(canonical2(root)), fold(canonical2(path18.resolve(cwd, file))));
|
|
17896
|
-
if (relative.startsWith("..") || path18.isAbsolute(relative))
|
|
17897
|
-
return true;
|
|
17898
|
-
const posix = relative.split(path18.sep).join("/");
|
|
17899
|
-
const segments = posix.split("/");
|
|
17900
|
-
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
17901
|
-
}, PREFIXES2, operands = (words) => words.filter((item) => !item.startsWith("-")), editsOf = (input) => {
|
|
17902
|
-
let words = input;
|
|
17903
|
-
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
|
|
17904
|
-
words = words.slice(1);
|
|
17905
|
-
const [verb, ...args] = words;
|
|
17906
|
-
const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
17907
|
-
if (!name || name === "workit" || name === "npx" && args.some((item) => /workit/.test(item)))
|
|
17908
|
-
return [];
|
|
17909
|
-
switch (name) {
|
|
17910
|
-
case "touch":
|
|
17911
|
-
case "mkdir":
|
|
17912
|
-
case "rm":
|
|
17913
|
-
case "rmdir":
|
|
17914
|
-
case "tee":
|
|
17915
|
-
case "mv":
|
|
17916
|
-
case "truncate":
|
|
17917
|
-
return operands(args);
|
|
17918
|
-
case "cp":
|
|
17919
|
-
case "ln":
|
|
17920
|
-
case "install": {
|
|
17921
|
-
const target = args.indexOf("-t");
|
|
17922
|
-
if (target >= 0)
|
|
17923
|
-
return args[target + 1] ? [args[target + 1]] : null;
|
|
17924
|
-
const last = operands(args).at(-1);
|
|
17925
|
-
return last ? [last] : null;
|
|
17926
|
-
}
|
|
17927
|
-
case "dd":
|
|
17928
|
-
return args.filter((item) => item.startsWith("of=")).map((item) => item.slice(3));
|
|
17929
|
-
case "sed":
|
|
17930
|
-
case "perl": {
|
|
17931
|
-
if (!args.some((item) => /^-[a-zA-Z]*i|^--in-place/.test(item)))
|
|
17932
|
-
return [];
|
|
17933
|
-
const scripted = args.some((item) => item === "-e" || item === "-f");
|
|
17934
|
-
const files = [];
|
|
17935
|
-
for (let index = 0;index < args.length; index++) {
|
|
17936
|
-
if (args[index] === "-e" || args[index] === "-f")
|
|
17937
|
-
index++;
|
|
17938
|
-
else if (!args[index].startsWith("-"))
|
|
17939
|
-
files.push(args[index]);
|
|
17940
|
-
}
|
|
17941
|
-
return scripted ? files : files.slice(1);
|
|
17942
|
-
}
|
|
17943
|
-
case "patch":
|
|
17944
|
-
return null;
|
|
17945
|
-
case "set-content":
|
|
17946
|
-
case "add-content":
|
|
17947
|
-
case "out-file":
|
|
17948
|
-
case "new-item":
|
|
17949
|
-
case "ni":
|
|
17950
|
-
case "remove-item":
|
|
17951
|
-
case "del":
|
|
17952
|
-
case "copy-item":
|
|
17953
|
-
case "move-item": {
|
|
17954
|
-
const flag = args.findIndex((item) => /^-(?:path|filepath|literalpath|destination)$/i.test(item));
|
|
17955
|
-
if (flag >= 0)
|
|
17956
|
-
return args[flag + 1] ? [args[flag + 1]] : null;
|
|
17957
|
-
const files = operands(args);
|
|
17958
|
-
if (name === "move-item")
|
|
17959
|
-
return files.length ? files : null;
|
|
17960
|
-
const file = name === "copy-item" ? files.at(-1) : files[0];
|
|
17961
|
-
return file ? [file] : null;
|
|
17962
|
-
}
|
|
17963
|
-
case "git": {
|
|
17964
|
-
const sub = operands(args)[0];
|
|
17965
|
-
if (sub === "apply")
|
|
17966
|
-
return null;
|
|
17967
|
-
if (sub === "restore" || sub === "mv" || sub === "rm")
|
|
17968
|
-
return operands(args).slice(1);
|
|
17969
|
-
if (sub === "checkout" && args.includes("--"))
|
|
17970
|
-
return args.slice(args.indexOf("--") + 1);
|
|
17971
|
-
return [];
|
|
17972
|
-
}
|
|
17973
|
-
default:
|
|
17974
|
-
return [];
|
|
17975
|
-
}
|
|
17976
|
-
}, shellWrites = (command) => {
|
|
17977
|
-
const targets = [];
|
|
17978
|
-
let unknown = false;
|
|
17979
|
-
for (const segment of segmentsOf(command)) {
|
|
17980
|
-
targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
|
|
17981
|
-
const edits = editsOf(segment.words);
|
|
17982
|
-
if (edits === null)
|
|
17983
|
-
unknown = true;
|
|
17984
|
-
else
|
|
17985
|
-
targets.push(...edits);
|
|
17986
|
-
}
|
|
17987
|
-
if (!unknown && targets.length === 0)
|
|
17988
|
-
return { writes: false, targets: null };
|
|
17989
|
-
return { writes: true, targets: unknown ? null : targets };
|
|
17990
|
-
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path18.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
|
|
17991
|
-
const store = new TaskStore(cwd);
|
|
17992
|
-
const listed = store.listTaskIndex();
|
|
17993
|
-
if (!listed.ok)
|
|
17994
|
-
return null;
|
|
17995
|
-
const key = store.currentKey();
|
|
17996
|
-
if (!key.ok)
|
|
17997
|
-
return null;
|
|
17998
|
-
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
17999
|
-
if (!entry)
|
|
18000
|
-
return { blockers: [], root: store.root, plans: new Set };
|
|
18001
|
-
const ledger = ledgerPath(store.root);
|
|
18002
|
-
const cacheKey = `${entry.id}\x00${entry.revision}\x00${ledger.ok && fileSignature(ledger.value) || "-"}`;
|
|
18003
|
-
const hit = cache2.get(store.root);
|
|
18004
|
-
if (hit && hit.key === cacheKey && hit.planKey === planKeyOf(store.root, hit.plans))
|
|
18005
|
-
return hit;
|
|
18006
|
-
const task = store.readTask(entry.id);
|
|
18007
|
-
if (!task.ok)
|
|
18008
|
-
return null;
|
|
18009
|
-
const workspace = store.readWorkspace();
|
|
18010
|
-
const plans = new Set((latestJudgment(task.data)?.refs ?? []).flatMap((ref) => ref.kind === "file" ? [ref.path] : []));
|
|
18011
|
-
const blockers = writeBlockers(task.data, store.root).filter((blocker) => {
|
|
18012
|
-
const requirement = task.data.policy?.requirements.find((item) => item.ruleId === blocker.ruleId && item.before === "write");
|
|
18013
|
-
return !(requirement?.acceptanceAllowed && workspace.ok && workspace.data && applicableDecision(task.data, workspace.data, requirement, store.root).length > 0);
|
|
18014
|
-
});
|
|
18015
|
-
const gate = {
|
|
18016
|
-
blockers,
|
|
18017
|
-
root: store.root,
|
|
18018
|
-
plans,
|
|
18019
|
-
key: cacheKey,
|
|
18020
|
-
planKey: planKeyOf(store.root, plans)
|
|
18021
|
-
};
|
|
18022
|
-
cache2.set(store.root, gate);
|
|
18023
|
-
if (cache2.size > 32)
|
|
18024
|
-
cache2.delete(cache2.keys().next().value);
|
|
18025
|
-
return gate;
|
|
18026
|
-
};
|
|
18027
|
-
var init_write_gate = __esm(() => {
|
|
18028
|
-
init_task_store();
|
|
18029
|
-
init_derive();
|
|
18030
|
-
init_task_evaluation();
|
|
18031
|
-
init_ledger();
|
|
18032
|
-
NONE2 = { kind: "none" };
|
|
18033
|
-
CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
18034
|
-
PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
18035
|
-
cache2 = new Map;
|
|
18036
|
-
});
|
|
18037
|
-
|
|
18038
|
-
// packages/workit-core/src/hooks/session-marker.ts
|
|
18039
|
-
import { createHash as createHash13 } from "node:crypto";
|
|
18040
|
-
import fs15 from "node:fs";
|
|
18041
|
-
import path19 from "node:path";
|
|
18042
|
-
var sessionMarkerFile = (input, session, store) => {
|
|
18043
|
-
let dir = store;
|
|
18044
|
-
if (dir === undefined) {
|
|
18045
|
-
const location = resolveStore(input.cwd);
|
|
18046
|
-
if (location instanceof Error || !location.shared)
|
|
18047
|
-
return null;
|
|
18048
|
-
dir = location.dir;
|
|
18049
|
-
}
|
|
18050
|
-
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
18051
|
-
return path19.join(dir, "hooks", `skills-${name}.json`);
|
|
18052
|
-
}, list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [], readSessionMarker = (file) => {
|
|
18053
|
-
try {
|
|
18054
|
-
const value = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
18055
|
-
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
18056
|
-
} catch {
|
|
18057
|
-
return { nudged: [], loaded: [], raw: [] };
|
|
18058
|
-
}
|
|
18059
|
-
}, writeSessionMarker = (file, marker) => {
|
|
18060
|
-
try {
|
|
18061
|
-
fs15.mkdirSync(path19.dirname(file), { recursive: true });
|
|
18062
|
-
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
18063
|
-
} catch {}
|
|
18064
|
-
};
|
|
18065
|
-
var init_session_marker = __esm(() => {
|
|
18066
|
-
init_paths();
|
|
18067
|
-
});
|
|
18068
|
-
|
|
18069
17969
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
18070
17970
|
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
18071
17971
|
import { createHash as createHash14 } from "node:crypto";
|
|
18072
17972
|
import fs16 from "node:fs";
|
|
18073
17973
|
import os4 from "node:os";
|
|
18074
|
-
import
|
|
17974
|
+
import path19 from "node:path";
|
|
18075
17975
|
function rawInvocations(command, dialect = "posix", depth = 0) {
|
|
18076
17976
|
const found = [];
|
|
18077
17977
|
let dir = null;
|
|
@@ -18181,7 +18081,7 @@ function rawAction(invocation) {
|
|
|
18181
18081
|
function rawGitPre(input, command, options = {}) {
|
|
18182
18082
|
try {
|
|
18183
18083
|
if (!mentionsRawTool(command))
|
|
18184
|
-
return
|
|
18084
|
+
return NONE2;
|
|
18185
18085
|
const dialect = dialectOf(input);
|
|
18186
18086
|
const nudges = new Map;
|
|
18187
18087
|
let store = null;
|
|
@@ -18189,25 +18089,36 @@ function rawGitPre(input, command, options = {}) {
|
|
|
18189
18089
|
store ??= repo.location.dir;
|
|
18190
18090
|
if (action.kind === "merge")
|
|
18191
18091
|
return deny(refusal(action, null));
|
|
18192
|
-
if (action.kind === "push")
|
|
18092
|
+
if (action.kind === "push" && toPushRemote(repo, action.remote)) {
|
|
18093
|
+
if (action.mirror)
|
|
18094
|
+
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")}.`);
|
|
18193
18095
|
for (const target of action.targets) {
|
|
18194
18096
|
const branch = target ?? repo.branch;
|
|
18195
|
-
if (branch && guardedBranch(repo.dir, branch)
|
|
18196
|
-
return deny(refusal(action, branch));
|
|
18097
|
+
if (branch && guardedBranch(repo.dir, branch))
|
|
18098
|
+
return deny(refusal(action, branch, repo.dir));
|
|
18197
18099
|
}
|
|
18100
|
+
const remote = action.remote ?? pushRemoteName(repo.dir, repo.branch) ?? "origin";
|
|
18101
|
+
const facts = refFacts(repo, remote);
|
|
18102
|
+
const tagDeny = tagRefusal(action, facts);
|
|
18103
|
+
if (tagDeny)
|
|
18104
|
+
return deny(tagDeny);
|
|
18105
|
+
const nameDeny = action.deletes ? null : offPolicyPush(action, repo, facts, remote);
|
|
18106
|
+
if (nameDeny)
|
|
18107
|
+
return deny(nameDeny);
|
|
18108
|
+
}
|
|
18198
18109
|
const nudge = nudgeFor(action, input.host, input.session.id);
|
|
18199
18110
|
if (nudge && !nudges.has(nudgeKind(action)))
|
|
18200
18111
|
nudges.set(nudgeKind(action), nudge);
|
|
18201
18112
|
}
|
|
18202
18113
|
if (options.nudge === false || !store || nudges.size === 0)
|
|
18203
|
-
return
|
|
18114
|
+
return NONE2;
|
|
18204
18115
|
if (workitInvocations(command, dialect).length > 0)
|
|
18205
|
-
return
|
|
18116
|
+
return NONE2;
|
|
18206
18117
|
const shown = unseenNudges(input, nudges, store);
|
|
18207
18118
|
return shown.length ? { kind: "context", text: shown.join(`
|
|
18208
|
-
`) } :
|
|
18119
|
+
`) } : NONE2;
|
|
18209
18120
|
} catch {
|
|
18210
|
-
return
|
|
18121
|
+
return NONE2;
|
|
18211
18122
|
}
|
|
18212
18123
|
}
|
|
18213
18124
|
function noteRawCommit(input, command, key) {
|
|
@@ -18219,7 +18130,7 @@ function noteRawCommit(input, command, key) {
|
|
|
18219
18130
|
if (action.kind !== "commit")
|
|
18220
18131
|
continue;
|
|
18221
18132
|
const file = markerFile(repo, input.session.id, key);
|
|
18222
|
-
fs16.mkdirSync(
|
|
18133
|
+
fs16.mkdirSync(path19.dirname(file), { recursive: true });
|
|
18223
18134
|
fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
18224
18135
|
`);
|
|
18225
18136
|
}
|
|
@@ -18246,11 +18157,11 @@ function settlePendingCommit(input) {
|
|
|
18246
18157
|
settle2(input, repo, file);
|
|
18247
18158
|
} catch {}
|
|
18248
18159
|
}
|
|
18249
|
-
var
|
|
18160
|
+
var NONE2, SHELLS, PREFIXES2, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\"), joinDir = (base, next) => {
|
|
18250
18161
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
18251
18162
|
return;
|
|
18252
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
18253
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
18163
|
+
const expanded = next === "~" || next.startsWith("~/") ? path19.join(os4.homedir(), next.slice(1)) : next;
|
|
18164
|
+
return base === null || isAbsolute(expanded) ? expanded : path19.join(base, expanded);
|
|
18254
18165
|
}, cdTarget = (words) => {
|
|
18255
18166
|
const args = words.slice(1);
|
|
18256
18167
|
const flag = args.findIndex((word) => /^-(?:literal)?path$/i.test(word));
|
|
@@ -18265,7 +18176,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18265
18176
|
return words;
|
|
18266
18177
|
if (/^\w+=/.test(first))
|
|
18267
18178
|
words = words.slice(1);
|
|
18268
|
-
else if (
|
|
18179
|
+
else if (PREFIXES2.has(first)) {
|
|
18269
18180
|
words = words.slice(1);
|
|
18270
18181
|
while (words[0]?.startsWith("-"))
|
|
18271
18182
|
words = words.slice(words[0] === "-u" ? 2 : 1);
|
|
@@ -18281,6 +18192,10 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18281
18192
|
let force = false;
|
|
18282
18193
|
let lease = false;
|
|
18283
18194
|
let wholesale = false;
|
|
18195
|
+
let allTags = false;
|
|
18196
|
+
let deletes = false;
|
|
18197
|
+
let allBranches = false;
|
|
18198
|
+
let mirror = false;
|
|
18284
18199
|
let remote = null;
|
|
18285
18200
|
const positionals = [];
|
|
18286
18201
|
for (let index = 1;index < args.length; index++) {
|
|
@@ -18301,14 +18216,22 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18301
18216
|
force = true;
|
|
18302
18217
|
else if (arg.startsWith("--force-with-lease") || arg === "--force-if-includes")
|
|
18303
18218
|
lease = true;
|
|
18304
|
-
else if (
|
|
18305
|
-
|
|
18219
|
+
else if (arg === "--delete" || arg === "-d")
|
|
18220
|
+
deletes = true;
|
|
18221
|
+
else if (arg === "--tags")
|
|
18222
|
+
allTags = wholesale = true;
|
|
18223
|
+
else if (arg === "--all" || arg === "--branches")
|
|
18224
|
+
allBranches = wholesale = true;
|
|
18225
|
+
else if (arg === "--mirror")
|
|
18226
|
+
mirror = wholesale = true;
|
|
18306
18227
|
else if (!arg.startsWith("-"))
|
|
18307
18228
|
positionals.push(arg);
|
|
18308
18229
|
}
|
|
18309
18230
|
const specs = remote === null ? positionals.slice(1) : positionals;
|
|
18310
18231
|
remote ??= positionals[0] ?? null;
|
|
18311
18232
|
const targets = [];
|
|
18233
|
+
const tags = [];
|
|
18234
|
+
const deleted = [];
|
|
18312
18235
|
for (const raw of specs) {
|
|
18313
18236
|
if (raw.startsWith("+"))
|
|
18314
18237
|
force = true;
|
|
@@ -18317,16 +18240,29 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18317
18240
|
const destination = colon >= 0 ? spec.slice(colon + 1) : spec;
|
|
18318
18241
|
if (!destination)
|
|
18319
18242
|
continue;
|
|
18320
|
-
|
|
18321
|
-
|
|
18322
|
-
|
|
18323
|
-
|
|
18324
|
-
|
|
18325
|
-
|
|
18243
|
+
const name = destination === "HEAD" ? null : destination.startsWith("refs/heads/") ? destination.slice(11) : destination.startsWith("refs/") ? undefined : destination;
|
|
18244
|
+
if (destination.startsWith("refs/tags/"))
|
|
18245
|
+
tags.push(destination.slice(10));
|
|
18246
|
+
if (name === undefined)
|
|
18247
|
+
continue;
|
|
18248
|
+
targets.push(name);
|
|
18249
|
+
if (colon === 0 && name !== null)
|
|
18250
|
+
deleted.push(name);
|
|
18326
18251
|
}
|
|
18327
18252
|
if (specs.length === 0 && !wholesale)
|
|
18328
18253
|
targets.push(null);
|
|
18329
|
-
return {
|
|
18254
|
+
return {
|
|
18255
|
+
kind: "push",
|
|
18256
|
+
targets,
|
|
18257
|
+
blindForce: force && !lease,
|
|
18258
|
+
remote,
|
|
18259
|
+
...tags.length ? { tags } : {},
|
|
18260
|
+
...allTags ? { allTags: true } : {},
|
|
18261
|
+
...deletes ? { deletes: true } : {},
|
|
18262
|
+
...deleted.length ? { deleted } : {},
|
|
18263
|
+
...allBranches ? { allBranches: true } : {},
|
|
18264
|
+
...mirror ? { mirror: true } : {}
|
|
18265
|
+
};
|
|
18330
18266
|
}, VALUE_OPTIONS, asksHelp = (invocation) => {
|
|
18331
18267
|
const { tool, args } = invocation;
|
|
18332
18268
|
if (args[0] === "help" || tool !== "git" && args[1] === "help")
|
|
@@ -18376,25 +18312,72 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18376
18312
|
if (!git || git.reftable)
|
|
18377
18313
|
return null;
|
|
18378
18314
|
try {
|
|
18379
|
-
const head = fs16.readFileSync(
|
|
18315
|
+
const head = fs16.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18380
18316
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18381
18317
|
return head;
|
|
18382
18318
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
18383
18319
|
if (!ref)
|
|
18384
18320
|
return null;
|
|
18385
|
-
const common =
|
|
18321
|
+
const common = path19.dirname(repo.location.dir);
|
|
18386
18322
|
try {
|
|
18387
|
-
return fs16.readFileSync(
|
|
18323
|
+
return fs16.readFileSync(path19.join(common, ref), "utf8").trim() || null;
|
|
18388
18324
|
} catch {}
|
|
18389
|
-
const packed = fs16.readFileSync(
|
|
18325
|
+
const packed = fs16.readFileSync(path19.join(common, "packed-refs"), "utf8");
|
|
18390
18326
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18391
18327
|
} catch {
|
|
18392
18328
|
return null;
|
|
18393
18329
|
}
|
|
18394
|
-
}, quote = (text) => `\`${text}\``, refusal = (action, branch) => {
|
|
18330
|
+
}, quote = (text) => `\`${text}\``, refusal = (action, branch, dir) => {
|
|
18395
18331
|
if (action.kind === "merge")
|
|
18396
18332
|
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")}).`;
|
|
18333
|
+
const plainPush = action.kind === "push" && !action.blindForce && !action.deletes;
|
|
18334
|
+
if (dir && branch && plainPush && releaseBranch(dir, branch))
|
|
18335
|
+
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")}.`;
|
|
18397
18336
|
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")}.`;
|
|
18337
|
+
}, refFacts = (repo, remote) => {
|
|
18338
|
+
const facts = { heads: new Set, tags: new Set, remoteHeads: new Set };
|
|
18339
|
+
const listed = spawnSync11("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/tags", `refs/remotes/${remote}`], { cwd: repo.dir, encoding: "utf8", timeout: 5000 });
|
|
18340
|
+
const remotePrefix = `refs/remotes/${remote}/`;
|
|
18341
|
+
for (const ref of (listed.stdout ?? "").split(`
|
|
18342
|
+
`)) {
|
|
18343
|
+
if (ref.startsWith("refs/heads/"))
|
|
18344
|
+
facts.heads.add(ref.slice(11));
|
|
18345
|
+
else if (ref.startsWith("refs/tags/"))
|
|
18346
|
+
facts.tags.add(ref.slice(10));
|
|
18347
|
+
else if (ref.startsWith(remotePrefix))
|
|
18348
|
+
facts.remoteHeads.add(ref.slice(remotePrefix.length));
|
|
18349
|
+
}
|
|
18350
|
+
return facts;
|
|
18351
|
+
}, isTag = (facts, name) => facts.tags.has(name) && !facts.heads.has(name), tagRefusal = (action, facts) => {
|
|
18352
|
+
const tags = [
|
|
18353
|
+
...action.tags ?? [],
|
|
18354
|
+
...action.targets.filter((name) => name !== null && isTag(facts, name))
|
|
18355
|
+
];
|
|
18356
|
+
if (!tags.length && !action.allTags)
|
|
18357
|
+
return null;
|
|
18358
|
+
const named = tags.length ? tags.join(" --release ") : "v<version>";
|
|
18359
|
+
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")}).`;
|
|
18360
|
+
}, remoteHas = (repo, remote, branch) => {
|
|
18361
|
+
const listed = spawnSync11("git", ["ls-remote", "--heads", remote, `refs/heads/${branch}`], {
|
|
18362
|
+
cwd: repo.dir,
|
|
18363
|
+
encoding: "utf8",
|
|
18364
|
+
timeout: 3000,
|
|
18365
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
18366
|
+
});
|
|
18367
|
+
return listed.status !== 0 || (listed.stdout ?? "").trim() !== "";
|
|
18368
|
+
}, offPolicyPush = (action, repo, facts, remote) => {
|
|
18369
|
+
const names = action.allBranches ? [...facts.heads] : action.targets.map((target) => target ?? repo.branch);
|
|
18370
|
+
for (const branch of names) {
|
|
18371
|
+
if (!branch || action.deleted?.includes(branch) || isTag(facts, branch))
|
|
18372
|
+
continue;
|
|
18373
|
+
if (facts.remoteHeads.has(branch))
|
|
18374
|
+
continue;
|
|
18375
|
+
const check = validateBranchNameFor(repo.dir, branch);
|
|
18376
|
+
if (check.ok || check.rule !== "allowed_pattern" || remoteHas(repo, remote, branch))
|
|
18377
|
+
continue;
|
|
18378
|
+
return `workit: raw ${quote("git push")} onto ${branch}: ${check.error}`;
|
|
18379
|
+
}
|
|
18380
|
+
return null;
|
|
18398
18381
|
}, nudgeFor = (action, host, session) => {
|
|
18399
18382
|
switch (action.kind) {
|
|
18400
18383
|
case "commit": {
|
|
@@ -18419,7 +18402,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18419
18402
|
const action = rawAction(invocation);
|
|
18420
18403
|
if (!action)
|
|
18421
18404
|
continue;
|
|
18422
|
-
const dir =
|
|
18405
|
+
const dir = path19.resolve(cwd, invocation.dir ?? ".");
|
|
18423
18406
|
if (!repos.has(dir)) {
|
|
18424
18407
|
const repo = repoAt(dir);
|
|
18425
18408
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -18446,7 +18429,7 @@ var NONE3, SHELLS, PREFIXES3, SCRIPT_FLAG, CD, PUSHD, POPD, GIT_VALUE_OPTIONS, i
|
|
|
18446
18429
|
raw: [...marker.raw, ...fresh.map((kind) => agent + kind)]
|
|
18447
18430
|
});
|
|
18448
18431
|
return fresh.map((kind) => nudges.get(kind));
|
|
18449
|
-
}, deny = (reason) => ({ kind: "deny", reason, unblock: null }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) =>
|
|
18432
|
+
}, deny = (reason) => ({ kind: "deny", reason, unblock: null }), NEXT_COMMAND = "next", MARKER_TTL_SECONDS = 3600, markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`), callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`, headCommit = (dir) => {
|
|
18450
18433
|
const run = spawnSync11("git", [
|
|
18451
18434
|
"log",
|
|
18452
18435
|
"-1",
|
|
@@ -18506,9 +18489,9 @@ var init_raw_git = __esm(() => {
|
|
|
18506
18489
|
init_ledger();
|
|
18507
18490
|
init_paths();
|
|
18508
18491
|
init_session_marker();
|
|
18509
|
-
|
|
18492
|
+
NONE2 = { kind: "none" };
|
|
18510
18493
|
SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
18511
|
-
|
|
18494
|
+
PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
|
|
18512
18495
|
SCRIPT_FLAG = /^-[a-zA-Z]*c[a-zA-Z]*$/;
|
|
18513
18496
|
CD = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
18514
18497
|
PUSHD = new Set(["pushd", "push-location"]);
|
|
@@ -18566,8 +18549,298 @@ var init_raw_git = __esm(() => {
|
|
|
18566
18549
|
};
|
|
18567
18550
|
});
|
|
18568
18551
|
|
|
18569
|
-
// packages/workit-core/src/
|
|
18552
|
+
// packages/workit-core/src/core/route-intent.ts
|
|
18570
18553
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
18554
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
18555
|
+
import path20 from "node:path";
|
|
18556
|
+
var UNKNOWABLE, SWITCH_CREATE, CHECKOUT_CREATE, WORKTREE_CREATE, BRANCH_NO_CREATE, BRANCH_RENAME, flagValue = (args, flags) => {
|
|
18557
|
+
for (let index = 1;index < args.length; index++) {
|
|
18558
|
+
const arg = args[index];
|
|
18559
|
+
if (arg === "--")
|
|
18560
|
+
return null;
|
|
18561
|
+
if (flags.has(arg))
|
|
18562
|
+
return args[index + 1] ?? null;
|
|
18563
|
+
const eq = arg.indexOf("=");
|
|
18564
|
+
if (arg.startsWith("--") && eq > 0 && flags.has(arg.slice(0, eq)))
|
|
18565
|
+
return arg.slice(eq + 1);
|
|
18566
|
+
if (!arg.startsWith("--") && arg.length > 2 && flags.has(arg.slice(0, 2)))
|
|
18567
|
+
return arg.slice(2);
|
|
18568
|
+
}
|
|
18569
|
+
return null;
|
|
18570
|
+
}, WORKTREE_VALUES, worktreeTarget = (args) => {
|
|
18571
|
+
const named = flagValue(args, WORKTREE_CREATE);
|
|
18572
|
+
if (named !== null)
|
|
18573
|
+
return named;
|
|
18574
|
+
const positionals = [];
|
|
18575
|
+
for (let index = 1;index < args.length; index++) {
|
|
18576
|
+
const arg = args[index];
|
|
18577
|
+
if (arg === "--detach" || arg === "-d")
|
|
18578
|
+
return null;
|
|
18579
|
+
if (WORKTREE_VALUES.has(arg))
|
|
18580
|
+
index++;
|
|
18581
|
+
else if (!arg.startsWith("-"))
|
|
18582
|
+
positionals.push(arg);
|
|
18583
|
+
}
|
|
18584
|
+
if (positionals.length !== 1)
|
|
18585
|
+
return null;
|
|
18586
|
+
const base = positionals[0].split(/[\\/]/u).findLast(Boolean) ?? "";
|
|
18587
|
+
return base && base !== "." && base !== ".." ? base : null;
|
|
18588
|
+
}, branchCommandTarget = (args) => {
|
|
18589
|
+
let rename = false;
|
|
18590
|
+
const positionals = [];
|
|
18591
|
+
for (let index = 1;index < args.length; index++) {
|
|
18592
|
+
const arg = args[index];
|
|
18593
|
+
if (arg === "--") {
|
|
18594
|
+
positionals.push(...args.slice(index + 1));
|
|
18595
|
+
break;
|
|
18596
|
+
}
|
|
18597
|
+
const flag = arg.split("=", 1)[0];
|
|
18598
|
+
if (BRANCH_NO_CREATE.has(flag))
|
|
18599
|
+
return null;
|
|
18600
|
+
if (BRANCH_RENAME.has(flag))
|
|
18601
|
+
rename = true;
|
|
18602
|
+
else if (!arg.startsWith("-"))
|
|
18603
|
+
positionals.push(arg);
|
|
18604
|
+
}
|
|
18605
|
+
if (positionals.length < 1 || positionals.length > 2)
|
|
18606
|
+
return null;
|
|
18607
|
+
return rename ? positionals.at(-1) ?? null : positionals[0];
|
|
18608
|
+
}, gitBranchCreation = (args) => {
|
|
18609
|
+
const [sub, second] = args;
|
|
18610
|
+
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;
|
|
18611
|
+
return target && !target.startsWith("-") && !UNKNOWABLE.test(target) ? target : null;
|
|
18612
|
+
}, shellBranchTargets = (command, dialect = "posix") => {
|
|
18613
|
+
if (!/\bgit\b/u.test(command))
|
|
18614
|
+
return [];
|
|
18615
|
+
const found = [];
|
|
18616
|
+
for (const invocation of rawInvocations(command, dialect)) {
|
|
18617
|
+
if (invocation.tool !== "git")
|
|
18618
|
+
continue;
|
|
18619
|
+
const branch = gitBranchCreation(invocation.args);
|
|
18620
|
+
if (branch)
|
|
18621
|
+
found.push({ branch, dir: invocation.dir });
|
|
18622
|
+
}
|
|
18623
|
+
return found;
|
|
18624
|
+
}, localBranchExists = (dir, branch) => spawnSync12("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
|
|
18625
|
+
cwd: dir,
|
|
18626
|
+
stdio: "ignore",
|
|
18627
|
+
timeout: 5000
|
|
18628
|
+
}).status === 0, shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
18629
|
+
let judged = false;
|
|
18630
|
+
for (const target of shellBranchTargets(command, dialect)) {
|
|
18631
|
+
if (target.dir === undefined)
|
|
18632
|
+
continue;
|
|
18633
|
+
const dir = target.dir === null ? root : path20.resolve(root, target.dir);
|
|
18634
|
+
if (target.dir !== null && (!existsSync6(dir) || !inWorkitWorkspace(dir)))
|
|
18635
|
+
continue;
|
|
18636
|
+
if (localBranchExists(dir, target.branch))
|
|
18637
|
+
continue;
|
|
18638
|
+
judged = true;
|
|
18639
|
+
const check = validateBranchNameFor(dir, target.branch);
|
|
18640
|
+
if (!check.ok)
|
|
18641
|
+
return check;
|
|
18642
|
+
}
|
|
18643
|
+
return judged ? { ok: true } : null;
|
|
18644
|
+
};
|
|
18645
|
+
var init_route_intent = __esm(() => {
|
|
18646
|
+
init_raw_git();
|
|
18647
|
+
init_branch();
|
|
18648
|
+
UNKNOWABLE = /[$`*?[\]{}~]/u;
|
|
18649
|
+
SWITCH_CREATE = new Set(["-c", "--create", "-C", "--force-create", "--orphan"]);
|
|
18650
|
+
CHECKOUT_CREATE = new Set(["-b", "-B", "--orphan"]);
|
|
18651
|
+
WORKTREE_CREATE = new Set(["-b", "-B"]);
|
|
18652
|
+
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
|
+
BRANCH_RENAME = new Set(["-m", "-M", "--move", "-c", "-C", "--copy"]);
|
|
18654
|
+
WORKTREE_VALUES = new Set(["--reason"]);
|
|
18655
|
+
});
|
|
18656
|
+
|
|
18657
|
+
// packages/workit-core/src/hooks/policy.ts
|
|
18658
|
+
var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
18659
|
+
const policy = shellBranchPolicyViolation(cwd, command, dialect);
|
|
18660
|
+
return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
|
|
18661
|
+
};
|
|
18662
|
+
var init_policy = __esm(() => {
|
|
18663
|
+
init_route_intent();
|
|
18664
|
+
});
|
|
18665
|
+
|
|
18666
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
18667
|
+
import { realpathSync as realpathSync5 } from "node:fs";
|
|
18668
|
+
import path21 from "node:path";
|
|
18669
|
+
function writeGate(cwd, paths) {
|
|
18670
|
+
try {
|
|
18671
|
+
const gate = gateFor(cwd);
|
|
18672
|
+
if (!gate || gate.blockers.length === 0)
|
|
18673
|
+
return NONE3;
|
|
18674
|
+
if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
18675
|
+
return NONE3;
|
|
18676
|
+
const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
|
|
18677
|
+
return {
|
|
18678
|
+
kind: "deny",
|
|
18679
|
+
reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
|
|
18680
|
+
unblock: gate.blockers[0].reason
|
|
18681
|
+
};
|
|
18682
|
+
} catch {
|
|
18683
|
+
return NONE3;
|
|
18684
|
+
}
|
|
18685
|
+
}
|
|
18686
|
+
var NONE3, CASE_INSENSITIVE, canonical2 = (file) => {
|
|
18687
|
+
let rest = "";
|
|
18688
|
+
for (let dir = path21.resolve(file);; dir = path21.dirname(dir)) {
|
|
18689
|
+
try {
|
|
18690
|
+
return path21.join(realpathSync5(dir), rest);
|
|
18691
|
+
} catch {
|
|
18692
|
+
if (path21.dirname(dir) === dir)
|
|
18693
|
+
return path21.resolve(file);
|
|
18694
|
+
rest = rest ? path21.join(path21.basename(dir), rest) : path21.basename(dir);
|
|
18695
|
+
}
|
|
18696
|
+
}
|
|
18697
|
+
}, exempt = (cwd, root, file, plans) => {
|
|
18698
|
+
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
18699
|
+
const relative = path21.relative(fold(canonical2(root)), fold(canonical2(path21.resolve(cwd, file))));
|
|
18700
|
+
if (relative.startsWith("..") || path21.isAbsolute(relative))
|
|
18701
|
+
return true;
|
|
18702
|
+
const posix = relative.split(path21.sep).join("/");
|
|
18703
|
+
const segments = posix.split("/");
|
|
18704
|
+
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
18705
|
+
}, PREFIXES3, operands = (words) => words.filter((item) => !item.startsWith("-")), editsOf = (input) => {
|
|
18706
|
+
let words = input;
|
|
18707
|
+
while (words.length && (PREFIXES3.has(words[0]) || /^\w+=/.test(words[0])))
|
|
18708
|
+
words = words.slice(1);
|
|
18709
|
+
const [verb, ...args] = words;
|
|
18710
|
+
const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
18711
|
+
if (!name || name === "workit" || name === "npx" && args.some((item) => /workit/.test(item)))
|
|
18712
|
+
return [];
|
|
18713
|
+
switch (name) {
|
|
18714
|
+
case "touch":
|
|
18715
|
+
case "mkdir":
|
|
18716
|
+
case "rm":
|
|
18717
|
+
case "rmdir":
|
|
18718
|
+
case "tee":
|
|
18719
|
+
case "mv":
|
|
18720
|
+
case "truncate":
|
|
18721
|
+
return operands(args);
|
|
18722
|
+
case "cp":
|
|
18723
|
+
case "ln":
|
|
18724
|
+
case "install": {
|
|
18725
|
+
const target = args.indexOf("-t");
|
|
18726
|
+
if (target >= 0)
|
|
18727
|
+
return args[target + 1] ? [args[target + 1]] : null;
|
|
18728
|
+
const last = operands(args).at(-1);
|
|
18729
|
+
return last ? [last] : null;
|
|
18730
|
+
}
|
|
18731
|
+
case "dd":
|
|
18732
|
+
return args.filter((item) => item.startsWith("of=")).map((item) => item.slice(3));
|
|
18733
|
+
case "sed":
|
|
18734
|
+
case "perl": {
|
|
18735
|
+
if (!args.some((item) => /^-[a-zA-Z]*i|^--in-place/.test(item)))
|
|
18736
|
+
return [];
|
|
18737
|
+
const scripted = args.some((item) => item === "-e" || item === "-f");
|
|
18738
|
+
const files = [];
|
|
18739
|
+
for (let index = 0;index < args.length; index++) {
|
|
18740
|
+
if (args[index] === "-e" || args[index] === "-f")
|
|
18741
|
+
index++;
|
|
18742
|
+
else if (!args[index].startsWith("-"))
|
|
18743
|
+
files.push(args[index]);
|
|
18744
|
+
}
|
|
18745
|
+
return scripted ? files : files.slice(1);
|
|
18746
|
+
}
|
|
18747
|
+
case "patch":
|
|
18748
|
+
return null;
|
|
18749
|
+
case "set-content":
|
|
18750
|
+
case "add-content":
|
|
18751
|
+
case "out-file":
|
|
18752
|
+
case "new-item":
|
|
18753
|
+
case "ni":
|
|
18754
|
+
case "remove-item":
|
|
18755
|
+
case "del":
|
|
18756
|
+
case "copy-item":
|
|
18757
|
+
case "move-item": {
|
|
18758
|
+
const flag = args.findIndex((item) => /^-(?:path|filepath|literalpath|destination)$/i.test(item));
|
|
18759
|
+
if (flag >= 0)
|
|
18760
|
+
return args[flag + 1] ? [args[flag + 1]] : null;
|
|
18761
|
+
const files = operands(args);
|
|
18762
|
+
if (name === "move-item")
|
|
18763
|
+
return files.length ? files : null;
|
|
18764
|
+
const file = name === "copy-item" ? files.at(-1) : files[0];
|
|
18765
|
+
return file ? [file] : null;
|
|
18766
|
+
}
|
|
18767
|
+
case "git": {
|
|
18768
|
+
const sub = operands(args)[0];
|
|
18769
|
+
if (sub === "apply")
|
|
18770
|
+
return null;
|
|
18771
|
+
if (sub === "restore" || sub === "mv" || sub === "rm")
|
|
18772
|
+
return operands(args).slice(1);
|
|
18773
|
+
if (sub === "checkout" && args.includes("--"))
|
|
18774
|
+
return args.slice(args.indexOf("--") + 1);
|
|
18775
|
+
return [];
|
|
18776
|
+
}
|
|
18777
|
+
default:
|
|
18778
|
+
return [];
|
|
18779
|
+
}
|
|
18780
|
+
}, shellWrites = (command) => {
|
|
18781
|
+
const targets = [];
|
|
18782
|
+
let unknown = false;
|
|
18783
|
+
for (const segment of segmentsOf(command)) {
|
|
18784
|
+
targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
|
|
18785
|
+
const edits = editsOf(segment.words);
|
|
18786
|
+
if (edits === null)
|
|
18787
|
+
unknown = true;
|
|
18788
|
+
else
|
|
18789
|
+
targets.push(...edits);
|
|
18790
|
+
}
|
|
18791
|
+
if (!unknown && targets.length === 0)
|
|
18792
|
+
return { writes: false, targets: null };
|
|
18793
|
+
return { writes: true, targets: unknown ? null : targets };
|
|
18794
|
+
}, cache2, planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path21.resolve(root, file)) ?? "-").join("\x00"), gateFor = (cwd) => {
|
|
18795
|
+
const store = new TaskStore(cwd);
|
|
18796
|
+
const listed = store.listTaskIndex();
|
|
18797
|
+
if (!listed.ok)
|
|
18798
|
+
return null;
|
|
18799
|
+
const key = store.currentKey();
|
|
18800
|
+
if (!key.ok)
|
|
18801
|
+
return null;
|
|
18802
|
+
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
18803
|
+
if (!entry)
|
|
18804
|
+
return { blockers: [], root: store.root, plans: new Set };
|
|
18805
|
+
const ledger = ledgerPath(store.root);
|
|
18806
|
+
const cacheKey = `${entry.id}\x00${entry.revision}\x00${ledger.ok && fileSignature(ledger.value) || "-"}`;
|
|
18807
|
+
const hit = cache2.get(store.root);
|
|
18808
|
+
if (hit && hit.key === cacheKey && hit.planKey === planKeyOf(store.root, hit.plans))
|
|
18809
|
+
return hit;
|
|
18810
|
+
const task = store.readTask(entry.id);
|
|
18811
|
+
if (!task.ok)
|
|
18812
|
+
return null;
|
|
18813
|
+
const workspace = store.readWorkspace();
|
|
18814
|
+
const plans = new Set((latestJudgment(task.data)?.refs ?? []).flatMap((ref) => ref.kind === "file" ? [ref.path] : []));
|
|
18815
|
+
const blockers = writeBlockers(task.data, store.root).filter((blocker) => {
|
|
18816
|
+
const requirement = task.data.policy?.requirements.find((item) => item.ruleId === blocker.ruleId && item.before === "write");
|
|
18817
|
+
return !(requirement?.acceptanceAllowed && workspace.ok && workspace.data && applicableDecision(task.data, workspace.data, requirement, store.root).length > 0);
|
|
18818
|
+
});
|
|
18819
|
+
const gate = {
|
|
18820
|
+
blockers,
|
|
18821
|
+
root: store.root,
|
|
18822
|
+
plans,
|
|
18823
|
+
key: cacheKey,
|
|
18824
|
+
planKey: planKeyOf(store.root, plans)
|
|
18825
|
+
};
|
|
18826
|
+
cache2.set(store.root, gate);
|
|
18827
|
+
if (cache2.size > 32)
|
|
18828
|
+
cache2.delete(cache2.keys().next().value);
|
|
18829
|
+
return gate;
|
|
18830
|
+
};
|
|
18831
|
+
var init_write_gate = __esm(() => {
|
|
18832
|
+
init_task_store();
|
|
18833
|
+
init_derive();
|
|
18834
|
+
init_task_evaluation();
|
|
18835
|
+
init_ledger();
|
|
18836
|
+
NONE3 = { kind: "none" };
|
|
18837
|
+
CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
18838
|
+
PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
18839
|
+
cache2 = new Map;
|
|
18840
|
+
});
|
|
18841
|
+
|
|
18842
|
+
// packages/workit-core/src/hooks/stop.ts
|
|
18843
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
18571
18844
|
function stopObligation(input, descriptor) {
|
|
18572
18845
|
try {
|
|
18573
18846
|
const session = input.session.id;
|
|
@@ -18631,7 +18904,7 @@ var NONE4, QUESTION, OFFER, YES_NO, LIST_ITEM, asksUser = (message) => {
|
|
|
18631
18904
|
`);
|
|
18632
18905
|
return QUESTION.test(tail) || OFFER.test(tail) || YES_NO.test(tail);
|
|
18633
18906
|
}, short = (sha) => (sha ?? "?").slice(0, 12), gitList = (cwd, args) => {
|
|
18634
|
-
const run =
|
|
18907
|
+
const run = spawnSync13("git", args, {
|
|
18635
18908
|
cwd,
|
|
18636
18909
|
encoding: "utf8",
|
|
18637
18910
|
timeout: 3000,
|
|
@@ -18941,10 +19214,10 @@ var init_skill_nudge = __esm(() => {
|
|
|
18941
19214
|
});
|
|
18942
19215
|
|
|
18943
19216
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
18944
|
-
import { existsSync as
|
|
18945
|
-
import
|
|
19217
|
+
import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
19218
|
+
import path22 from "node:path";
|
|
18946
19219
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value), nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "", optionalText = (value) => typeof value === "string" ? value : null, existingDirectory = (value) => {
|
|
18947
|
-
if (!nonEmpty2(value) || !
|
|
19220
|
+
if (!nonEmpty2(value) || !path22.isAbsolute(value) || !existsSync7(value))
|
|
18948
19221
|
return null;
|
|
18949
19222
|
try {
|
|
18950
19223
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -19040,7 +19313,7 @@ function handleHook(input, deps) {
|
|
|
19040
19313
|
if (skillRead)
|
|
19041
19314
|
recordSkillLoad(input, skillRead, "read");
|
|
19042
19315
|
const canDeny = usable2(descriptor.shellPolicy.deny);
|
|
19043
|
-
const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE5;
|
|
19316
|
+
const policy = canDeny ? shellPolicy(input.cwd, event.command, event.dialect) : NONE5;
|
|
19044
19317
|
if (policy.kind !== "none")
|
|
19045
19318
|
return policy;
|
|
19046
19319
|
const postTool = usable2(descriptor.events["shell.post"].support);
|
|
@@ -19094,13 +19367,14 @@ var usable2 = (value) => value === "native" || value === "partial", NONE5, resen
|
|
|
19094
19367
|
const role = workitRole(descriptor, event.agentType);
|
|
19095
19368
|
const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
|
|
19096
19369
|
const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer" || CLAUDE_WORKTREE.test(input.cwd));
|
|
19370
|
+
const guide = role === "implementer" ? branchGuidance(input.cwd) : null;
|
|
19097
19371
|
if (role === "implementer" && isolated)
|
|
19098
|
-
return `Workit observed ${who} working in its own git worktree: it may edit and commit there, within its brief's scope. ${ISOLATED_GIT}
|
|
19372
|
+
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.`;
|
|
19099
19373
|
if (isolated)
|
|
19100
19374
|
return `${ISOLATED_GIT}
|
|
19101
19375
|
${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
19102
19376
|
if (role === "implementer")
|
|
19103
|
-
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>\` (
|
|
19377
|
+
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.`;
|
|
19104
19378
|
const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
|
|
19105
19379
|
if (role !== "judge" || !event.agentId)
|
|
19106
19380
|
return readOnly;
|
|
@@ -19138,6 +19412,7 @@ ${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
|
19138
19412
|
};
|
|
19139
19413
|
var init_handle = __esm(() => {
|
|
19140
19414
|
init_context();
|
|
19415
|
+
init_branch();
|
|
19141
19416
|
init_host_session();
|
|
19142
19417
|
init_policy();
|
|
19143
19418
|
init_raw_git();
|
|
@@ -19682,7 +19957,7 @@ var init_codex = __esm(() => {
|
|
|
19682
19957
|
|
|
19683
19958
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
19684
19959
|
import fs17 from "node:fs";
|
|
19685
|
-
import
|
|
19960
|
+
import path23 from "node:path";
|
|
19686
19961
|
var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (value) => {
|
|
19687
19962
|
if (!isRecord4(value) || !Object.hasOwn(EVENTS3, String(value.hook_event_name)))
|
|
19688
19963
|
return { ok: false, error: "hook_event_name is required" };
|
|
@@ -19763,7 +20038,7 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
19763
20038
|
};
|
|
19764
20039
|
}
|
|
19765
20040
|
}, TRANSCRIPT_TAIL_BYTES, lastAssistantText = (file) => {
|
|
19766
|
-
if (!file || !
|
|
20041
|
+
if (!file || !path23.isAbsolute(file))
|
|
19767
20042
|
return null;
|
|
19768
20043
|
try {
|
|
19769
20044
|
const size = fs17.statSync(file).size;
|
|
@@ -19799,7 +20074,7 @@ var undocumented, none, CURSOR_DESCRIPTOR, EVENTS3, parseCursorHookInput = (valu
|
|
|
19799
20074
|
} catch {
|
|
19800
20075
|
return null;
|
|
19801
20076
|
}
|
|
19802
|
-
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
20077
|
+
}, within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path23.sep) ? root : `${root}${path23.sep}`), hookCwd = (input) => {
|
|
19803
20078
|
const cwd = existingDirectory(input.cwd);
|
|
19804
20079
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
|
19805
20080
|
return cwd;
|