@brainervirus/workit-codex 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/dist/launch-mcp.js
CHANGED
|
@@ -22270,6 +22270,71 @@ var gitContext = (workspaceRoot, paths = []) => {
|
|
|
22270
22270
|
...failure ? { stderr: failure.stderr, exitCode: failure.exitCode } : {}
|
|
22271
22271
|
};
|
|
22272
22272
|
};
|
|
22273
|
+
|
|
22274
|
+
// packages/workit-core/src/hooks/raw-git.ts
|
|
22275
|
+
var SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
22276
|
+
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
|
|
22277
|
+
var CD = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
22278
|
+
var PUSHD = new Set(["pushd", "push-location"]);
|
|
22279
|
+
var POPD = new Set(["popd", "pop-location"]);
|
|
22280
|
+
var GIT_VALUE_OPTIONS = new Set([
|
|
22281
|
+
"-c",
|
|
22282
|
+
"--git-dir",
|
|
22283
|
+
"--work-tree",
|
|
22284
|
+
"--namespace",
|
|
22285
|
+
"--exec-path",
|
|
22286
|
+
"--config-env",
|
|
22287
|
+
"--super-prefix"
|
|
22288
|
+
]);
|
|
22289
|
+
var LAUNCHERS = new Set([
|
|
22290
|
+
"npx",
|
|
22291
|
+
"bunx",
|
|
22292
|
+
"pnpx",
|
|
22293
|
+
"-y",
|
|
22294
|
+
"--yes",
|
|
22295
|
+
"pnpm",
|
|
22296
|
+
"dlx",
|
|
22297
|
+
"exec",
|
|
22298
|
+
"env",
|
|
22299
|
+
"sudo",
|
|
22300
|
+
"command"
|
|
22301
|
+
]);
|
|
22302
|
+
var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
|
|
22303
|
+
var VALUE_OPTIONS = {
|
|
22304
|
+
git: new Set([
|
|
22305
|
+
"-m",
|
|
22306
|
+
"--message",
|
|
22307
|
+
"-F",
|
|
22308
|
+
"--file",
|
|
22309
|
+
"-C",
|
|
22310
|
+
"-c",
|
|
22311
|
+
"--reuse-message",
|
|
22312
|
+
"--reedit-message",
|
|
22313
|
+
"--author",
|
|
22314
|
+
"--date",
|
|
22315
|
+
"--trailer",
|
|
22316
|
+
"-t",
|
|
22317
|
+
"--template",
|
|
22318
|
+
"--cleanup",
|
|
22319
|
+
"--fixup",
|
|
22320
|
+
"--squash",
|
|
22321
|
+
"-o",
|
|
22322
|
+
"--push-option",
|
|
22323
|
+
"--repo",
|
|
22324
|
+
"--receive-pack",
|
|
22325
|
+
"--exec"
|
|
22326
|
+
]),
|
|
22327
|
+
gh: new Set(["-b", "--body", "-F", "--body-file", "-t", "--title", "--subject", "-A", "--author-email"].concat(["-B", "--base", "-H", "--head", "-l", "--label", "-a", "--assignee"]).concat(["-r", "--reviewer", "-m", "--milestone", "-p", "--project", "-T", "--template"]).concat(["--match-head-commit", "-R", "--repo"])),
|
|
22328
|
+
glab: new Set(["-m", "--message", "--squash-message", "--sha", "-t", "--title", "-d", "--description"].concat(["-b", "--target-branch", "-s", "--source-branch", "-l", "--label"]).concat(["-a", "--assignee", "--reviewer", "-R", "--repo"]))
|
|
22329
|
+
};
|
|
22330
|
+
|
|
22331
|
+
// packages/workit-core/src/core/route-intent.ts
|
|
22332
|
+
var SWITCH_CREATE = new Set(["-c", "--create", "-C", "--force-create", "--orphan"]);
|
|
22333
|
+
var CHECKOUT_CREATE = new Set(["-b", "-B", "--orphan"]);
|
|
22334
|
+
var WORKTREE_CREATE = new Set(["-b", "-B"]);
|
|
22335
|
+
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"]));
|
|
22336
|
+
var BRANCH_RENAME = new Set(["-m", "-M", "--move", "-c", "-C", "--copy"]);
|
|
22337
|
+
var WORKTREE_VALUES = new Set(["--reason"]);
|
|
22273
22338
|
// packages/workit-core/src/core/task-engine.ts
|
|
22274
22339
|
import { realpathSync as realpathSync4 } from "node:fs";
|
|
22275
22340
|
|
|
@@ -28101,64 +28166,8 @@ var cache = new Map;
|
|
|
28101
28166
|
var TURN_TTL_MS = 7 * 24 * 60 * 60 * 1000;
|
|
28102
28167
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
28103
28168
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
28104
|
-
var
|
|
28169
|
+
var PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
28105
28170
|
var cache2 = new Map;
|
|
28106
|
-
// packages/workit-core/src/hooks/raw-git.ts
|
|
28107
|
-
var SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
28108
|
-
var PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
|
|
28109
|
-
var CD = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
28110
|
-
var PUSHD = new Set(["pushd", "push-location"]);
|
|
28111
|
-
var POPD = new Set(["popd", "pop-location"]);
|
|
28112
|
-
var GIT_VALUE_OPTIONS = new Set([
|
|
28113
|
-
"-c",
|
|
28114
|
-
"--git-dir",
|
|
28115
|
-
"--work-tree",
|
|
28116
|
-
"--namespace",
|
|
28117
|
-
"--exec-path",
|
|
28118
|
-
"--config-env",
|
|
28119
|
-
"--super-prefix"
|
|
28120
|
-
]);
|
|
28121
|
-
var LAUNCHERS = new Set([
|
|
28122
|
-
"npx",
|
|
28123
|
-
"bunx",
|
|
28124
|
-
"pnpx",
|
|
28125
|
-
"-y",
|
|
28126
|
-
"--yes",
|
|
28127
|
-
"pnpm",
|
|
28128
|
-
"dlx",
|
|
28129
|
-
"exec",
|
|
28130
|
-
"env",
|
|
28131
|
-
"sudo",
|
|
28132
|
-
"command"
|
|
28133
|
-
]);
|
|
28134
|
-
var PUSH_VALUE_OPTIONS = new Set(["-o", "--push-option", "--receive-pack", "--exec", "--repo"]);
|
|
28135
|
-
var VALUE_OPTIONS = {
|
|
28136
|
-
git: new Set([
|
|
28137
|
-
"-m",
|
|
28138
|
-
"--message",
|
|
28139
|
-
"-F",
|
|
28140
|
-
"--file",
|
|
28141
|
-
"-C",
|
|
28142
|
-
"-c",
|
|
28143
|
-
"--reuse-message",
|
|
28144
|
-
"--reedit-message",
|
|
28145
|
-
"--author",
|
|
28146
|
-
"--date",
|
|
28147
|
-
"--trailer",
|
|
28148
|
-
"-t",
|
|
28149
|
-
"--template",
|
|
28150
|
-
"--cleanup",
|
|
28151
|
-
"--fixup",
|
|
28152
|
-
"--squash",
|
|
28153
|
-
"-o",
|
|
28154
|
-
"--push-option",
|
|
28155
|
-
"--repo",
|
|
28156
|
-
"--receive-pack",
|
|
28157
|
-
"--exec"
|
|
28158
|
-
]),
|
|
28159
|
-
gh: new Set(["-b", "--body", "-F", "--body-file", "-t", "--title", "--subject", "-A", "--author-email"].concat(["-B", "--base", "-H", "--head", "-l", "--label", "-a", "--assignee"]).concat(["-r", "--reviewer", "-m", "--milestone", "-p", "--project", "-T", "--template"]).concat(["--match-head-commit", "-R", "--repo"])),
|
|
28160
|
-
glab: new Set(["-m", "--message", "--squash-message", "--sha", "-t", "--title", "-d", "--description"].concat(["-b", "--target-branch", "-s", "--source-branch", "-l", "--label"]).concat(["-a", "--assignee", "--reviewer", "-R", "--repo"]))
|
|
28161
|
-
};
|
|
28162
28171
|
// packages/workit-core/src/core/skill-manifests.ts
|
|
28163
28172
|
if (false) {}
|
|
28164
28173
|
|
|
@@ -12377,6 +12377,7 @@ var branchNames = (workspaceRoot) => {
|
|
|
12377
12377
|
return new Set((r.stdout ?? "").split(`
|
|
12378
12378
|
`).filter(Boolean).map((ref) => ref.startsWith("refs/heads/") ? ref.slice("refs/heads/".length) : ref.slice("refs/remotes/origin/".length)));
|
|
12379
12379
|
};
|
|
12380
|
+
var productionBranchOf = (names) => names.has("main") ? "main" : names.has("master") ? "master" : "main";
|
|
12380
12381
|
var detectBranchPolicy = (workspaceRoot) => {
|
|
12381
12382
|
const names = branchNames(workspaceRoot);
|
|
12382
12383
|
const develop = names.has("develop");
|
|
@@ -17664,6 +17665,18 @@ var seedTurnContext = (input, descriptor, text = turnContextText(input, descript
|
|
|
17664
17665
|
else
|
|
17665
17666
|
clearTurnDigest(file);
|
|
17666
17667
|
};
|
|
17668
|
+
// packages/workit-core/src/core/route-intent.ts
|
|
17669
|
+
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
17670
|
+
import { existsSync as existsSync6 } from "node:fs";
|
|
17671
|
+
import path20 from "node:path";
|
|
17672
|
+
|
|
17673
|
+
// packages/workit-core/src/hooks/raw-git.ts
|
|
17674
|
+
import { spawnSync as spawnSync11 } from "node:child_process";
|
|
17675
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
17676
|
+
import fs16 from "node:fs";
|
|
17677
|
+
import os4 from "node:os";
|
|
17678
|
+
import path19 from "node:path";
|
|
17679
|
+
|
|
17667
17680
|
// packages/workit-core/src/core/branch.ts
|
|
17668
17681
|
import { createHash as createHash12 } from "node:crypto";
|
|
17669
17682
|
var withReleaseTracks = (workspaceRoot, policy) => {
|
|
@@ -17701,10 +17714,72 @@ function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
|
|
|
17701
17714
|
provenance: { commitPolicy: selected.source }
|
|
17702
17715
|
};
|
|
17703
17716
|
}
|
|
17717
|
+
var KIND_SYNONYMS = {
|
|
17718
|
+
fix: "bugfix",
|
|
17719
|
+
fixes: "bugfix",
|
|
17720
|
+
bug: "bugfix",
|
|
17721
|
+
bugs: "bugfix",
|
|
17722
|
+
bugfixes: "bugfix",
|
|
17723
|
+
feat: "feature",
|
|
17724
|
+
features: "feature",
|
|
17725
|
+
chore: "feature",
|
|
17726
|
+
docs: "feature",
|
|
17727
|
+
doc: "feature",
|
|
17728
|
+
refactor: "feature",
|
|
17729
|
+
test: "feature",
|
|
17730
|
+
tests: "feature",
|
|
17731
|
+
ci: "feature",
|
|
17732
|
+
build: "feature",
|
|
17733
|
+
perf: "feature",
|
|
17734
|
+
style: "feature",
|
|
17735
|
+
deps: "feature",
|
|
17736
|
+
task: "feature",
|
|
17737
|
+
enhancement: "feature",
|
|
17738
|
+
hot: "hotfix",
|
|
17739
|
+
hotfixes: "hotfix",
|
|
17740
|
+
releases: "release",
|
|
17741
|
+
rel: "release"
|
|
17742
|
+
};
|
|
17743
|
+
var allowedKinds = (allowed) => [
|
|
17744
|
+
...new Set(allowed.map((pattern) => /^\^([\w.-]+)\\?\/\.\*\$$/u.exec(pattern.source)?.[1]?.toLowerCase()).filter((kind) => Boolean(kind)))
|
|
17745
|
+
];
|
|
17746
|
+
var editDistance = (a, b) => {
|
|
17747
|
+
const row = Array.from({ length: b.length + 1 }, (_, index) => index);
|
|
17748
|
+
for (let i = 1;i <= a.length; i++) {
|
|
17749
|
+
let previous = row[0];
|
|
17750
|
+
row[0] = i;
|
|
17751
|
+
for (let j = 1;j <= b.length; j++) {
|
|
17752
|
+
const saved = row[j];
|
|
17753
|
+
row[j] = Math.min(row[j] + 1, row[j - 1] + 1, previous + (a[i - 1] === b[j - 1] ? 0 : 1));
|
|
17754
|
+
previous = saved;
|
|
17755
|
+
}
|
|
17756
|
+
}
|
|
17757
|
+
return row[b.length];
|
|
17758
|
+
};
|
|
17759
|
+
var suggestBranchName = (name, kinds) => {
|
|
17760
|
+
if (!kinds.length)
|
|
17761
|
+
return null;
|
|
17762
|
+
const slash = name.indexOf("/");
|
|
17763
|
+
const prefix = slash > 0 ? name.slice(0, slash).toLowerCase() : "";
|
|
17764
|
+
const rest = (slash > 0 ? name.slice(slash + 1) : name) || "name";
|
|
17765
|
+
const synonym = KIND_SYNONYMS[prefix];
|
|
17766
|
+
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;
|
|
17767
|
+
const kind = (synonym && kinds.includes(synonym) ? synonym : null) ?? near ?? (kinds.includes("feature") ? "feature" : kinds[0]);
|
|
17768
|
+
return `${kind}/${rest}`;
|
|
17769
|
+
};
|
|
17770
|
+
var kindList = (kinds) => kinds.map((kind) => `${kind}/`).join(", ");
|
|
17771
|
+
var aliasNote = (kinds) => {
|
|
17772
|
+
const notes = [
|
|
17773
|
+
["fix", "bugfix"],
|
|
17774
|
+
["chore", "feature"]
|
|
17775
|
+
].filter(([, kind]) => kinds.includes(kind)).map(([alias, kind]) => `${alias}/ is ${kind}/`);
|
|
17776
|
+
return notes.length ? ` (${notes.join(", ")})` : "";
|
|
17777
|
+
};
|
|
17704
17778
|
var validateBranchNamePolicy = (name, policy, source) => {
|
|
17705
17779
|
const allowed = policy.allowed.map((pattern) => `/${pattern.source}/${pattern.flags}`);
|
|
17780
|
+
const kinds = allowedKinds(policy.allowed);
|
|
17706
17781
|
if (policy.protected.has(name.toLowerCase())) {
|
|
17707
|
-
const correction = `choose a non-protected branch matching one of: ${allowed.join(", ")}`;
|
|
17782
|
+
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(", ")}`;
|
|
17708
17783
|
return {
|
|
17709
17784
|
ok: false,
|
|
17710
17785
|
rule: "protected_ref",
|
|
@@ -17715,7 +17790,8 @@ var validateBranchNamePolicy = (name, policy, source) => {
|
|
|
17715
17790
|
};
|
|
17716
17791
|
}
|
|
17717
17792
|
if (!policy.allowed.some((pattern) => pattern.test(name))) {
|
|
17718
|
-
const
|
|
17793
|
+
const suggestion = suggestBranchName(name, kinds);
|
|
17794
|
+
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(", ")}`;
|
|
17719
17795
|
return {
|
|
17720
17796
|
ok: false,
|
|
17721
17797
|
rule: "allowed_pattern",
|
|
@@ -17758,31 +17834,87 @@ var validateBranchNameFor = (workspaceRoot, name) => {
|
|
|
17758
17834
|
}
|
|
17759
17835
|
};
|
|
17760
17836
|
var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
|
|
17761
|
-
|
|
17762
|
-
|
|
17763
|
-
|
|
17764
|
-
|
|
17765
|
-
|
|
17766
|
-
|
|
17767
|
-
|
|
17837
|
+
var gitFlowOf = (root, integration) => {
|
|
17838
|
+
try {
|
|
17839
|
+
if (resolveBranchPolicyFor(root).preset !== "gitflow")
|
|
17840
|
+
return null;
|
|
17841
|
+
const selected = resolveRuntimeWorkspacePolicy(root, "branch", process.env.WORKFLOW_PROFILE?.trim() || undefined).policy;
|
|
17842
|
+
const names = branchNames(root);
|
|
17843
|
+
const production = productionBranchOf(names);
|
|
17844
|
+
const develop = selected?.developBranch?.trim() || (integration && integration.toLowerCase() !== production ? integration : "develop");
|
|
17845
|
+
return { develop, production, developExists: names.has(develop) };
|
|
17846
|
+
} catch {
|
|
17768
17847
|
return null;
|
|
17769
|
-
|
|
17770
|
-
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;
|
|
17771
|
-
return target && !target.startsWith("-") ? target : null;
|
|
17848
|
+
}
|
|
17772
17849
|
};
|
|
17773
|
-
var
|
|
17774
|
-
|
|
17775
|
-
|
|
17850
|
+
var releaseBranchesOf = (cwd) => {
|
|
17851
|
+
try {
|
|
17852
|
+
const resolved = resolveBranchPolicyFor(cwd);
|
|
17853
|
+
const flow = resolved.preset === "gitflow" ? gitFlowOf(cwd) : null;
|
|
17854
|
+
return {
|
|
17855
|
+
preset: resolved.preset,
|
|
17856
|
+
branches: flow ? [flow.production, flow.develop] : [],
|
|
17857
|
+
production: flow ? flow.production : resolved.defaultTargetBranch
|
|
17858
|
+
};
|
|
17859
|
+
} catch {
|
|
17860
|
+
return { preset: "unknown", branches: [], production: null };
|
|
17861
|
+
}
|
|
17776
17862
|
};
|
|
17777
|
-
|
|
17778
|
-
|
|
17779
|
-
|
|
17780
|
-
|
|
17781
|
-
|
|
17863
|
+
var releaseBranch = (cwd, branch) => releaseBranchesOf(cwd).branches.some((name) => name.toLowerCase() === branch.toLowerCase());
|
|
17864
|
+
var branchGuidance = (cwd) => {
|
|
17865
|
+
try {
|
|
17866
|
+
const patterns = branchPolicySnapshotFor(cwd).namePolicy.allowed;
|
|
17867
|
+
const kinds = allowedKinds(patterns);
|
|
17868
|
+
const kindShaped = new Set(kinds.map((kind) => branchGlobPattern(`${kind}/*`).source));
|
|
17869
|
+
const anyName = patterns.some((pattern) => pattern.source === "^.*$");
|
|
17870
|
+
const others = patterns.filter((pattern) => !kindShaped.has(pattern.source)).map((pattern) => globOf(pattern));
|
|
17871
|
+
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.`;
|
|
17872
|
+
const resolved = vcsConfig("resolve", cwd);
|
|
17873
|
+
const integration = resolved.ok === false ? null : String(resolved.defaultTargetBranch ?? "") || null;
|
|
17874
|
+
const flow = resolved.releaseTrack?.track ? null : gitFlowOf(cwd, integration);
|
|
17875
|
+
if (flow?.developExists)
|
|
17876
|
+
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}.`;
|
|
17877
|
+
const names = branchNames(cwd);
|
|
17878
|
+
const configured = String(resolved.baseBranch ?? integration ?? "") || null;
|
|
17879
|
+
const trunk = productionBranchOf(names);
|
|
17880
|
+
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.";
|
|
17881
|
+
return `${allowed} ${base}`;
|
|
17882
|
+
} catch {
|
|
17883
|
+
return null;
|
|
17884
|
+
}
|
|
17782
17885
|
};
|
|
17783
|
-
|
|
17784
|
-
|
|
17886
|
+
var globOf = (pattern) => pattern.source.replace(/^\^|\$$/gu, "").replaceAll(".*", "*").replace(/\\(.)/gu, "$1");
|
|
17887
|
+
|
|
17888
|
+
// packages/workit-core/src/hooks/session-marker.ts
|
|
17889
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
17890
|
+
import fs15 from "node:fs";
|
|
17785
17891
|
import path18 from "node:path";
|
|
17892
|
+
var sessionMarkerFile = (input, session, store) => {
|
|
17893
|
+
let dir = store;
|
|
17894
|
+
if (dir === undefined) {
|
|
17895
|
+
const location = resolveStore(input.cwd);
|
|
17896
|
+
if (location instanceof Error || !location.shared)
|
|
17897
|
+
return null;
|
|
17898
|
+
dir = location.dir;
|
|
17899
|
+
}
|
|
17900
|
+
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
17901
|
+
return path18.join(dir, "hooks", `skills-${name}.json`);
|
|
17902
|
+
};
|
|
17903
|
+
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
17904
|
+
var readSessionMarker = (file) => {
|
|
17905
|
+
try {
|
|
17906
|
+
const value = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
17907
|
+
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
17908
|
+
} catch {
|
|
17909
|
+
return { nudged: [], loaded: [], raw: [] };
|
|
17910
|
+
}
|
|
17911
|
+
};
|
|
17912
|
+
var writeSessionMarker = (file, marker) => {
|
|
17913
|
+
try {
|
|
17914
|
+
fs15.mkdirSync(path18.dirname(file), { recursive: true });
|
|
17915
|
+
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
17916
|
+
} catch {}
|
|
17917
|
+
};
|
|
17786
17918
|
|
|
17787
17919
|
// packages/workit-core/src/hooks/shell-words.ts
|
|
17788
17920
|
var closingQuote = (command, start, dialect) => {
|
|
@@ -17907,224 +18039,10 @@ var segmentsOf = (command, dialect = "posix") => {
|
|
|
17907
18039
|
return segments;
|
|
17908
18040
|
};
|
|
17909
18041
|
|
|
17910
|
-
// packages/workit-core/src/hooks/write-gate.ts
|
|
17911
|
-
var NONE2 = { kind: "none" };
|
|
17912
|
-
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
17913
|
-
var canonical2 = (file) => {
|
|
17914
|
-
let rest = "";
|
|
17915
|
-
for (let dir = path18.resolve(file);; dir = path18.dirname(dir)) {
|
|
17916
|
-
try {
|
|
17917
|
-
return path18.join(realpathSync5(dir), rest);
|
|
17918
|
-
} catch {
|
|
17919
|
-
if (path18.dirname(dir) === dir)
|
|
17920
|
-
return path18.resolve(file);
|
|
17921
|
-
rest = rest ? path18.join(path18.basename(dir), rest) : path18.basename(dir);
|
|
17922
|
-
}
|
|
17923
|
-
}
|
|
17924
|
-
};
|
|
17925
|
-
var exempt = (cwd, root, file, plans) => {
|
|
17926
|
-
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
17927
|
-
const relative = path18.relative(fold(canonical2(root)), fold(canonical2(path18.resolve(cwd, file))));
|
|
17928
|
-
if (relative.startsWith("..") || path18.isAbsolute(relative))
|
|
17929
|
-
return true;
|
|
17930
|
-
const posix = relative.split(path18.sep).join("/");
|
|
17931
|
-
const segments = posix.split("/");
|
|
17932
|
-
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
17933
|
-
};
|
|
17934
|
-
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
17935
|
-
var operands = (words) => words.filter((item) => !item.startsWith("-"));
|
|
17936
|
-
var editsOf = (input) => {
|
|
17937
|
-
let words = input;
|
|
17938
|
-
while (words.length && (PREFIXES2.has(words[0]) || /^\w+=/.test(words[0])))
|
|
17939
|
-
words = words.slice(1);
|
|
17940
|
-
const [verb, ...args] = words;
|
|
17941
|
-
const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
17942
|
-
if (!name || name === "workit" || name === "npx" && args.some((item) => /workit/.test(item)))
|
|
17943
|
-
return [];
|
|
17944
|
-
switch (name) {
|
|
17945
|
-
case "touch":
|
|
17946
|
-
case "mkdir":
|
|
17947
|
-
case "rm":
|
|
17948
|
-
case "rmdir":
|
|
17949
|
-
case "tee":
|
|
17950
|
-
case "mv":
|
|
17951
|
-
case "truncate":
|
|
17952
|
-
return operands(args);
|
|
17953
|
-
case "cp":
|
|
17954
|
-
case "ln":
|
|
17955
|
-
case "install": {
|
|
17956
|
-
const target = args.indexOf("-t");
|
|
17957
|
-
if (target >= 0)
|
|
17958
|
-
return args[target + 1] ? [args[target + 1]] : null;
|
|
17959
|
-
const last = operands(args).at(-1);
|
|
17960
|
-
return last ? [last] : null;
|
|
17961
|
-
}
|
|
17962
|
-
case "dd":
|
|
17963
|
-
return args.filter((item) => item.startsWith("of=")).map((item) => item.slice(3));
|
|
17964
|
-
case "sed":
|
|
17965
|
-
case "perl": {
|
|
17966
|
-
if (!args.some((item) => /^-[a-zA-Z]*i|^--in-place/.test(item)))
|
|
17967
|
-
return [];
|
|
17968
|
-
const scripted = args.some((item) => item === "-e" || item === "-f");
|
|
17969
|
-
const files = [];
|
|
17970
|
-
for (let index = 0;index < args.length; index++) {
|
|
17971
|
-
if (args[index] === "-e" || args[index] === "-f")
|
|
17972
|
-
index++;
|
|
17973
|
-
else if (!args[index].startsWith("-"))
|
|
17974
|
-
files.push(args[index]);
|
|
17975
|
-
}
|
|
17976
|
-
return scripted ? files : files.slice(1);
|
|
17977
|
-
}
|
|
17978
|
-
case "patch":
|
|
17979
|
-
return null;
|
|
17980
|
-
case "set-content":
|
|
17981
|
-
case "add-content":
|
|
17982
|
-
case "out-file":
|
|
17983
|
-
case "new-item":
|
|
17984
|
-
case "ni":
|
|
17985
|
-
case "remove-item":
|
|
17986
|
-
case "del":
|
|
17987
|
-
case "copy-item":
|
|
17988
|
-
case "move-item": {
|
|
17989
|
-
const flag = args.findIndex((item) => /^-(?:path|filepath|literalpath|destination)$/i.test(item));
|
|
17990
|
-
if (flag >= 0)
|
|
17991
|
-
return args[flag + 1] ? [args[flag + 1]] : null;
|
|
17992
|
-
const files = operands(args);
|
|
17993
|
-
if (name === "move-item")
|
|
17994
|
-
return files.length ? files : null;
|
|
17995
|
-
const file = name === "copy-item" ? files.at(-1) : files[0];
|
|
17996
|
-
return file ? [file] : null;
|
|
17997
|
-
}
|
|
17998
|
-
case "git": {
|
|
17999
|
-
const sub = operands(args)[0];
|
|
18000
|
-
if (sub === "apply")
|
|
18001
|
-
return null;
|
|
18002
|
-
if (sub === "restore" || sub === "mv" || sub === "rm")
|
|
18003
|
-
return operands(args).slice(1);
|
|
18004
|
-
if (sub === "checkout" && args.includes("--"))
|
|
18005
|
-
return args.slice(args.indexOf("--") + 1);
|
|
18006
|
-
return [];
|
|
18007
|
-
}
|
|
18008
|
-
default:
|
|
18009
|
-
return [];
|
|
18010
|
-
}
|
|
18011
|
-
};
|
|
18012
|
-
var shellWrites = (command) => {
|
|
18013
|
-
const targets = [];
|
|
18014
|
-
let unknown = false;
|
|
18015
|
-
for (const segment of segmentsOf(command)) {
|
|
18016
|
-
targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
|
|
18017
|
-
const edits = editsOf(segment.words);
|
|
18018
|
-
if (edits === null)
|
|
18019
|
-
unknown = true;
|
|
18020
|
-
else
|
|
18021
|
-
targets.push(...edits);
|
|
18022
|
-
}
|
|
18023
|
-
if (!unknown && targets.length === 0)
|
|
18024
|
-
return { writes: false, targets: null };
|
|
18025
|
-
return { writes: true, targets: unknown ? null : targets };
|
|
18026
|
-
};
|
|
18027
|
-
var cache2 = new Map;
|
|
18028
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path18.resolve(root, file)) ?? "-").join("\x00");
|
|
18029
|
-
var gateFor = (cwd) => {
|
|
18030
|
-
const store = new TaskStore(cwd);
|
|
18031
|
-
const listed = store.listTaskIndex();
|
|
18032
|
-
if (!listed.ok)
|
|
18033
|
-
return null;
|
|
18034
|
-
const key = store.currentKey();
|
|
18035
|
-
if (!key.ok)
|
|
18036
|
-
return null;
|
|
18037
|
-
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
18038
|
-
if (!entry)
|
|
18039
|
-
return { blockers: [], root: store.root, plans: new Set };
|
|
18040
|
-
const ledger = ledgerPath(store.root);
|
|
18041
|
-
const cacheKey = `${entry.id}\x00${entry.revision}\x00${ledger.ok && fileSignature(ledger.value) || "-"}`;
|
|
18042
|
-
const hit = cache2.get(store.root);
|
|
18043
|
-
if (hit && hit.key === cacheKey && hit.planKey === planKeyOf(store.root, hit.plans))
|
|
18044
|
-
return hit;
|
|
18045
|
-
const task = store.readTask(entry.id);
|
|
18046
|
-
if (!task.ok)
|
|
18047
|
-
return null;
|
|
18048
|
-
const workspace = store.readWorkspace();
|
|
18049
|
-
const plans = new Set((latestJudgment(task.data)?.refs ?? []).flatMap((ref) => ref.kind === "file" ? [ref.path] : []));
|
|
18050
|
-
const blockers = writeBlockers(task.data, store.root).filter((blocker) => {
|
|
18051
|
-
const requirement = task.data.policy?.requirements.find((item) => item.ruleId === blocker.ruleId && item.before === "write");
|
|
18052
|
-
return !(requirement?.acceptanceAllowed && workspace.ok && workspace.data && applicableDecision(task.data, workspace.data, requirement, store.root).length > 0);
|
|
18053
|
-
});
|
|
18054
|
-
const gate = {
|
|
18055
|
-
blockers,
|
|
18056
|
-
root: store.root,
|
|
18057
|
-
plans,
|
|
18058
|
-
key: cacheKey,
|
|
18059
|
-
planKey: planKeyOf(store.root, plans)
|
|
18060
|
-
};
|
|
18061
|
-
cache2.set(store.root, gate);
|
|
18062
|
-
if (cache2.size > 32)
|
|
18063
|
-
cache2.delete(cache2.keys().next().value);
|
|
18064
|
-
return gate;
|
|
18065
|
-
};
|
|
18066
|
-
function writeGate(cwd, paths) {
|
|
18067
|
-
try {
|
|
18068
|
-
const gate = gateFor(cwd);
|
|
18069
|
-
if (!gate || gate.blockers.length === 0)
|
|
18070
|
-
return NONE2;
|
|
18071
|
-
if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
18072
|
-
return NONE2;
|
|
18073
|
-
const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
|
|
18074
|
-
return {
|
|
18075
|
-
kind: "deny",
|
|
18076
|
-
reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
|
|
18077
|
-
unblock: gate.blockers[0].reason
|
|
18078
|
-
};
|
|
18079
|
-
} catch {
|
|
18080
|
-
return NONE2;
|
|
18081
|
-
}
|
|
18082
|
-
}
|
|
18083
|
-
// packages/workit-core/src/hooks/stop.ts
|
|
18084
|
-
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
18085
|
-
|
|
18086
18042
|
// packages/workit-core/src/hooks/raw-git.ts
|
|
18087
|
-
|
|
18088
|
-
import { createHash as createHash14 } from "node:crypto";
|
|
18089
|
-
import fs16 from "node:fs";
|
|
18090
|
-
import os4 from "node:os";
|
|
18091
|
-
import path20 from "node:path";
|
|
18092
|
-
|
|
18093
|
-
// packages/workit-core/src/hooks/session-marker.ts
|
|
18094
|
-
import { createHash as createHash13 } from "node:crypto";
|
|
18095
|
-
import fs15 from "node:fs";
|
|
18096
|
-
import path19 from "node:path";
|
|
18097
|
-
var sessionMarkerFile = (input, session, store) => {
|
|
18098
|
-
let dir = store;
|
|
18099
|
-
if (dir === undefined) {
|
|
18100
|
-
const location = resolveStore(input.cwd);
|
|
18101
|
-
if (location instanceof Error || !location.shared)
|
|
18102
|
-
return null;
|
|
18103
|
-
dir = location.dir;
|
|
18104
|
-
}
|
|
18105
|
-
const name = createHash13("sha256").update(`${input.host}\x00${session}`).digest("hex").slice(0, 32);
|
|
18106
|
-
return path19.join(dir, "hooks", `skills-${name}.json`);
|
|
18107
|
-
};
|
|
18108
|
-
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
18109
|
-
var readSessionMarker = (file) => {
|
|
18110
|
-
try {
|
|
18111
|
-
const value = JSON.parse(fs15.readFileSync(file, "utf8"));
|
|
18112
|
-
return { nudged: list2(value.nudged), loaded: list2(value.loaded), raw: list2(value.raw) };
|
|
18113
|
-
} catch {
|
|
18114
|
-
return { nudged: [], loaded: [], raw: [] };
|
|
18115
|
-
}
|
|
18116
|
-
};
|
|
18117
|
-
var writeSessionMarker = (file, marker) => {
|
|
18118
|
-
try {
|
|
18119
|
-
fs15.mkdirSync(path19.dirname(file), { recursive: true });
|
|
18120
|
-
fs15.writeFileSync(file, JSON.stringify(marker));
|
|
18121
|
-
} catch {}
|
|
18122
|
-
};
|
|
18123
|
-
|
|
18124
|
-
// packages/workit-core/src/hooks/raw-git.ts
|
|
18125
|
-
var NONE3 = { kind: "none" };
|
|
18043
|
+
var NONE2 = { kind: "none" };
|
|
18126
18044
|
var SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh"]);
|
|
18127
|
-
var
|
|
18045
|
+
var PREFIXES2 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "nice", "&"]);
|
|
18128
18046
|
var SCRIPT_FLAG = /^-[a-zA-Z]*c[a-zA-Z]*$/;
|
|
18129
18047
|
var CD = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
18130
18048
|
var PUSHD = new Set(["pushd", "push-location"]);
|
|
@@ -18138,12 +18056,12 @@ var GIT_VALUE_OPTIONS = new Set([
|
|
|
18138
18056
|
"--config-env",
|
|
18139
18057
|
"--super-prefix"
|
|
18140
18058
|
]);
|
|
18141
|
-
var isAbsolute = (value) =>
|
|
18059
|
+
var isAbsolute = (value) => path19.isAbsolute(value) || /^[A-Za-z]:[\\/]/.test(value) || value.startsWith("\\\\");
|
|
18142
18060
|
var joinDir = (base, next) => {
|
|
18143
18061
|
if (base === undefined || /[$`*?]/.test(next) || next === "-")
|
|
18144
18062
|
return;
|
|
18145
|
-
const expanded = next === "~" || next.startsWith("~/") ?
|
|
18146
|
-
return base === null || isAbsolute(expanded) ? expanded :
|
|
18063
|
+
const expanded = next === "~" || next.startsWith("~/") ? path19.join(os4.homedir(), next.slice(1)) : next;
|
|
18064
|
+
return base === null || isAbsolute(expanded) ? expanded : path19.join(base, expanded);
|
|
18147
18065
|
};
|
|
18148
18066
|
var cdTarget = (words) => {
|
|
18149
18067
|
const args = words.slice(1);
|
|
@@ -18160,7 +18078,7 @@ var stripPrefixes = (input) => {
|
|
|
18160
18078
|
return words;
|
|
18161
18079
|
if (/^\w+=/.test(first))
|
|
18162
18080
|
words = words.slice(1);
|
|
18163
|
-
else if (
|
|
18081
|
+
else if (PREFIXES2.has(first)) {
|
|
18164
18082
|
words = words.slice(1);
|
|
18165
18083
|
while (words[0]?.startsWith("-"))
|
|
18166
18084
|
words = words.slice(words[0] === "-u" ? 2 : 1);
|
|
@@ -18275,6 +18193,10 @@ var pushAction = (args) => {
|
|
|
18275
18193
|
let force = false;
|
|
18276
18194
|
let lease = false;
|
|
18277
18195
|
let wholesale = false;
|
|
18196
|
+
let allTags = false;
|
|
18197
|
+
let deletes = false;
|
|
18198
|
+
let allBranches = false;
|
|
18199
|
+
let mirror = false;
|
|
18278
18200
|
let remote = null;
|
|
18279
18201
|
const positionals = [];
|
|
18280
18202
|
for (let index = 1;index < args.length; index++) {
|
|
@@ -18295,14 +18217,22 @@ var pushAction = (args) => {
|
|
|
18295
18217
|
force = true;
|
|
18296
18218
|
else if (arg.startsWith("--force-with-lease") || arg === "--force-if-includes")
|
|
18297
18219
|
lease = true;
|
|
18298
|
-
else if (
|
|
18299
|
-
|
|
18220
|
+
else if (arg === "--delete" || arg === "-d")
|
|
18221
|
+
deletes = true;
|
|
18222
|
+
else if (arg === "--tags")
|
|
18223
|
+
allTags = wholesale = true;
|
|
18224
|
+
else if (arg === "--all" || arg === "--branches")
|
|
18225
|
+
allBranches = wholesale = true;
|
|
18226
|
+
else if (arg === "--mirror")
|
|
18227
|
+
mirror = wholesale = true;
|
|
18300
18228
|
else if (!arg.startsWith("-"))
|
|
18301
18229
|
positionals.push(arg);
|
|
18302
18230
|
}
|
|
18303
18231
|
const specs = remote === null ? positionals.slice(1) : positionals;
|
|
18304
18232
|
remote ??= positionals[0] ?? null;
|
|
18305
18233
|
const targets = [];
|
|
18234
|
+
const tags = [];
|
|
18235
|
+
const deleted = [];
|
|
18306
18236
|
for (const raw of specs) {
|
|
18307
18237
|
if (raw.startsWith("+"))
|
|
18308
18238
|
force = true;
|
|
@@ -18311,16 +18241,29 @@ var pushAction = (args) => {
|
|
|
18311
18241
|
const destination = colon >= 0 ? spec.slice(colon + 1) : spec;
|
|
18312
18242
|
if (!destination)
|
|
18313
18243
|
continue;
|
|
18314
|
-
|
|
18315
|
-
|
|
18316
|
-
|
|
18317
|
-
|
|
18318
|
-
|
|
18319
|
-
|
|
18244
|
+
const name = destination === "HEAD" ? null : destination.startsWith("refs/heads/") ? destination.slice(11) : destination.startsWith("refs/") ? undefined : destination;
|
|
18245
|
+
if (destination.startsWith("refs/tags/"))
|
|
18246
|
+
tags.push(destination.slice(10));
|
|
18247
|
+
if (name === undefined)
|
|
18248
|
+
continue;
|
|
18249
|
+
targets.push(name);
|
|
18250
|
+
if (colon === 0 && name !== null)
|
|
18251
|
+
deleted.push(name);
|
|
18320
18252
|
}
|
|
18321
18253
|
if (specs.length === 0 && !wholesale)
|
|
18322
18254
|
targets.push(null);
|
|
18323
|
-
return {
|
|
18255
|
+
return {
|
|
18256
|
+
kind: "push",
|
|
18257
|
+
targets,
|
|
18258
|
+
blindForce: force && !lease,
|
|
18259
|
+
remote,
|
|
18260
|
+
...tags.length ? { tags } : {},
|
|
18261
|
+
...allTags ? { allTags: true } : {},
|
|
18262
|
+
...deletes ? { deletes: true } : {},
|
|
18263
|
+
...deleted.length ? { deleted } : {},
|
|
18264
|
+
...allBranches ? { allBranches: true } : {},
|
|
18265
|
+
...mirror ? { mirror: true } : {}
|
|
18266
|
+
};
|
|
18324
18267
|
};
|
|
18325
18268
|
var VALUE_OPTIONS = {
|
|
18326
18269
|
git: new Set([
|
|
@@ -18427,28 +18370,80 @@ var headSha2 = (repo) => {
|
|
|
18427
18370
|
if (!git || git.reftable)
|
|
18428
18371
|
return null;
|
|
18429
18372
|
try {
|
|
18430
|
-
const head = fs16.readFileSync(
|
|
18373
|
+
const head = fs16.readFileSync(path19.join(git.gitDir, "HEAD"), "utf8").trim();
|
|
18431
18374
|
if (/^[0-9a-f]{40,64}$/.test(head))
|
|
18432
18375
|
return head;
|
|
18433
18376
|
const ref = /^ref: (refs\/.+)$/.exec(head)?.[1];
|
|
18434
18377
|
if (!ref)
|
|
18435
18378
|
return null;
|
|
18436
|
-
const common =
|
|
18379
|
+
const common = path19.dirname(repo.location.dir);
|
|
18437
18380
|
try {
|
|
18438
|
-
return fs16.readFileSync(
|
|
18381
|
+
return fs16.readFileSync(path19.join(common, ref), "utf8").trim() || null;
|
|
18439
18382
|
} catch {}
|
|
18440
|
-
const packed = fs16.readFileSync(
|
|
18383
|
+
const packed = fs16.readFileSync(path19.join(common, "packed-refs"), "utf8");
|
|
18441
18384
|
return new RegExp(`^([0-9a-f]{40,64}) ${ref.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}$`, "m").exec(packed)?.[1] ?? null;
|
|
18442
18385
|
} catch {
|
|
18443
18386
|
return null;
|
|
18444
18387
|
}
|
|
18445
18388
|
};
|
|
18446
18389
|
var quote = (text) => `\`${text}\``;
|
|
18447
|
-
var refusal = (action, branch) => {
|
|
18390
|
+
var refusal = (action, branch, dir) => {
|
|
18448
18391
|
if (action.kind === "merge")
|
|
18449
18392
|
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")}).`;
|
|
18393
|
+
const plainPush = action.kind === "push" && !action.blindForce && !action.deletes;
|
|
18394
|
+
if (dir && branch && plainPush && releaseBranch(dir, branch))
|
|
18395
|
+
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")}.`;
|
|
18450
18396
|
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")}.`;
|
|
18451
18397
|
};
|
|
18398
|
+
var refFacts = (repo, remote) => {
|
|
18399
|
+
const facts = { heads: new Set, tags: new Set, remoteHeads: new Set };
|
|
18400
|
+
const listed = spawnSync11("git", ["for-each-ref", "--format=%(refname)", "refs/heads", "refs/tags", `refs/remotes/${remote}`], { cwd: repo.dir, encoding: "utf8", timeout: 5000 });
|
|
18401
|
+
const remotePrefix = `refs/remotes/${remote}/`;
|
|
18402
|
+
for (const ref of (listed.stdout ?? "").split(`
|
|
18403
|
+
`)) {
|
|
18404
|
+
if (ref.startsWith("refs/heads/"))
|
|
18405
|
+
facts.heads.add(ref.slice(11));
|
|
18406
|
+
else if (ref.startsWith("refs/tags/"))
|
|
18407
|
+
facts.tags.add(ref.slice(10));
|
|
18408
|
+
else if (ref.startsWith(remotePrefix))
|
|
18409
|
+
facts.remoteHeads.add(ref.slice(remotePrefix.length));
|
|
18410
|
+
}
|
|
18411
|
+
return facts;
|
|
18412
|
+
};
|
|
18413
|
+
var isTag = (facts, name) => facts.tags.has(name) && !facts.heads.has(name);
|
|
18414
|
+
var tagRefusal = (action, facts) => {
|
|
18415
|
+
const tags = [
|
|
18416
|
+
...action.tags ?? [],
|
|
18417
|
+
...action.targets.filter((name) => name !== null && isTag(facts, name))
|
|
18418
|
+
];
|
|
18419
|
+
if (!tags.length && !action.allTags)
|
|
18420
|
+
return null;
|
|
18421
|
+
const named = tags.length ? tags.join(" --release ") : "v<version>";
|
|
18422
|
+
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")}).`;
|
|
18423
|
+
};
|
|
18424
|
+
var remoteHas = (repo, remote, branch) => {
|
|
18425
|
+
const listed = spawnSync11("git", ["ls-remote", "--heads", remote, `refs/heads/${branch}`], {
|
|
18426
|
+
cwd: repo.dir,
|
|
18427
|
+
encoding: "utf8",
|
|
18428
|
+
timeout: 3000,
|
|
18429
|
+
env: { ...process.env, GIT_TERMINAL_PROMPT: "0" }
|
|
18430
|
+
});
|
|
18431
|
+
return listed.status !== 0 || (listed.stdout ?? "").trim() !== "";
|
|
18432
|
+
};
|
|
18433
|
+
var offPolicyPush = (action, repo, facts, remote) => {
|
|
18434
|
+
const names = action.allBranches ? [...facts.heads] : action.targets.map((target) => target ?? repo.branch);
|
|
18435
|
+
for (const branch of names) {
|
|
18436
|
+
if (!branch || action.deleted?.includes(branch) || isTag(facts, branch))
|
|
18437
|
+
continue;
|
|
18438
|
+
if (facts.remoteHeads.has(branch))
|
|
18439
|
+
continue;
|
|
18440
|
+
const check = validateBranchNameFor(repo.dir, branch);
|
|
18441
|
+
if (check.ok || check.rule !== "allowed_pattern" || remoteHas(repo, remote, branch))
|
|
18442
|
+
continue;
|
|
18443
|
+
return `workit: raw ${quote("git push")} onto ${branch}: ${check.error}`;
|
|
18444
|
+
}
|
|
18445
|
+
return null;
|
|
18446
|
+
};
|
|
18452
18447
|
var nudgeFor = (action, host, session) => {
|
|
18453
18448
|
switch (action.kind) {
|
|
18454
18449
|
case "commit": {
|
|
@@ -18474,7 +18469,7 @@ var actionsIn = (cwd, command, dialect) => {
|
|
|
18474
18469
|
const action = rawAction(invocation);
|
|
18475
18470
|
if (!action)
|
|
18476
18471
|
continue;
|
|
18477
|
-
const dir =
|
|
18472
|
+
const dir = path19.resolve(cwd, invocation.dir ?? ".");
|
|
18478
18473
|
if (!repos.has(dir)) {
|
|
18479
18474
|
const repo = repoAt(dir);
|
|
18480
18475
|
repos.set(dir, repo && isWorkitWorkspace(repo) ? repo : null);
|
|
@@ -18511,7 +18506,7 @@ var unseenNudges = (input, nudges, store) => {
|
|
|
18511
18506
|
function rawGitPre(input, command, options = {}) {
|
|
18512
18507
|
try {
|
|
18513
18508
|
if (!mentionsRawTool(command))
|
|
18514
|
-
return
|
|
18509
|
+
return NONE2;
|
|
18515
18510
|
const dialect = dialectOf(input);
|
|
18516
18511
|
const nudges = new Map;
|
|
18517
18512
|
let store = null;
|
|
@@ -18519,31 +18514,42 @@ function rawGitPre(input, command, options = {}) {
|
|
|
18519
18514
|
store ??= repo.location.dir;
|
|
18520
18515
|
if (action.kind === "merge")
|
|
18521
18516
|
return deny(refusal(action, null));
|
|
18522
|
-
if (action.kind === "push")
|
|
18517
|
+
if (action.kind === "push" && toPushRemote(repo, action.remote)) {
|
|
18518
|
+
if (action.mirror)
|
|
18519
|
+
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")}.`);
|
|
18523
18520
|
for (const target of action.targets) {
|
|
18524
18521
|
const branch = target ?? repo.branch;
|
|
18525
|
-
if (branch && guardedBranch(repo.dir, branch)
|
|
18526
|
-
return deny(refusal(action, branch));
|
|
18522
|
+
if (branch && guardedBranch(repo.dir, branch))
|
|
18523
|
+
return deny(refusal(action, branch, repo.dir));
|
|
18527
18524
|
}
|
|
18525
|
+
const remote = action.remote ?? pushRemoteName(repo.dir, repo.branch) ?? "origin";
|
|
18526
|
+
const facts = refFacts(repo, remote);
|
|
18527
|
+
const tagDeny = tagRefusal(action, facts);
|
|
18528
|
+
if (tagDeny)
|
|
18529
|
+
return deny(tagDeny);
|
|
18530
|
+
const nameDeny = action.deletes ? null : offPolicyPush(action, repo, facts, remote);
|
|
18531
|
+
if (nameDeny)
|
|
18532
|
+
return deny(nameDeny);
|
|
18533
|
+
}
|
|
18528
18534
|
const nudge = nudgeFor(action, input.host, input.session.id);
|
|
18529
18535
|
if (nudge && !nudges.has(nudgeKind(action)))
|
|
18530
18536
|
nudges.set(nudgeKind(action), nudge);
|
|
18531
18537
|
}
|
|
18532
18538
|
if (options.nudge === false || !store || nudges.size === 0)
|
|
18533
|
-
return
|
|
18539
|
+
return NONE2;
|
|
18534
18540
|
if (workitInvocations(command, dialect).length > 0)
|
|
18535
|
-
return
|
|
18541
|
+
return NONE2;
|
|
18536
18542
|
const shown = unseenNudges(input, nudges, store);
|
|
18537
18543
|
return shown.length ? { kind: "context", text: shown.join(`
|
|
18538
|
-
`) } :
|
|
18544
|
+
`) } : NONE2;
|
|
18539
18545
|
} catch {
|
|
18540
|
-
return
|
|
18546
|
+
return NONE2;
|
|
18541
18547
|
}
|
|
18542
18548
|
}
|
|
18543
18549
|
var deny = (reason) => ({ kind: "deny", reason, unblock: null });
|
|
18544
18550
|
var NEXT_COMMAND = "next";
|
|
18545
18551
|
var MARKER_TTL_SECONDS = 3600;
|
|
18546
|
-
var markerFile = (repo, session, key) =>
|
|
18552
|
+
var markerFile = (repo, session, key) => path19.join(repo.location.dir, "hooks", `commit-${createHash14("sha256").update(`${session}\x00${key}`).digest("hex").slice(0, 32)}.json`);
|
|
18547
18553
|
var callKey = (toolUseId, command) => toolUseId ?? `cmd:${createHash14("sha256").update(command).digest("hex")}`;
|
|
18548
18554
|
function noteRawCommit(input, command, key) {
|
|
18549
18555
|
try {
|
|
@@ -18554,7 +18560,7 @@ function noteRawCommit(input, command, key) {
|
|
|
18554
18560
|
if (action.kind !== "commit")
|
|
18555
18561
|
continue;
|
|
18556
18562
|
const file = markerFile(repo, input.session.id, key);
|
|
18557
|
-
fs16.mkdirSync(
|
|
18563
|
+
fs16.mkdirSync(path19.dirname(file), { recursive: true });
|
|
18558
18564
|
fs16.writeFileSync(file, `${JSON.stringify({ head: headSha2(repo), at })}
|
|
18559
18565
|
`);
|
|
18560
18566
|
}
|
|
@@ -18636,7 +18642,292 @@ function settlePendingCommit(input) {
|
|
|
18636
18642
|
} catch {}
|
|
18637
18643
|
}
|
|
18638
18644
|
|
|
18645
|
+
// packages/workit-core/src/core/route-intent.ts
|
|
18646
|
+
var UNKNOWABLE = /[$`*?[\]{}~]/u;
|
|
18647
|
+
var SWITCH_CREATE = new Set(["-c", "--create", "-C", "--force-create", "--orphan"]);
|
|
18648
|
+
var CHECKOUT_CREATE = new Set(["-b", "-B", "--orphan"]);
|
|
18649
|
+
var WORKTREE_CREATE = new Set(["-b", "-B"]);
|
|
18650
|
+
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"]));
|
|
18651
|
+
var BRANCH_RENAME = new Set(["-m", "-M", "--move", "-c", "-C", "--copy"]);
|
|
18652
|
+
var flagValue = (args, flags) => {
|
|
18653
|
+
for (let index = 1;index < args.length; index++) {
|
|
18654
|
+
const arg = args[index];
|
|
18655
|
+
if (arg === "--")
|
|
18656
|
+
return null;
|
|
18657
|
+
if (flags.has(arg))
|
|
18658
|
+
return args[index + 1] ?? null;
|
|
18659
|
+
const eq = arg.indexOf("=");
|
|
18660
|
+
if (arg.startsWith("--") && eq > 0 && flags.has(arg.slice(0, eq)))
|
|
18661
|
+
return arg.slice(eq + 1);
|
|
18662
|
+
if (!arg.startsWith("--") && arg.length > 2 && flags.has(arg.slice(0, 2)))
|
|
18663
|
+
return arg.slice(2);
|
|
18664
|
+
}
|
|
18665
|
+
return null;
|
|
18666
|
+
};
|
|
18667
|
+
var WORKTREE_VALUES = new Set(["--reason"]);
|
|
18668
|
+
var worktreeTarget = (args) => {
|
|
18669
|
+
const named = flagValue(args, WORKTREE_CREATE);
|
|
18670
|
+
if (named !== null)
|
|
18671
|
+
return named;
|
|
18672
|
+
const positionals = [];
|
|
18673
|
+
for (let index = 1;index < args.length; index++) {
|
|
18674
|
+
const arg = args[index];
|
|
18675
|
+
if (arg === "--detach" || arg === "-d")
|
|
18676
|
+
return null;
|
|
18677
|
+
if (WORKTREE_VALUES.has(arg))
|
|
18678
|
+
index++;
|
|
18679
|
+
else if (!arg.startsWith("-"))
|
|
18680
|
+
positionals.push(arg);
|
|
18681
|
+
}
|
|
18682
|
+
if (positionals.length !== 1)
|
|
18683
|
+
return null;
|
|
18684
|
+
const base = positionals[0].split(/[\\/]/u).findLast(Boolean) ?? "";
|
|
18685
|
+
return base && base !== "." && base !== ".." ? base : null;
|
|
18686
|
+
};
|
|
18687
|
+
var branchCommandTarget = (args) => {
|
|
18688
|
+
let rename = false;
|
|
18689
|
+
const positionals = [];
|
|
18690
|
+
for (let index = 1;index < args.length; index++) {
|
|
18691
|
+
const arg = args[index];
|
|
18692
|
+
if (arg === "--") {
|
|
18693
|
+
positionals.push(...args.slice(index + 1));
|
|
18694
|
+
break;
|
|
18695
|
+
}
|
|
18696
|
+
const flag = arg.split("=", 1)[0];
|
|
18697
|
+
if (BRANCH_NO_CREATE.has(flag))
|
|
18698
|
+
return null;
|
|
18699
|
+
if (BRANCH_RENAME.has(flag))
|
|
18700
|
+
rename = true;
|
|
18701
|
+
else if (!arg.startsWith("-"))
|
|
18702
|
+
positionals.push(arg);
|
|
18703
|
+
}
|
|
18704
|
+
if (positionals.length < 1 || positionals.length > 2)
|
|
18705
|
+
return null;
|
|
18706
|
+
return rename ? positionals.at(-1) ?? null : positionals[0];
|
|
18707
|
+
};
|
|
18708
|
+
var gitBranchCreation = (args) => {
|
|
18709
|
+
const [sub, second] = args;
|
|
18710
|
+
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;
|
|
18711
|
+
return target && !target.startsWith("-") && !UNKNOWABLE.test(target) ? target : null;
|
|
18712
|
+
};
|
|
18713
|
+
var shellBranchTargets = (command, dialect = "posix") => {
|
|
18714
|
+
if (!/\bgit\b/u.test(command))
|
|
18715
|
+
return [];
|
|
18716
|
+
const found = [];
|
|
18717
|
+
for (const invocation of rawInvocations(command, dialect)) {
|
|
18718
|
+
if (invocation.tool !== "git")
|
|
18719
|
+
continue;
|
|
18720
|
+
const branch = gitBranchCreation(invocation.args);
|
|
18721
|
+
if (branch)
|
|
18722
|
+
found.push({ branch, dir: invocation.dir });
|
|
18723
|
+
}
|
|
18724
|
+
return found;
|
|
18725
|
+
};
|
|
18726
|
+
var localBranchExists = (dir, branch) => spawnSync12("git", ["show-ref", "--verify", "--quiet", `refs/heads/${branch}`], {
|
|
18727
|
+
cwd: dir,
|
|
18728
|
+
stdio: "ignore",
|
|
18729
|
+
timeout: 5000
|
|
18730
|
+
}).status === 0;
|
|
18731
|
+
var shellBranchPolicyViolation = (root, command, dialect = "posix") => {
|
|
18732
|
+
let judged = false;
|
|
18733
|
+
for (const target of shellBranchTargets(command, dialect)) {
|
|
18734
|
+
if (target.dir === undefined)
|
|
18735
|
+
continue;
|
|
18736
|
+
const dir = target.dir === null ? root : path20.resolve(root, target.dir);
|
|
18737
|
+
if (target.dir !== null && (!existsSync6(dir) || !inWorkitWorkspace(dir)))
|
|
18738
|
+
continue;
|
|
18739
|
+
if (localBranchExists(dir, target.branch))
|
|
18740
|
+
continue;
|
|
18741
|
+
judged = true;
|
|
18742
|
+
const check = validateBranchNameFor(dir, target.branch);
|
|
18743
|
+
if (!check.ok)
|
|
18744
|
+
return check;
|
|
18745
|
+
}
|
|
18746
|
+
return judged ? { ok: true } : null;
|
|
18747
|
+
};
|
|
18748
|
+
|
|
18749
|
+
// packages/workit-core/src/hooks/policy.ts
|
|
18750
|
+
var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
18751
|
+
const policy = shellBranchPolicyViolation(cwd, command, dialect);
|
|
18752
|
+
return policy && !policy.ok ? { kind: "deny", reason: `branch_policy_denied: ${policy.error}`, unblock: null } : { kind: "none" };
|
|
18753
|
+
};
|
|
18754
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
18755
|
+
import { realpathSync as realpathSync5 } from "node:fs";
|
|
18756
|
+
import path21 from "node:path";
|
|
18757
|
+
var NONE3 = { kind: "none" };
|
|
18758
|
+
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
18759
|
+
var canonical2 = (file) => {
|
|
18760
|
+
let rest = "";
|
|
18761
|
+
for (let dir = path21.resolve(file);; dir = path21.dirname(dir)) {
|
|
18762
|
+
try {
|
|
18763
|
+
return path21.join(realpathSync5(dir), rest);
|
|
18764
|
+
} catch {
|
|
18765
|
+
if (path21.dirname(dir) === dir)
|
|
18766
|
+
return path21.resolve(file);
|
|
18767
|
+
rest = rest ? path21.join(path21.basename(dir), rest) : path21.basename(dir);
|
|
18768
|
+
}
|
|
18769
|
+
}
|
|
18770
|
+
};
|
|
18771
|
+
var exempt = (cwd, root, file, plans) => {
|
|
18772
|
+
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
18773
|
+
const relative = path21.relative(fold(canonical2(root)), fold(canonical2(path21.resolve(cwd, file))));
|
|
18774
|
+
if (relative.startsWith("..") || path21.isAbsolute(relative))
|
|
18775
|
+
return true;
|
|
18776
|
+
const posix = relative.split(path21.sep).join("/");
|
|
18777
|
+
const segments = posix.split("/");
|
|
18778
|
+
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
18779
|
+
};
|
|
18780
|
+
var PREFIXES3 = new Set(["sudo", "command", "env", "nohup", "time", "exec", "xargs"]);
|
|
18781
|
+
var operands = (words) => words.filter((item) => !item.startsWith("-"));
|
|
18782
|
+
var editsOf = (input) => {
|
|
18783
|
+
let words = input;
|
|
18784
|
+
while (words.length && (PREFIXES3.has(words[0]) || /^\w+=/.test(words[0])))
|
|
18785
|
+
words = words.slice(1);
|
|
18786
|
+
const [verb, ...args] = words;
|
|
18787
|
+
const name = (verb?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
18788
|
+
if (!name || name === "workit" || name === "npx" && args.some((item) => /workit/.test(item)))
|
|
18789
|
+
return [];
|
|
18790
|
+
switch (name) {
|
|
18791
|
+
case "touch":
|
|
18792
|
+
case "mkdir":
|
|
18793
|
+
case "rm":
|
|
18794
|
+
case "rmdir":
|
|
18795
|
+
case "tee":
|
|
18796
|
+
case "mv":
|
|
18797
|
+
case "truncate":
|
|
18798
|
+
return operands(args);
|
|
18799
|
+
case "cp":
|
|
18800
|
+
case "ln":
|
|
18801
|
+
case "install": {
|
|
18802
|
+
const target = args.indexOf("-t");
|
|
18803
|
+
if (target >= 0)
|
|
18804
|
+
return args[target + 1] ? [args[target + 1]] : null;
|
|
18805
|
+
const last = operands(args).at(-1);
|
|
18806
|
+
return last ? [last] : null;
|
|
18807
|
+
}
|
|
18808
|
+
case "dd":
|
|
18809
|
+
return args.filter((item) => item.startsWith("of=")).map((item) => item.slice(3));
|
|
18810
|
+
case "sed":
|
|
18811
|
+
case "perl": {
|
|
18812
|
+
if (!args.some((item) => /^-[a-zA-Z]*i|^--in-place/.test(item)))
|
|
18813
|
+
return [];
|
|
18814
|
+
const scripted = args.some((item) => item === "-e" || item === "-f");
|
|
18815
|
+
const files = [];
|
|
18816
|
+
for (let index = 0;index < args.length; index++) {
|
|
18817
|
+
if (args[index] === "-e" || args[index] === "-f")
|
|
18818
|
+
index++;
|
|
18819
|
+
else if (!args[index].startsWith("-"))
|
|
18820
|
+
files.push(args[index]);
|
|
18821
|
+
}
|
|
18822
|
+
return scripted ? files : files.slice(1);
|
|
18823
|
+
}
|
|
18824
|
+
case "patch":
|
|
18825
|
+
return null;
|
|
18826
|
+
case "set-content":
|
|
18827
|
+
case "add-content":
|
|
18828
|
+
case "out-file":
|
|
18829
|
+
case "new-item":
|
|
18830
|
+
case "ni":
|
|
18831
|
+
case "remove-item":
|
|
18832
|
+
case "del":
|
|
18833
|
+
case "copy-item":
|
|
18834
|
+
case "move-item": {
|
|
18835
|
+
const flag = args.findIndex((item) => /^-(?:path|filepath|literalpath|destination)$/i.test(item));
|
|
18836
|
+
if (flag >= 0)
|
|
18837
|
+
return args[flag + 1] ? [args[flag + 1]] : null;
|
|
18838
|
+
const files = operands(args);
|
|
18839
|
+
if (name === "move-item")
|
|
18840
|
+
return files.length ? files : null;
|
|
18841
|
+
const file = name === "copy-item" ? files.at(-1) : files[0];
|
|
18842
|
+
return file ? [file] : null;
|
|
18843
|
+
}
|
|
18844
|
+
case "git": {
|
|
18845
|
+
const sub = operands(args)[0];
|
|
18846
|
+
if (sub === "apply")
|
|
18847
|
+
return null;
|
|
18848
|
+
if (sub === "restore" || sub === "mv" || sub === "rm")
|
|
18849
|
+
return operands(args).slice(1);
|
|
18850
|
+
if (sub === "checkout" && args.includes("--"))
|
|
18851
|
+
return args.slice(args.indexOf("--") + 1);
|
|
18852
|
+
return [];
|
|
18853
|
+
}
|
|
18854
|
+
default:
|
|
18855
|
+
return [];
|
|
18856
|
+
}
|
|
18857
|
+
};
|
|
18858
|
+
var shellWrites = (command) => {
|
|
18859
|
+
const targets = [];
|
|
18860
|
+
let unknown = false;
|
|
18861
|
+
for (const segment of segmentsOf(command)) {
|
|
18862
|
+
targets.push(...segment.redirects.filter((item) => !/^\/dev\/(?:null|stdout|stderr|tty)$/.test(item)));
|
|
18863
|
+
const edits = editsOf(segment.words);
|
|
18864
|
+
if (edits === null)
|
|
18865
|
+
unknown = true;
|
|
18866
|
+
else
|
|
18867
|
+
targets.push(...edits);
|
|
18868
|
+
}
|
|
18869
|
+
if (!unknown && targets.length === 0)
|
|
18870
|
+
return { writes: false, targets: null };
|
|
18871
|
+
return { writes: true, targets: unknown ? null : targets };
|
|
18872
|
+
};
|
|
18873
|
+
var cache2 = new Map;
|
|
18874
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path21.resolve(root, file)) ?? "-").join("\x00");
|
|
18875
|
+
var gateFor = (cwd) => {
|
|
18876
|
+
const store = new TaskStore(cwd);
|
|
18877
|
+
const listed = store.listTaskIndex();
|
|
18878
|
+
if (!listed.ok)
|
|
18879
|
+
return null;
|
|
18880
|
+
const key = store.currentKey();
|
|
18881
|
+
if (!key.ok)
|
|
18882
|
+
return null;
|
|
18883
|
+
const entry = listed.data.filter((item) => item.key === key.data.key && item.status !== "closed").toSorted((left, right) => right.updatedAt.localeCompare(left.updatedAt))[0];
|
|
18884
|
+
if (!entry)
|
|
18885
|
+
return { blockers: [], root: store.root, plans: new Set };
|
|
18886
|
+
const ledger = ledgerPath(store.root);
|
|
18887
|
+
const cacheKey = `${entry.id}\x00${entry.revision}\x00${ledger.ok && fileSignature(ledger.value) || "-"}`;
|
|
18888
|
+
const hit = cache2.get(store.root);
|
|
18889
|
+
if (hit && hit.key === cacheKey && hit.planKey === planKeyOf(store.root, hit.plans))
|
|
18890
|
+
return hit;
|
|
18891
|
+
const task = store.readTask(entry.id);
|
|
18892
|
+
if (!task.ok)
|
|
18893
|
+
return null;
|
|
18894
|
+
const workspace = store.readWorkspace();
|
|
18895
|
+
const plans = new Set((latestJudgment(task.data)?.refs ?? []).flatMap((ref) => ref.kind === "file" ? [ref.path] : []));
|
|
18896
|
+
const blockers = writeBlockers(task.data, store.root).filter((blocker) => {
|
|
18897
|
+
const requirement = task.data.policy?.requirements.find((item) => item.ruleId === blocker.ruleId && item.before === "write");
|
|
18898
|
+
return !(requirement?.acceptanceAllowed && workspace.ok && workspace.data && applicableDecision(task.data, workspace.data, requirement, store.root).length > 0);
|
|
18899
|
+
});
|
|
18900
|
+
const gate = {
|
|
18901
|
+
blockers,
|
|
18902
|
+
root: store.root,
|
|
18903
|
+
plans,
|
|
18904
|
+
key: cacheKey,
|
|
18905
|
+
planKey: planKeyOf(store.root, plans)
|
|
18906
|
+
};
|
|
18907
|
+
cache2.set(store.root, gate);
|
|
18908
|
+
if (cache2.size > 32)
|
|
18909
|
+
cache2.delete(cache2.keys().next().value);
|
|
18910
|
+
return gate;
|
|
18911
|
+
};
|
|
18912
|
+
function writeGate(cwd, paths) {
|
|
18913
|
+
try {
|
|
18914
|
+
const gate = gateFor(cwd);
|
|
18915
|
+
if (!gate || gate.blockers.length === 0)
|
|
18916
|
+
return NONE3;
|
|
18917
|
+
if (paths?.length && paths.every((file) => exempt(cwd, gate.root, file, gate.plans)))
|
|
18918
|
+
return NONE3;
|
|
18919
|
+
const reasons = gate.blockers.map((item) => `${item.ruleId}: ${item.reason}`).join("; ");
|
|
18920
|
+
return {
|
|
18921
|
+
kind: "deny",
|
|
18922
|
+
reason: `workit before-write gate: ${reasons}. Markdown, docs/, plans/ and files outside the checkout stay writable.`,
|
|
18923
|
+
unblock: gate.blockers[0].reason
|
|
18924
|
+
};
|
|
18925
|
+
} catch {
|
|
18926
|
+
return NONE3;
|
|
18927
|
+
}
|
|
18928
|
+
}
|
|
18639
18929
|
// packages/workit-core/src/hooks/stop.ts
|
|
18930
|
+
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
18640
18931
|
var NONE4 = { kind: "none" };
|
|
18641
18932
|
var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
|
|
18642
18933
|
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;
|
|
@@ -18657,7 +18948,7 @@ var asksUser = (message) => {
|
|
|
18657
18948
|
};
|
|
18658
18949
|
var short = (sha) => (sha ?? "?").slice(0, 12);
|
|
18659
18950
|
var gitList = (cwd, args) => {
|
|
18660
|
-
const run =
|
|
18951
|
+
const run = spawnSync13("git", args, {
|
|
18661
18952
|
cwd,
|
|
18662
18953
|
encoding: "utf8",
|
|
18663
18954
|
timeout: 3000,
|
|
@@ -19020,13 +19311,13 @@ ${line}` };
|
|
|
19020
19311
|
return decision;
|
|
19021
19312
|
};
|
|
19022
19313
|
// packages/workit-core/src/hooks/hosts/fields.ts
|
|
19023
|
-
import { existsSync as
|
|
19024
|
-
import
|
|
19314
|
+
import { existsSync as existsSync7, realpathSync as realpathSync6, statSync as statSync2 } from "node:fs";
|
|
19315
|
+
import path22 from "node:path";
|
|
19025
19316
|
var isRecord4 = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
|
|
19026
19317
|
var nonEmpty2 = (value) => typeof value === "string" && value.trim() !== "";
|
|
19027
19318
|
var optionalText = (value) => typeof value === "string" ? value : null;
|
|
19028
19319
|
var existingDirectory = (value) => {
|
|
19029
|
-
if (!nonEmpty2(value) || !
|
|
19320
|
+
if (!nonEmpty2(value) || !path22.isAbsolute(value) || !existsSync7(value))
|
|
19030
19321
|
return null;
|
|
19031
19322
|
try {
|
|
19032
19323
|
return statSync2(value).isDirectory() ? realpathSync6(value) : null;
|
|
@@ -19115,13 +19406,14 @@ var subagentStartText = (input, descriptor, event) => {
|
|
|
19115
19406
|
const role = workitRole(descriptor, event.agentType);
|
|
19116
19407
|
const who = `${descriptor.label} subagent ${event.agentId} (${event.agentType})`;
|
|
19117
19408
|
const isolated = descriptor.subagents.worktreeIsolation === "native" && (role === "implementer" || CLAUDE_WORKTREE.test(input.cwd));
|
|
19409
|
+
const guide = role === "implementer" ? branchGuidance(input.cwd) : null;
|
|
19118
19410
|
if (role === "implementer" && isolated)
|
|
19119
|
-
return `Workit observed ${who} working in its own git worktree: it may edit and commit there, within its brief's scope. ${ISOLATED_GIT}
|
|
19411
|
+
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.`;
|
|
19120
19412
|
if (isolated)
|
|
19121
19413
|
return `${ISOLATED_GIT}
|
|
19122
19414
|
${subagentStartText({ ...input, cwd: "" }, descriptor, event)}`;
|
|
19123
19415
|
if (role === "implementer")
|
|
19124
|
-
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>\` (
|
|
19416
|
+
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.`;
|
|
19125
19417
|
const readOnly = `Workit observed ${who} as read-only/agent-guided.`;
|
|
19126
19418
|
if (role !== "judge" || !event.agentId)
|
|
19127
19419
|
return readOnly;
|
|
@@ -19161,7 +19453,7 @@ function handleHook(input, deps) {
|
|
|
19161
19453
|
if (skillRead)
|
|
19162
19454
|
recordSkillLoad(input, skillRead, "read");
|
|
19163
19455
|
const canDeny = usable2(descriptor.shellPolicy.deny);
|
|
19164
|
-
const policy = canDeny ? shellPolicy(input.cwd, event.command) : NONE5;
|
|
19456
|
+
const policy = canDeny ? shellPolicy(input.cwd, event.command, event.dialect) : NONE5;
|
|
19165
19457
|
if (policy.kind !== "none")
|
|
19166
19458
|
return policy;
|
|
19167
19459
|
const postTool = usable2(descriptor.events["shell.post"].support);
|
|
@@ -19764,7 +20056,7 @@ var codexAdapter = {
|
|
|
19764
20056
|
|
|
19765
20057
|
// packages/workit-core/src/hooks/hosts/cursor.ts
|
|
19766
20058
|
import fs17 from "node:fs";
|
|
19767
|
-
import
|
|
20059
|
+
import path23 from "node:path";
|
|
19768
20060
|
var undocumented = { support: "undocumented", native: null };
|
|
19769
20061
|
var none = { support: "none", native: null };
|
|
19770
20062
|
var CURSOR_DESCRIPTOR = {
|
|
@@ -19961,7 +20253,7 @@ var protocolEvent2 = (input) => {
|
|
|
19961
20253
|
};
|
|
19962
20254
|
var TRANSCRIPT_TAIL_BYTES = 256 * 1024;
|
|
19963
20255
|
var lastAssistantText = (file) => {
|
|
19964
|
-
if (!file || !
|
|
20256
|
+
if (!file || !path23.isAbsolute(file))
|
|
19965
20257
|
return null;
|
|
19966
20258
|
try {
|
|
19967
20259
|
const size = fs17.statSync(file).size;
|
|
@@ -19998,7 +20290,7 @@ var lastAssistantText = (file) => {
|
|
|
19998
20290
|
return null;
|
|
19999
20291
|
}
|
|
20000
20292
|
};
|
|
20001
|
-
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(
|
|
20293
|
+
var within = (root, dir) => dir === root || dir.startsWith(root.endsWith(path23.sep) ? root : `${root}${path23.sep}`);
|
|
20002
20294
|
var hookCwd = (input) => {
|
|
20003
20295
|
const cwd = existingDirectory(input.cwd);
|
|
20004
20296
|
if (cwd && input.hook_event_name === "beforeShellExecution")
|
package/dist/workit-hook.js
CHANGED
|
@@ -37,6 +37,6 @@ if (quiet)
|
|
|
37
37
|
process.stdout.write(`${JSON.stringify({ hookSpecificOutput: { hookEventName: quiet } })}
|
|
38
38
|
`);
|
|
39
39
|
else {
|
|
40
|
-
const { runCodexHook } = await import("./workit-hook-
|
|
40
|
+
const { runCodexHook } = await import("./workit-hook-gtdf2ra8.js");
|
|
41
41
|
await runCodexHook([payload]);
|
|
42
42
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-codex",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.11.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit Codex CLI/desktop plugin — shared MCP transport, hooks, method skills, and verifier/reviewer/implementer agents",
|
|
6
6
|
"keywords": [
|
|
@@ -39,8 +39,8 @@
|
|
|
39
39
|
"build": "bun scripts/build.ts"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@brainervirus/workit-core": "^8.
|
|
43
|
-
"@brainervirus/workit-mcp": "^8.
|
|
42
|
+
"@brainervirus/workit-core": "^8.11.0",
|
|
43
|
+
"@brainervirus/workit-mcp": "^8.11.0"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|
|
46
46
|
"node": ">=24"
|