@brainervirus/workit-claude-code 7.6.0 → 8.0.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/.claude-plugin/plugin.json +1 -1
- package/dist/workit-hook.js +28 -28
- package/dist/workit.js +870 -446
- package/package.json +3 -3
- package/skills/fanout/SKILL.md +46 -46
- package/skills/fanout/references/brief.md +15 -8
- package/skills/fanout/references/orchestration.md +117 -0
- package/skills/ship/SKILL.md +2 -2
package/dist/workit.js
CHANGED
|
@@ -65,7 +65,7 @@ var package_default;
|
|
|
65
65
|
var init_package = __esm(() => {
|
|
66
66
|
package_default = {
|
|
67
67
|
name: "@brainervirus/workit-cli",
|
|
68
|
-
version: "
|
|
68
|
+
version: "8.0.0",
|
|
69
69
|
private: false,
|
|
70
70
|
description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
71
71
|
keywords: [
|
|
@@ -103,9 +103,9 @@ var init_package = __esm(() => {
|
|
|
103
103
|
},
|
|
104
104
|
dependencies: {
|
|
105
105
|
"@babel/parser": "7.29.9",
|
|
106
|
-
"@brainervirus/workit-core": "^
|
|
107
|
-
"@brainervirus/workit-cursor": "^
|
|
108
|
-
"@brainervirus/workit-opencode": "^
|
|
106
|
+
"@brainervirus/workit-core": "^8.0.0",
|
|
107
|
+
"@brainervirus/workit-cursor": "^8.0.0",
|
|
108
|
+
"@brainervirus/workit-opencode": "^8.0.0",
|
|
109
109
|
"@inkjs/ui": "2.0.0",
|
|
110
110
|
ink: "7.1.1",
|
|
111
111
|
react: "19.2.8"
|
|
@@ -165,7 +165,7 @@ var init_output = __esm(() => {
|
|
|
165
165
|
};
|
|
166
166
|
});
|
|
167
167
|
|
|
168
|
-
// packages/workit-core/src/check-config.ts
|
|
168
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/check-config.ts
|
|
169
169
|
import { spawnSync } from "node:child_process";
|
|
170
170
|
import fs from "node:fs";
|
|
171
171
|
import path from "node:path";
|
|
@@ -7974,7 +7974,7 @@ var init_zod = __esm(() => {
|
|
|
7974
7974
|
init_external();
|
|
7975
7975
|
});
|
|
7976
7976
|
|
|
7977
|
-
// packages/workit-core/src/core/task-contract.ts
|
|
7977
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/task-contract.ts
|
|
7978
7978
|
import { createHash, randomUUID } from "node:crypto";
|
|
7979
7979
|
function parseOperation(family, input) {
|
|
7980
7980
|
if (typeof input === "object" && input !== null && "schemaVersion" in input && input.schemaVersion !== SCHEMA_VERSION)
|
|
@@ -8812,7 +8812,7 @@ var init_task_contract = __esm(() => {
|
|
|
8812
8812
|
compiledOperationSchemas = Object.fromEntries(OPERATION_FAMILIES.map((family) => [family, compile(operationSchemas[family])]));
|
|
8813
8813
|
});
|
|
8814
8814
|
|
|
8815
|
-
// packages/workit-core/src/core/operation-input.ts
|
|
8815
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/operation-input.ts
|
|
8816
8816
|
function normalizeOperationInput(family, input, host) {
|
|
8817
8817
|
if (!isObject2(input))
|
|
8818
8818
|
return input;
|
|
@@ -9149,7 +9149,7 @@ var init_operation_input = __esm(() => {
|
|
|
9149
9149
|
]);
|
|
9150
9150
|
});
|
|
9151
9151
|
|
|
9152
|
-
// packages/workit-core/src/core/policy/judgment.ts
|
|
9152
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/policy/judgment.ts
|
|
9153
9153
|
function normalizeJudgment(raw, previous) {
|
|
9154
9154
|
const input = isObject3(raw) ? raw : {};
|
|
9155
9155
|
const legacy = Object.keys(input).some((key) => LEGACY_KEYS.has(key));
|
|
@@ -9914,7 +9914,7 @@ var init_file_lock = __esm(() => {
|
|
|
9914
9914
|
init_file_lock_sync();
|
|
9915
9915
|
});
|
|
9916
9916
|
|
|
9917
|
-
// packages/workit-core/src/core/package-root.ts
|
|
9917
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/package-root.ts
|
|
9918
9918
|
import { existsSync } from "node:fs";
|
|
9919
9919
|
import path4 from "node:path";
|
|
9920
9920
|
import { fileURLToPath } from "node:url";
|
|
@@ -9933,7 +9933,7 @@ var packageRoot = () => {
|
|
|
9933
9933
|
};
|
|
9934
9934
|
var init_package_root = () => {};
|
|
9935
9935
|
|
|
9936
|
-
// packages/workit-core/src/store/paths.ts
|
|
9936
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/store/paths.ts
|
|
9937
9937
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
9938
9938
|
import { createHash as createHash2 } from "node:crypto";
|
|
9939
9939
|
import fs5 from "node:fs";
|
|
@@ -10209,7 +10209,7 @@ var init_paths = __esm(() => {
|
|
|
10209
10209
|
];
|
|
10210
10210
|
});
|
|
10211
10211
|
|
|
10212
|
-
// packages/workit-core/src/core/store-lock.ts
|
|
10212
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/store-lock.ts
|
|
10213
10213
|
import { spawnSync as spawnSync3 } from "node:child_process";
|
|
10214
10214
|
import fs6 from "node:fs";
|
|
10215
10215
|
import { hostname } from "node:os";
|
|
@@ -10401,7 +10401,7 @@ var init_store_lock = __esm(() => {
|
|
|
10401
10401
|
FOREIGN_LOCK_TTL_MS = 10 * 60000;
|
|
10402
10402
|
});
|
|
10403
10403
|
|
|
10404
|
-
// packages/workit-core/src/store/event-log.ts
|
|
10404
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/store/event-log.ts
|
|
10405
10405
|
import { randomUUID as randomUUID2 } from "node:crypto";
|
|
10406
10406
|
import fs7 from "node:fs";
|
|
10407
10407
|
import path7 from "node:path";
|
|
@@ -10556,7 +10556,7 @@ var init_event_log = __esm(() => {
|
|
|
10556
10556
|
TRANSIENT_WINDOWS_CODES = new Set(["EPERM", "EACCES", "EBUSY"]);
|
|
10557
10557
|
});
|
|
10558
10558
|
|
|
10559
|
-
// packages/workit-core/src/store/patch.ts
|
|
10559
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/store/patch.ts
|
|
10560
10560
|
function diff(before, after, path = []) {
|
|
10561
10561
|
if (equal(before, after))
|
|
10562
10562
|
return [];
|
|
@@ -10662,7 +10662,7 @@ var isPlainObject2 = (value) => typeof value === "object" && value !== null && !
|
|
|
10662
10662
|
return false;
|
|
10663
10663
|
});
|
|
10664
10664
|
|
|
10665
|
-
// packages/workit-core/src/store/reduce.ts
|
|
10665
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/store/reduce.ts
|
|
10666
10666
|
import { createHash as createHash3 } from "node:crypto";
|
|
10667
10667
|
function reduce(state, event, blobs) {
|
|
10668
10668
|
const data = event.data;
|
|
@@ -10788,7 +10788,7 @@ var init_reduce = __esm(() => {
|
|
|
10788
10788
|
]);
|
|
10789
10789
|
});
|
|
10790
10790
|
|
|
10791
|
-
// packages/workit-core/src/store/notes.ts
|
|
10791
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/store/notes.ts
|
|
10792
10792
|
var SLOT, reportMigration = (report) => {
|
|
10793
10793
|
try {
|
|
10794
10794
|
const reporter = globalThis[SLOT];
|
|
@@ -10800,7 +10800,7 @@ var init_notes = __esm(() => {
|
|
|
10800
10800
|
SLOT = Symbol.for("workit.migrationReporter");
|
|
10801
10801
|
});
|
|
10802
10802
|
|
|
10803
|
-
// packages/workit-core/src/core/task-store.ts
|
|
10803
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/task-store.ts
|
|
10804
10804
|
import { createHash as createHash4, randomUUID as randomUUID3 } from "node:crypto";
|
|
10805
10805
|
import * as fs8 from "node:fs";
|
|
10806
10806
|
import path8 from "node:path";
|
|
@@ -12427,13 +12427,13 @@ var init_task_store = __esm(() => {
|
|
|
12427
12427
|
heldInProcess = new Map;
|
|
12428
12428
|
});
|
|
12429
12429
|
|
|
12430
|
-
// packages/workit-core/src/core/workers.ts
|
|
12430
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/workers.ts
|
|
12431
12431
|
var UNCERTAIN_WORKER_STATES, isUncertainWorker = (state) => UNCERTAIN_WORKER_STATES.includes(state), workerBlocksTransition = (transition, state) => transition === "pause" ? false : isUncertainWorker(state);
|
|
12432
12432
|
var init_workers = __esm(() => {
|
|
12433
12433
|
UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
|
|
12434
12434
|
});
|
|
12435
12435
|
|
|
12436
|
-
// packages/workit-core/src/git/rev.ts
|
|
12436
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/git/rev.ts
|
|
12437
12437
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
12438
12438
|
import { createHash as createHash5 } from "node:crypto";
|
|
12439
12439
|
import fs9 from "node:fs";
|
|
@@ -12964,7 +12964,7 @@ var init_rev = __esm(() => {
|
|
|
12964
12964
|
};
|
|
12965
12965
|
});
|
|
12966
12966
|
|
|
12967
|
-
// packages/workit-core/src/ledger.ts
|
|
12967
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/ledger.ts
|
|
12968
12968
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
12969
12969
|
import { createHash as createHash6, randomBytes as randomBytes2 } from "node:crypto";
|
|
12970
12970
|
import fs10 from "node:fs";
|
|
@@ -13426,6 +13426,70 @@ function recordRuling(context, input) {
|
|
|
13426
13426
|
refs: input.refs ?? []
|
|
13427
13427
|
}, { now: context.now });
|
|
13428
13428
|
}
|
|
13429
|
+
function activeStanding(rows, fanout, leads = null) {
|
|
13430
|
+
const active = [];
|
|
13431
|
+
for (const row of rows) {
|
|
13432
|
+
if (row.fanout !== fanout || !fromLead(row, leads))
|
|
13433
|
+
continue;
|
|
13434
|
+
if (row.type === "standing" && typeof row.what === "string" && row.what.trim()) {
|
|
13435
|
+
if (!row.superseded)
|
|
13436
|
+
active.push(row);
|
|
13437
|
+
} else if (row.type === "standing.cleared") {
|
|
13438
|
+
const target = str2(row.target);
|
|
13439
|
+
if (target === null)
|
|
13440
|
+
active.length = 0;
|
|
13441
|
+
else {
|
|
13442
|
+
const index = active.findIndex((order) => order.id === target);
|
|
13443
|
+
if (index >= 0)
|
|
13444
|
+
active.splice(index, 1);
|
|
13445
|
+
}
|
|
13446
|
+
}
|
|
13447
|
+
}
|
|
13448
|
+
return active;
|
|
13449
|
+
}
|
|
13450
|
+
function leadOnly(context, lead) {
|
|
13451
|
+
const session = context.actor.session;
|
|
13452
|
+
if (lead !== null ? session === lead : !isDelegateSession(session))
|
|
13453
|
+
return { ok: true, value: undefined };
|
|
13454
|
+
return err("blocked", lead !== null ? `session ${session ?? "(none)"} is not the fanout's lead (${lead}): standing orders come from the lead` : `session ${session} is a worker or verifier: standing orders come from the lead`, lead !== null ? `the lead in a new session? workit fanout plan <plan.json> --take-lead from this session; a worker or verifier reports the order to the lead instead` : "a worker or verifier reports the order to the lead instead");
|
|
13455
|
+
}
|
|
13456
|
+
function recordStanding(context, input) {
|
|
13457
|
+
const what = required2(input.what, "<order>");
|
|
13458
|
+
if (!what.ok)
|
|
13459
|
+
return what;
|
|
13460
|
+
if (/\p{Cc}/u.test(what.value))
|
|
13461
|
+
return err("invalid_input", "a standing order is one line: no newlines or control characters", 'add each order separately: workit ledger standing add "<order>"');
|
|
13462
|
+
const lead = leadOnly(context, input.lead);
|
|
13463
|
+
if (!lead.ok)
|
|
13464
|
+
return lead;
|
|
13465
|
+
const link = checkSupersede(context, "standing");
|
|
13466
|
+
if (!link.ok)
|
|
13467
|
+
return link;
|
|
13468
|
+
return appendRow(context.cwd, {
|
|
13469
|
+
...common(context, noteKey(context)),
|
|
13470
|
+
type: "standing",
|
|
13471
|
+
fanout: input.fanout,
|
|
13472
|
+
what: what.value
|
|
13473
|
+
}, { now: context.now });
|
|
13474
|
+
}
|
|
13475
|
+
function clearStanding(context, input) {
|
|
13476
|
+
const lead = leadOnly(context, input.lead);
|
|
13477
|
+
if (!lead.ok)
|
|
13478
|
+
return lead;
|
|
13479
|
+
if (input.target !== null) {
|
|
13480
|
+
const ledger = readLedger(context.cwd);
|
|
13481
|
+
if (!ledger.ok)
|
|
13482
|
+
return ledger;
|
|
13483
|
+
if (!activeStanding(ledger.value.rows, input.fanout, input.leads ?? null).some((row) => row.id === input.target))
|
|
13484
|
+
return err("not_found", `no standing order ${input.target} in force for fanout ${input.fanout}`, `workit ledger standing list --fanout ${input.fanout}`);
|
|
13485
|
+
}
|
|
13486
|
+
return appendRow(context.cwd, {
|
|
13487
|
+
...common(context, noteKey(context)),
|
|
13488
|
+
type: "standing.cleared",
|
|
13489
|
+
fanout: input.fanout,
|
|
13490
|
+
target: input.target
|
|
13491
|
+
}, { now: context.now });
|
|
13492
|
+
}
|
|
13429
13493
|
function recordVerdict(context, input) {
|
|
13430
13494
|
if (!VERDICT_RESULTS.includes(input.result))
|
|
13431
13495
|
return err("invalid_input", `unknown verdict "${input.result}"; expected ${VERDICT_RESULTS.join("|")}`);
|
|
@@ -13504,6 +13568,12 @@ function summarizeRow(row) {
|
|
|
13504
13568
|
case "handoff":
|
|
13505
13569
|
summary = `next: ${text("next")}${text("note") ? ` (note: ${text("note")})` : ""}`;
|
|
13506
13570
|
break;
|
|
13571
|
+
case "standing":
|
|
13572
|
+
summary = `${text("what")} (fanout ${text("fanout")})`;
|
|
13573
|
+
break;
|
|
13574
|
+
case "standing.cleared":
|
|
13575
|
+
summary = `cleared ${text("target") || "every standing order"} (fanout ${text("fanout")})`;
|
|
13576
|
+
break;
|
|
13507
13577
|
case "commit.recorded":
|
|
13508
13578
|
summary = `${(row.head ?? "?").slice(0, 12)} ${text("subject")}${row.actor.session ? "" : " (no session)"}`;
|
|
13509
13579
|
break;
|
|
@@ -13730,7 +13800,7 @@ var LEDGER_VERSION = 1, MAX_LINE_BYTES = 4096, VERDICT_RESULTS, PASSING, FAILING
|
|
|
13730
13800
|
}, required2 = (value, flag) => {
|
|
13731
13801
|
const text = value?.trim();
|
|
13732
13802
|
return text ? { ok: true, value: text } : err("invalid_input", `${flag} is required`);
|
|
13733
|
-
}, OBSERVED_ONLY, MAX_DIRTY_PATHS = 10, dirtyState = (cwd) => {
|
|
13803
|
+
}, fromLead = (row, leads) => leads === null || row.actor.session !== null && leads.includes(row.actor.session), isDelegateSession = (session) => session !== null && (/-w-[a-z0-9._-]+(?:\+try\d)?$/u.test(session) || /-v\d+$/u.test(session) || session.includes(":")), OBSERVED_ONLY, MAX_DIRTY_PATHS = 10, dirtyState = (cwd) => {
|
|
13734
13804
|
const run = gitRun2(cwd, ["status", "--porcelain=v1", "-z", "--untracked-files=normal"], GIT_TIMEOUTS.worktree);
|
|
13735
13805
|
if (!run.ok)
|
|
13736
13806
|
return { dirty: false, files: 0, paths: [] };
|
|
@@ -13844,7 +13914,7 @@ var init_ledger = __esm(() => {
|
|
|
13844
13914
|
OBSERVED_ONLY = new Set(["check", ...PR_ROW_TYPES]);
|
|
13845
13915
|
});
|
|
13846
13916
|
|
|
13847
|
-
// packages/workit-core/src/core/policy/derive.ts
|
|
13917
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/policy/derive.ts
|
|
13848
13918
|
import { existsSync as existsSync3 } from "node:fs";
|
|
13849
13919
|
import path11 from "node:path";
|
|
13850
13920
|
function deriveRequirements(judgment, scope, constraints, verification = "self") {
|
|
@@ -14032,7 +14102,7 @@ var init_derive = __esm(() => {
|
|
|
14032
14102
|
};
|
|
14033
14103
|
});
|
|
14034
14104
|
|
|
14035
|
-
// packages/workit-core/src/core/task-evaluation.ts
|
|
14105
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/task-evaluation.ts
|
|
14036
14106
|
import { createHash as createHash7 } from "node:crypto";
|
|
14037
14107
|
import * as fs11 from "node:fs";
|
|
14038
14108
|
import { spawnSync as spawnSync6 } from "node:child_process";
|
|
@@ -14680,7 +14750,7 @@ var init_task_evaluation = __esm(() => {
|
|
|
14680
14750
|
scopeCovers2 = scopeCovers;
|
|
14681
14751
|
});
|
|
14682
14752
|
|
|
14683
|
-
// packages/workit-core/src/core/methods.ts
|
|
14753
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/methods.ts
|
|
14684
14754
|
function selectMethods(policy, capabilities) {
|
|
14685
14755
|
return Object.entries(METHODS).flatMap(([id, definition]) => {
|
|
14686
14756
|
const requirements = policy.requirements.filter((requirement) => methodMatches(definition, requirement));
|
|
@@ -14727,7 +14797,7 @@ var init_methods = __esm(() => {
|
|
|
14727
14797
|
};
|
|
14728
14798
|
});
|
|
14729
14799
|
|
|
14730
|
-
// packages/workit-core/src/core/task-context.ts
|
|
14800
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/task-context.ts
|
|
14731
14801
|
function reconcileResume(view, observations = []) {
|
|
14732
14802
|
if (observations.length > 0)
|
|
14733
14803
|
return failure2("permission_denied", "worker observations require native host verification");
|
|
@@ -14840,7 +14910,7 @@ var init_task_context = __esm(() => {
|
|
|
14840
14910
|
privateKey = /-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/gi;
|
|
14841
14911
|
});
|
|
14842
14912
|
|
|
14843
|
-
// packages/workit-core/src/core/commit-flavors.ts
|
|
14913
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/commit-flavors.ts
|
|
14844
14914
|
var CONVENTIONAL_RE, TICKET_RE, GITMOJI_RE, matchCommitFlavor = (message, flavor, pattern) => {
|
|
14845
14915
|
const subject = message.split(`
|
|
14846
14916
|
`, 1)[0] ?? "";
|
|
@@ -14891,10 +14961,10 @@ var init_commit_flavors = __esm(() => {
|
|
|
14891
14961
|
DETECTABLE = ["conventional", "gitmoji", "ticket-prefix"];
|
|
14892
14962
|
});
|
|
14893
14963
|
|
|
14894
|
-
// packages/workit-core/src/core/git.ts
|
|
14964
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/git.ts
|
|
14895
14965
|
var init_git = () => {};
|
|
14896
14966
|
|
|
14897
|
-
// packages/workit-core/src/core/boundary.ts
|
|
14967
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/boundary.ts
|
|
14898
14968
|
var EVENT, errorDetail = (err) => {
|
|
14899
14969
|
if (err instanceof Error) {
|
|
14900
14970
|
return { error_name: err.name, error: err.message };
|
|
@@ -14917,7 +14987,7 @@ var init_boundary = __esm(() => {
|
|
|
14917
14987
|
};
|
|
14918
14988
|
});
|
|
14919
14989
|
|
|
14920
|
-
// packages/workit-core/src/core/config.ts
|
|
14990
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/config.ts
|
|
14921
14991
|
import {
|
|
14922
14992
|
copyFileSync as copyFileSync2,
|
|
14923
14993
|
cpSync as cpSync2,
|
|
@@ -15086,7 +15156,7 @@ var init_config = __esm(() => {
|
|
|
15086
15156
|
];
|
|
15087
15157
|
});
|
|
15088
15158
|
|
|
15089
|
-
// packages/workit-core/src/core/workspaces.ts
|
|
15159
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/workspaces.ts
|
|
15090
15160
|
import { createHash as createHash8 } from "node:crypto";
|
|
15091
15161
|
import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
|
|
15092
15162
|
import path14 from "node:path";
|
|
@@ -15522,7 +15592,7 @@ var init_workspaces = __esm(() => {
|
|
|
15522
15592
|
}).passthrough();
|
|
15523
15593
|
});
|
|
15524
15594
|
|
|
15525
|
-
// packages/workit-core/src/core/release-tracks.ts
|
|
15595
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/release-tracks.ts
|
|
15526
15596
|
import { createHash as createHash9 } from "node:crypto";
|
|
15527
15597
|
import { spawnSync as spawnSync7 } from "node:child_process";
|
|
15528
15598
|
import { readFileSync as readFileSync5 } from "node:fs";
|
|
@@ -15984,7 +16054,7 @@ var init_release_tracks = __esm(() => {
|
|
|
15984
16054
|
memo3 = new Map;
|
|
15985
16055
|
});
|
|
15986
16056
|
|
|
15987
|
-
// packages/workit-core/src/core/vcs-config.ts
|
|
16057
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/vcs-config.ts
|
|
15988
16058
|
import fs12 from "node:fs";
|
|
15989
16059
|
import path15 from "node:path";
|
|
15990
16060
|
import { spawnSync as spawnSync8 } from "node:child_process";
|
|
@@ -16112,7 +16182,7 @@ var init_vcs_config = __esm(() => {
|
|
|
16112
16182
|
init_release_tracks();
|
|
16113
16183
|
});
|
|
16114
16184
|
|
|
16115
|
-
// packages/workit-core/src/core/branch.ts
|
|
16185
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/branch.ts
|
|
16116
16186
|
import { execFileSync } from "node:child_process";
|
|
16117
16187
|
import { createHash as createHash10 } from "node:crypto";
|
|
16118
16188
|
import path16 from "node:path";
|
|
@@ -16285,12 +16355,12 @@ var init_branch = __esm(() => {
|
|
|
16285
16355
|
init_release_tracks();
|
|
16286
16356
|
});
|
|
16287
16357
|
|
|
16288
|
-
// packages/workit-core/src/core/route-intent.ts
|
|
16358
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/route-intent.ts
|
|
16289
16359
|
var init_route_intent = __esm(() => {
|
|
16290
16360
|
init_branch();
|
|
16291
16361
|
});
|
|
16292
16362
|
|
|
16293
|
-
// packages/workit-core/src/core/runtime-identity.ts
|
|
16363
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/runtime-identity.ts
|
|
16294
16364
|
import { createHash as createHash11 } from "node:crypto";
|
|
16295
16365
|
import { readFileSync as readFileSync6 } from "node:fs";
|
|
16296
16366
|
var sha256Hex = (bytes) => createHash11("sha256").update(bytes).digest("hex"), bundleHashOfFile = (file) => {
|
|
@@ -16315,7 +16385,7 @@ var init_runtime_identity = __esm(() => {
|
|
|
16315
16385
|
];
|
|
16316
16386
|
});
|
|
16317
16387
|
|
|
16318
|
-
// packages/workit-core/src/core/policy-resolver.ts
|
|
16388
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/policy-resolver.ts
|
|
16319
16389
|
function resolvePolicy(input) {
|
|
16320
16390
|
const ids = input.constraints.map((constraint) => constraint.id);
|
|
16321
16391
|
if (new Set(ids).size !== ids.length)
|
|
@@ -16375,7 +16445,7 @@ var init_policy_resolver = __esm(() => {
|
|
|
16375
16445
|
init_derive();
|
|
16376
16446
|
});
|
|
16377
16447
|
|
|
16378
|
-
// packages/workit-core/src/core/safe-write.ts
|
|
16448
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/safe-write.ts
|
|
16379
16449
|
import { randomUUID as randomUUID4 } from "node:crypto";
|
|
16380
16450
|
import {
|
|
16381
16451
|
closeSync as closeSync2,
|
|
@@ -16453,7 +16523,7 @@ function writeFileAtomic(file, content, mode) {
|
|
|
16453
16523
|
}
|
|
16454
16524
|
var init_safe_write = () => {};
|
|
16455
16525
|
|
|
16456
|
-
// packages/workit-core/src/autonomy.ts
|
|
16526
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/autonomy.ts
|
|
16457
16527
|
import { copyFileSync as copyFileSync3, existsSync as existsSync6 } from "node:fs";
|
|
16458
16528
|
import os3 from "node:os";
|
|
16459
16529
|
import path18 from "node:path";
|
|
@@ -16734,7 +16804,7 @@ var init_autonomy = __esm(() => {
|
|
|
16734
16804
|
DEFAULT_ENDPOINTS = ["commit", "pr", "green", "merged"];
|
|
16735
16805
|
});
|
|
16736
16806
|
|
|
16737
|
-
// packages/workit-core/src/core/task-engine.ts
|
|
16807
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/task-engine.ts
|
|
16738
16808
|
import { realpathSync as realpathSync4 } from "node:fs";
|
|
16739
16809
|
|
|
16740
16810
|
class WorkitCore {
|
|
@@ -18316,13 +18386,13 @@ var init_task_engine = __esm(() => {
|
|
|
18316
18386
|
retryPause = new Int32Array(new SharedArrayBuffer(4));
|
|
18317
18387
|
});
|
|
18318
18388
|
|
|
18319
|
-
// packages/workit-core/src/core/repo-context.ts
|
|
18389
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/repo-context.ts
|
|
18320
18390
|
var init_repo_context = __esm(() => {
|
|
18321
18391
|
init_vcs_config();
|
|
18322
18392
|
init_core3();
|
|
18323
18393
|
});
|
|
18324
18394
|
|
|
18325
|
-
// packages/workit-core/src/forge/pr-body.ts
|
|
18395
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/pr-body.ts
|
|
18326
18396
|
function parseGhIssue(value) {
|
|
18327
18397
|
const m = /issues\/(\d+)/.exec(value);
|
|
18328
18398
|
return m ? m[1] : String(value).trim().replace(/^#/, "");
|
|
@@ -18383,7 +18453,7 @@ var init_pr_body = __esm(() => {
|
|
|
18383
18453
|
init_vcs_config();
|
|
18384
18454
|
});
|
|
18385
18455
|
|
|
18386
|
-
// packages/workit-core/src/core/pr-create.ts
|
|
18456
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/pr-create.ts
|
|
18387
18457
|
import path19 from "node:path";
|
|
18388
18458
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
18389
18459
|
import { pathToFileURL } from "node:url";
|
|
@@ -18426,13 +18496,13 @@ var init_pr_create = __esm(() => {
|
|
|
18426
18496
|
init_pr_body();
|
|
18427
18497
|
});
|
|
18428
18498
|
|
|
18429
|
-
// packages/workit-core/src/core/tracker-issues.ts
|
|
18499
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/tracker-issues.ts
|
|
18430
18500
|
var init_tracker_issues = __esm(() => {
|
|
18431
18501
|
init_pr_create();
|
|
18432
18502
|
init_vcs_config();
|
|
18433
18503
|
});
|
|
18434
18504
|
|
|
18435
|
-
// packages/workit-core/src/core/templates.ts
|
|
18505
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/templates.ts
|
|
18436
18506
|
var repoRoot;
|
|
18437
18507
|
var init_templates = __esm(() => {
|
|
18438
18508
|
init_config();
|
|
@@ -18440,7 +18510,7 @@ var init_templates = __esm(() => {
|
|
|
18440
18510
|
repoRoot = assetRoot();
|
|
18441
18511
|
});
|
|
18442
18512
|
|
|
18443
|
-
// packages/workit-core/src/core/scripts.ts
|
|
18513
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/scripts.ts
|
|
18444
18514
|
var PLUGIN_ROOT, ASSET_ROOT, resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
18445
18515
|
var init_scripts = __esm(() => {
|
|
18446
18516
|
init_package_root();
|
|
@@ -18448,7 +18518,7 @@ var init_scripts = __esm(() => {
|
|
|
18448
18518
|
ASSET_ROOT = assetRoot();
|
|
18449
18519
|
});
|
|
18450
18520
|
|
|
18451
|
-
// packages/workit-core/src/core/youtrack.ts
|
|
18521
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/youtrack.ts
|
|
18452
18522
|
import fs13 from "node:fs";
|
|
18453
18523
|
import path20 from "node:path";
|
|
18454
18524
|
function readYouTrackConfig(required) {
|
|
@@ -18920,7 +18990,7 @@ var init_youtrack = __esm(() => {
|
|
|
18920
18990
|
};
|
|
18921
18991
|
});
|
|
18922
18992
|
|
|
18923
|
-
// packages/workit-core/src/core/context-read.ts
|
|
18993
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/context-read.ts
|
|
18924
18994
|
var contextReadSchema;
|
|
18925
18995
|
var init_context_read = __esm(() => {
|
|
18926
18996
|
init_zod();
|
|
@@ -18951,7 +19021,7 @@ var init_context_read = __esm(() => {
|
|
|
18951
19021
|
}).strict();
|
|
18952
19022
|
});
|
|
18953
19023
|
|
|
18954
|
-
// packages/workit-core/src/core.ts
|
|
19024
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core.ts
|
|
18955
19025
|
import { existsSync as existsSync7, realpathSync as realpathSync5 } from "node:fs";
|
|
18956
19026
|
import path21 from "node:path";
|
|
18957
19027
|
function resolveInside(root, candidate) {
|
|
@@ -62107,7 +62177,7 @@ var init_build3 = __esm(async () => {
|
|
|
62107
62177
|
init_types();
|
|
62108
62178
|
});
|
|
62109
62179
|
|
|
62110
|
-
// packages/workit-core/src/core/gitignore.ts
|
|
62180
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/gitignore.ts
|
|
62111
62181
|
var GITIGNORE_ENTRIES;
|
|
62112
62182
|
var init_gitignore = __esm(() => {
|
|
62113
62183
|
GITIGNORE_ENTRIES = [
|
|
@@ -62128,7 +62198,7 @@ var init_gitignore = __esm(() => {
|
|
|
62128
62198
|
];
|
|
62129
62199
|
});
|
|
62130
62200
|
|
|
62131
|
-
// packages/workit-core/src/core/changelog.ts
|
|
62201
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/changelog.ts
|
|
62132
62202
|
import fs16 from "node:fs";
|
|
62133
62203
|
import path23 from "node:path";
|
|
62134
62204
|
import { createHash as createHash12 } from "node:crypto";
|
|
@@ -62457,7 +62527,7 @@ var init_changelog = __esm(() => {
|
|
|
62457
62527
|
HEADING_RE = /^###\s+/;
|
|
62458
62528
|
});
|
|
62459
62529
|
|
|
62460
|
-
// packages/workit-core/src/core/hygiene.ts
|
|
62530
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/hygiene.ts
|
|
62461
62531
|
import { existsSync as existsSync9, readFileSync as readFileSync9, writeFileSync as writeFileSync4 } from "node:fs";
|
|
62462
62532
|
import path24 from "node:path";
|
|
62463
62533
|
var repoRoot2, templatesDir = () => path24.join(repoRoot2, "templates", "hygiene"), packageJson = (root) => {
|
|
@@ -62662,7 +62732,7 @@ var init_registration = __esm(() => {
|
|
|
62662
62732
|
CURSOR_HOOK_RUN_COMMAND = `npx -y --prefer-online --min-release-age=0 --package=${CURSOR_RUNTIME_PACKAGE} workit-cursor-hook`;
|
|
62663
62733
|
});
|
|
62664
62734
|
|
|
62665
|
-
// packages/workit-core/src/core/support-matrix.ts
|
|
62735
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/support-matrix.ts
|
|
62666
62736
|
var SUPPORT_MATRIX;
|
|
62667
62737
|
var init_support_matrix = __esm(() => {
|
|
62668
62738
|
SUPPORT_MATRIX = {
|
|
@@ -63106,7 +63176,7 @@ var init_host_install = __esm(() => {
|
|
|
63106
63176
|
CLAUDE_SCOPES = new Set(["user", "project", "local"]);
|
|
63107
63177
|
});
|
|
63108
63178
|
|
|
63109
|
-
// packages/workit-core/src/core/skill-manifests.ts
|
|
63179
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/skill-manifests.ts
|
|
63110
63180
|
import { cpSync as cpSync3, existsSync as existsSync12, readFileSync as readFileSync12, readdirSync as readdirSync5, statSync as statSync3, writeFileSync as writeFileSync5 } from "node:fs";
|
|
63111
63181
|
import path28 from "node:path";
|
|
63112
63182
|
var WORKIT_METHOD_SKILLS, skillManifestNames = (root) => existsSync12(root) ? readdirSync5(root).filter((name) => existsSync12(path28.join(root, name, "SKILL.md"))).toSorted() : [], validateSkillManifests = (root, expected, label) => {
|
|
@@ -65369,7 +65439,7 @@ var init_setup = __esm(() => {
|
|
|
65369
65439
|
SETUP_OVERRIDES = [...YT_OVERRIDES, ...VCS_OVERRIDES];
|
|
65370
65440
|
});
|
|
65371
65441
|
|
|
65372
|
-
// packages/workit-core/src/core/branch-policy.ts
|
|
65442
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/branch-policy.ts
|
|
65373
65443
|
import { spawnSync as spawnSync12 } from "node:child_process";
|
|
65374
65444
|
var PREFIXES, detectBranchPolicy = (workspaceRoot) => {
|
|
65375
65445
|
const branchExists = (name) => {
|
|
@@ -65432,7 +65502,7 @@ var init_branch_policy = __esm(() => {
|
|
|
65432
65502
|
};
|
|
65433
65503
|
});
|
|
65434
65504
|
|
|
65435
|
-
// packages/workit-core/src/core/workspace-branch-policy.ts
|
|
65505
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/workspace-branch-policy.ts
|
|
65436
65506
|
import { mkdirSync as mkdirSync6, readFileSync as readFileSync15, renameSync as renameSync4, rmSync as rmSync4, writeFileSync as writeFileSync8 } from "node:fs";
|
|
65437
65507
|
import path31 from "node:path";
|
|
65438
65508
|
import { isDeepStrictEqual as isDeepStrictEqual3 } from "node:util";
|
|
@@ -68910,7 +68980,7 @@ var init_steps = __esm(async () => {
|
|
|
68910
68980
|
];
|
|
68911
68981
|
});
|
|
68912
68982
|
|
|
68913
|
-
// packages/workit-core/src/core/logger.ts
|
|
68983
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/logger.ts
|
|
68914
68984
|
import { appendFileSync, mkdirSync as mkdirSync7, readdirSync as readdirSync7, unlinkSync as unlinkSync3 } from "node:fs";
|
|
68915
68985
|
import os11 from "node:os";
|
|
68916
68986
|
import path34 from "node:path";
|
|
@@ -69918,7 +69988,7 @@ var init_launch = __esm(() => {
|
|
|
69918
69988
|
init_upgrade();
|
|
69919
69989
|
});
|
|
69920
69990
|
|
|
69921
|
-
// packages/workit-core/src/knowledge.ts
|
|
69991
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/knowledge.ts
|
|
69922
69992
|
import { spawnSync as spawnSync14 } from "node:child_process";
|
|
69923
69993
|
import fs17 from "node:fs";
|
|
69924
69994
|
import path36 from "node:path";
|
|
@@ -70315,7 +70385,7 @@ var init_doctor2 = __esm(() => {
|
|
|
70315
70385
|
init_task();
|
|
70316
70386
|
});
|
|
70317
70387
|
|
|
70318
|
-
// packages/workit-core/src/forge/redact.ts
|
|
70388
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/redact.ts
|
|
70319
70389
|
function redactText(value) {
|
|
70320
70390
|
let out = stripAnsi2(value).replace(PRIVATE_KEY, "[REDACTED PRIVATE KEY]");
|
|
70321
70391
|
out = out.replace(WEBHOOK, `$1/${R}`);
|
|
@@ -70418,7 +70488,7 @@ var init_redact = __esm(() => {
|
|
|
70418
70488
|
NOT_FIRST_ERROR = /^\s*(?:\(pass\)|\(skip\)|\(todo\)|✓|✔|ok\b|PASS\b)|^\s*\d+ fail\b|##\[error\]Process completed|^\s*0 (?:errors?|fail)/u;
|
|
70419
70489
|
});
|
|
70420
70490
|
|
|
70421
|
-
// packages/workit-core/src/checks.ts
|
|
70491
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/checks.ts
|
|
70422
70492
|
import { spawn, spawnSync as spawnSync15 } from "node:child_process";
|
|
70423
70493
|
import { createHash as createHash14 } from "node:crypto";
|
|
70424
70494
|
import fs18 from "node:fs";
|
|
@@ -71320,10 +71390,10 @@ var init_check = __esm(() => {
|
|
|
71320
71390
|
FINGERPRINT_VARS = ["CI", "NODE_ENV", "npm_config_script_shell", "NODE_OPTIONS", "SHELL"];
|
|
71321
71391
|
});
|
|
71322
71392
|
|
|
71323
|
-
// packages/workit-core/src/forge/types.ts
|
|
71393
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/types.ts
|
|
71324
71394
|
var success2 = (data) => ({ ok: true, data }), failure3 = (code, error, unblock) => ({ ok: false, code, error, ...unblock ? { unblock } : {} });
|
|
71325
71395
|
|
|
71326
|
-
// packages/workit-core/src/git/ops.ts
|
|
71396
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/git/ops.ts
|
|
71327
71397
|
import { spawnSync as spawnSync16 } from "node:child_process";
|
|
71328
71398
|
function dirtEntries(cwd) {
|
|
71329
71399
|
const run = git5(cwd, ["status", "--porcelain=v1", "-z", "--untracked-files=all"]);
|
|
@@ -71703,7 +71773,7 @@ var init_ops = __esm(() => {
|
|
|
71703
71773
|
SHA = /^[0-9a-f]{40,64}$/u;
|
|
71704
71774
|
});
|
|
71705
71775
|
|
|
71706
|
-
// packages/workit-core/src/forge/reruns.ts
|
|
71776
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/reruns.ts
|
|
71707
71777
|
import fs19 from "node:fs";
|
|
71708
71778
|
import path39 from "node:path";
|
|
71709
71779
|
function rerunLogPath(cwd) {
|
|
@@ -71794,7 +71864,7 @@ var init_reruns = __esm(() => {
|
|
|
71794
71864
|
init_rev();
|
|
71795
71865
|
});
|
|
71796
71866
|
|
|
71797
|
-
// packages/workit-core/src/forge/report.ts
|
|
71867
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/report.ts
|
|
71798
71868
|
function gatingChecks(status) {
|
|
71799
71869
|
const required = status.checks.filter((check) => check.required === true);
|
|
71800
71870
|
const protectionNamesChecks = (status.requiredContexts?.length ?? 0) > 0;
|
|
@@ -72148,7 +72218,7 @@ var init_report = __esm(() => {
|
|
|
72148
72218
|
GITHUB_MERGEABLE = new Set(["clean", "unstable", "has_hooks"]);
|
|
72149
72219
|
});
|
|
72150
72220
|
|
|
72151
|
-
// packages/workit-core/src/forge/pr-ops.ts
|
|
72221
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/pr-ops.ts
|
|
72152
72222
|
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
72153
72223
|
function fillFromCommits(cwd, resolved, base) {
|
|
72154
72224
|
const tracking = `refs/remotes/${resolved.baseRemote}/${base}`;
|
|
@@ -72431,7 +72501,7 @@ var init_pr_ops = __esm(() => {
|
|
|
72431
72501
|
};
|
|
72432
72502
|
});
|
|
72433
72503
|
|
|
72434
|
-
// packages/workit-core/src/forge/exec.ts
|
|
72504
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/exec.ts
|
|
72435
72505
|
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
72436
72506
|
import fs20 from "node:fs";
|
|
72437
72507
|
import path40 from "node:path";
|
|
@@ -72549,7 +72619,7 @@ var init_exec = __esm(() => {
|
|
|
72549
72619
|
MAX_BUFFER2 = 64 * 1024 * 1024;
|
|
72550
72620
|
});
|
|
72551
72621
|
|
|
72552
|
-
// packages/workit-core/src/forge/github.ts
|
|
72622
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/github.ts
|
|
72553
72623
|
function checkFromContext(node) {
|
|
72554
72624
|
const required = node.isRequired ?? null;
|
|
72555
72625
|
if (node.__typename === "StatusContext") {
|
|
@@ -72848,7 +72918,7 @@ var init_github = __esm(() => {
|
|
|
72848
72918
|
]);
|
|
72849
72919
|
});
|
|
72850
72920
|
|
|
72851
|
-
// packages/workit-core/src/forge/gitlab.ts
|
|
72921
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/gitlab.ts
|
|
72852
72922
|
function checkFromBridge(bridge, pipelineId, scope) {
|
|
72853
72923
|
const status = bridge.downstream_pipeline?.status ?? bridge.status;
|
|
72854
72924
|
const allow = bridge.allow_failure === true;
|
|
@@ -73136,7 +73206,7 @@ var init_gitlab = __esm(() => {
|
|
|
73136
73206
|
UNSETTLED = new Set(["checking", "unchecked", "preparing", "approvals_syncing"]);
|
|
73137
73207
|
});
|
|
73138
73208
|
|
|
73139
|
-
// packages/workit-core/src/forge/resolve.ts
|
|
73209
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/resolve.ts
|
|
73140
73210
|
import fs21 from "node:fs";
|
|
73141
73211
|
function resolveForge(cwd, options = {}) {
|
|
73142
73212
|
let workspace;
|
|
@@ -73256,7 +73326,7 @@ var init_resolve = __esm(() => {
|
|
|
73256
73326
|
init_gitlab();
|
|
73257
73327
|
});
|
|
73258
73328
|
|
|
73259
|
-
// packages/workit-core/src/duration.ts
|
|
73329
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/duration.ts
|
|
73260
73330
|
function parseDuration2(value) {
|
|
73261
73331
|
const text = value.trim().toLowerCase();
|
|
73262
73332
|
if (/^\d+(?:\.\d+)?$/u.test(text))
|
|
@@ -73315,6 +73385,14 @@ function parseFlags(argv, spec) {
|
|
|
73315
73385
|
}
|
|
73316
73386
|
return parsed;
|
|
73317
73387
|
}
|
|
73388
|
+
function unverifiedFlag(flags) {
|
|
73389
|
+
const reason = flags.values.reason?.trim();
|
|
73390
|
+
if (!flags.booleans.has("unverified"))
|
|
73391
|
+
return reason === undefined ? null : "--reason is only used with --unverified";
|
|
73392
|
+
if (!reason)
|
|
73393
|
+
return "--unverified needs --reason <why the user asked for it>";
|
|
73394
|
+
return reason.length > MAX_REASON ? `--reason is capped at ${MAX_REASON} characters` : { reason };
|
|
73395
|
+
}
|
|
73318
73396
|
function positiveInt(value, flag) {
|
|
73319
73397
|
if (value === undefined)
|
|
73320
73398
|
return null;
|
|
@@ -73398,7 +73476,7 @@ function renderStatus(doc) {
|
|
|
73398
73476
|
lines.push(`next: ${doc.next}`);
|
|
73399
73477
|
return lines;
|
|
73400
73478
|
}
|
|
73401
|
-
var forgeDeps, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })), short = (sha) => sha ? sha.slice(0, 7) : "?";
|
|
73479
|
+
var forgeDeps, MAX_REASON = 500, usage3 = (io, message, line) => emit(io, fail("invalid_input", message, { unblock: line })), forgeFail = (io, result, data) => emit(io, fail(result.code, result.error, { data, unblock: result.unblock })), short = (sha) => sha ? sha.slice(0, 7) : "?";
|
|
73402
73480
|
var init_forge_common = __esm(() => {
|
|
73403
73481
|
init_resolve();
|
|
73404
73482
|
init_vcs_config();
|
|
@@ -73544,13 +73622,23 @@ async function create4(argv, io) {
|
|
|
73544
73622
|
...data.releaseTrack?.name ? [`release track: ${data.releaseTrack.name} (${data.releaseTrack.detail})`] : [],
|
|
73545
73623
|
...(data.releaseTrack?.warnings ?? []).map((warning) => `note: ${warning}`),
|
|
73546
73624
|
`head ${data.head.slice(0, 12)} verified on the forge`,
|
|
73547
|
-
..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : []
|
|
73625
|
+
..."error" in data.recorded ? [`ledger: not recorded (${data.recorded.error})`] : [],
|
|
73626
|
+
`next: ${data.next}`
|
|
73548
73627
|
]);
|
|
73549
73628
|
}
|
|
73550
73629
|
async function merge3(argv, io) {
|
|
73551
|
-
const flags = parseFlags(argv, {
|
|
73630
|
+
const flags = parseFlags(argv, {
|
|
73631
|
+
pr: "value",
|
|
73632
|
+
method: "value",
|
|
73633
|
+
"delete-branch": "boolean",
|
|
73634
|
+
unverified: "boolean",
|
|
73635
|
+
reason: "value"
|
|
73636
|
+
});
|
|
73552
73637
|
if (typeof flags === "string")
|
|
73553
73638
|
return usage3(io, flags, MERGE_USAGE);
|
|
73639
|
+
const unverified = unverifiedFlag(flags);
|
|
73640
|
+
if (typeof unverified === "string")
|
|
73641
|
+
return usage3(io, unverified, MERGE_USAGE);
|
|
73554
73642
|
if (flags.positionals.length)
|
|
73555
73643
|
return usage3(io, `unexpected argument ${flags.positionals[0]}`, MERGE_USAGE);
|
|
73556
73644
|
const pr = positiveInt(flags.values.pr, "--pr");
|
|
@@ -73562,7 +73650,13 @@ async function merge3(argv, io) {
|
|
|
73562
73650
|
const connected = connect(io);
|
|
73563
73651
|
if (!connected.ok)
|
|
73564
73652
|
return forgeFail(io, connected);
|
|
73565
|
-
const result = await mergePullRequest(io.cwd, connected.data, {
|
|
73653
|
+
const result = await mergePullRequest(io.cwd, connected.data, {
|
|
73654
|
+
pr,
|
|
73655
|
+
method,
|
|
73656
|
+
deleteBranch: flags.booleans.has("delete-branch"),
|
|
73657
|
+
actor: actorFromEnv(io.env),
|
|
73658
|
+
...unverified ? { unverified } : {}
|
|
73659
|
+
}, forgeDeps.sleep);
|
|
73566
73660
|
if (!result.ok)
|
|
73567
73661
|
return emit(io, fail(result.code, result.error, {
|
|
73568
73662
|
unblock: result.unblock,
|
|
@@ -73570,7 +73664,7 @@ async function merge3(argv, io) {
|
|
|
73570
73664
|
}));
|
|
73571
73665
|
return emit(io, ok(result.data), (data) => [
|
|
73572
73666
|
`merged ${connected.data.forge.kind === "github" ? "PR #" : "MR !"}${data.number} (${data.method}) at head ${data.head.slice(0, 12)}${data.mergeSha ? ` -> ${data.mergeSha.slice(0, 12)}` : ""}`,
|
|
73573
|
-
data.verdict.
|
|
73667
|
+
data.unverified ? `UNVERIFIED: merged without a verdict (--unverified: ${data.unverified.reason}); ledger ${data.unverified.recorded}` : `verdict ${data.verdict.verdictId ?? "?"} accepted for that head`,
|
|
73574
73668
|
...data.mergeBack.length ? [
|
|
73575
73669
|
`merge back: ${data.base} is the ${data.mergeBackTrack} production branch; bring it into ${data.mergeBack.join(", ")}`
|
|
73576
73670
|
] : [],
|
|
@@ -73588,7 +73682,7 @@ async function run10(argv, io) {
|
|
|
73588
73682
|
return merge3(rest, io);
|
|
73589
73683
|
return usage3(io, sub && !sub.startsWith("-") ? `unknown pr subcommand "${sub}"` : "missing pr subcommand", USAGE4);
|
|
73590
73684
|
}
|
|
73591
|
-
var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f>] | --fill) [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch] [--json]", USAGE4 = "workit pr status|create|merge ... (workit help pr)", METHODS2;
|
|
73685
|
+
var STATUS_USAGE = "workit pr status [--pr <n> | --branch <b>] [--log-lines 60] [--json]", CREATE_USAGE = "workit pr create [--base <b> | --track <t>] (--title <t> [--body <text> | --body-file <f>] | --fill) [--draft] [--json]", MERGE_USAGE = "workit pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch] [--unverified --reason <why>] [--json]", USAGE4 = "workit pr status|create|merge ... (workit help pr)", METHODS2;
|
|
73592
73686
|
var init_pr = __esm(() => {
|
|
73593
73687
|
init_pr_body();
|
|
73594
73688
|
init_pr_ops();
|
|
@@ -73980,7 +74074,7 @@ var init_git2 = __esm(() => {
|
|
|
73980
74074
|
init_forge_common();
|
|
73981
74075
|
});
|
|
73982
74076
|
|
|
73983
|
-
// packages/workit-core/src/forge/verify.ts
|
|
74077
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/forge/verify.ts
|
|
73984
74078
|
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
73985
74079
|
function finish(cwd, report, actor) {
|
|
73986
74080
|
const delivered = report.observations.length > 0 && report.observations.every((entry) => entry.ok);
|
|
@@ -74300,7 +74394,7 @@ var init_verify_delivery = __esm(() => {
|
|
|
74300
74394
|
};
|
|
74301
74395
|
});
|
|
74302
74396
|
|
|
74303
|
-
// packages/workit-core/src/stack.ts
|
|
74397
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/stack.ts
|
|
74304
74398
|
import { spawnSync as spawnSync20 } from "node:child_process";
|
|
74305
74399
|
import { createHash as createHash15, randomBytes as randomBytes3 } from "node:crypto";
|
|
74306
74400
|
import fs22 from "node:fs";
|
|
@@ -75631,10 +75725,15 @@ async function land(argv, io) {
|
|
|
75631
75725
|
max: "value",
|
|
75632
75726
|
method: "value",
|
|
75633
75727
|
timeout: "value",
|
|
75634
|
-
interval: "value"
|
|
75728
|
+
interval: "value",
|
|
75729
|
+
unverified: "boolean",
|
|
75730
|
+
reason: "value"
|
|
75635
75731
|
});
|
|
75636
75732
|
if (typeof flags === "string")
|
|
75637
75733
|
return usage3(io, flags, LAND_USAGE);
|
|
75734
|
+
const unverified = unverifiedFlag(flags);
|
|
75735
|
+
if (typeof unverified === "string")
|
|
75736
|
+
return usage3(io, unverified, LAND_USAGE);
|
|
75638
75737
|
if (flags.positionals.length)
|
|
75639
75738
|
return usage3(io, `unexpected argument ${flags.positionals[0]}`, LAND_USAGE);
|
|
75640
75739
|
const max = positiveInt(flags.values.max, "--max");
|
|
@@ -75665,7 +75764,7 @@ async function land(argv, io) {
|
|
|
75665
75764
|
resolved: connected.data,
|
|
75666
75765
|
actor: actorFromEnv(io.env),
|
|
75667
75766
|
sleep: forgeDeps.sleep
|
|
75668
|
-
}, fresh.data ?? selected.data, { dryRun: flags.booleans.has("dry-run"), max, method, timeoutMs, intervalMs });
|
|
75767
|
+
}, fresh.data ?? selected.data, { dryRun: flags.booleans.has("dry-run"), max, method, timeoutMs, intervalMs, unverified });
|
|
75669
75768
|
});
|
|
75670
75769
|
if (!result.ok)
|
|
75671
75770
|
return stackFailed(io, result);
|
|
@@ -75674,7 +75773,7 @@ async function land(argv, io) {
|
|
|
75674
75773
|
...data.dryRun ? [
|
|
75675
75774
|
data.wouldLand.length ? `would land: ${data.wouldLand.map((item) => `${label(kind, item.pr)} (${item.branch})`).join(", ")}` : "would land nothing"
|
|
75676
75775
|
] : [
|
|
75677
|
-
data.landed.length ? `landed: ${data.landed.map((item) => `${label(kind, item.pr)} (${item.branch}) -> ${short4(item.mergeSha)}`).join(", ")}` : "landed nothing"
|
|
75776
|
+
data.landed.length ? `landed: ${data.landed.map((item) => `${label(kind, item.pr)} (${item.branch}) -> ${short4(item.mergeSha)}${item.unverified ? ` UNVERIFIED (ledger ${item.unverified})` : ""}`).join(", ")}` : "landed nothing"
|
|
75678
75777
|
],
|
|
75679
75778
|
...data.retargeted.length ? [`retargeted: ${data.retargeted.map((pr) => label(kind, pr)).join(", ")}`] : [],
|
|
75680
75779
|
...data.restacked.length ? [`restacked: ${data.restacked.join(", ")}`] : [],
|
|
@@ -75694,7 +75793,7 @@ async function run14(argv, io) {
|
|
|
75694
75793
|
return land(rest, io);
|
|
75695
75794
|
return usage3(io, sub && !sub.startsWith("-") ? `unknown stack subcommand "${sub}"` : "missing stack subcommand", USAGE7);
|
|
75696
75795
|
}
|
|
75697
|
-
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] [--timeout 20m] [--interval 30s] [--json]", USAGE7 = "workit stack plan|status|sync|land ... (workit help stack)", short4 = (sha) => sha ? sha.slice(0, 12) : "?", stackFailed = (io, result) => emit(io, fail(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), label = (forge, pr) => pr === null ? "no PR" : `${forge === "gitlab" ? "MR !" : "PR #"}${pr}`, renderSync = (data) => [
|
|
75796
|
+
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]", USAGE7 = "workit stack plan|status|sync|land ... (workit help stack)", short4 = (sha) => sha ? sha.slice(0, 12) : "?", stackFailed = (io, result) => emit(io, fail(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), label = (forge, pr) => pr === null ? "no PR" : `${forge === "gitlab" ? "MR !" : "PR #"}${pr}`, renderSync = (data) => [
|
|
75698
75797
|
`${data.dryRun ? "would sync" : "synced"} stack ${data.name}${data.merged.length ? `; merged: ${data.merged.map((item) => item.branch).join(", ")}` : ""}`,
|
|
75699
75798
|
...data.steps.map((step) => [
|
|
75700
75799
|
` ${step.branch} on ${step.parent}:`,
|
|
@@ -75711,8 +75810,9 @@ var init_stack2 = __esm(() => {
|
|
|
75711
75810
|
METHODS3 = new Set(["squash", "merge", "rebase"]);
|
|
75712
75811
|
});
|
|
75713
75812
|
|
|
75714
|
-
// packages/workit-core/src/fanout.ts
|
|
75813
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout.ts
|
|
75715
75814
|
import { spawnSync as spawnSync21 } from "node:child_process";
|
|
75815
|
+
import { createHash as createHash16 } from "node:crypto";
|
|
75716
75816
|
import fs23 from "node:fs";
|
|
75717
75817
|
import path43 from "node:path";
|
|
75718
75818
|
function expandBraces(pattern, limit = 64) {
|
|
@@ -75874,6 +75974,9 @@ function parsePlan(raw, defaults, name) {
|
|
|
75874
75974
|
else
|
|
75875
75975
|
shared.push(normalized.pattern);
|
|
75876
75976
|
}
|
|
75977
|
+
const fanIn = raw.fanIn ?? "prs";
|
|
75978
|
+
if (!FAN_IN_MODES.includes(fanIn))
|
|
75979
|
+
problems.push(`fanIn must be one of ${FAN_IN_MODES.join(", ")}`);
|
|
75877
75980
|
if (!Array.isArray(raw.slices) || raw.slices.length === 0)
|
|
75878
75981
|
return { ok: false, problems: [...problems, "slices: list at least one slice"] };
|
|
75879
75982
|
const slices = [];
|
|
@@ -75955,7 +76058,9 @@ function parsePlan(raw, defaults, name) {
|
|
|
75955
76058
|
verify,
|
|
75956
76059
|
forbidden,
|
|
75957
76060
|
context: optional("context"),
|
|
75958
|
-
timebox: optional("timebox")
|
|
76061
|
+
timebox: optional("timebox"),
|
|
76062
|
+
hash: "",
|
|
76063
|
+
hashSince: ""
|
|
75959
76064
|
});
|
|
75960
76065
|
});
|
|
75961
76066
|
const byId = new Map(slices.map((slice) => [slice.id, slice]));
|
|
@@ -75988,8 +76093,11 @@ function parsePlan(raw, defaults, name) {
|
|
|
75988
76093
|
v: FANOUT_VERSION,
|
|
75989
76094
|
name: planName,
|
|
75990
76095
|
trunk,
|
|
76096
|
+
fanIn,
|
|
75991
76097
|
shared,
|
|
75992
76098
|
slices,
|
|
76099
|
+
leadSession: null,
|
|
76100
|
+
formerLeads: [],
|
|
75993
76101
|
createdAt: "",
|
|
75994
76102
|
updatedAt: ""
|
|
75995
76103
|
}
|
|
@@ -76175,6 +76283,18 @@ function worktreeRoot(cwd) {
|
|
|
76175
76283
|
const main = mainCheckout(cwd);
|
|
76176
76284
|
return path43.join(path43.dirname(main), `${path43.basename(main)}-wt`);
|
|
76177
76285
|
}
|
|
76286
|
+
function sliceHash(slice) {
|
|
76287
|
+
const {
|
|
76288
|
+
worktree: _worktree,
|
|
76289
|
+
hash: _hash,
|
|
76290
|
+
hashSince: _since,
|
|
76291
|
+
tier: _tier,
|
|
76292
|
+
timebox: _timebox,
|
|
76293
|
+
context: _context,
|
|
76294
|
+
...definition
|
|
76295
|
+
} = slice;
|
|
76296
|
+
return createHash16("sha256").update(JSON.stringify(definition)).digest("hex").slice(0, 16);
|
|
76297
|
+
}
|
|
76178
76298
|
function fanoutsDir(cwd) {
|
|
76179
76299
|
const root = storeRoot(cwd);
|
|
76180
76300
|
if (!root.ok)
|
|
@@ -76215,20 +76335,29 @@ function parseStored(raw) {
|
|
|
76215
76335
|
verify: list(item.verify),
|
|
76216
76336
|
forbidden: list(item.forbidden),
|
|
76217
76337
|
context: typeof item.context === "string" ? item.context : null,
|
|
76218
|
-
timebox: typeof item.timebox === "string" ? item.timebox : null
|
|
76338
|
+
timebox: typeof item.timebox === "string" ? item.timebox : null,
|
|
76339
|
+
hash: text(item.hash),
|
|
76340
|
+
hashSince: text(item.hashSince) || text(raw.createdAt)
|
|
76219
76341
|
});
|
|
76220
76342
|
}
|
|
76343
|
+
for (const slice of slices)
|
|
76344
|
+
slice.hash ||= sliceHash(slice);
|
|
76221
76345
|
const shared = globs(raw.shared);
|
|
76222
76346
|
const ids = new Set(slices.map((slice) => slice.id));
|
|
76223
76347
|
if (!globsValid || ids.size !== slices.length || slices.some((slice) => slice.dependsOn.some((dep) => dep === slice.id || !ids.has(dep))) || findCycle(slices))
|
|
76224
76348
|
return null;
|
|
76349
|
+
const fanIn = raw.fanIn === "integration" ? "integration" : "prs";
|
|
76350
|
+
const createdAt = text(raw.createdAt);
|
|
76225
76351
|
return {
|
|
76226
76352
|
v: typeof raw.v === "number" ? raw.v : FANOUT_VERSION,
|
|
76227
76353
|
name: raw.name,
|
|
76228
76354
|
trunk: raw.trunk,
|
|
76355
|
+
fanIn,
|
|
76229
76356
|
shared,
|
|
76230
76357
|
slices,
|
|
76231
|
-
|
|
76358
|
+
leadSession: typeof raw.leadSession === "string" && raw.leadSession ? raw.leadSession : null,
|
|
76359
|
+
formerLeads: list(raw.formerLeads),
|
|
76360
|
+
createdAt,
|
|
76232
76361
|
updatedAt: text(raw.updatedAt)
|
|
76233
76362
|
};
|
|
76234
76363
|
}
|
|
@@ -76308,6 +76437,11 @@ function planFanout(cwd, input, now = new Date) {
|
|
|
76308
76437
|
const dir = fanoutsDir(cwd);
|
|
76309
76438
|
if (!dir.ok)
|
|
76310
76439
|
return dir;
|
|
76440
|
+
const session = input.actor.session;
|
|
76441
|
+
if (input.takeLead && !session)
|
|
76442
|
+
return fanoutFail("blocked", "--take-lead needs a session: WORKIT_SESSION_ID is not set", "export WORKIT_SESSION_ID=<your session id>, then plan again with --take-lead");
|
|
76443
|
+
if (input.takeLead && isDelegateSession(session))
|
|
76444
|
+
return fanoutFail("blocked", `session ${session} is a worker or verifier id; only a lead takes the lead`, "run --take-lead from the lead's own session");
|
|
76311
76445
|
const parsed = parsePlan(input.trunk && isRecord8(input.raw) ? { ...input.raw, trunk: input.trunk } : input.raw, {
|
|
76312
76446
|
trunk: input.trunk ?? defaultTrunk(cwd),
|
|
76313
76447
|
root: mainCheckout(cwd),
|
|
@@ -76316,6 +76450,8 @@ function planFanout(cwd, input, now = new Date) {
|
|
|
76316
76450
|
if (!parsed.ok)
|
|
76317
76451
|
return fanoutFail("invalid_input", `the plan is incomplete: ${parsed.problems[0]}${parsed.problems.length > 1 ? ` (+${parsed.problems.length - 1} more)` : ""}`, "fill every listed field, then run workit fanout plan again", { problems: parsed.problems });
|
|
76318
76452
|
const plan = parsed.plan;
|
|
76453
|
+
if (plan.fanIn === "integration" && plan.trunk === defaultTrunk(cwd))
|
|
76454
|
+
return fanoutFail("invalid_input", `fanIn "integration" needs an integration branch as the trunk, not ${plan.trunk}`, `workit git branch <integration-branch> --base ${plan.trunk}, then plan with --trunk <integration-branch>`);
|
|
76319
76455
|
const badBranches = plan.slices.filter((slice) => !gitRun4(cwd, ["check-ref-format", "--branch", slice.branch]).ok).map((slice) => `slice ${slice.id}: ${slice.branch} is not a valid branch name`);
|
|
76320
76456
|
if (badBranches.length)
|
|
76321
76457
|
return fanoutFail("invalid_input", badBranches[0], "rename the branch in the plan", {
|
|
@@ -76344,20 +76480,50 @@ function planFanout(cwd, input, now = new Date) {
|
|
|
76344
76480
|
const existing = readFanout(cwd, plan.name);
|
|
76345
76481
|
const previous = existing.ok ? existing.data : null;
|
|
76346
76482
|
plan.createdAt = previous?.createdAt || now.toISOString();
|
|
76483
|
+
plan.leadSession = input.takeLead ? session : previous ? previous.leadSession : session;
|
|
76484
|
+
plan.formerLeads = [
|
|
76485
|
+
...new Set([
|
|
76486
|
+
...previous?.formerLeads ?? [],
|
|
76487
|
+
...previous?.leadSession && previous.leadSession !== plan.leadSession ? [previous.leadSession] : []
|
|
76488
|
+
])
|
|
76489
|
+
].filter((former) => former !== plan.leadSession);
|
|
76347
76490
|
plan.updatedAt = now.toISOString();
|
|
76491
|
+
for (const slice of plan.slices) {
|
|
76492
|
+
slice.hash = sliceHash(slice);
|
|
76493
|
+
const before = previous?.slices.find((old) => old.id === slice.id);
|
|
76494
|
+
slice.hashSince = before && before.hash === slice.hash ? before.hashSince || plan.updatedAt : plan.updatedAt;
|
|
76495
|
+
}
|
|
76348
76496
|
const written = writeFanout(cwd, plan);
|
|
76349
76497
|
if (!written.ok)
|
|
76350
76498
|
return written;
|
|
76499
|
+
if (previous && plan.leadSession !== previous.leadSession)
|
|
76500
|
+
appendObserved(cwd, {
|
|
76501
|
+
type: "fanout.lead.changed",
|
|
76502
|
+
actor: input.actor,
|
|
76503
|
+
fanout: plan.name,
|
|
76504
|
+
from: previous.leadSession,
|
|
76505
|
+
to: plan.leadSession
|
|
76506
|
+
});
|
|
76351
76507
|
appendObserved(cwd, {
|
|
76352
76508
|
type: "fanout.planned",
|
|
76353
76509
|
actor: input.actor,
|
|
76354
76510
|
fanout: plan.name,
|
|
76355
76511
|
trunk: plan.trunk,
|
|
76512
|
+
fanIn: plan.fanIn,
|
|
76356
76513
|
slices: plan.slices.length,
|
|
76357
76514
|
ids: plan.slices.slice(0, 20).map((slice) => slice.id)
|
|
76358
76515
|
});
|
|
76359
76516
|
const grouped = waves(plan.slices);
|
|
76360
76517
|
const notes = [];
|
|
76518
|
+
const warnings = [];
|
|
76519
|
+
if (!input.takeLead && previous?.leadSession && session && session !== previous.leadSession)
|
|
76520
|
+
notes.push(`the lead stays ${previous.leadSession}; this session is ${session}. Leading from this session now? workit fanout plan <plan.json> --take-lead`);
|
|
76521
|
+
if (previous === null) {
|
|
76522
|
+
const ledger = readLedger(cwd);
|
|
76523
|
+
const inForce = ledger.ok ? activeStanding(ledger.value.rows, plan.name, planLeads(plan)) : [];
|
|
76524
|
+
if (inForce.length)
|
|
76525
|
+
warnings.push(`${inForce.length} standing order${inForce.length === 1 ? " is" : "s are"} already in force for fanout ${plan.name}, and every brief will carry ${inForce.length === 1 ? "it" : "them"}: ${inForce.map((row) => `"${String(row.what)}"`).join("; ")}. From an earlier run? workit ledger standing clear --fanout ${plan.name}`);
|
|
76526
|
+
}
|
|
76361
76527
|
if (!ref)
|
|
76362
76528
|
notes.push(`trunk ${plan.trunk} does not resolve here; only sample paths of each glob were compared`);
|
|
76363
76529
|
grouped.forEach((wave, index) => {
|
|
@@ -76372,6 +76538,7 @@ function planFanout(cwd, input, now = new Date) {
|
|
|
76372
76538
|
data: {
|
|
76373
76539
|
name: plan.name,
|
|
76374
76540
|
trunk: plan.trunk,
|
|
76541
|
+
fanIn: plan.fanIn,
|
|
76375
76542
|
file: written.data,
|
|
76376
76543
|
created: previous === null,
|
|
76377
76544
|
slices: plan.slices.map(({ id, branch, base, worktree, tier, dependsOn, scope, owns }) => ({
|
|
@@ -76387,11 +76554,12 @@ function planFanout(cwd, input, now = new Date) {
|
|
|
76387
76554
|
overlaps,
|
|
76388
76555
|
waves: grouped,
|
|
76389
76556
|
landingOrder: landingOrder(plan.slices),
|
|
76390
|
-
notes
|
|
76557
|
+
notes,
|
|
76558
|
+
warnings
|
|
76391
76559
|
}
|
|
76392
76560
|
};
|
|
76393
76561
|
}
|
|
76394
|
-
var FANOUT_VERSION = 1, TIERS, fanoutFail = (code, error, unblock, data) => ({
|
|
76562
|
+
var FANOUT_VERSION = 1, TIERS, FAN_IN_MODES, fanoutFail = (code, error, unblock, data) => ({
|
|
76395
76563
|
ok: false,
|
|
76396
76564
|
code,
|
|
76397
76565
|
error,
|
|
@@ -76431,12 +76599,13 @@ var FANOUT_VERSION = 1, TIERS, fanoutFail = (code, error, unblock, data) => ({
|
|
|
76431
76599
|
stdout: run.stdout ?? "",
|
|
76432
76600
|
stderr: run.error ? String(run.error.message) : run.stderr ?? ""
|
|
76433
76601
|
};
|
|
76434
|
-
}, nulList2 = (text) => text.split("\x00").filter(Boolean), IN_FLIGHT_CAP = 6;
|
|
76602
|
+
}, nulList2 = (text) => text.split("\x00").filter(Boolean), planLeads = (plan) => plan.leadSession ? [plan.leadSession, ...plan.formerLeads] : null, IN_FLIGHT_CAP = 6;
|
|
76435
76603
|
var init_fanout = __esm(() => {
|
|
76436
76604
|
init_rev();
|
|
76437
76605
|
init_ledger();
|
|
76438
76606
|
init_stack();
|
|
76439
76607
|
TIERS = ["mundane", "standard", "hard"];
|
|
76608
|
+
FAN_IN_MODES = ["prs", "integration"];
|
|
76440
76609
|
DEFAULT_SHARED = [
|
|
76441
76610
|
"**/package.json",
|
|
76442
76611
|
"**/{package-lock.json,npm-shrinkwrap.json,bun.lock,bun.lockb,pnpm-lock.yaml,pnpm-workspace.yaml,yarn.lock}",
|
|
@@ -76452,7 +76621,489 @@ var init_fanout = __esm(() => {
|
|
|
76452
76621
|
ID = /^[a-z0-9][a-z0-9._-]{0,63}$/u;
|
|
76453
76622
|
});
|
|
76454
76623
|
|
|
76455
|
-
// packages/workit-core/src/fanout-
|
|
76624
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout-worktree.ts
|
|
76625
|
+
import { randomBytes as randomBytes4 } from "node:crypto";
|
|
76626
|
+
import fs24 from "node:fs";
|
|
76627
|
+
import path44 from "node:path";
|
|
76628
|
+
function rowSample(lines) {
|
|
76629
|
+
const out = [];
|
|
76630
|
+
let bytes = 0;
|
|
76631
|
+
for (const line of lines) {
|
|
76632
|
+
const entry = line.slice(0, 200);
|
|
76633
|
+
bytes += Buffer.byteLength(entry) + 3;
|
|
76634
|
+
if (bytes > DIRTY_ROW_BYTES)
|
|
76635
|
+
break;
|
|
76636
|
+
out.push(entry);
|
|
76637
|
+
}
|
|
76638
|
+
return out;
|
|
76639
|
+
}
|
|
76640
|
+
function listWorktrees(cwd) {
|
|
76641
|
+
const out = [];
|
|
76642
|
+
let current = null;
|
|
76643
|
+
for (const field of gitRun4(cwd, ["worktree", "list", "--porcelain", "-z"]).stdout.split("\x00")) {
|
|
76644
|
+
if (field.startsWith("worktree ")) {
|
|
76645
|
+
current = { path: field.slice(9), head: null, branch: null, locked: false };
|
|
76646
|
+
out.push(current);
|
|
76647
|
+
} else if (!current)
|
|
76648
|
+
continue;
|
|
76649
|
+
else if (field.startsWith("HEAD "))
|
|
76650
|
+
current.head = field.slice(5);
|
|
76651
|
+
else if (field.startsWith("branch "))
|
|
76652
|
+
current.branch = field.slice(7);
|
|
76653
|
+
else if (field === "locked" || field.startsWith("locked "))
|
|
76654
|
+
current.locked = true;
|
|
76655
|
+
}
|
|
76656
|
+
return out;
|
|
76657
|
+
}
|
|
76658
|
+
function createdRow(cwd, plan, slice, target) {
|
|
76659
|
+
const ledger = readLedger(cwd);
|
|
76660
|
+
if (!ledger.ok)
|
|
76661
|
+
return null;
|
|
76662
|
+
const atPath = (row) => row.fanout === plan.name && row.slice === slice.id && typeof row.path === "string" && samePath(row.path, target);
|
|
76663
|
+
const rows = ledger.value.rows;
|
|
76664
|
+
const row = rows.findLast((candidate) => candidate.type === "fanout.worktree.created" && atPath(candidate));
|
|
76665
|
+
if (!row)
|
|
76666
|
+
return null;
|
|
76667
|
+
const removedSince = rows.some((candidate) => candidate.seq > row.seq && candidate.type === "fanout.worktree.released" && candidate.outcome === "removed" && atPath(candidate));
|
|
76668
|
+
if (removedSince)
|
|
76669
|
+
return null;
|
|
76670
|
+
const admin = adminDir(target);
|
|
76671
|
+
if (!admin)
|
|
76672
|
+
return null;
|
|
76673
|
+
if (typeof row.adminToken === "string")
|
|
76674
|
+
return row.adminToken === admin.token && row.adminId === admin.id && row.adminIno === admin.ino ? { row, exact: true } : null;
|
|
76675
|
+
const made = admin.birthMs ?? admin.mtimeMs;
|
|
76676
|
+
if (made + CLOCK_SLACK_MS < Date.parse(row.at))
|
|
76677
|
+
return null;
|
|
76678
|
+
return { row, exact: false };
|
|
76679
|
+
}
|
|
76680
|
+
function adminDir(worktree) {
|
|
76681
|
+
const dir = gitRun4(worktree, ["rev-parse", "--absolute-git-dir"]);
|
|
76682
|
+
if (!dir.ok)
|
|
76683
|
+
return null;
|
|
76684
|
+
const file = dir.stdout.trim();
|
|
76685
|
+
let token = null;
|
|
76686
|
+
try {
|
|
76687
|
+
token = fs24.readFileSync(path44.join(file, TOKEN_FILE), "utf8").trim() || null;
|
|
76688
|
+
} catch {
|
|
76689
|
+
token = null;
|
|
76690
|
+
}
|
|
76691
|
+
try {
|
|
76692
|
+
const stat = worktreeDeps.stat(file);
|
|
76693
|
+
return {
|
|
76694
|
+
path: file,
|
|
76695
|
+
id: path44.basename(file),
|
|
76696
|
+
ino: stat.ino.toString(),
|
|
76697
|
+
token,
|
|
76698
|
+
birthMs: stat.birthtimeNs > 0n ? Number(stat.birthtimeMs) : null,
|
|
76699
|
+
mtimeMs: Number(stat.mtimeMs)
|
|
76700
|
+
};
|
|
76701
|
+
} catch {
|
|
76702
|
+
return null;
|
|
76703
|
+
}
|
|
76704
|
+
}
|
|
76705
|
+
function markAdminDir(worktree) {
|
|
76706
|
+
const admin = adminDir(worktree);
|
|
76707
|
+
if (!admin)
|
|
76708
|
+
return null;
|
|
76709
|
+
const token = randomBytes4(16).toString("hex");
|
|
76710
|
+
try {
|
|
76711
|
+
fs24.writeFileSync(path44.join(admin.path, TOKEN_FILE), `${token}
|
|
76712
|
+
`);
|
|
76713
|
+
} catch {
|
|
76714
|
+
return null;
|
|
76715
|
+
}
|
|
76716
|
+
return { ...admin, token };
|
|
76717
|
+
}
|
|
76718
|
+
function findSlice(plan, id) {
|
|
76719
|
+
const slice = plan.slices.find((candidate) => candidate.id === id);
|
|
76720
|
+
return slice ? { ok: true, data: slice } : fanoutFail("invalid_input", `${id}: not a slice of fanout ${plan.name}`, `slices: ${plan.slices.map((candidate) => candidate.id).join(", ")}`);
|
|
76721
|
+
}
|
|
76722
|
+
function excludeScratch(cwd) {
|
|
76723
|
+
const common = gitCommonDir(cwd);
|
|
76724
|
+
if (!common)
|
|
76725
|
+
return;
|
|
76726
|
+
const file = path44.join(common, "info", "exclude");
|
|
76727
|
+
let text = "";
|
|
76728
|
+
try {
|
|
76729
|
+
text = fs24.readFileSync(file, "utf8");
|
|
76730
|
+
} catch {
|
|
76731
|
+
text = "";
|
|
76732
|
+
}
|
|
76733
|
+
if (text.split(/\r?\n/u).includes(EXCLUDE_LINE))
|
|
76734
|
+
return;
|
|
76735
|
+
fs24.mkdirSync(path44.dirname(file), { recursive: true });
|
|
76736
|
+
fs24.appendFileSync(file, `${text && !text.endsWith(`
|
|
76737
|
+
`) ? `
|
|
76738
|
+
` : ""}# workit fanout: per-worker scratch dirs
|
|
76739
|
+
${EXCLUDE_LINE}
|
|
76740
|
+
`);
|
|
76741
|
+
}
|
|
76742
|
+
function ensureScratch(worktree) {
|
|
76743
|
+
const scratch = path44.join(worktree, SCRATCH_DIR);
|
|
76744
|
+
fs24.mkdirSync(scratch, { recursive: true });
|
|
76745
|
+
return scratch;
|
|
76746
|
+
}
|
|
76747
|
+
function createSliceWorktree(cwd, plan, input) {
|
|
76748
|
+
const found = findSlice(plan, input.slice);
|
|
76749
|
+
if (!found.ok)
|
|
76750
|
+
return found;
|
|
76751
|
+
const slice = found.data;
|
|
76752
|
+
if (!slice.worktree)
|
|
76753
|
+
return fanoutFail("invalid_input", `slice ${slice.id} has no worktree path`, "re-plan it with workit fanout plan");
|
|
76754
|
+
const target = slicePath(cwd, slice);
|
|
76755
|
+
const worktrees = listWorktrees(cwd);
|
|
76756
|
+
const localRef = `refs/heads/${slice.branch}`;
|
|
76757
|
+
const existing = worktrees.find((entry) => samePath(entry.path, target));
|
|
76758
|
+
const base = {
|
|
76759
|
+
fanout: plan.name,
|
|
76760
|
+
slice: slice.id,
|
|
76761
|
+
branch: slice.branch,
|
|
76762
|
+
base: slice.base,
|
|
76763
|
+
path: target
|
|
76764
|
+
};
|
|
76765
|
+
if (existing) {
|
|
76766
|
+
if (existing.branch !== null && existing.branch !== localRef)
|
|
76767
|
+
return fanoutFail("blocked", `${target} is a worktree on ${existing.branch.replace(/^refs\/heads\//u, "")}, not ${slice.branch}`, `release it first, or set slice ${slice.id}'s worktree to another path`);
|
|
76768
|
+
if (!createdRow(cwd, plan, slice, target))
|
|
76769
|
+
return fanoutFail("blocked", `${target} is a worktree that workit fanout worktree create did not make for slice ${slice.id}`, `remove it yourself (git worktree remove ${target}) once you know what it holds`);
|
|
76770
|
+
excludeScratch(cwd);
|
|
76771
|
+
return {
|
|
76772
|
+
ok: true,
|
|
76773
|
+
data: {
|
|
76774
|
+
...base,
|
|
76775
|
+
scratch: ensureScratch(target),
|
|
76776
|
+
head: existing.head ?? "",
|
|
76777
|
+
mode: resolveRef(cwd, localRef) ? "resume" : "new",
|
|
76778
|
+
created: false,
|
|
76779
|
+
notes: ["the worktree already exists; nothing was changed"]
|
|
76780
|
+
}
|
|
76781
|
+
};
|
|
76782
|
+
}
|
|
76783
|
+
const elsewhere = worktrees.find((entry) => entry.branch === localRef);
|
|
76784
|
+
if (elsewhere)
|
|
76785
|
+
return fanoutFail("blocked", `${slice.branch} is checked out at ${elsewhere.path}: never two live workers on one branch`, `stop that worker, then workit fanout worktree release ${slice.id} (or git worktree remove ${elsewhere.path})`);
|
|
76786
|
+
let dirExisted = false;
|
|
76787
|
+
try {
|
|
76788
|
+
const stat = fs24.statSync(target);
|
|
76789
|
+
if (!stat.isDirectory() || fs24.readdirSync(target).length > 0)
|
|
76790
|
+
return fanoutFail("blocked", `${target} exists and is ${stat.isDirectory() ? "not empty" : "not a directory"} (not a worktree of this repository)`, `move it away, or set slice ${slice.id}'s worktree to another path`);
|
|
76791
|
+
dirExisted = true;
|
|
76792
|
+
} catch (error) {
|
|
76793
|
+
if (error.code !== "ENOENT")
|
|
76794
|
+
return fanoutFail("failed", `cannot read ${target}: ${error.message}`);
|
|
76795
|
+
}
|
|
76796
|
+
const notes = [];
|
|
76797
|
+
let mode = "resume";
|
|
76798
|
+
if (resolveRef(cwd, localRef)) {
|
|
76799
|
+
const added = gitRun4(cwd, ["worktree", "add", target, slice.branch], SLOW);
|
|
76800
|
+
if (!added.ok)
|
|
76801
|
+
return worktreeFailed(added.stderr);
|
|
76802
|
+
} else {
|
|
76803
|
+
const remote = pushRemoteName(cwd) ?? "origin";
|
|
76804
|
+
const remoteRef = `refs/remotes/${remote}/${slice.branch}`;
|
|
76805
|
+
if (resolveRef(cwd, remoteRef)) {
|
|
76806
|
+
const added = gitRun4(cwd, ["worktree", "add", "-b", slice.branch, target, remoteRef], SLOW);
|
|
76807
|
+
if (!added.ok)
|
|
76808
|
+
return worktreeFailed(added.stderr);
|
|
76809
|
+
notes.push(`resumed ${slice.branch} from ${remote}/${slice.branch}`);
|
|
76810
|
+
} else {
|
|
76811
|
+
mode = "new";
|
|
76812
|
+
const parent = plan.slices.find((other) => other.branch === slice.base);
|
|
76813
|
+
const from = slice.base === plan.trunk ? trunkRef(cwd, plan.trunk) : parent ? branchRef(cwd, parent.branch) : branchRef(cwd, slice.base);
|
|
76814
|
+
if (!from)
|
|
76815
|
+
return fanoutFail("not_found", `base ${slice.base} of slice ${slice.id} does not resolve here`, parent ? `start slice ${parent.id} first (its branch ${parent.branch} is the base)` : `git fetch origin ${slice.base}`);
|
|
76816
|
+
const added = gitRun4(cwd, ["worktree", "add", "--detach", target, from], SLOW);
|
|
76817
|
+
if (!added.ok)
|
|
76818
|
+
return worktreeFailed(added.stderr);
|
|
76819
|
+
const branched = gitBranch(target, { name: slice.branch, base: slice.base });
|
|
76820
|
+
if (!branched.ok) {
|
|
76821
|
+
gitRun4(cwd, ["worktree", "remove", target], SLOW);
|
|
76822
|
+
return fanoutFail(branched.code, `workit git branch ${slice.branch} refused: ${branched.error}`, branched.unblock ?? `fix the branch name in the plan, then workit fanout worktree create ${slice.id}`);
|
|
76823
|
+
}
|
|
76824
|
+
notes.push(...branched.data.notes);
|
|
76825
|
+
}
|
|
76826
|
+
}
|
|
76827
|
+
excludeScratch(cwd);
|
|
76828
|
+
const scratch = ensureScratch(target);
|
|
76829
|
+
const head = gitRun4(target, ["rev-parse", "HEAD"]).stdout.trim();
|
|
76830
|
+
const baseSha = mode === "new" ? head : null;
|
|
76831
|
+
const admin = markAdminDir(target);
|
|
76832
|
+
appendObserved(cwd, {
|
|
76833
|
+
type: "fanout.worktree.created",
|
|
76834
|
+
actor: input.actor,
|
|
76835
|
+
fanout: plan.name,
|
|
76836
|
+
slice: slice.id,
|
|
76837
|
+
branch: slice.branch,
|
|
76838
|
+
path: target,
|
|
76839
|
+
scratch,
|
|
76840
|
+
mode,
|
|
76841
|
+
head,
|
|
76842
|
+
dirExisted,
|
|
76843
|
+
planCreatedAt: plan.createdAt,
|
|
76844
|
+
sliceHash: slice.hash,
|
|
76845
|
+
...admin ? { adminId: admin.id, adminIno: admin.ino, adminToken: admin.token } : {},
|
|
76846
|
+
...baseSha ? { baseSha } : {}
|
|
76847
|
+
});
|
|
76848
|
+
return { ok: true, data: { ...base, scratch, head, mode, created: true, notes } };
|
|
76849
|
+
}
|
|
76850
|
+
function releaseSliceWorktree(cwd, plan, input) {
|
|
76851
|
+
const found = findSlice(plan, input.slice);
|
|
76852
|
+
if (!found.ok)
|
|
76853
|
+
return found;
|
|
76854
|
+
const slice = found.data;
|
|
76855
|
+
const target = slicePath(cwd, slice);
|
|
76856
|
+
const worktrees = listWorktrees(cwd);
|
|
76857
|
+
const index = worktrees.findIndex((entry) => samePath(entry.path, target));
|
|
76858
|
+
const out = {
|
|
76859
|
+
fanout: plan.name,
|
|
76860
|
+
slice: slice.id,
|
|
76861
|
+
branch: slice.branch,
|
|
76862
|
+
path: target,
|
|
76863
|
+
head: null,
|
|
76864
|
+
dirty: [],
|
|
76865
|
+
forced: input.force,
|
|
76866
|
+
removed: false,
|
|
76867
|
+
unpushed: null,
|
|
76868
|
+
notes: []
|
|
76869
|
+
};
|
|
76870
|
+
if (index < 0) {
|
|
76871
|
+
if (fs24.existsSync(target))
|
|
76872
|
+
return fanoutFail("blocked", `${target} is not a worktree of this repository; nothing was removed`, "remove it yourself once you know what it holds");
|
|
76873
|
+
out.notes.push(`no worktree at ${target}; nothing to release`);
|
|
76874
|
+
return { ok: true, data: out };
|
|
76875
|
+
}
|
|
76876
|
+
const entry = worktrees[index];
|
|
76877
|
+
if (index === 0)
|
|
76878
|
+
return fanoutFail("blocked", `${target} is the main checkout; it is never released`);
|
|
76879
|
+
if (inside2(cwd, entry.path))
|
|
76880
|
+
return fanoutFail("blocked", `this command runs inside ${entry.path}`, `run it from the main checkout: workit --cwd <main checkout> fanout worktree release ${slice.id}`);
|
|
76881
|
+
if (entry.branch !== null && entry.branch !== `refs/heads/${slice.branch}`)
|
|
76882
|
+
return fanoutFail("blocked", `${entry.path} is on ${entry.branch.replace(/^refs\/heads\//u, "")}, not slice ${slice.id}'s ${slice.branch}; nothing was removed`);
|
|
76883
|
+
const owned = createdRow(cwd, plan, slice, entry.path);
|
|
76884
|
+
if (!owned)
|
|
76885
|
+
return fanoutFail("blocked", `${entry.path} was not made by workit fanout worktree create for slice ${slice.id} (no ledger row); nothing was removed`, `remove it yourself (git worktree remove ${entry.path}) once you know what it holds`);
|
|
76886
|
+
if (input.force && !owned.exact)
|
|
76887
|
+
return fanoutFail("blocked", `${entry.path} was created before workit recorded worktree identity, so it cannot be told from a worktree someone re-added there; --force is refused and nothing was removed`, `release it without --force, or remove it yourself (git worktree remove ${entry.path}) once you know what it holds`);
|
|
76888
|
+
const made = owned.row;
|
|
76889
|
+
out.head = entry.head;
|
|
76890
|
+
const status = gitRun4(entry.path, [
|
|
76891
|
+
"status",
|
|
76892
|
+
"--porcelain=v1",
|
|
76893
|
+
"-z",
|
|
76894
|
+
"--untracked-files=all",
|
|
76895
|
+
"--ignore-submodules=none"
|
|
76896
|
+
]);
|
|
76897
|
+
if (!status.ok)
|
|
76898
|
+
return fanoutFail("failed", `git status in ${entry.path} failed: ${status.stderr.trim().split(`
|
|
76899
|
+
`)[0]}`, "nothing was removed; inspect the worktree by hand");
|
|
76900
|
+
const entries = porcelainEntries(status.stdout);
|
|
76901
|
+
const inScratch = (line) => line.slice(3).startsWith(`${SCRATCH_DIR}/`);
|
|
76902
|
+
out.dirty = entries.filter((line) => !inScratch(line));
|
|
76903
|
+
out.unpushed = unpushedCount(cwd, slice.branch);
|
|
76904
|
+
const refused = out.dirty.length > 0 && !input.force;
|
|
76905
|
+
const recorded = appendObserved(cwd, {
|
|
76906
|
+
type: "fanout.worktree.released",
|
|
76907
|
+
actor: input.actor,
|
|
76908
|
+
fanout: plan.name,
|
|
76909
|
+
slice: slice.id,
|
|
76910
|
+
branch: slice.branch,
|
|
76911
|
+
path: entry.path,
|
|
76912
|
+
head: entry.head,
|
|
76913
|
+
uncommittedCount: out.dirty.length,
|
|
76914
|
+
uncommitted: rowSample(out.dirty),
|
|
76915
|
+
forced: input.force,
|
|
76916
|
+
outcome: refused ? "refused" : "removed"
|
|
76917
|
+
});
|
|
76918
|
+
if (!recorded.ok)
|
|
76919
|
+
return fanoutFail("unavailable", `cannot record the worktree state in the ledger (${recorded.error}); nothing was removed`);
|
|
76920
|
+
if (refused)
|
|
76921
|
+
return fanoutFail("blocked", `${entry.path} has ${out.dirty.length} uncommitted change${out.dirty.length === 1 ? "" : "s"} (${out.dirty.slice(0, 3).join(", ")}${out.dirty.length > 3 ? ", …" : ""}); recorded in the ledger, nothing removed`, `commit them on ${slice.branch} (workit git commit), or pass --force to drop them`, { ...out });
|
|
76922
|
+
const args = ["worktree", "remove", ...input.force ? ["--force"] : [], entry.path];
|
|
76923
|
+
let removed = gitRun4(cwd, args, SLOW);
|
|
76924
|
+
if (!removed.ok && entries.some(inScratch)) {
|
|
76925
|
+
removeScratch(entry.path);
|
|
76926
|
+
removed = gitRun4(cwd, args, SLOW);
|
|
76927
|
+
}
|
|
76928
|
+
if (!removed.ok)
|
|
76929
|
+
return fanoutFail("failed", `git worktree remove failed: ${removed.stderr.trim().split(`
|
|
76930
|
+
`)[0]}`, entry.locked ? `git worktree unlock ${entry.path}, then release again` : undefined, { ...out });
|
|
76931
|
+
out.removed = true;
|
|
76932
|
+
if (made.dirExisted === true)
|
|
76933
|
+
fs24.mkdirSync(entry.path, { recursive: true });
|
|
76934
|
+
if (out.unpushed)
|
|
76935
|
+
out.notes.push(`${slice.branch} is kept with ${out.unpushed} commit(s) not on the remote`);
|
|
76936
|
+
return { ok: true, data: out };
|
|
76937
|
+
}
|
|
76938
|
+
function porcelainEntries(stdout) {
|
|
76939
|
+
const fields = nulList2(stdout);
|
|
76940
|
+
const out = [];
|
|
76941
|
+
for (let index = 0;index < fields.length; index += 1) {
|
|
76942
|
+
const entry = fields[index];
|
|
76943
|
+
out.push(entry);
|
|
76944
|
+
if (entry[0] === "R" || entry[0] === "C")
|
|
76945
|
+
index += 1;
|
|
76946
|
+
}
|
|
76947
|
+
return out;
|
|
76948
|
+
}
|
|
76949
|
+
function unpushedCount(cwd, branch) {
|
|
76950
|
+
const remote = pushRemoteName(cwd) ?? "origin";
|
|
76951
|
+
const local = resolveRef(cwd, `refs/heads/${branch}`);
|
|
76952
|
+
if (!local)
|
|
76953
|
+
return null;
|
|
76954
|
+
const tracking = resolveRef(cwd, `refs/remotes/${remote}/${branch}`);
|
|
76955
|
+
if (!tracking)
|
|
76956
|
+
return null;
|
|
76957
|
+
const count = gitRun4(cwd, ["rev-list", "--count", `${tracking}..${local}`]);
|
|
76958
|
+
return count.ok ? Number(count.stdout.trim()) : null;
|
|
76959
|
+
}
|
|
76960
|
+
function removeScratch(worktree) {
|
|
76961
|
+
const scratch = path44.join(worktree, SCRATCH_DIR);
|
|
76962
|
+
let stat;
|
|
76963
|
+
try {
|
|
76964
|
+
stat = fs24.lstatSync(scratch);
|
|
76965
|
+
} catch {
|
|
76966
|
+
return;
|
|
76967
|
+
}
|
|
76968
|
+
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
76969
|
+
fs24.unlinkSync(scratch);
|
|
76970
|
+
else
|
|
76971
|
+
fs24.rmSync(scratch, { recursive: true, force: true, maxRetries: 2 });
|
|
76972
|
+
}
|
|
76973
|
+
var SLOW, SCRATCH_DIR = ".workit-scratch", EXCLUDE_LINE, DIRTY_ROW_BYTES = 2000, canonical2 = (target) => {
|
|
76974
|
+
try {
|
|
76975
|
+
return fs24.realpathSync.native(target);
|
|
76976
|
+
} catch {
|
|
76977
|
+
return path44.resolve(target);
|
|
76978
|
+
}
|
|
76979
|
+
}, samePath = (a, b) => {
|
|
76980
|
+
const left = canonical2(a);
|
|
76981
|
+
const right = canonical2(b);
|
|
76982
|
+
return process.platform === "win32" || process.platform === "darwin" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
76983
|
+
}, inside2 = (child, parent) => {
|
|
76984
|
+
const relative = path44.relative(canonical2(parent), canonical2(child));
|
|
76985
|
+
return relative === "" || !relative.startsWith("..") && !path44.isAbsolute(relative);
|
|
76986
|
+
}, slicePath = (cwd, slice) => path44.isAbsolute(slice.worktree) ? slice.worktree : path44.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, TOKEN_FILE = "workit-fanout-token", worktreeDeps, worktreeFailed = (stderr) => fanoutFail("failed", `git worktree add failed: ${stderr.trim().split(`
|
|
76987
|
+
`)[0] || "unknown error"}`);
|
|
76988
|
+
var init_fanout_worktree = __esm(() => {
|
|
76989
|
+
init_ops();
|
|
76990
|
+
init_rev();
|
|
76991
|
+
init_ledger();
|
|
76992
|
+
init_fanout();
|
|
76993
|
+
SLOW = GIT_TIMEOUTS.worktree;
|
|
76994
|
+
EXCLUDE_LINE = `/${SCRATCH_DIR}/`;
|
|
76995
|
+
worktreeDeps = {
|
|
76996
|
+
stat: (file) => fs24.statSync(file, { bigint: true })
|
|
76997
|
+
};
|
|
76998
|
+
});
|
|
76999
|
+
|
|
77000
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout-brief.ts
|
|
77001
|
+
import { createHash as createHash17 } from "node:crypto";
|
|
77002
|
+
function workerSession(lead, slice, attempt) {
|
|
77003
|
+
const tail = attempt === null ? "" : `+try${attempt}`;
|
|
77004
|
+
const full = `${lead}-w-${slice}${tail}`;
|
|
77005
|
+
if (full.length <= SESSION_MAX)
|
|
77006
|
+
return full;
|
|
77007
|
+
const digest = createHash17("sha256").update(slice).digest("hex").slice(0, 12);
|
|
77008
|
+
return `${lead.slice(0, SESSION_MAX - 3 - digest.length - tail.length)}-w-${digest}${tail}`;
|
|
77009
|
+
}
|
|
77010
|
+
function renderBrief(cwd, plan, input) {
|
|
77011
|
+
const found = findSlice(plan, input.slice);
|
|
77012
|
+
if (!found.ok)
|
|
77013
|
+
return found;
|
|
77014
|
+
const slice = found.data;
|
|
77015
|
+
const ledger = readLedger(cwd);
|
|
77016
|
+
if (!ledger.ok)
|
|
77017
|
+
return fanoutFail(ledger.code, ledger.error, ledger.unblock);
|
|
77018
|
+
if (!input.actor.session)
|
|
77019
|
+
return fanoutFail("blocked", "WORKIT_SESSION_ID is not set: worker session ids derive from the lead's", "export WORKIT_SESSION_ID=<your session id>, then render the brief again");
|
|
77020
|
+
const branch = input.attempt === null ? slice.branch : `${slice.branch}-try${input.attempt}`;
|
|
77021
|
+
const taken = plan.slices.find((other) => other.id !== slice.id && other.branch === branch);
|
|
77022
|
+
if (taken)
|
|
77023
|
+
return fanoutFail("invalid_input", `attempt branch ${branch} is slice ${taken.id}'s branch`, "pick another --attempt number, or rename one of the branches in the plan");
|
|
77024
|
+
const head = branchRef(cwd, branch);
|
|
77025
|
+
const mode = input.mode ?? (head ? "resume" : "new");
|
|
77026
|
+
if (mode === "resume" && !head)
|
|
77027
|
+
return fanoutFail("not_found", `MODE resume needs the branch ${branch}, which does not exist here`, `workit fanout brief ${slice.id} --mode new`);
|
|
77028
|
+
const planned = `${slicePath(cwd, slice)}${input.attempt === null ? "" : `+try${input.attempt}`}`;
|
|
77029
|
+
const exists = listWorktrees(cwd).some((entry) => samePath(entry.path, planned));
|
|
77030
|
+
excludeScratch(cwd);
|
|
77031
|
+
const scratch = exists ? `${planned}/${SCRATCH_DIR}` : SCRATCH_DIR;
|
|
77032
|
+
const scratchLine = exists ? `SCRATCH: ${scratch} (yours alone; temp files go here, never a shared path)` : `SCRATCH: ${SCRATCH_DIR}/ at your worktree root (mkdir -p it; git ignores it; never a shared path)`;
|
|
77033
|
+
const standing = activeStanding(ledger.value.rows, plan.name, planLeads(plan)).map((row) => ({
|
|
77034
|
+
id: row.id,
|
|
77035
|
+
what: String(row.what)
|
|
77036
|
+
}));
|
|
77037
|
+
const session = workerSession(sessionSafe(input.actor.session), slice.id, input.attempt);
|
|
77038
|
+
const fanIn = plan.fanIn === "integration" ? [
|
|
77039
|
+
`FAN-IN: integration branch ${plan.trunk}. Before you report, merge its tip into your branch`,
|
|
77040
|
+
` (\`git merge --no-edit ${plan.trunk}\`), resolve conflicts inside SCOPE and re-run VERIFY, so the`,
|
|
77041
|
+
" lead's merge is a fast-forward. No other merge, and no rebase, retarget or force-push."
|
|
77042
|
+
] : [
|
|
77043
|
+
"FAN-IN: one PR per slice. Never rebase, retarget, merge or force-push: the lead owns topology."
|
|
77044
|
+
];
|
|
77045
|
+
const lines = [
|
|
77046
|
+
`MODE: ${mode}${input.attempt === null ? "" : ` (race attempt ${input.attempt}: the lead keeps the attempt with the best verdict)`}`,
|
|
77047
|
+
`GOAL: ${slice.goal}`,
|
|
77048
|
+
`SCOPE: ${slice.scope.join(", ")}`,
|
|
77049
|
+
...slice.owns.length ? [` owns: ${slice.owns.join(", ")}`] : [],
|
|
77050
|
+
` branch: ${branch} base: ${slice.base}`,
|
|
77051
|
+
`CONTEXT: ${slice.context ?? "none given: read the files in SCOPE and the neighbours they import"}`,
|
|
77052
|
+
...mode === "resume" && head ? [
|
|
77053
|
+
` resume: continue ${branch} from ${head.slice(0, 12)}; read \`git log ${slice.base}..${branch}\` and \`workit ledger list --branch ${branch}\` (the last report, verdicts, rulings)`
|
|
77054
|
+
] : [],
|
|
77055
|
+
"ACCEPTANCE:",
|
|
77056
|
+
...slice.acceptance.map((line) => ` - ${line}`),
|
|
77057
|
+
`VERIFY: ${slice.verify.join("; ")}`,
|
|
77058
|
+
`TIER: ${slice.tier}`,
|
|
77059
|
+
`TIMEBOX: ${slice.timebox ?? DEFAULT_TIMEBOX}; past it without a new commit you will be replaced`,
|
|
77060
|
+
scratchLine,
|
|
77061
|
+
`FORBIDDEN: ${slice.forbidden.join("; ")}`,
|
|
77062
|
+
...fanIn,
|
|
77063
|
+
...REPORT,
|
|
77064
|
+
"STANDING:",
|
|
77065
|
+
...standing.length ? standing.map((order) => ` - ${order.what}`) : [" - none recorded beyond this brief"],
|
|
77066
|
+
` export WORKIT_SESSION_ID=${session}`,
|
|
77067
|
+
...rules(mode, branch, slice.base)
|
|
77068
|
+
];
|
|
77069
|
+
return {
|
|
77070
|
+
ok: true,
|
|
77071
|
+
data: {
|
|
77072
|
+
fanout: plan.name,
|
|
77073
|
+
slice: slice.id,
|
|
77074
|
+
mode,
|
|
77075
|
+
attempt: input.attempt,
|
|
77076
|
+
branch,
|
|
77077
|
+
base: slice.base,
|
|
77078
|
+
tier: slice.tier,
|
|
77079
|
+
scratch,
|
|
77080
|
+
session,
|
|
77081
|
+
standing,
|
|
77082
|
+
text: `${lines.join(`
|
|
77083
|
+
`)}
|
|
77084
|
+
`
|
|
77085
|
+
}
|
|
77086
|
+
};
|
|
77087
|
+
}
|
|
77088
|
+
var DEFAULT_TIMEBOX = "30 minutes", REPORT, rules = (mode, branch, base) => [
|
|
77089
|
+
"RULES:",
|
|
77090
|
+
mode === "new" ? ` 1. First command: \`workit git branch ${branch} --base ${base}\` (your worktree may start on another name).` : ` 1. First command: \`git switch ${branch}\`, then continue from its head.`,
|
|
77091
|
+
' 2. Decide ambiguities yourself: `workit ledger ruling "<what>" --why "<why>" --cost-if-wrong "<cost>"`; stop only for an irreversible, security-sensitive or out-of-worktree action.',
|
|
77092
|
+
" 3. Commit with `workit git commit -m <msg> -- <paths in SCOPE>`; push only if this brief says so.",
|
|
77093
|
+
" 4. Never record a verdict on your own work."
|
|
77094
|
+
], sessionSafe = (text) => text.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-"), SESSION_MAX = 128;
|
|
77095
|
+
var init_fanout_brief = __esm(() => {
|
|
77096
|
+
init_ledger();
|
|
77097
|
+
init_fanout();
|
|
77098
|
+
init_fanout_worktree();
|
|
77099
|
+
REPORT = [
|
|
77100
|
+
"REPORT: branch, head SHA, files changed, each VERIFY command with its exit code,",
|
|
77101
|
+
" each ACCEPTANCE line met / not met, rulings you made (`workit ledger ruling`),",
|
|
77102
|
+
" anything out of scope as a follow-up, not a diff."
|
|
77103
|
+
];
|
|
77104
|
+
});
|
|
77105
|
+
|
|
77106
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout-landed.ts
|
|
76456
77107
|
import { spawnSync as spawnSync22 } from "node:child_process";
|
|
76457
77108
|
function forgeBreaker(resolved) {
|
|
76458
77109
|
if (!resolved)
|
|
@@ -76562,9 +77213,20 @@ function trunkPatchIds(ctx, fork) {
|
|
|
76562
77213
|
cache.set(fork, ids);
|
|
76563
77214
|
return ids;
|
|
76564
77215
|
}
|
|
77216
|
+
function inSliceRun(row, slice) {
|
|
77217
|
+
if (row.type === "fanout.worktree.created" && typeof row.sliceHash === "string" && slice.hash)
|
|
77218
|
+
return row.sliceHash === slice.hash;
|
|
77219
|
+
const since = Date.parse(slice.hashSince);
|
|
77220
|
+
return Number.isNaN(since) || Date.parse(String(row.at)) >= since;
|
|
77221
|
+
}
|
|
76565
77222
|
function linked(rows, input, slice, head) {
|
|
76566
77223
|
const since = input.since ? Date.parse(input.since) : Number.NaN;
|
|
76567
|
-
|
|
77224
|
+
const recent = (row) => Number.isNaN(since) || Date.parse(String(row.at)) >= since;
|
|
77225
|
+
return rows.some((row) => {
|
|
77226
|
+
if (row.type === "fanout.worktree.created" && row.fanout === input.fanout && row.slice === slice.id)
|
|
77227
|
+
return typeof row.sliceHash === "string" && input.sliceHash ? row.sliceHash === input.sliceHash : recent(row);
|
|
77228
|
+
return recent(row) && head !== null && row.branch === slice.branch && row.head === head;
|
|
77229
|
+
});
|
|
76568
77230
|
}
|
|
76569
77231
|
function detectLanding(ctx, slice, input) {
|
|
76570
77232
|
const out = {
|
|
@@ -76637,7 +77299,7 @@ function sliceStart(cwd, rows, fanout, slice) {
|
|
|
76637
77299
|
const reflog = branchCreatedAt(cwd, slice.branch);
|
|
76638
77300
|
if (reflog)
|
|
76639
77301
|
return reflog;
|
|
76640
|
-
const row = rows.findLast((candidate) => candidate.type === "fanout.worktree.created" && candidate.fanout === fanout && candidate.slice === slice.id && typeof candidate.baseSha === "string");
|
|
77302
|
+
const row = rows.findLast((candidate) => candidate.type === "fanout.worktree.created" && candidate.fanout === fanout && candidate.slice === slice.id && typeof candidate.baseSha === "string" && inSliceRun(candidate, slice));
|
|
76641
77303
|
return row?.baseSha ?? null;
|
|
76642
77304
|
}
|
|
76643
77305
|
var isAncestor2 = (cwd, a, b) => gitRun4(cwd, ["merge-base", "--is-ancestor", a, b]).ok, TRUNK_SCAN = 500;
|
|
@@ -76646,7 +77308,7 @@ var init_fanout_landed = __esm(() => {
|
|
|
76646
77308
|
init_fanout();
|
|
76647
77309
|
});
|
|
76648
77310
|
|
|
76649
|
-
// packages/workit-core/src/fanout-check.ts
|
|
77311
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout-check.ts
|
|
76650
77312
|
function mergeConflicts(cwd, a, b) {
|
|
76651
77313
|
const run = gitRun4(cwd, [
|
|
76652
77314
|
"merge-tree",
|
|
@@ -76786,7 +77448,8 @@ function sliceLandings(cwd, plan, trunk, tips, rows, forge) {
|
|
|
76786
77448
|
createdFrom: sliceStart(cwd, rows, plan.name, slice),
|
|
76787
77449
|
parentHead,
|
|
76788
77450
|
fanout: plan.name,
|
|
76789
|
-
since: plan.createdAt || null,
|
|
77451
|
+
since: slice.hashSince || plan.createdAt || null,
|
|
77452
|
+
sliceHash: slice.hash || null,
|
|
76790
77453
|
rows
|
|
76791
77454
|
}));
|
|
76792
77455
|
}
|
|
@@ -76903,7 +77566,7 @@ var init_fanout_check = __esm(() => {
|
|
|
76903
77566
|
init_fanout_landed();
|
|
76904
77567
|
});
|
|
76905
77568
|
|
|
76906
|
-
// packages/workit-core/src/fanout-status.ts
|
|
77569
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/fanout-status.ts
|
|
76907
77570
|
function timeboxMs(text) {
|
|
76908
77571
|
if (!text || /^\s*\d+(?:\.\d+)?\s*$/u.test(text))
|
|
76909
77572
|
return null;
|
|
@@ -76928,7 +77591,7 @@ function activity(cwd, plan, slice, rows) {
|
|
|
76928
77591
|
head,
|
|
76929
77592
|
commit,
|
|
76930
77593
|
last: newest(commit, reflog, ...ledger),
|
|
76931
|
-
worktreeCreated: rows.some((row) => row.type === "fanout.worktree.created" && row.fanout === plan.name && row.slice === slice.id)
|
|
77594
|
+
worktreeCreated: rows.some((row) => row.type === "fanout.worktree.created" && row.fanout === plan.name && row.slice === slice.id && inSliceRun(row, slice))
|
|
76932
77595
|
};
|
|
76933
77596
|
}
|
|
76934
77597
|
function fanoutStatus(cwd, plan, options) {
|
|
@@ -77038,7 +77701,7 @@ function fanoutStatus(cwd, plan, options) {
|
|
|
77038
77701
|
else if (row.branchExists)
|
|
77039
77702
|
row.state = "active";
|
|
77040
77703
|
else if (row.waitsFor.length === 0 || parent && row.waitsFor.length === 1 && row.waitsFor[0] === parent.id && out.get(parent.id)?.verdict.accepted) {
|
|
77041
|
-
const created = rows.some((candidate) => candidate.type === "fanout.worktree.created" && candidate.fanout === plan.name && candidate.slice === id);
|
|
77704
|
+
const created = rows.some((candidate) => candidate.type === "fanout.worktree.created" && candidate.fanout === plan.name && candidate.slice === id && inSliceRun(candidate, byId.get(id)));
|
|
77042
77705
|
row.state = created ? "active" : "not_started";
|
|
77043
77706
|
if (!created)
|
|
77044
77707
|
spawnable.push(id);
|
|
@@ -77090,340 +77753,6 @@ var init_fanout_status = __esm(() => {
|
|
|
77090
77753
|
DEFAULT_STUCK_MS = 30 * 60000;
|
|
77091
77754
|
});
|
|
77092
77755
|
|
|
77093
|
-
// packages/workit-core/src/fanout-worktree.ts
|
|
77094
|
-
import fs24 from "node:fs";
|
|
77095
|
-
import path44 from "node:path";
|
|
77096
|
-
function rowSample(lines) {
|
|
77097
|
-
const out = [];
|
|
77098
|
-
let bytes = 0;
|
|
77099
|
-
for (const line of lines) {
|
|
77100
|
-
const entry = line.slice(0, 200);
|
|
77101
|
-
bytes += Buffer.byteLength(entry) + 3;
|
|
77102
|
-
if (bytes > DIRTY_ROW_BYTES)
|
|
77103
|
-
break;
|
|
77104
|
-
out.push(entry);
|
|
77105
|
-
}
|
|
77106
|
-
return out;
|
|
77107
|
-
}
|
|
77108
|
-
function listWorktrees(cwd) {
|
|
77109
|
-
const out = [];
|
|
77110
|
-
let current = null;
|
|
77111
|
-
for (const field of gitRun4(cwd, ["worktree", "list", "--porcelain", "-z"]).stdout.split("\x00")) {
|
|
77112
|
-
if (field.startsWith("worktree ")) {
|
|
77113
|
-
current = { path: field.slice(9), head: null, branch: null, locked: false };
|
|
77114
|
-
out.push(current);
|
|
77115
|
-
} else if (!current)
|
|
77116
|
-
continue;
|
|
77117
|
-
else if (field.startsWith("HEAD "))
|
|
77118
|
-
current.head = field.slice(5);
|
|
77119
|
-
else if (field.startsWith("branch "))
|
|
77120
|
-
current.branch = field.slice(7);
|
|
77121
|
-
else if (field === "locked" || field.startsWith("locked "))
|
|
77122
|
-
current.locked = true;
|
|
77123
|
-
}
|
|
77124
|
-
return out;
|
|
77125
|
-
}
|
|
77126
|
-
function createdRow(cwd, plan, slice, target) {
|
|
77127
|
-
const ledger = readLedger(cwd);
|
|
77128
|
-
if (!ledger.ok)
|
|
77129
|
-
return null;
|
|
77130
|
-
const atPath = (row) => row.fanout === plan.name && row.slice === slice.id && typeof row.path === "string" && samePath(row.path, target);
|
|
77131
|
-
const rows = ledger.value.rows;
|
|
77132
|
-
const row = rows.findLast((candidate) => candidate.type === "fanout.worktree.created" && atPath(candidate));
|
|
77133
|
-
if (!row)
|
|
77134
|
-
return null;
|
|
77135
|
-
const removedSince = rows.some((candidate) => candidate.seq > row.seq && candidate.type === "fanout.worktree.released" && candidate.outcome === "removed" && atPath(candidate));
|
|
77136
|
-
if (removedSince)
|
|
77137
|
-
return null;
|
|
77138
|
-
const made = adminDirTime(target);
|
|
77139
|
-
if (made === null || made + CLOCK_SLACK_MS < Date.parse(row.at))
|
|
77140
|
-
return null;
|
|
77141
|
-
return row;
|
|
77142
|
-
}
|
|
77143
|
-
function adminDirTime(worktree) {
|
|
77144
|
-
const dir = gitRun4(worktree, ["rev-parse", "--absolute-git-dir"]);
|
|
77145
|
-
if (!dir.ok)
|
|
77146
|
-
return null;
|
|
77147
|
-
try {
|
|
77148
|
-
const stat = fs24.statSync(dir.stdout.trim());
|
|
77149
|
-
return stat.birthtimeMs > 0 ? stat.birthtimeMs : stat.mtimeMs;
|
|
77150
|
-
} catch {
|
|
77151
|
-
return null;
|
|
77152
|
-
}
|
|
77153
|
-
}
|
|
77154
|
-
function findSlice(plan, id) {
|
|
77155
|
-
const slice = plan.slices.find((candidate) => candidate.id === id);
|
|
77156
|
-
return slice ? { ok: true, data: slice } : fanoutFail("invalid_input", `${id}: not a slice of fanout ${plan.name}`, `slices: ${plan.slices.map((candidate) => candidate.id).join(", ")}`);
|
|
77157
|
-
}
|
|
77158
|
-
function excludeScratch(cwd) {
|
|
77159
|
-
const common = gitCommonDir(cwd);
|
|
77160
|
-
if (!common)
|
|
77161
|
-
return;
|
|
77162
|
-
const file = path44.join(common, "info", "exclude");
|
|
77163
|
-
let text = "";
|
|
77164
|
-
try {
|
|
77165
|
-
text = fs24.readFileSync(file, "utf8");
|
|
77166
|
-
} catch {
|
|
77167
|
-
text = "";
|
|
77168
|
-
}
|
|
77169
|
-
if (text.split(/\r?\n/u).includes(EXCLUDE_LINE))
|
|
77170
|
-
return;
|
|
77171
|
-
fs24.mkdirSync(path44.dirname(file), { recursive: true });
|
|
77172
|
-
fs24.appendFileSync(file, `${text && !text.endsWith(`
|
|
77173
|
-
`) ? `
|
|
77174
|
-
` : ""}# workit fanout: per-worker scratch dirs
|
|
77175
|
-
${EXCLUDE_LINE}
|
|
77176
|
-
`);
|
|
77177
|
-
}
|
|
77178
|
-
function ensureScratch(worktree) {
|
|
77179
|
-
const scratch = path44.join(worktree, SCRATCH_DIR);
|
|
77180
|
-
fs24.mkdirSync(scratch, { recursive: true });
|
|
77181
|
-
return scratch;
|
|
77182
|
-
}
|
|
77183
|
-
function createSliceWorktree(cwd, plan, input) {
|
|
77184
|
-
const found = findSlice(plan, input.slice);
|
|
77185
|
-
if (!found.ok)
|
|
77186
|
-
return found;
|
|
77187
|
-
const slice = found.data;
|
|
77188
|
-
if (!slice.worktree)
|
|
77189
|
-
return fanoutFail("invalid_input", `slice ${slice.id} has no worktree path`, "re-plan it with workit fanout plan");
|
|
77190
|
-
const target = slicePath(cwd, slice);
|
|
77191
|
-
const worktrees = listWorktrees(cwd);
|
|
77192
|
-
const localRef = `refs/heads/${slice.branch}`;
|
|
77193
|
-
const existing = worktrees.find((entry) => samePath(entry.path, target));
|
|
77194
|
-
const base = {
|
|
77195
|
-
fanout: plan.name,
|
|
77196
|
-
slice: slice.id,
|
|
77197
|
-
branch: slice.branch,
|
|
77198
|
-
base: slice.base,
|
|
77199
|
-
path: target
|
|
77200
|
-
};
|
|
77201
|
-
if (existing) {
|
|
77202
|
-
if (existing.branch !== null && existing.branch !== localRef)
|
|
77203
|
-
return fanoutFail("blocked", `${target} is a worktree on ${existing.branch.replace(/^refs\/heads\//u, "")}, not ${slice.branch}`, `release it first, or set slice ${slice.id}'s worktree to another path`);
|
|
77204
|
-
if (!createdRow(cwd, plan, slice, target))
|
|
77205
|
-
return fanoutFail("blocked", `${target} is a worktree that workit fanout worktree create did not make for slice ${slice.id}`, `remove it yourself (git worktree remove ${target}) once you know what it holds`);
|
|
77206
|
-
excludeScratch(cwd);
|
|
77207
|
-
return {
|
|
77208
|
-
ok: true,
|
|
77209
|
-
data: {
|
|
77210
|
-
...base,
|
|
77211
|
-
scratch: ensureScratch(target),
|
|
77212
|
-
head: existing.head ?? "",
|
|
77213
|
-
mode: resolveRef(cwd, localRef) ? "resume" : "new",
|
|
77214
|
-
created: false,
|
|
77215
|
-
notes: ["the worktree already exists; nothing was changed"]
|
|
77216
|
-
}
|
|
77217
|
-
};
|
|
77218
|
-
}
|
|
77219
|
-
const elsewhere = worktrees.find((entry) => entry.branch === localRef);
|
|
77220
|
-
if (elsewhere)
|
|
77221
|
-
return fanoutFail("blocked", `${slice.branch} is checked out at ${elsewhere.path}: never two live workers on one branch`, `stop that worker, then workit fanout worktree release ${slice.id} (or git worktree remove ${elsewhere.path})`);
|
|
77222
|
-
let dirExisted = false;
|
|
77223
|
-
try {
|
|
77224
|
-
const stat = fs24.statSync(target);
|
|
77225
|
-
if (!stat.isDirectory() || fs24.readdirSync(target).length > 0)
|
|
77226
|
-
return fanoutFail("blocked", `${target} exists and is ${stat.isDirectory() ? "not empty" : "not a directory"} (not a worktree of this repository)`, `move it away, or set slice ${slice.id}'s worktree to another path`);
|
|
77227
|
-
dirExisted = true;
|
|
77228
|
-
} catch (error) {
|
|
77229
|
-
if (error.code !== "ENOENT")
|
|
77230
|
-
return fanoutFail("failed", `cannot read ${target}: ${error.message}`);
|
|
77231
|
-
}
|
|
77232
|
-
const notes = [];
|
|
77233
|
-
let mode = "resume";
|
|
77234
|
-
if (resolveRef(cwd, localRef)) {
|
|
77235
|
-
const added = gitRun4(cwd, ["worktree", "add", target, slice.branch], SLOW);
|
|
77236
|
-
if (!added.ok)
|
|
77237
|
-
return worktreeFailed(added.stderr);
|
|
77238
|
-
} else {
|
|
77239
|
-
const remote = pushRemoteName(cwd) ?? "origin";
|
|
77240
|
-
const remoteRef = `refs/remotes/${remote}/${slice.branch}`;
|
|
77241
|
-
if (resolveRef(cwd, remoteRef)) {
|
|
77242
|
-
const added = gitRun4(cwd, ["worktree", "add", "-b", slice.branch, target, remoteRef], SLOW);
|
|
77243
|
-
if (!added.ok)
|
|
77244
|
-
return worktreeFailed(added.stderr);
|
|
77245
|
-
notes.push(`resumed ${slice.branch} from ${remote}/${slice.branch}`);
|
|
77246
|
-
} else {
|
|
77247
|
-
mode = "new";
|
|
77248
|
-
const parent = plan.slices.find((other) => other.branch === slice.base);
|
|
77249
|
-
const from = slice.base === plan.trunk ? trunkRef(cwd, plan.trunk) : parent ? branchRef(cwd, parent.branch) : branchRef(cwd, slice.base);
|
|
77250
|
-
if (!from)
|
|
77251
|
-
return fanoutFail("not_found", `base ${slice.base} of slice ${slice.id} does not resolve here`, parent ? `start slice ${parent.id} first (its branch ${parent.branch} is the base)` : `git fetch origin ${slice.base}`);
|
|
77252
|
-
const added = gitRun4(cwd, ["worktree", "add", "--detach", target, from], SLOW);
|
|
77253
|
-
if (!added.ok)
|
|
77254
|
-
return worktreeFailed(added.stderr);
|
|
77255
|
-
const branched = gitBranch(target, { name: slice.branch, base: slice.base });
|
|
77256
|
-
if (!branched.ok) {
|
|
77257
|
-
gitRun4(cwd, ["worktree", "remove", target], SLOW);
|
|
77258
|
-
return fanoutFail(branched.code, `workit git branch ${slice.branch} refused: ${branched.error}`, branched.unblock ?? `fix the branch name in the plan, then workit fanout worktree create ${slice.id}`);
|
|
77259
|
-
}
|
|
77260
|
-
notes.push(...branched.data.notes);
|
|
77261
|
-
}
|
|
77262
|
-
}
|
|
77263
|
-
excludeScratch(cwd);
|
|
77264
|
-
const scratch = ensureScratch(target);
|
|
77265
|
-
const head = gitRun4(target, ["rev-parse", "HEAD"]).stdout.trim();
|
|
77266
|
-
const baseSha = mode === "new" ? head : null;
|
|
77267
|
-
appendObserved(cwd, {
|
|
77268
|
-
type: "fanout.worktree.created",
|
|
77269
|
-
actor: input.actor,
|
|
77270
|
-
fanout: plan.name,
|
|
77271
|
-
slice: slice.id,
|
|
77272
|
-
branch: slice.branch,
|
|
77273
|
-
path: target,
|
|
77274
|
-
scratch,
|
|
77275
|
-
mode,
|
|
77276
|
-
head,
|
|
77277
|
-
dirExisted,
|
|
77278
|
-
planCreatedAt: plan.createdAt,
|
|
77279
|
-
...baseSha ? { baseSha } : {}
|
|
77280
|
-
});
|
|
77281
|
-
return { ok: true, data: { ...base, scratch, head, mode, created: true, notes } };
|
|
77282
|
-
}
|
|
77283
|
-
function releaseSliceWorktree(cwd, plan, input) {
|
|
77284
|
-
const found = findSlice(plan, input.slice);
|
|
77285
|
-
if (!found.ok)
|
|
77286
|
-
return found;
|
|
77287
|
-
const slice = found.data;
|
|
77288
|
-
const target = slicePath(cwd, slice);
|
|
77289
|
-
const worktrees = listWorktrees(cwd);
|
|
77290
|
-
const index = worktrees.findIndex((entry) => samePath(entry.path, target));
|
|
77291
|
-
const out = {
|
|
77292
|
-
fanout: plan.name,
|
|
77293
|
-
slice: slice.id,
|
|
77294
|
-
branch: slice.branch,
|
|
77295
|
-
path: target,
|
|
77296
|
-
head: null,
|
|
77297
|
-
dirty: [],
|
|
77298
|
-
forced: input.force,
|
|
77299
|
-
removed: false,
|
|
77300
|
-
unpushed: null,
|
|
77301
|
-
notes: []
|
|
77302
|
-
};
|
|
77303
|
-
if (index < 0) {
|
|
77304
|
-
if (fs24.existsSync(target))
|
|
77305
|
-
return fanoutFail("blocked", `${target} is not a worktree of this repository; nothing was removed`, "remove it yourself once you know what it holds");
|
|
77306
|
-
out.notes.push(`no worktree at ${target}; nothing to release`);
|
|
77307
|
-
return { ok: true, data: out };
|
|
77308
|
-
}
|
|
77309
|
-
const entry = worktrees[index];
|
|
77310
|
-
if (index === 0)
|
|
77311
|
-
return fanoutFail("blocked", `${target} is the main checkout; it is never released`);
|
|
77312
|
-
if (inside2(cwd, entry.path))
|
|
77313
|
-
return fanoutFail("blocked", `this command runs inside ${entry.path}`, `run it from the main checkout: workit --cwd <main checkout> fanout worktree release ${slice.id}`);
|
|
77314
|
-
if (entry.branch !== null && entry.branch !== `refs/heads/${slice.branch}`)
|
|
77315
|
-
return fanoutFail("blocked", `${entry.path} is on ${entry.branch.replace(/^refs\/heads\//u, "")}, not slice ${slice.id}'s ${slice.branch}; nothing was removed`);
|
|
77316
|
-
const made = createdRow(cwd, plan, slice, entry.path);
|
|
77317
|
-
if (!made)
|
|
77318
|
-
return fanoutFail("blocked", `${entry.path} was not made by workit fanout worktree create for slice ${slice.id} (no ledger row); nothing was removed`, `remove it yourself (git worktree remove ${entry.path}) once you know what it holds`);
|
|
77319
|
-
out.head = entry.head;
|
|
77320
|
-
const status = gitRun4(entry.path, [
|
|
77321
|
-
"status",
|
|
77322
|
-
"--porcelain=v1",
|
|
77323
|
-
"-z",
|
|
77324
|
-
"--untracked-files=all",
|
|
77325
|
-
"--ignore-submodules=none"
|
|
77326
|
-
]);
|
|
77327
|
-
if (!status.ok)
|
|
77328
|
-
return fanoutFail("failed", `git status in ${entry.path} failed: ${status.stderr.trim().split(`
|
|
77329
|
-
`)[0]}`, "nothing was removed; inspect the worktree by hand");
|
|
77330
|
-
const entries = porcelainEntries(status.stdout);
|
|
77331
|
-
const inScratch = (line) => line.slice(3).startsWith(`${SCRATCH_DIR}/`);
|
|
77332
|
-
out.dirty = entries.filter((line) => !inScratch(line));
|
|
77333
|
-
out.unpushed = unpushedCount(cwd, slice.branch);
|
|
77334
|
-
const refused = out.dirty.length > 0 && !input.force;
|
|
77335
|
-
const recorded = appendObserved(cwd, {
|
|
77336
|
-
type: "fanout.worktree.released",
|
|
77337
|
-
actor: input.actor,
|
|
77338
|
-
fanout: plan.name,
|
|
77339
|
-
slice: slice.id,
|
|
77340
|
-
branch: slice.branch,
|
|
77341
|
-
path: entry.path,
|
|
77342
|
-
head: entry.head,
|
|
77343
|
-
uncommittedCount: out.dirty.length,
|
|
77344
|
-
uncommitted: rowSample(out.dirty),
|
|
77345
|
-
forced: input.force,
|
|
77346
|
-
outcome: refused ? "refused" : "removed"
|
|
77347
|
-
});
|
|
77348
|
-
if (!recorded.ok)
|
|
77349
|
-
return fanoutFail("unavailable", `cannot record the worktree state in the ledger (${recorded.error}); nothing was removed`);
|
|
77350
|
-
if (refused)
|
|
77351
|
-
return fanoutFail("blocked", `${entry.path} has ${out.dirty.length} uncommitted change${out.dirty.length === 1 ? "" : "s"} (${out.dirty.slice(0, 3).join(", ")}${out.dirty.length > 3 ? ", …" : ""}); recorded in the ledger, nothing removed`, `commit them on ${slice.branch} (workit git commit), or pass --force to drop them`, { ...out });
|
|
77352
|
-
const args = ["worktree", "remove", ...input.force ? ["--force"] : [], entry.path];
|
|
77353
|
-
let removed = gitRun4(cwd, args, SLOW);
|
|
77354
|
-
if (!removed.ok && entries.some(inScratch)) {
|
|
77355
|
-
removeScratch(entry.path);
|
|
77356
|
-
removed = gitRun4(cwd, args, SLOW);
|
|
77357
|
-
}
|
|
77358
|
-
if (!removed.ok)
|
|
77359
|
-
return fanoutFail("failed", `git worktree remove failed: ${removed.stderr.trim().split(`
|
|
77360
|
-
`)[0]}`, entry.locked ? `git worktree unlock ${entry.path}, then release again` : undefined, { ...out });
|
|
77361
|
-
out.removed = true;
|
|
77362
|
-
if (made.dirExisted === true)
|
|
77363
|
-
fs24.mkdirSync(entry.path, { recursive: true });
|
|
77364
|
-
if (out.unpushed)
|
|
77365
|
-
out.notes.push(`${slice.branch} is kept with ${out.unpushed} commit(s) not on the remote`);
|
|
77366
|
-
return { ok: true, data: out };
|
|
77367
|
-
}
|
|
77368
|
-
function porcelainEntries(stdout) {
|
|
77369
|
-
const fields = nulList2(stdout);
|
|
77370
|
-
const out = [];
|
|
77371
|
-
for (let index = 0;index < fields.length; index += 1) {
|
|
77372
|
-
const entry = fields[index];
|
|
77373
|
-
out.push(entry);
|
|
77374
|
-
if (entry[0] === "R" || entry[0] === "C")
|
|
77375
|
-
index += 1;
|
|
77376
|
-
}
|
|
77377
|
-
return out;
|
|
77378
|
-
}
|
|
77379
|
-
function unpushedCount(cwd, branch) {
|
|
77380
|
-
const remote = pushRemoteName(cwd) ?? "origin";
|
|
77381
|
-
const local = resolveRef(cwd, `refs/heads/${branch}`);
|
|
77382
|
-
if (!local)
|
|
77383
|
-
return null;
|
|
77384
|
-
const tracking = resolveRef(cwd, `refs/remotes/${remote}/${branch}`);
|
|
77385
|
-
if (!tracking)
|
|
77386
|
-
return null;
|
|
77387
|
-
const count = gitRun4(cwd, ["rev-list", "--count", `${tracking}..${local}`]);
|
|
77388
|
-
return count.ok ? Number(count.stdout.trim()) : null;
|
|
77389
|
-
}
|
|
77390
|
-
function removeScratch(worktree) {
|
|
77391
|
-
const scratch = path44.join(worktree, SCRATCH_DIR);
|
|
77392
|
-
let stat;
|
|
77393
|
-
try {
|
|
77394
|
-
stat = fs24.lstatSync(scratch);
|
|
77395
|
-
} catch {
|
|
77396
|
-
return;
|
|
77397
|
-
}
|
|
77398
|
-
if (stat.isSymbolicLink() || !stat.isDirectory())
|
|
77399
|
-
fs24.unlinkSync(scratch);
|
|
77400
|
-
else
|
|
77401
|
-
fs24.rmSync(scratch, { recursive: true, force: true, maxRetries: 2 });
|
|
77402
|
-
}
|
|
77403
|
-
var SLOW, SCRATCH_DIR = ".workit-scratch", EXCLUDE_LINE, DIRTY_ROW_BYTES = 2000, canonical2 = (target) => {
|
|
77404
|
-
try {
|
|
77405
|
-
return fs24.realpathSync.native(target);
|
|
77406
|
-
} catch {
|
|
77407
|
-
return path44.resolve(target);
|
|
77408
|
-
}
|
|
77409
|
-
}, samePath = (a, b) => {
|
|
77410
|
-
const left = canonical2(a);
|
|
77411
|
-
const right = canonical2(b);
|
|
77412
|
-
return process.platform === "win32" || process.platform === "darwin" ? left.toLowerCase() === right.toLowerCase() : left === right;
|
|
77413
|
-
}, inside2 = (child, parent) => {
|
|
77414
|
-
const relative = path44.relative(canonical2(parent), canonical2(child));
|
|
77415
|
-
return relative === "" || !relative.startsWith("..") && !path44.isAbsolute(relative);
|
|
77416
|
-
}, slicePath = (cwd, slice) => path44.isAbsolute(slice.worktree) ? slice.worktree : path44.resolve(mainCheckout(cwd), slice.worktree), CLOCK_SLACK_MS = 2000, worktreeFailed = (stderr) => fanoutFail("failed", `git worktree add failed: ${stderr.trim().split(`
|
|
77417
|
-
`)[0] || "unknown error"}`);
|
|
77418
|
-
var init_fanout_worktree = __esm(() => {
|
|
77419
|
-
init_ops();
|
|
77420
|
-
init_rev();
|
|
77421
|
-
init_ledger();
|
|
77422
|
-
init_fanout();
|
|
77423
|
-
SLOW = GIT_TIMEOUTS.worktree;
|
|
77424
|
-
EXCLUDE_LINE = `/${SCRATCH_DIR}/`;
|
|
77425
|
-
});
|
|
77426
|
-
|
|
77427
77756
|
// packages/workit-cli/src/verbs/fanout.ts
|
|
77428
77757
|
var exports_fanout = {};
|
|
77429
77758
|
__export(exports_fanout, {
|
|
@@ -77478,7 +77807,12 @@ function resolveTrunk(io, raw, flag, track) {
|
|
|
77478
77807
|
return { trunk: derived.tracked || track !== null ? derived.trunk : null };
|
|
77479
77808
|
}
|
|
77480
77809
|
function plan2(argv, io) {
|
|
77481
|
-
const flags = parseFlags(argv, {
|
|
77810
|
+
const flags = parseFlags(argv, {
|
|
77811
|
+
name: "value",
|
|
77812
|
+
trunk: "value",
|
|
77813
|
+
track: "value",
|
|
77814
|
+
"take-lead": "boolean"
|
|
77815
|
+
});
|
|
77482
77816
|
if (typeof flags === "string")
|
|
77483
77817
|
return usage3(io, flags, PLAN_USAGE2);
|
|
77484
77818
|
const track = flags.values.track ?? null;
|
|
@@ -77500,7 +77834,8 @@ function plan2(argv, io) {
|
|
|
77500
77834
|
raw,
|
|
77501
77835
|
name: flags.values.name ?? null,
|
|
77502
77836
|
trunk: trunk.trunk,
|
|
77503
|
-
actor: actorFromEnv(io.env)
|
|
77837
|
+
actor: actorFromEnv(io.env),
|
|
77838
|
+
takeLead: flags.booleans.has("take-lead")
|
|
77504
77839
|
});
|
|
77505
77840
|
if (!result.ok) {
|
|
77506
77841
|
const code = failed(io, result);
|
|
@@ -77508,15 +77843,42 @@ function plan2(argv, io) {
|
|
|
77508
77843
|
return code;
|
|
77509
77844
|
}
|
|
77510
77845
|
return emit(io, ok(result.data), (data) => [
|
|
77511
|
-
`${data.created ? "planned" : "re-planned"} fanout ${data.name} on ${data.trunk}: ${data.slices.length} slice${data.slices.length === 1 ? "" : "s"}`,
|
|
77846
|
+
`${data.created ? "planned" : "re-planned"} fanout ${data.name} on ${data.trunk}${data.fanIn === "integration" ? " (integration branch: one PR)" : ""}: ${data.slices.length} slice${data.slices.length === 1 ? "" : "s"}`,
|
|
77512
77847
|
...data.slices.map((slice) => ` ${slice.id} [${slice.tier}] ${slice.branch} <- ${slice.base}${slice.dependsOn.length ? ` after ${slice.dependsOn.join(", ")}` : ""}
|
|
77513
77848
|
worktree ${slice.worktree}`),
|
|
77514
77849
|
...data.overlaps.map((overlap) => ` shared ${overlap.path}: ${overlap.resolution === "owner" ? `owned by ${overlap.owner}` : `serialized (${overlap.slices.join(" -> ")})`}`),
|
|
77515
77850
|
`waves: ${data.waves.map((wave) => wave.join(", ")).join(" | ")}`,
|
|
77516
77851
|
`landing order: ${data.landingOrder.join(", ")}`,
|
|
77517
|
-
...data.notes.map((note) => `note: ${note}`)
|
|
77852
|
+
...data.notes.map((note) => `note: ${note}`),
|
|
77853
|
+
...data.warnings.map((warning) => `warning: ${warning}`)
|
|
77518
77854
|
]);
|
|
77519
77855
|
}
|
|
77856
|
+
function brief(argv, io) {
|
|
77857
|
+
const flags = parseFlags(argv, { name: "value", mode: "value", attempt: "value" });
|
|
77858
|
+
if (typeof flags === "string")
|
|
77859
|
+
return usage3(io, flags, BRIEF_USAGE);
|
|
77860
|
+
if (flags.positionals.length !== 1)
|
|
77861
|
+
return usage3(io, flags.positionals.length ? `unexpected argument ${flags.positionals[1]}` : "missing <slice>", BRIEF_USAGE);
|
|
77862
|
+
const mode = flags.values.mode ?? null;
|
|
77863
|
+
if (mode !== null && mode !== "new" && mode !== "resume")
|
|
77864
|
+
return usage3(io, "--mode takes new or resume", BRIEF_USAGE);
|
|
77865
|
+
const rawAttempt = flags.values.attempt;
|
|
77866
|
+
const attempt = rawAttempt === undefined ? null : Number(rawAttempt);
|
|
77867
|
+
if (attempt !== null && !(Number.isInteger(attempt) && attempt >= 1 && attempt <= 9))
|
|
77868
|
+
return usage3(io, "--attempt takes a number from 1 to 9", BRIEF_USAGE);
|
|
77869
|
+
const selected = selectFanout(io.cwd, flags.values.name ?? null, currentBranch(io.cwd));
|
|
77870
|
+
if (!selected.ok)
|
|
77871
|
+
return failed(io, selected);
|
|
77872
|
+
const result = renderBrief(io.cwd, selected.data, {
|
|
77873
|
+
slice: flags.positionals[0],
|
|
77874
|
+
mode,
|
|
77875
|
+
attempt,
|
|
77876
|
+
actor: actorFromEnv(io.env)
|
|
77877
|
+
});
|
|
77878
|
+
if (!result.ok)
|
|
77879
|
+
return failed(io, result);
|
|
77880
|
+
return emit(io, ok(result.data), (data) => data.text.trimEnd());
|
|
77881
|
+
}
|
|
77520
77882
|
function check2(argv, io) {
|
|
77521
77883
|
const flags = parseFlags(argv, { name: "value", base: "value", offline: "boolean" });
|
|
77522
77884
|
if (typeof flags === "string")
|
|
@@ -77618,6 +77980,8 @@ async function run15(argv, io) {
|
|
|
77618
77980
|
const [sub, ...rest] = argv.filter((arg) => arg !== "--json");
|
|
77619
77981
|
if (sub === "plan")
|
|
77620
77982
|
return plan2(rest, io);
|
|
77983
|
+
if (sub === "brief")
|
|
77984
|
+
return brief(rest, io);
|
|
77621
77985
|
if (sub === "check")
|
|
77622
77986
|
return check2(rest, io);
|
|
77623
77987
|
if (sub === "status")
|
|
@@ -77626,7 +77990,7 @@ async function run15(argv, io) {
|
|
|
77626
77990
|
return worktree(rest, io);
|
|
77627
77991
|
return usage3(io, sub && !sub.startsWith("-") ? `unknown fanout subcommand "${sub}"` : "missing fanout subcommand", USAGE8);
|
|
77628
77992
|
}
|
|
77629
|
-
var PLAN_USAGE2 = "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] [--json]", CHECK_USAGE = "workit fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline] [--json]", STATUS_USAGE3 = "workit fanout status [--name <n>] [--stuck-after 30m] [--offline] [--json]", WORKTREE_USAGE = "workit fanout worktree create <slice> [--name <n>] | worktree release <slice> [--name <n>] [--force] [--json]", USAGE8 = "workit fanout plan|check|status|worktree ... (workit help fanout)", failed = (io, result) => emit(io, fail(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), renderCheck = (data) => [
|
|
77993
|
+
var PLAN_USAGE2 = "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] [--take-lead] [--json]", BRIEF_USAGE = "workit fanout brief <slice> [--name <n>] [--mode new|resume] [--attempt <n>] [--json]", CHECK_USAGE = "workit fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline] [--json]", STATUS_USAGE3 = "workit fanout status [--name <n>] [--stuck-after 30m] [--offline] [--json]", WORKTREE_USAGE = "workit fanout worktree create <slice> [--name <n>] | worktree release <slice> [--name <n>] [--force] [--json]", USAGE8 = "workit fanout plan|brief|check|status|worktree ... (workit help fanout)", failed = (io, result) => emit(io, fail(result.code, result.error, { unblock: result.unblock, data: result.data ?? {} })), renderCheck = (data) => [
|
|
77630
77994
|
`fanout ${data.name} against ${data.trunkRef}: ${data.ready ? "ready to land" : "blocked"}`,
|
|
77631
77995
|
...data.slices.map((slice) => slice.status === "landed" ? ` -- ${slice.id} (${slice.branch}) landed (${slice.landed?.how}${slice.landed?.pr ? ` #${slice.landed.pr}` : ""})` : ` ${slice.status === "ready" ? "ok " : "x "}${slice.id} (${slice.branch}) ${slice.changed} file${slice.changed === 1 ? "" : "s"} · verdict ${slice.verdict.accepted ? "accepted" : slice.verdict.basis}${slice.reasons.length ? `
|
|
77632
77996
|
${slice.reasons.join(`
|
|
@@ -77664,6 +78028,7 @@ var PLAN_USAGE2 = "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --
|
|
|
77664
78028
|
];
|
|
77665
78029
|
};
|
|
77666
78030
|
var init_fanout2 = __esm(() => {
|
|
78031
|
+
init_fanout_brief();
|
|
77667
78032
|
init_fanout_check();
|
|
77668
78033
|
init_fanout_status();
|
|
77669
78034
|
init_fanout_worktree();
|
|
@@ -77679,7 +78044,7 @@ var exports_ledger = {};
|
|
|
77679
78044
|
__export(exports_ledger, {
|
|
77680
78045
|
run: () => run16
|
|
77681
78046
|
});
|
|
77682
|
-
import { randomBytes as
|
|
78047
|
+
import { randomBytes as randomBytes5 } from "node:crypto";
|
|
77683
78048
|
import { parseArgs } from "node:util";
|
|
77684
78049
|
function targetBranch(io, rows, branch, pr) {
|
|
77685
78050
|
if (pr !== undefined) {
|
|
@@ -77725,8 +78090,10 @@ async function run16(argv, io) {
|
|
|
77725
78090
|
const text = rest.join(" ");
|
|
77726
78091
|
if (sub === undefined)
|
|
77727
78092
|
return usage4(io, "missing subcommand");
|
|
77728
|
-
if (!["decision", "ruling", "verdict", "list", "show", "check"].includes(sub))
|
|
78093
|
+
if (!["decision", "ruling", "verdict", "standing", "list", "show", "check"].includes(sub))
|
|
77729
78094
|
return usage4(io, `unknown ledger subcommand "${sub}"`);
|
|
78095
|
+
if (sub === "standing")
|
|
78096
|
+
return standing(io, values, rest);
|
|
77730
78097
|
const ledger = readLedger(io.cwd);
|
|
77731
78098
|
if (!ledger.ok)
|
|
77732
78099
|
return failed2(io, ledger);
|
|
@@ -77804,7 +78171,62 @@ async function run16(argv, io) {
|
|
|
77804
78171
|
`);
|
|
77805
78172
|
return fromResult(io, verdict, (row) => `recorded verdict ${row.id}: ${row.result} [${row.kind}] for ${row.branch} @ ${(row.head ?? "").slice(0, 12)} as ${actor.session ?? "no session"}${row.self ? ` (self${row.selfReason === "no_session" ? ": WORKIT_SESSION_ID unset" : ""}; never accepted)` : ""}`);
|
|
77806
78173
|
}
|
|
77807
|
-
|
|
78174
|
+
function standingFanout(io, values) {
|
|
78175
|
+
if (values.fanout !== undefined) {
|
|
78176
|
+
const name = values.fanout.trim();
|
|
78177
|
+
if (!name)
|
|
78178
|
+
return new Error("--fanout needs a plan name");
|
|
78179
|
+
const read = readFanout(io.cwd, name);
|
|
78180
|
+
return { name, plan: read.ok ? read.data : null };
|
|
78181
|
+
}
|
|
78182
|
+
const selected = selectFanout(io.cwd, null, currentBranch(io.cwd));
|
|
78183
|
+
return selected.ok ? { name: selected.data.name, plan: selected.data } : new Error(`${selected.error}; pass --fanout <name>`);
|
|
78184
|
+
}
|
|
78185
|
+
async function standing(io, values, args) {
|
|
78186
|
+
const [action, ...rest] = args;
|
|
78187
|
+
if (action !== "add" && action !== "list" && action !== "clear")
|
|
78188
|
+
return emit(io, fail("invalid_input", action ? `unknown standing action "${action}"` : "missing add, list or clear", { unblock: STANDING_USAGE }));
|
|
78189
|
+
const target = standingFanout(io, values);
|
|
78190
|
+
if (target instanceof Error)
|
|
78191
|
+
return emit(io, fail("invalid_input", target.message, { unblock: STANDING_USAGE }));
|
|
78192
|
+
const fanout = target.name;
|
|
78193
|
+
const lead = target.plan?.leadSession ?? null;
|
|
78194
|
+
const leads = target.plan ? planLeads(target.plan) : null;
|
|
78195
|
+
if (action === "list") {
|
|
78196
|
+
if (rest.length)
|
|
78197
|
+
return usage4(io, `unexpected argument: ${rest[0]}`);
|
|
78198
|
+
const ledger = readLedger(io.cwd);
|
|
78199
|
+
if (!ledger.ok)
|
|
78200
|
+
return failed2(io, ledger);
|
|
78201
|
+
const orders = activeStanding(ledger.value.rows, fanout, leads).map((row) => ({
|
|
78202
|
+
id: row.id,
|
|
78203
|
+
at: row.at,
|
|
78204
|
+
what: row.what
|
|
78205
|
+
}));
|
|
78206
|
+
return emit(io, ok({ fanout, orders }), (data) => data.orders.length ? [
|
|
78207
|
+
`standing orders for fanout ${fanout}:`,
|
|
78208
|
+
...data.orders.map((o) => ` ${o.id} ${o.what}`)
|
|
78209
|
+
] : `no standing orders for fanout ${fanout}`);
|
|
78210
|
+
}
|
|
78211
|
+
const acting = actorFor(io, values);
|
|
78212
|
+
if (acting instanceof Error)
|
|
78213
|
+
return usage4(io, acting.message);
|
|
78214
|
+
const context = {
|
|
78215
|
+
cwd: io.cwd,
|
|
78216
|
+
actor: acting.actor,
|
|
78217
|
+
...values.supersedes ? { supersedes: values.supersedes } : {}
|
|
78218
|
+
};
|
|
78219
|
+
if (action === "add" && target.plan === null)
|
|
78220
|
+
return emit(io, fail("not_found", `no fanout plan named ${fanout}`, {
|
|
78221
|
+
unblock: "workit fanout plan <plan.json> first, or name an existing plan with --fanout"
|
|
78222
|
+
}));
|
|
78223
|
+
if (action === "add")
|
|
78224
|
+
return fromResult(io, recordStanding(context, { fanout, lead, what: rest.join(" ") }), (row) => `recorded standing order ${row.id} for fanout ${fanout}`);
|
|
78225
|
+
if (rest.length > 1)
|
|
78226
|
+
return usage4(io, `unexpected argument: ${rest[1]}`);
|
|
78227
|
+
return fromResult(io, clearStanding(context, { fanout, lead, leads, target: rest[0] ?? null }), (row) => row.target ? `cleared standing order ${row.target} for fanout ${fanout}` : `cleared every standing order for fanout ${fanout}`);
|
|
78228
|
+
}
|
|
78229
|
+
var USAGE9 = "workit ledger decision|ruling|verdict|standing|list|check ... (workit help ledger for the grammar)", STANDING_USAGE = 'workit ledger standing add "<order>" | list | clear [<id>] [--fanout <name>] [--json]', OPTIONS, SESSION_SAFE2, ROLE_SAFE, actorFor = (io, values) => {
|
|
77808
78230
|
const actor = actorFromEnv(io.env);
|
|
77809
78231
|
if (values.session !== undefined && values.as !== undefined)
|
|
77810
78232
|
return new Error("pass --session or --as, not both");
|
|
@@ -77818,7 +78240,7 @@ var USAGE9 = "workit ledger decision|ruling|verdict|list|check ... (workit help
|
|
|
77818
78240
|
return new Error("--as takes a lowercase role, e.g. verifier");
|
|
77819
78241
|
const prefix = (actor.session ?? actor.host).slice(0, 96);
|
|
77820
78242
|
return {
|
|
77821
|
-
actor: { ...actor, session: `${prefix}:${values.as}:${
|
|
78243
|
+
actor: { ...actor, session: `${prefix}:${values.as}:${randomBytes5(4).toString("hex")}` },
|
|
77822
78244
|
derivedFrom: actor.session
|
|
77823
78245
|
};
|
|
77824
78246
|
}
|
|
@@ -77841,6 +78263,7 @@ var USAGE9 = "workit ledger decision|ruling|verdict|list|check ... (workit help
|
|
|
77841
78263
|
};
|
|
77842
78264
|
var init_ledger2 = __esm(() => {
|
|
77843
78265
|
init_ledger();
|
|
78266
|
+
init_fanout();
|
|
77844
78267
|
init_rev();
|
|
77845
78268
|
init_implicit_task();
|
|
77846
78269
|
init_output();
|
|
@@ -77861,6 +78284,7 @@ var init_ledger2 = __esm(() => {
|
|
|
77861
78284
|
supersedes: { type: "string" },
|
|
77862
78285
|
session: { type: "string" },
|
|
77863
78286
|
as: { type: "string" },
|
|
78287
|
+
fanout: { type: "string" },
|
|
77864
78288
|
json: { type: "boolean" }
|
|
77865
78289
|
};
|
|
77866
78290
|
SESSION_SAFE2 = /^[A-Za-z0-9_.:@/+-]{1,128}$/;
|
|
@@ -94561,7 +94985,7 @@ var init_knowledge2 = __esm(() => {
|
|
|
94561
94985
|
init_output();
|
|
94562
94986
|
});
|
|
94563
94987
|
|
|
94564
|
-
// packages/workit-core/src/core/youtrack-tools.ts
|
|
94988
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/youtrack-tools.ts
|
|
94565
94989
|
async function logTimeUpdate(input, operation = defaultOperations) {
|
|
94566
94990
|
try {
|
|
94567
94991
|
const value = await operation.logTime(input);
|
|
@@ -94610,7 +95034,7 @@ var init_youtrack_tools = __esm(() => {
|
|
|
94610
95034
|
};
|
|
94611
95035
|
});
|
|
94612
95036
|
|
|
94613
|
-
// packages/workit-core/src/core/youtrack-writes.ts
|
|
95037
|
+
// packages/workit-cli/node_modules/@brainervirus/workit-core/src/core/youtrack-writes.ts
|
|
94614
95038
|
async function markerPresent(issueId, marker, where) {
|
|
94615
95039
|
const creds = youTrackToken();
|
|
94616
95040
|
if ("error" in creds)
|
|
@@ -95009,7 +95433,7 @@ var VERBS = [
|
|
|
95009
95433
|
{
|
|
95010
95434
|
name: "pr",
|
|
95011
95435
|
group: "delivery",
|
|
95012
|
-
usage: "workit pr status [--pr <n>] | pr create [--base <b> | --track <t>] (--title <t> | --fill) [--draft] | pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch]",
|
|
95436
|
+
usage: "workit pr status [--pr <n>] | pr create [--base <b> | --track <t>] (--title <t> | --fill) [--draft] | pr merge [--pr <n>] [--method squash|merge|rebase] [--delete-branch] [--unverified --reason <why>]",
|
|
95013
95437
|
summary: "PR/MR state with failing log tails and next action; open a SHA-verified PR; merge only when READY, verified and granted",
|
|
95014
95438
|
load: () => Promise.resolve().then(() => (init_pr(), exports_pr))
|
|
95015
95439
|
},
|
|
@@ -95037,22 +95461,22 @@ var VERBS = [
|
|
|
95037
95461
|
{
|
|
95038
95462
|
name: "stack",
|
|
95039
95463
|
group: "delivery",
|
|
95040
|
-
usage: "workit stack plan [<bottom> … <top>] | status | sync [--local] | land [--dry-run] [--max <n>] [--json]",
|
|
95464
|
+
usage: "workit stack plan [<bottom> … <top>] | status | sync [--local] | land [--dry-run] [--max <n>] [--unverified --reason <why>] [--json]",
|
|
95041
95465
|
summary: "Plan, restack (lease push + retarget) and land a base-branch stack of PRs, root first",
|
|
95042
95466
|
load: () => Promise.resolve().then(() => (init_stack2(), exports_stack))
|
|
95043
95467
|
},
|
|
95044
95468
|
{
|
|
95045
95469
|
name: "fanout",
|
|
95046
95470
|
group: "delivery",
|
|
95047
|
-
usage: "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] | fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline] | fanout status [--name <n>] [--stuck-after 30m] [--offline] | fanout worktree create|release <slice> [--force] [--json]",
|
|
95048
|
-
summary: "Register parallel slices (complete briefs, disjoint scopes, shared-file owners), watch them (STUCK, PR, CI, verdict, landed, landing order), make and release slice worktrees, and gate fan-in: out-of-scope edits, trunk and sibling merge conflicts",
|
|
95471
|
+
usage: "workit fanout plan <plan.json> [--name <n>] [--trunk <b> | --track <t>] [--take-lead] | fanout brief <slice> [--mode new|resume] [--attempt <n>] | fanout check [<slice>…] [--name <n>] [--base <ref>] [--offline] | fanout status [--name <n>] [--stuck-after 30m] [--offline] | fanout worktree create|release <slice> [--force] [--json]",
|
|
95472
|
+
summary: "Register parallel slices (complete briefs, disjoint scopes, shared-file owners), render each worker's brief with the standing orders, watch them (STUCK, PR, CI, verdict, landed, landing order), make and release slice worktrees, and gate fan-in: out-of-scope edits, trunk and sibling merge conflicts",
|
|
95049
95473
|
load: () => Promise.resolve().then(() => (init_fanout2(), exports_fanout))
|
|
95050
95474
|
},
|
|
95051
95475
|
{
|
|
95052
95476
|
name: "ledger",
|
|
95053
95477
|
group: "delivery",
|
|
95054
|
-
usage: "workit ledger decision|ruling|verdict|list|check ...",
|
|
95055
|
-
summary: "Record and query decisions, rulings
|
|
95478
|
+
usage: "workit ledger decision|ruling|verdict|standing|list|check ...",
|
|
95479
|
+
summary: "Record and query decisions, rulings, SHA-keyed verdicts (patch-id carry-over across rebases) and a fanout's standing orders",
|
|
95056
95480
|
load: () => Promise.resolve().then(() => (init_ledger2(), exports_ledger))
|
|
95057
95481
|
},
|
|
95058
95482
|
{
|