@brainervirus/workit-cli 8.35.0 → 8.35.2
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/index.js +241 -59
- package/package.json +3 -3
package/dist/index.js
CHANGED
|
@@ -6401,9 +6401,16 @@ function workspaceDefaultTarget(cwd, globalDefault) {
|
|
|
6401
6401
|
}
|
|
6402
6402
|
var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspaceCommitPolicySchema, workspaceProfileSchema, versionSourceSchema, releaseTrackSchema, workspaceConfigSchema, workspacesFileSchema, workspacesRevision = (contents) => createHash("sha256").update(contents ?? "<missing>").digest("hex"), validateWorkspacesDocument = (parsed, file) => {
|
|
6403
6403
|
if (!isConfigObject(parsed)) {
|
|
6404
|
-
return {
|
|
6404
|
+
return {
|
|
6405
|
+
status: "malformed",
|
|
6406
|
+
path: file,
|
|
6407
|
+
entries: [],
|
|
6408
|
+
error: `${file} is not a JSON object`
|
|
6409
|
+
};
|
|
6405
6410
|
}
|
|
6406
|
-
const validated = workspacesFileSchema.safeParse(parsed, {
|
|
6411
|
+
const validated = workspacesFileSchema.safeParse(parsed, {
|
|
6412
|
+
reportInput: true
|
|
6413
|
+
});
|
|
6407
6414
|
if (!validated.success) {
|
|
6408
6415
|
const detail = validated.error.issues.map((issue) => {
|
|
6409
6416
|
const field = String(issue.path[issue.path.length - 1] ?? "");
|
|
@@ -6442,7 +6449,12 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
6442
6449
|
raw = readFileSync2(file, "utf8");
|
|
6443
6450
|
} catch (error) {
|
|
6444
6451
|
if (error.code === "ENOENT")
|
|
6445
|
-
return {
|
|
6452
|
+
return {
|
|
6453
|
+
status: "missing",
|
|
6454
|
+
path: file,
|
|
6455
|
+
entries: [],
|
|
6456
|
+
revision: workspacesRevision(null)
|
|
6457
|
+
};
|
|
6446
6458
|
return {
|
|
6447
6459
|
status: "malformed",
|
|
6448
6460
|
path: file,
|
|
@@ -6462,7 +6474,10 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
6462
6474
|
error: `${file} is not valid JSON`
|
|
6463
6475
|
};
|
|
6464
6476
|
}
|
|
6465
|
-
return {
|
|
6477
|
+
return {
|
|
6478
|
+
...validateWorkspacesDocument(parsed, file),
|
|
6479
|
+
revision: workspacesRevision(raw)
|
|
6480
|
+
};
|
|
6466
6481
|
}, loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries, globToRegExp = (glob) => {
|
|
6467
6482
|
let out = "";
|
|
6468
6483
|
for (let i = 0;i < glob.length; i++) {
|
|
@@ -6591,7 +6606,7 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
6591
6606
|
for (const b of entries.slice(i + 1)) {
|
|
6592
6607
|
const aInB = matchWorkspace(b.glob, globRoot(a.glob));
|
|
6593
6608
|
const bInA = matchWorkspace(a.glob, globRoot(b.glob));
|
|
6594
|
-
if (!aInB && !bInA)
|
|
6609
|
+
if (!aInB && !bInA && !globsMayOverlap(a.glob, b.glob))
|
|
6595
6610
|
continue;
|
|
6596
6611
|
const [inner, outer] = aInB && !bInA ? [a, b] : bInA && !aInB ? [b, a] : globSpecificity(a.glob) >= globSpecificity(b.glob) ? [a, b] : [b, a];
|
|
6597
6612
|
const si = globSpecificity(inner.glob);
|
|
@@ -6681,7 +6696,10 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
6681
6696
|
return selected ? { file, selected, matches } : null;
|
|
6682
6697
|
}, resolveRuntimeReleaseTracks = (cwd) => {
|
|
6683
6698
|
const matched = matchRuntimeWorkspace(cwd);
|
|
6684
|
-
return matched ? {
|
|
6699
|
+
return matched ? {
|
|
6700
|
+
workspace: matched.selected.name,
|
|
6701
|
+
releaseTracks: matched.selected.releaseTracks
|
|
6702
|
+
} : null;
|
|
6685
6703
|
}, inheritedSetting = (cwd, dir, has) => {
|
|
6686
6704
|
let matched;
|
|
6687
6705
|
try {
|
|
@@ -6693,7 +6711,10 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
6693
6711
|
return null;
|
|
6694
6712
|
const { selected, matches } = matched;
|
|
6695
6713
|
const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
6696
|
-
return {
|
|
6714
|
+
return {
|
|
6715
|
+
workspace: selected.name,
|
|
6716
|
+
source: [selected, ...enclosing].find(has) ?? null
|
|
6717
|
+
};
|
|
6697
6718
|
}, workspaceDeleteBranch = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.deleteBranch === "boolean")?.source?.deleteBranch !== false, workspaceTrailers = (cwd, dir) => {
|
|
6698
6719
|
const found = inheritedSetting(cwd, dir, (entry) => typeof entry.trailers === "boolean");
|
|
6699
6720
|
return {
|
|
@@ -6821,7 +6842,10 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
6821
6842
|
status: "resolved",
|
|
6822
6843
|
profileName: null,
|
|
6823
6844
|
workspace: null,
|
|
6824
|
-
provenance: {
|
|
6845
|
+
provenance: {
|
|
6846
|
+
branchPolicy: "user-default",
|
|
6847
|
+
commitPolicy: "user-default"
|
|
6848
|
+
}
|
|
6825
6849
|
};
|
|
6826
6850
|
if (selected.status === "invalid")
|
|
6827
6851
|
return selected;
|
|
@@ -6892,7 +6916,11 @@ var init_workspaces = __esm(() => {
|
|
|
6892
6916
|
["requiredChecks", track.requiredChecks ?? []]
|
|
6893
6917
|
]) {
|
|
6894
6918
|
if (new Set(values).size !== values.length) {
|
|
6895
|
-
context.addIssue({
|
|
6919
|
+
context.addIssue({
|
|
6920
|
+
code: "custom",
|
|
6921
|
+
path: [field],
|
|
6922
|
+
message: "entries must be unique"
|
|
6923
|
+
});
|
|
6896
6924
|
}
|
|
6897
6925
|
}
|
|
6898
6926
|
if (track.versionSource?.kind === "package-json" && (path2.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
|
|
@@ -6925,7 +6953,10 @@ var init_workspaces = __esm(() => {
|
|
|
6925
6953
|
kind,
|
|
6926
6954
|
union([boolean2(), literal("verified")]).optional()
|
|
6927
6955
|
]))).passthrough().optional(),
|
|
6928
|
-
youtrack: object({
|
|
6956
|
+
youtrack: object({
|
|
6957
|
+
baseUrl: nonBlank.optional(),
|
|
6958
|
+
link_issues: boolean2().optional()
|
|
6959
|
+
}).passthrough().optional(),
|
|
6929
6960
|
issues: object({
|
|
6930
6961
|
provider: _enum(["github", "gitlab", "youtrack"]).optional(),
|
|
6931
6962
|
link: boolean2().optional(),
|
|
@@ -9286,7 +9317,7 @@ var package_default;
|
|
|
9286
9317
|
var init_package = __esm(() => {
|
|
9287
9318
|
package_default = {
|
|
9288
9319
|
name: "@brainervirus/workit-cli",
|
|
9289
|
-
version: "8.35.
|
|
9320
|
+
version: "8.35.2",
|
|
9290
9321
|
private: false,
|
|
9291
9322
|
description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
9292
9323
|
keywords: [
|
|
@@ -9324,13 +9355,13 @@ var init_package = __esm(() => {
|
|
|
9324
9355
|
},
|
|
9325
9356
|
dependencies: {
|
|
9326
9357
|
"@babel/parser": "7.29.9",
|
|
9327
|
-
"@brainervirus/workit-cursor": "^8.35.
|
|
9358
|
+
"@brainervirus/workit-cursor": "^8.35.2",
|
|
9328
9359
|
"@inkjs/ui": "2.0.0",
|
|
9329
9360
|
ink: "7.1.1",
|
|
9330
9361
|
react: "19.2.8"
|
|
9331
9362
|
},
|
|
9332
9363
|
devDependencies: {
|
|
9333
|
-
"@brainervirus/workit-core": "^8.35.
|
|
9364
|
+
"@brainervirus/workit-core": "^8.35.2"
|
|
9334
9365
|
},
|
|
9335
9366
|
engines: {
|
|
9336
9367
|
node: ">=24"
|
|
@@ -14889,7 +14920,12 @@ function readLedger(cwd, options = {}) {
|
|
|
14889
14920
|
});
|
|
14890
14921
|
return {
|
|
14891
14922
|
ok: true,
|
|
14892
|
-
value: {
|
|
14923
|
+
value: {
|
|
14924
|
+
path: file.value,
|
|
14925
|
+
rows,
|
|
14926
|
+
skipped,
|
|
14927
|
+
integrity: options.integrity ? integrity : null
|
|
14928
|
+
}
|
|
14893
14929
|
};
|
|
14894
14930
|
}
|
|
14895
14931
|
function ledgerTampering(cwd) {
|
|
@@ -15014,7 +15050,10 @@ function branchScope(cwd, branch) {
|
|
|
15014
15050
|
return at >= since && at <= until;
|
|
15015
15051
|
};
|
|
15016
15052
|
}
|
|
15017
|
-
function authorSessions(cwd, rows, branch, range = {
|
|
15053
|
+
function authorSessions(cwd, rows, branch, range = {
|
|
15054
|
+
base: null,
|
|
15055
|
+
head: null
|
|
15056
|
+
}, scope = branchScope(cwd, branch)) {
|
|
15018
15057
|
const sessions = new Set;
|
|
15019
15058
|
for (const row of rows) {
|
|
15020
15059
|
if (!scope(row) || row.type !== "commit.recorded" && row.type !== "task.opened")
|
|
@@ -15089,12 +15128,13 @@ function restackCarries(exactIn, rows, branch, from, to) {
|
|
|
15089
15128
|
const cwd = exactIn;
|
|
15090
15129
|
let known = restackExact.get(row);
|
|
15091
15130
|
if (known === undefined) {
|
|
15092
|
-
|
|
15131
|
+
const range = typeof row.fromBase === "string" && typeof row.toBase === "string" ? {
|
|
15093
15132
|
from: fromHead,
|
|
15094
15133
|
to: head,
|
|
15095
15134
|
fromBase: row.fromBase,
|
|
15096
15135
|
toBase: row.toBase
|
|
15097
|
-
}
|
|
15136
|
+
} : null;
|
|
15137
|
+
known = range !== null && (sameOwnChange(cwd, range) || cleanRebase(cwd, range));
|
|
15098
15138
|
restackExact.set(row, known);
|
|
15099
15139
|
}
|
|
15100
15140
|
return known;
|
|
@@ -15129,7 +15169,10 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
15129
15169
|
const cacheKey = `${base}\x00${at}`;
|
|
15130
15170
|
let key = keyCache.get(cacheKey);
|
|
15131
15171
|
if (!key) {
|
|
15132
|
-
key = {
|
|
15172
|
+
key = {
|
|
15173
|
+
patchId: patchId(cwd, base, at),
|
|
15174
|
+
diffHash: diffHash(cwd, base, at)
|
|
15175
|
+
};
|
|
15133
15176
|
keyCache.set(cacheKey, key);
|
|
15134
15177
|
}
|
|
15135
15178
|
return key;
|
|
@@ -15169,7 +15212,10 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
15169
15212
|
};
|
|
15170
15213
|
const exactCarry = (row) => FAILING.has(String(row.result)) || exactMatch(row) !== null;
|
|
15171
15214
|
const keyBasis = (row) => {
|
|
15172
|
-
const basis = verdictBasis(row, {
|
|
15215
|
+
const basis = verdictBasis(row, {
|
|
15216
|
+
head,
|
|
15217
|
+
...keyFor(row.base ?? fallbackBase)
|
|
15218
|
+
});
|
|
15173
15219
|
return basis === "carried" && !exactCarry(row) ? "stale" : basis;
|
|
15174
15220
|
};
|
|
15175
15221
|
const rebaseNote = (row) => row.head && head && row.head !== head && !FAILING.has(String(row.result)) && keyBasis(row) === "carried" && exactMatch(row) === "rebase" ? { rule: "rebase", since: row.head, files: [] } : null;
|
|
@@ -15274,7 +15320,11 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
15274
15320
|
const newestCurrent = verdicts.findLast((entry) => entry.current) ?? verdicts.at(-1) ?? null;
|
|
15275
15321
|
const failing = verdicts.find((entry) => entry.current && entry.independent && FAILING.has(String(entry.verdict.result)));
|
|
15276
15322
|
const newestAccepted = verdicts.findLast((entry) => entry.accepted && STRONG_RESULTS.has(String(entry.verdict.result))) ?? null;
|
|
15277
|
-
const accepted = tampered.length ? {
|
|
15323
|
+
const accepted = tampered.length ? {
|
|
15324
|
+
accepted: false,
|
|
15325
|
+
verdict: null,
|
|
15326
|
+
reasons: ["ledger_tampered"]
|
|
15327
|
+
} : newestAccepted && !failing ? { accepted: true, verdict: newestAccepted.verdict, reasons: [] } : {
|
|
15278
15328
|
accepted: false,
|
|
15279
15329
|
verdict: failing?.verdict ?? null,
|
|
15280
15330
|
reasons: failing ? ["failing_verdict"] : !newestCurrent ? ["no_verdict"] : newestCurrent.reasons.length ? newestCurrent.reasons : ["type_check_only"]
|
|
@@ -15292,7 +15342,10 @@ function checkVerdicts(cwd, branch, ledgerRows) {
|
|
|
15292
15342
|
selfVerdict: accepted.accepted ? null : selfEntry?.verdict ?? null,
|
|
15293
15343
|
base: newestCurrent?.verdict.base ?? fallbackBase,
|
|
15294
15344
|
patchId: keyFor(newestCurrent?.verdict.base ?? fallbackBase).patchId,
|
|
15295
|
-
current: {
|
|
15345
|
+
current: {
|
|
15346
|
+
basis: newestCurrent?.basis ?? "none",
|
|
15347
|
+
verdict: newestCurrent?.verdict ?? null
|
|
15348
|
+
},
|
|
15296
15349
|
carry: carryEntry?.carry ?? null,
|
|
15297
15350
|
reverify,
|
|
15298
15351
|
accepted,
|
|
@@ -15531,10 +15584,18 @@ function recordVerdict(context, input) {
|
|
|
15531
15584
|
}
|
|
15532
15585
|
function untrackedField(cwd) {
|
|
15533
15586
|
const files = untrackedFiles2(cwd);
|
|
15534
|
-
return files.length ? {
|
|
15587
|
+
return files.length ? {
|
|
15588
|
+
untracked: files.slice(0, MAX_UNTRACKED_LISTED),
|
|
15589
|
+
untrackedCount: files.length
|
|
15590
|
+
} : {};
|
|
15535
15591
|
}
|
|
15536
15592
|
function recordHandoff(context, input) {
|
|
15537
|
-
return appendRow(context.cwd, {
|
|
15593
|
+
return appendRow(context.cwd, {
|
|
15594
|
+
...common(context, noteKey(context)),
|
|
15595
|
+
type: "handoff",
|
|
15596
|
+
note: input.note,
|
|
15597
|
+
next: input.next
|
|
15598
|
+
}, { now: context.now });
|
|
15538
15599
|
}
|
|
15539
15600
|
function rowLabels(row) {
|
|
15540
15601
|
const labels = [];
|
|
@@ -15689,7 +15750,11 @@ function buildHandoff(cwd, options = {}) {
|
|
|
15689
15750
|
}));
|
|
15690
15751
|
const lastHandoffRow = mine.findLast((row) => row.type === "handoff");
|
|
15691
15752
|
const partial = {
|
|
15692
|
-
store: {
|
|
15753
|
+
store: {
|
|
15754
|
+
root: store.value.root,
|
|
15755
|
+
shared: store.value.shared,
|
|
15756
|
+
ledger: ledger.path
|
|
15757
|
+
},
|
|
15693
15758
|
branch,
|
|
15694
15759
|
head,
|
|
15695
15760
|
subject: head ? git4(cwd, ["log", "-1", "--format=%s", head]) : null,
|
|
@@ -15722,7 +15787,11 @@ function buildHandoff(cwd, options = {}) {
|
|
|
15722
15787
|
const nextCommand = nextCommandFor(partial);
|
|
15723
15788
|
return {
|
|
15724
15789
|
ok: true,
|
|
15725
|
-
value: {
|
|
15790
|
+
value: {
|
|
15791
|
+
...partial,
|
|
15792
|
+
nextCommand,
|
|
15793
|
+
next: options.next?.trim() || nextCommand
|
|
15794
|
+
}
|
|
15726
15795
|
};
|
|
15727
15796
|
}
|
|
15728
15797
|
var LEDGER_VERSION = 1, MAX_LINE_BYTES = 4096, VERDICT_RESULTS, PASSING, FAILING, STRONG_RESULTS, VERDICT_KINDS, VERDICT_SURFACES, PR_ROW_TYPES, err = (code, error, unblock) => ({
|
|
@@ -15887,7 +15956,11 @@ ${body}`), TAIL_BYTES, lastLine = (fd, size) => {
|
|
|
15887
15956
|
if (entry[0] === "R" || entry[0] === "C")
|
|
15888
15957
|
index += 1;
|
|
15889
15958
|
}
|
|
15890
|
-
return {
|
|
15959
|
+
return {
|
|
15960
|
+
dirty: paths.length > 0,
|
|
15961
|
+
files: paths.length,
|
|
15962
|
+
paths: paths.slice(0, MAX_DIRTY_PATHS)
|
|
15963
|
+
};
|
|
15891
15964
|
}, nextCommandFor = (brief) => {
|
|
15892
15965
|
const branchFlag = brief.branch ? ` --branch ${brief.branch}` : "";
|
|
15893
15966
|
const verify = `workit ledger verdict verified${branchFlag} --how "<method and evidence>" (from a session that did not author the branch, with WORKIT_SESSION_ID set)`;
|
|
@@ -80569,12 +80642,22 @@ function gitRestack(cwd, input) {
|
|
|
80569
80642
|
if (!dir) {
|
|
80570
80643
|
const scratch = scratchDir(cwd);
|
|
80571
80644
|
if (!scratch)
|
|
80572
|
-
return {
|
|
80645
|
+
return {
|
|
80646
|
+
ok: false,
|
|
80647
|
+
conflict: false,
|
|
80648
|
+
worktree: cwd,
|
|
80649
|
+
error: "no store root"
|
|
80650
|
+
};
|
|
80573
80651
|
fs28.mkdirSync(scratch, { recursive: true });
|
|
80574
80652
|
dir = path56.join(scratch, `${input.branch.replace(/[^A-Za-z0-9._-]+/gu, "_").slice(0, 40)}-${randomBytes4(4).toString("hex")}`);
|
|
80575
80653
|
const added = gitRun3(cwd, ["worktree", "add", "-q", dir, input.branch], GIT_TIMEOUTS.worktree);
|
|
80576
80654
|
if (!added.ok)
|
|
80577
|
-
return {
|
|
80655
|
+
return {
|
|
80656
|
+
ok: false,
|
|
80657
|
+
conflict: false,
|
|
80658
|
+
worktree: cwd,
|
|
80659
|
+
error: firstLine(added)
|
|
80660
|
+
};
|
|
80578
80661
|
}
|
|
80579
80662
|
const run = gitRun3(dir, [
|
|
80580
80663
|
"-c",
|
|
@@ -80597,7 +80680,12 @@ function gitRestack(cwd, input) {
|
|
|
80597
80680
|
error: `git rebase did not finish within ${stackDeps.rebaseTimeoutMs} ms`
|
|
80598
80681
|
};
|
|
80599
80682
|
if (rebaseInProgress(dir))
|
|
80600
|
-
return {
|
|
80683
|
+
return {
|
|
80684
|
+
ok: false,
|
|
80685
|
+
conflict: true,
|
|
80686
|
+
worktree: dir,
|
|
80687
|
+
error: firstLine(run)
|
|
80688
|
+
};
|
|
80601
80689
|
if (!owner)
|
|
80602
80690
|
gitRun3(cwd, ["worktree", "remove", "--force", dir]);
|
|
80603
80691
|
return { ok: false, conflict: false, worktree: dir, error: firstLine(run) };
|
|
@@ -80605,7 +80693,12 @@ function gitRestack(cwd, input) {
|
|
|
80605
80693
|
if (!owner)
|
|
80606
80694
|
gitRun3(cwd, ["worktree", "remove", "--force", dir]);
|
|
80607
80695
|
const head = branchTip(cwd, input.branch);
|
|
80608
|
-
return head ? { ok: true, head } : {
|
|
80696
|
+
return head ? { ok: true, head } : {
|
|
80697
|
+
ok: false,
|
|
80698
|
+
conflict: false,
|
|
80699
|
+
worktree: dir,
|
|
80700
|
+
error: `${input.branch} vanished`
|
|
80701
|
+
};
|
|
80609
80702
|
}
|
|
80610
80703
|
function pruneScratch(cwd) {
|
|
80611
80704
|
const scratch = scratchDir(cwd);
|
|
@@ -80730,7 +80823,10 @@ function listStacks(cwd) {
|
|
|
80730
80823
|
if (parsed && fileFor(parsed.name) === name)
|
|
80731
80824
|
out.push(parsed);
|
|
80732
80825
|
}
|
|
80733
|
-
return {
|
|
80826
|
+
return {
|
|
80827
|
+
ok: true,
|
|
80828
|
+
data: out.toSorted((a, b) => a.name.localeCompare(b.name))
|
|
80829
|
+
};
|
|
80734
80830
|
}
|
|
80735
80831
|
function selectStack(cwd, name) {
|
|
80736
80832
|
if (name !== null && !validName(name))
|
|
@@ -81075,7 +81171,10 @@ function statusDoc(ctx, pr) {
|
|
|
81075
81171
|
const status = ctx.resolved.forge.prStatus(pr);
|
|
81076
81172
|
if (!status.ok)
|
|
81077
81173
|
return status;
|
|
81078
|
-
return buildStatusDoc(ctx.cwd, ctx.resolved, status.data, {
|
|
81174
|
+
return buildStatusDoc(ctx.cwd, ctx.resolved, status.data, {
|
|
81175
|
+
logLines: 0,
|
|
81176
|
+
behind: false
|
|
81177
|
+
});
|
|
81079
81178
|
}
|
|
81080
81179
|
function prBinding(ctx, entry, doc) {
|
|
81081
81180
|
const label = noun2(ctx.resolved, entry.pr);
|
|
@@ -81095,7 +81194,10 @@ function prBinding(ctx, entry, doc) {
|
|
|
81095
81194
|
function repoMismatch(ctx, stack) {
|
|
81096
81195
|
const { kind, repo } = ctx.resolved.forge;
|
|
81097
81196
|
if (stack.repo && stack.repo !== repo || stack.forge && stack.forge !== kind)
|
|
81098
|
-
return stackFail("blocked", `repo_mismatch: stack ${stack.name} was planned on ${stack.forge ?? "?"} ${stack.repo ?? "?"}, but this checkout resolves to ${kind} ${repo}`, `workit stack plan --name ${stack.name} <branch…> # re-plan it here, or run from the right checkout`, {
|
|
81197
|
+
return stackFail("blocked", `repo_mismatch: stack ${stack.name} was planned on ${stack.forge ?? "?"} ${stack.repo ?? "?"}, but this checkout resolves to ${kind} ${repo}`, `workit stack plan --name ${stack.name} <branch…> # re-plan it here, or run from the right checkout`, {
|
|
81198
|
+
planned: { forge: stack.forge, repo: stack.repo },
|
|
81199
|
+
resolved: { forge: kind, repo }
|
|
81200
|
+
});
|
|
81099
81201
|
return null;
|
|
81100
81202
|
}
|
|
81101
81203
|
function qualify(ctx, stack, entry, doc, verdict, grant, binding = null, bypass = false) {
|
|
@@ -81236,7 +81338,11 @@ function detectMerged(ctx, cwd, stack, trunkSha, state) {
|
|
|
81236
81338
|
merged = tip !== null && hasOwnCommits(cwd, tip, entry.lastParentHead) && isAncestor2(cwd, tip, trunkSha);
|
|
81237
81339
|
}
|
|
81238
81340
|
if (merged) {
|
|
81239
|
-
entry.merged = {
|
|
81341
|
+
entry.merged = {
|
|
81342
|
+
pr: entry.pr,
|
|
81343
|
+
mergeSha: null,
|
|
81344
|
+
at: new Date().toISOString()
|
|
81345
|
+
};
|
|
81240
81346
|
newly.push({ branch: entry.branch, pr: entry.pr });
|
|
81241
81347
|
}
|
|
81242
81348
|
}
|
|
@@ -81250,18 +81356,31 @@ function pushBranch(cwd, actor, branch, priorTip) {
|
|
|
81250
81356
|
if (!remote.ok)
|
|
81251
81357
|
return stackFail(remote.code, remote.error);
|
|
81252
81358
|
if (remote.sha === plan.data.sha)
|
|
81253
|
-
return {
|
|
81359
|
+
return {
|
|
81360
|
+
ok: true,
|
|
81361
|
+
data: { pushed: false, forced: false, sha: plan.data.sha }
|
|
81362
|
+
};
|
|
81254
81363
|
const grant = requireGrant(cwd, "push", { forge: !plan.data.local });
|
|
81255
81364
|
if (!grant.allowed)
|
|
81256
|
-
return stackFail("blocked", grant.error, grant.unblock, {
|
|
81365
|
+
return stackFail("blocked", grant.error, grant.unblock, {
|
|
81366
|
+
reason: grant.reason
|
|
81367
|
+
});
|
|
81257
81368
|
const force = remote.sha !== null && !isAncestor2(cwd, remote.sha, plan.data.sha);
|
|
81258
81369
|
const expect = !force ? null : remote.sha === priorTip ? priorTip : recordedRemoteTip(cwd, plan.data) ?? null;
|
|
81259
|
-
const pushed = executePush(cwd, plan.data, {
|
|
81370
|
+
const pushed = executePush(cwd, plan.data, {
|
|
81371
|
+
forceWithLease: force,
|
|
81372
|
+
expect,
|
|
81373
|
+
actor
|
|
81374
|
+
});
|
|
81260
81375
|
if (!pushed.ok)
|
|
81261
81376
|
return { ...fromForge(pushed), data: { branch } };
|
|
81262
81377
|
return {
|
|
81263
81378
|
ok: true,
|
|
81264
|
-
data: {
|
|
81379
|
+
data: {
|
|
81380
|
+
pushed: pushed.data.pushed,
|
|
81381
|
+
forced: pushed.data.forced,
|
|
81382
|
+
sha: plan.data.sha
|
|
81383
|
+
}
|
|
81265
81384
|
};
|
|
81266
81385
|
}
|
|
81267
81386
|
function syncStack(cwd, ctx, stack, options, actor) {
|
|
@@ -81371,7 +81490,11 @@ function syncStack(cwd, ctx, stack, options, actor) {
|
|
|
81371
81490
|
worktree: rebased.worktree,
|
|
81372
81491
|
progress: outcome
|
|
81373
81492
|
});
|
|
81374
|
-
return rebased.conflict ? stackFail("blocked", `conflict restacking ${entry.branch} onto ${parent.name}: ${rebased.error}`, `resolve the conflicts in ${rebased.worktree}, git rebase --continue, then workit stack sync # or git rebase --abort`, {
|
|
81493
|
+
return rebased.conflict ? stackFail("blocked", `conflict restacking ${entry.branch} onto ${parent.name}: ${rebased.error}`, `resolve the conflicts in ${rebased.worktree}, git rebase --continue, then workit stack sync # or git rebase --abort`, {
|
|
81494
|
+
conflict: entry.branch,
|
|
81495
|
+
worktree: rebased.worktree,
|
|
81496
|
+
progress: outcome
|
|
81497
|
+
}) : stackFail("failed", `restacking ${entry.branch} failed: ${rebased.error}`, undefined, {
|
|
81375
81498
|
branch: entry.branch,
|
|
81376
81499
|
progress: outcome
|
|
81377
81500
|
});
|
|
@@ -81431,8 +81554,16 @@ function syncStack(cwd, ctx, stack, options, actor) {
|
|
|
81431
81554
|
p: patchId(cwd, fromBase, fromHead),
|
|
81432
81555
|
d: diffHash(cwd, fromBase, fromHead)
|
|
81433
81556
|
};
|
|
81434
|
-
const after = {
|
|
81435
|
-
|
|
81557
|
+
const after = {
|
|
81558
|
+
p: patchId(cwd, parentTip, newTip),
|
|
81559
|
+
d: diffHash(cwd, parentTip, newTip)
|
|
81560
|
+
};
|
|
81561
|
+
const equal = before.d !== null && before.d === after.d && before.p !== null && before.p === after.p && sameOwnChange(cwd, {
|
|
81562
|
+
from: fromHead,
|
|
81563
|
+
to: newTip,
|
|
81564
|
+
fromBase,
|
|
81565
|
+
toBase: parentTip
|
|
81566
|
+
});
|
|
81436
81567
|
appendObserved(cwd, {
|
|
81437
81568
|
type: "stack.restacked",
|
|
81438
81569
|
actor,
|
|
@@ -81450,7 +81581,12 @@ function syncStack(cwd, ctx, stack, options, actor) {
|
|
|
81450
81581
|
if (step.restack)
|
|
81451
81582
|
step.restack.carried = equal;
|
|
81452
81583
|
else
|
|
81453
|
-
step.restack = {
|
|
81584
|
+
step.restack = {
|
|
81585
|
+
from: fromBase,
|
|
81586
|
+
onto: parentTip,
|
|
81587
|
+
head: newTip,
|
|
81588
|
+
carried: equal
|
|
81589
|
+
};
|
|
81454
81590
|
}
|
|
81455
81591
|
if (options.publish) {
|
|
81456
81592
|
touchStackLock();
|
|
@@ -81479,7 +81615,10 @@ function syncStack(cwd, ctx, stack, options, actor) {
|
|
|
81479
81615
|
});
|
|
81480
81616
|
const updated = ctx.resolved.forge.updateBase(entry.pr, parent.name);
|
|
81481
81617
|
if (!updated.ok)
|
|
81482
|
-
return {
|
|
81618
|
+
return {
|
|
81619
|
+
...fromForge(updated),
|
|
81620
|
+
data: { branch: entry.branch, progress: outcome }
|
|
81621
|
+
};
|
|
81483
81622
|
appendObserved(cwd, {
|
|
81484
81623
|
type: "pr.retargeted",
|
|
81485
81624
|
actor,
|
|
@@ -81554,7 +81693,9 @@ async function landStack(ctx, stack, options) {
|
|
|
81554
81693
|
if (bypass) {
|
|
81555
81694
|
const grant = requireGrant(ctx.cwd, "merge");
|
|
81556
81695
|
if (!grant.allowed)
|
|
81557
|
-
return failWith(stackFail("blocked", grant.error, grant.unblock, {
|
|
81696
|
+
return failWith(stackFail("blocked", grant.error, grant.unblock, {
|
|
81697
|
+
reason: "grant_required"
|
|
81698
|
+
}));
|
|
81558
81699
|
if (!grant.allowUnverified)
|
|
81559
81700
|
return failWith(stackFail("blocked", `unverified_refused: workspace ${grant.workspace ? `"${grant.workspace}"` : "(none)"} grants merge: "verified", which never lands without an accepted independent verdict`, "a non-author session verifies each head (workit-review), then land without --unverified", { reason: "unverified_refused" }));
|
|
81560
81701
|
}
|
|
@@ -81627,7 +81768,8 @@ async function landStack(ctx, stack, options) {
|
|
|
81627
81768
|
deleteBranch: false,
|
|
81628
81769
|
actor: ctx.actor,
|
|
81629
81770
|
expect: { base: stack.trunk, branch: entry.branch },
|
|
81630
|
-
...bypass && !verdict.satisfied ? { unverified: bypass } : {}
|
|
81771
|
+
...bypass && !verdict.satisfied ? { unverified: bypass } : {},
|
|
81772
|
+
...options.baseRedOk ? { baseRedOk: options.baseRedOk } : {}
|
|
81631
81773
|
}, ctx.sleep);
|
|
81632
81774
|
if (!merged.ok) {
|
|
81633
81775
|
if (merged.code === "blocked" && "refusal" in merged && merged.refusal) {
|
|
@@ -81649,7 +81791,11 @@ async function landStack(ctx, stack, options) {
|
|
|
81649
81791
|
mergeSha: merged.data.mergeSha,
|
|
81650
81792
|
unverified: merged.data.unverified?.recorded ?? null
|
|
81651
81793
|
});
|
|
81652
|
-
entry.merged = {
|
|
81794
|
+
entry.merged = {
|
|
81795
|
+
pr,
|
|
81796
|
+
mergeSha: merged.data.mergeSha,
|
|
81797
|
+
at: new Date().toISOString()
|
|
81798
|
+
};
|
|
81653
81799
|
const written = writeStack(ctx.cwd, stack);
|
|
81654
81800
|
if (!written.ok)
|
|
81655
81801
|
return failWith(written);
|
|
@@ -87411,7 +87557,11 @@ __export(exports_stack, {
|
|
|
87411
87557
|
run: () => run20
|
|
87412
87558
|
});
|
|
87413
87559
|
async function plan(argv, io) {
|
|
87414
|
-
const flags = parseFlags(argv, {
|
|
87560
|
+
const flags = parseFlags(argv, {
|
|
87561
|
+
name: "value",
|
|
87562
|
+
trunk: "value",
|
|
87563
|
+
track: "value"
|
|
87564
|
+
});
|
|
87415
87565
|
if (typeof flags === "string")
|
|
87416
87566
|
return usage4(io, flags, PLAN_USAGE);
|
|
87417
87567
|
const name = flags.values.name ?? null;
|
|
@@ -87472,7 +87622,12 @@ async function status3(argv, io) {
|
|
|
87472
87622
|
const connected = connect(io, selected.data.branches[0]?.branch ?? null);
|
|
87473
87623
|
if (!connected.ok)
|
|
87474
87624
|
return forgeFail(io, connected);
|
|
87475
|
-
const result = stackStatus({
|
|
87625
|
+
const result = stackStatus({
|
|
87626
|
+
cwd: io.cwd,
|
|
87627
|
+
resolved: connected.data,
|
|
87628
|
+
actor: actorFromEnv(io.env),
|
|
87629
|
+
sleep: forgeDeps.sleep
|
|
87630
|
+
}, selected.data);
|
|
87476
87631
|
if (!result.ok)
|
|
87477
87632
|
return stackFailed(io, result);
|
|
87478
87633
|
return emit(io, ok2(result.data), (data) => [
|
|
@@ -87536,11 +87691,16 @@ async function land(argv, io) {
|
|
|
87536
87691
|
timeout: "value",
|
|
87537
87692
|
interval: "value",
|
|
87538
87693
|
unverified: "boolean",
|
|
87694
|
+
"base-red-ok": "boolean",
|
|
87539
87695
|
reason: "value"
|
|
87540
87696
|
});
|
|
87541
87697
|
if (typeof flags === "string")
|
|
87542
87698
|
return usage4(io, flags, LAND_USAGE);
|
|
87543
|
-
const
|
|
87699
|
+
const baseRedOk = flags.booleans.has("base-red-ok");
|
|
87700
|
+
const baseRedReason = flags.values.reason?.trim();
|
|
87701
|
+
if (baseRedOk && !baseRedReason)
|
|
87702
|
+
return usage4(io, "--base-red-ok needs --reason <why merging onto a red base is fine>", LAND_USAGE);
|
|
87703
|
+
const unverified = baseRedOk && !flags.booleans.has("unverified") ? null : unverifiedFlag(flags);
|
|
87544
87704
|
if (typeof unverified === "string")
|
|
87545
87705
|
return usage4(io, unverified, LAND_USAGE);
|
|
87546
87706
|
if (flags.positionals.length)
|
|
@@ -87573,7 +87733,15 @@ async function land(argv, io) {
|
|
|
87573
87733
|
resolved: connected.data,
|
|
87574
87734
|
actor: actorFromEnv(io.env),
|
|
87575
87735
|
sleep: forgeDeps.sleep
|
|
87576
|
-
}, fresh.data ?? selected.data, {
|
|
87736
|
+
}, fresh.data ?? selected.data, {
|
|
87737
|
+
dryRun: flags.booleans.has("dry-run"),
|
|
87738
|
+
max,
|
|
87739
|
+
method,
|
|
87740
|
+
timeoutMs,
|
|
87741
|
+
intervalMs,
|
|
87742
|
+
unverified,
|
|
87743
|
+
baseRedOk: baseRedOk && baseRedReason ? { reason: baseRedReason } : null
|
|
87744
|
+
});
|
|
87577
87745
|
});
|
|
87578
87746
|
if (!result.ok)
|
|
87579
87747
|
return stackFailed(io, result);
|
|
@@ -87602,7 +87770,10 @@ async function run20(argv, io) {
|
|
|
87602
87770
|
return land(rest, io);
|
|
87603
87771
|
return usage4(io, sub && !sub.startsWith("-") ? `unknown stack subcommand "${sub}"` : "missing stack subcommand", USAGE12);
|
|
87604
87772
|
}
|
|
87605
|
-
var PLAN_USAGE = "workit stack plan [--name <n>] [--trunk <b> | --track <t>] [<bottom> … <top>] [--json]", STATUS_USAGE2 = "workit stack status [--name <n>] [--json]", SYNC_USAGE = "workit stack sync [--name <n>] [--local] [--dry-run] [--force <branch>]… [--json]", LAND_USAGE = "workit stack land [--name <n>] [--dry-run] [--max <n>] [--method squash|merge|rebase] [--unverified --reason <why>] [--timeout 20m] [--interval 30s] [--json]", USAGE12 = "workit stack plan|status|sync|land ... (workit help stack)", short5 = (sha) => sha ? sha.slice(0, 12) : "?", stackFailed = (io, result) => emit(io, fail2(result.code, result.error, {
|
|
87773
|
+
var PLAN_USAGE = "workit stack plan [--name <n>] [--trunk <b> | --track <t>] [<bottom> … <top>] [--json]", STATUS_USAGE2 = "workit stack status [--name <n>] [--json]", SYNC_USAGE = "workit stack sync [--name <n>] [--local] [--dry-run] [--force <branch>]… [--json]", LAND_USAGE = "workit stack land [--name <n>] [--dry-run] [--max <n>] [--method squash|merge|rebase] [--unverified --reason <why>] [--base-red-ok --reason <why>] [--timeout 20m] [--interval 30s] [--json]", USAGE12 = "workit stack plan|status|sync|land ... (workit help stack)", short5 = (sha) => sha ? sha.slice(0, 12) : "?", stackFailed = (io, result) => emit(io, fail2(result.code, result.error, {
|
|
87774
|
+
unblock: result.unblock,
|
|
87775
|
+
data: result.data ?? {}
|
|
87776
|
+
})), label3 = (forge, pr) => pr === null ? "no PR" : `${forge === "gitlab" ? "MR !" : "PR #"}${pr}`, renderSync = (data) => [
|
|
87606
87777
|
`${data.dryRun ? "would sync" : "synced"} stack ${data.name}${data.merged.length ? `; merged: ${data.merged.map((item) => item.branch).join(", ")}` : ""}`,
|
|
87607
87778
|
...data.steps.map((step) => [
|
|
87608
87779
|
` ${step.branch} on ${step.parent}:`,
|
|
@@ -106014,19 +106185,22 @@ function bounded(brief, cwd) {
|
|
|
106014
106185
|
file = path73.join(dir, `${(brief.branch ?? "detached").replaceAll(/[^\w.-]+/gu, "-")}.txt`);
|
|
106015
106186
|
fs37.writeFileSync(file, `${[...body, ...tail].join(`
|
|
106016
106187
|
`)}
|
|
106017
|
-
`, {
|
|
106188
|
+
`, {
|
|
106189
|
+
mode: 384
|
|
106190
|
+
});
|
|
106018
106191
|
} catch {
|
|
106019
106192
|
file = null;
|
|
106020
106193
|
}
|
|
106021
106194
|
const pointer = `… cut to ${MAX_BRIEF_BYTES} bytes; full brief: ${file ?? "not written (workit handoff --json has it all)"}`;
|
|
106195
|
+
const cappedTail = tail.map(capLine);
|
|
106022
106196
|
const kept = [];
|
|
106023
106197
|
for (const line of body) {
|
|
106024
106198
|
const cut = line.length > 400 ? `${line.slice(0, 400)}…` : line;
|
|
106025
|
-
if (bytes([...kept, cut, pointer, ...
|
|
106199
|
+
if (bytes([...kept, cut, pointer, ...cappedTail]) > MAX_BRIEF_BYTES)
|
|
106026
106200
|
break;
|
|
106027
106201
|
kept.push(cut);
|
|
106028
106202
|
}
|
|
106029
|
-
return [...kept, pointer, ...
|
|
106203
|
+
return [...kept, pointer, ...cappedTail];
|
|
106030
106204
|
}
|
|
106031
106205
|
async function resumeBrief(argv, io) {
|
|
106032
106206
|
let values;
|
|
@@ -106048,7 +106222,9 @@ async function resumeBrief(argv, io) {
|
|
|
106048
106222
|
}
|
|
106049
106223
|
const last = values.last === undefined ? 10 : Number(values.last);
|
|
106050
106224
|
if (!Number.isInteger(last) || last < 1)
|
|
106051
|
-
return emit(io, fail2("invalid_input", "--last must be a positive integer", {
|
|
106225
|
+
return emit(io, fail2("invalid_input", "--last must be a positive integer", {
|
|
106226
|
+
unblock: USAGE19
|
|
106227
|
+
}));
|
|
106052
106228
|
const built = buildHandoff(io.cwd, {
|
|
106053
106229
|
last,
|
|
106054
106230
|
note: values.note ?? null,
|
|
@@ -106074,7 +106250,7 @@ async function run27(argv, io) {
|
|
|
106074
106250
|
}
|
|
106075
106251
|
var USAGE19 = "workit handoff [--note <text>] [--next <text>] [--record] [--last <n>] [--json]", MAX_BRIEF_BYTES = 4096, short7 = (sha) => sha ? sha.slice(0, 12) : "no commit", bytes = (lines) => Buffer.byteLength(`${lines.join(`
|
|
106076
106252
|
`)}
|
|
106077
|
-
`)
|
|
106253
|
+
`), TAIL_LINE_BYTES = 480, capLine = (line) => Buffer.byteLength(line) <= TAIL_LINE_BYTES ? line : `${Buffer.from(line).subarray(0, TAIL_LINE_BYTES).toString().replace(/\uFFFD$/u, "")}… (cut)`;
|
|
106078
106254
|
var init_handoff = __esm(() => {
|
|
106079
106255
|
init_ledger();
|
|
106080
106256
|
init_output();
|
|
@@ -106114,13 +106290,19 @@ var FAMILY_ACTIONS = {
|
|
|
106114
106290
|
},
|
|
106115
106291
|
evidence: { record: "Record evidence against a task's requirements" },
|
|
106116
106292
|
finding: { record: "Record a finding", resolve: "Resolve a finding" },
|
|
106117
|
-
decision: {
|
|
106293
|
+
decision: {
|
|
106294
|
+
record: "Record a task decision",
|
|
106295
|
+
revoke: "Revoke a task decision"
|
|
106296
|
+
},
|
|
106118
106297
|
worker: {
|
|
106119
106298
|
assign: "Assign a worker to a task",
|
|
106120
106299
|
report: "Report a worker's result",
|
|
106121
106300
|
cancel: "Cancel a worker assignment"
|
|
106122
106301
|
},
|
|
106123
|
-
state: {
|
|
106302
|
+
state: {
|
|
106303
|
+
export: "Export a task's state (v1)",
|
|
106304
|
+
import: "Import an exported task state"
|
|
106305
|
+
}
|
|
106124
106306
|
};
|
|
106125
106307
|
var POLICY_JUDGE = "--judge risk=trivial|normal|high behavior=yes|no product-choice=yes|no plan=yes|no [--ref <path>]… [--why <reason>]";
|
|
106126
106308
|
var TASK_IMPLICIT = [
|
|
@@ -106554,7 +106736,7 @@ var VERBS = [
|
|
|
106554
106736
|
},
|
|
106555
106737
|
{
|
|
106556
106738
|
name: "land",
|
|
106557
|
-
usage: "workit stack land [--name <n>] [--dry-run] [--max <n>] [--method squash|merge|rebase] [--unverified --reason <why>] [--timeout 20m] [--interval 30s]",
|
|
106739
|
+
usage: "workit stack land [--name <n>] [--dry-run] [--max <n>] [--method squash|merge|rebase] [--unverified --reason <why>] [--base-red-ok --reason <why>] [--timeout 20m] [--interval 30s]",
|
|
106558
106740
|
summary: "Merge the stack root first, restacking the rest as each lands"
|
|
106559
106741
|
}
|
|
106560
106742
|
],
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cli",
|
|
3
|
-
"version": "8.35.
|
|
3
|
+
"version": "8.35.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
6
6
|
"keywords": [
|
|
@@ -38,13 +38,13 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@babel/parser": "7.29.9",
|
|
41
|
-
"@brainervirus/workit-cursor": "^8.35.
|
|
41
|
+
"@brainervirus/workit-cursor": "^8.35.2",
|
|
42
42
|
"@inkjs/ui": "2.0.0",
|
|
43
43
|
"ink": "7.1.1",
|
|
44
44
|
"react": "19.2.8"
|
|
45
45
|
},
|
|
46
46
|
"devDependencies": {
|
|
47
|
-
"@brainervirus/workit-core": "^8.35.
|
|
47
|
+
"@brainervirus/workit-core": "^8.35.2"
|
|
48
48
|
},
|
|
49
49
|
"engines": {
|
|
50
50
|
"node": ">=24"
|