@brainervirus/workit-opencode 8.13.1 → 8.14.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.
|
@@ -25,9 +25,9 @@ ceremony; do it yourself. Workit never spawns agents; you do.
|
|
|
25
25
|
it; change the plan or the standing orders and render again.
|
|
26
26
|
4. **Spawn in a rolling window**, in the background, each in its own worktree:
|
|
27
27
|
keep 4-6 in flight and refill from `spawnable` in `workit fanout status` as
|
|
28
|
-
each one finishes.
|
|
29
|
-
Claude Code: the `implementer` agent;
|
|
30
|
-
worktree create <slice>`.
|
|
28
|
+
each one finishes. Model: the brief's `model:` line, else TIER
|
|
29
|
+
(`references/orchestration.md`). Claude Code: the `implementer` agent;
|
|
30
|
+
elsewhere first `workit fanout worktree create <slice>`.
|
|
31
31
|
5. **Judge liveness by side effects only:** `workit fanout status` (head age,
|
|
32
32
|
PR, CI, verdict, landed; STUCK past the TIMEBOX). Stop a stuck worker and
|
|
33
33
|
observe that it exited (a timeout is not proof). Removing a worktree drops
|
|
@@ -53,7 +53,7 @@ ceremony; do it yourself. Workit never spawns agents; you do.
|
|
|
53
53
|
status` order, release the worktrees you made, and `workit ledger standing
|
|
54
54
|
clear`. Only you touch topology: workers never rebase, retarget or merge,
|
|
55
55
|
except the integration-tip merge in integration mode. Then ship
|
|
56
|
-
(call the skill tool with `workit-ship`)
|
|
56
|
+
(call the skill tool with `workit-ship`) up to `defaultEndpoint` from `workit grant show`.
|
|
57
57
|
|
|
58
58
|
## Example
|
|
59
59
|
|
|
@@ -5,8 +5,10 @@ and fans in. Workit records and checks; the host's own subagents run the work.
|
|
|
5
5
|
|
|
6
6
|
## TIER to model
|
|
7
7
|
|
|
8
|
-
TIER is a field of each slice and of its brief.
|
|
9
|
-
|
|
8
|
+
TIER is a field of each slice and of its brief. When the workspace sets
|
|
9
|
+
`fanout.tierModels`, the brief has a `model:` line under TIER and `workit
|
|
10
|
+
fanout status` names the model next to each spawnable slice: use that model.
|
|
11
|
+
Without one, map TIER where the host lets you pick a model per spawn:
|
|
10
12
|
|
|
11
13
|
| TIER | Claude Code (`model` on the Agent call) | Hosts without a per-spawn model |
|
|
12
14
|
| --- | --- | --- |
|
package/dist/plugin.js
CHANGED
|
@@ -13161,6 +13161,8 @@ var workspaceConfigSchema = object({
|
|
|
13161
13161
|
defaultEndpoint: unknown().optional(),
|
|
13162
13162
|
verification: _enum(["self", "independent"]).optional(),
|
|
13163
13163
|
trailers: boolean2().optional(),
|
|
13164
|
+
liveSessionGuard: boolean2().optional(),
|
|
13165
|
+
fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
|
|
13164
13166
|
autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
|
|
13165
13167
|
kind,
|
|
13166
13168
|
union([boolean2(), literal("verified")]).optional()
|
|
@@ -13478,6 +13480,31 @@ var resolveRuntimeReleaseTracks = (cwd) => {
|
|
|
13478
13480
|
const matched = matchRuntimeWorkspace(cwd);
|
|
13479
13481
|
return matched ? { workspace: matched.selected.name, releaseTracks: matched.selected.releaseTracks } : null;
|
|
13480
13482
|
};
|
|
13483
|
+
var inheritedSetting = (cwd, dir, has) => {
|
|
13484
|
+
let matched;
|
|
13485
|
+
try {
|
|
13486
|
+
matched = matchRuntimeWorkspace(cwd, dir);
|
|
13487
|
+
} catch {
|
|
13488
|
+
return null;
|
|
13489
|
+
}
|
|
13490
|
+
if (!matched)
|
|
13491
|
+
return null;
|
|
13492
|
+
const { selected, matches } = matched;
|
|
13493
|
+
const enclosing = matches.filter((entry) => entry !== selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
13494
|
+
return { workspace: selected.name, source: [selected, ...enclosing].find(has) ?? null };
|
|
13495
|
+
};
|
|
13496
|
+
var workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false;
|
|
13497
|
+
var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
13498
|
+
var TIER_HOSTS = new Set([
|
|
13499
|
+
"claude_code",
|
|
13500
|
+
"opencode",
|
|
13501
|
+
"codex",
|
|
13502
|
+
"codex_cli",
|
|
13503
|
+
"codex_desktop",
|
|
13504
|
+
"cursor",
|
|
13505
|
+
"pi",
|
|
13506
|
+
"default"
|
|
13507
|
+
]);
|
|
13481
13508
|
var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
13482
13509
|
const matched = matchRuntimeWorkspace(cwd);
|
|
13483
13510
|
if (!matched)
|
|
@@ -21684,27 +21711,231 @@ var shellPolicy = (cwd, command, dialect = "posix") => {
|
|
|
21684
21711
|
};
|
|
21685
21712
|
// packages/workit-core/src/hooks/write-gate.ts
|
|
21686
21713
|
import { realpathSync as realpathSync6 } from "node:fs";
|
|
21714
|
+
import os7 from "node:os";
|
|
21715
|
+
import path27 from "node:path";
|
|
21716
|
+
|
|
21717
|
+
// packages/workit-core/src/core/live-session.ts
|
|
21718
|
+
import { spawnSync as spawnSync17 } from "node:child_process";
|
|
21719
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
21720
|
+
import fs18 from "node:fs";
|
|
21721
|
+
import os6 from "node:os";
|
|
21687
21722
|
import path26 from "node:path";
|
|
21723
|
+
var LIVE_SESSION_TTL_MS = 15 * 60000;
|
|
21724
|
+
var HEARTBEAT_EVERY_MS = 30000;
|
|
21725
|
+
var liveProbe = {
|
|
21726
|
+
platform: process.platform,
|
|
21727
|
+
run: (args) => spawnSync17("ps", args, { encoding: "utf8", timeout: 2000 }).stdout ?? "",
|
|
21728
|
+
start: (pid) => processStartOf(pid)
|
|
21729
|
+
};
|
|
21730
|
+
var leadOf = (session) => session.split(":")[0].replace(/-w-.*$/su, "") || session;
|
|
21731
|
+
var familyOf = (host) => host.replace(/_(?:cli|desktop)$/u, "");
|
|
21732
|
+
var processAlive = (pid) => {
|
|
21733
|
+
try {
|
|
21734
|
+
process.kill(pid, 0);
|
|
21735
|
+
return true;
|
|
21736
|
+
} catch (error) {
|
|
21737
|
+
return error.code === "EPERM";
|
|
21738
|
+
}
|
|
21739
|
+
};
|
|
21740
|
+
var processInfo = (pid) => {
|
|
21741
|
+
try {
|
|
21742
|
+
if (liveProbe.platform === "linux") {
|
|
21743
|
+
const stat = fs18.readFileSync(`/proc/${pid}/stat`, "utf8");
|
|
21744
|
+
const ppid = Number(stat.slice(stat.lastIndexOf(")") + 2).split(" ")[1]);
|
|
21745
|
+
const command = fs18.readFileSync(`/proc/${pid}/cmdline`, "utf8").replaceAll("\x00", " ");
|
|
21746
|
+
return Number.isInteger(ppid) ? { ppid, command: command.trim() } : null;
|
|
21747
|
+
}
|
|
21748
|
+
if (liveProbe.platform === "win32")
|
|
21749
|
+
return null;
|
|
21750
|
+
const match = /^\s*(\d+)\s+(.*)$/su.exec(liveProbe.run(["-o", "ppid=,command=", "-p", String(pid)]));
|
|
21751
|
+
return match ? { ppid: Number(match[1]), command: match[2].trim() } : null;
|
|
21752
|
+
} catch {
|
|
21753
|
+
return null;
|
|
21754
|
+
}
|
|
21755
|
+
};
|
|
21756
|
+
var WRAPPER = /^(?:\S*\/)?(?:sh|bash|dash|zsh|ash|ksh|fish|env|timeout|npx|npm|bunx|cmd\.exe|pwsh|powershell)(?:\s|$)|launch\.mjs|npx-cli\.js/u;
|
|
21757
|
+
var PIDS = new Map;
|
|
21758
|
+
var hostPid = (transport) => {
|
|
21759
|
+
if (transport !== "hook-process")
|
|
21760
|
+
return process.pid;
|
|
21761
|
+
const key = `${liveProbe.platform}\x00${process.ppid}`;
|
|
21762
|
+
if (PIDS.has(key))
|
|
21763
|
+
return PIDS.get(key) ?? null;
|
|
21764
|
+
if (liveProbe.platform === "win32") {
|
|
21765
|
+
PIDS.set(key, null);
|
|
21766
|
+
return null;
|
|
21767
|
+
}
|
|
21768
|
+
let pid = process.ppid;
|
|
21769
|
+
for (let hop = 0;hop < 4; hop++) {
|
|
21770
|
+
const info = processInfo(pid);
|
|
21771
|
+
if (!info || !WRAPPER.test(info.command) || info.ppid <= 1)
|
|
21772
|
+
break;
|
|
21773
|
+
pid = info.ppid;
|
|
21774
|
+
}
|
|
21775
|
+
PIDS.set(key, pid);
|
|
21776
|
+
return pid;
|
|
21777
|
+
};
|
|
21778
|
+
var sessionSafe = (value) => value.replace(/[^A-Za-z0-9_.:@/+-]/gu, "-");
|
|
21779
|
+
var liveOwner = (input, transport, env = process.env) => {
|
|
21780
|
+
const session = input.session.id?.trim();
|
|
21781
|
+
if (!session)
|
|
21782
|
+
return null;
|
|
21783
|
+
const leads = new Set([leadOf(sessionSafe(session))]);
|
|
21784
|
+
if (input.session.parentId)
|
|
21785
|
+
leads.add(leadOf(sessionSafe(input.session.parentId)));
|
|
21786
|
+
if (env.WORKIT_PI_WORKER_SESSION && env.WORKIT_SESSION_ID)
|
|
21787
|
+
leads.add(leadOf(sessionSafe(env.WORKIT_SESSION_ID)));
|
|
21788
|
+
return {
|
|
21789
|
+
host: input.host,
|
|
21790
|
+
session: sessionSafe(session),
|
|
21791
|
+
leads: [...leads],
|
|
21792
|
+
pid: () => hostPid(transport)
|
|
21793
|
+
};
|
|
21794
|
+
};
|
|
21795
|
+
var checkoutOf = (target) => {
|
|
21796
|
+
let dir = path26.resolve(target);
|
|
21797
|
+
while (!fs18.existsSync(dir) && path26.dirname(dir) !== dir)
|
|
21798
|
+
dir = path26.dirname(dir);
|
|
21799
|
+
if (fs18.existsSync(dir) && !fs18.statSync(dir).isDirectory())
|
|
21800
|
+
dir = path26.dirname(dir);
|
|
21801
|
+
const parts = dir.split(path26.sep);
|
|
21802
|
+
const dotGit = parts.lastIndexOf(".git");
|
|
21803
|
+
if (dotGit > 0)
|
|
21804
|
+
dir = parts.slice(0, dotGit).join(path26.sep) || path26.sep;
|
|
21805
|
+
const location = resolveStore(dir);
|
|
21806
|
+
if (location instanceof Error || !location.shared || !location.top)
|
|
21807
|
+
return null;
|
|
21808
|
+
let top = location.top;
|
|
21809
|
+
try {
|
|
21810
|
+
top = fs18.realpathSync.native(top);
|
|
21811
|
+
} catch {}
|
|
21812
|
+
return top;
|
|
21813
|
+
};
|
|
21814
|
+
var markerFile2 = (top) => path26.join(resolveStateDir(), "live-sessions", `${createHash14("sha256").update(checkoutKey(top)).digest("hex").slice(0, 32)}.json`);
|
|
21815
|
+
var checkoutKey = (top, platform = process.platform) => {
|
|
21816
|
+
let key = path26.resolve(top);
|
|
21817
|
+
try {
|
|
21818
|
+
key = fs18.realpathSync.native(key);
|
|
21819
|
+
} catch {}
|
|
21820
|
+
return platform === "win32" ? key.replaceAll("/", "\\").toLowerCase() : key;
|
|
21821
|
+
};
|
|
21822
|
+
var readMarker = (file) => {
|
|
21823
|
+
try {
|
|
21824
|
+
const value = JSON.parse(fs18.readFileSync(file, "utf8"));
|
|
21825
|
+
if (value?.version !== 1 || typeof value.session !== "string" || typeof value.host !== "string" || typeof value.heartbeatAt !== "string" || Number.isNaN(Date.parse(value.heartbeatAt)))
|
|
21826
|
+
return null;
|
|
21827
|
+
return {
|
|
21828
|
+
version: 1,
|
|
21829
|
+
session: value.session,
|
|
21830
|
+
leads: Array.isArray(value.leads) ? value.leads.filter((item) => typeof item === "string") : [],
|
|
21831
|
+
host: value.host,
|
|
21832
|
+
pid: typeof value.pid === "number" ? value.pid : null,
|
|
21833
|
+
pidStart: typeof value.pidStart === "string" ? value.pidStart : null,
|
|
21834
|
+
machine: typeof value.machine === "string" ? value.machine : "",
|
|
21835
|
+
checkout: typeof value.checkout === "string" ? value.checkout : "",
|
|
21836
|
+
heartbeatAt: value.heartbeatAt
|
|
21837
|
+
};
|
|
21838
|
+
} catch {
|
|
21839
|
+
return null;
|
|
21840
|
+
}
|
|
21841
|
+
};
|
|
21842
|
+
var ageOf2 = (marker, now) => now.getTime() - Date.parse(marker.heartbeatAt);
|
|
21843
|
+
var isLive = (marker, deps) => {
|
|
21844
|
+
const age = ageOf2(marker, deps.now ?? new Date);
|
|
21845
|
+
if (age > (deps.ttlMs ?? LIVE_SESSION_TTL_MS))
|
|
21846
|
+
return false;
|
|
21847
|
+
if (marker.machine !== (deps.machine ?? os6.hostname()) || marker.pid === null)
|
|
21848
|
+
return true;
|
|
21849
|
+
if (!(deps.alive ?? processAlive)(marker.pid))
|
|
21850
|
+
return false;
|
|
21851
|
+
if (marker.pidStart === null)
|
|
21852
|
+
return true;
|
|
21853
|
+
const start = liveProbe.start(marker.pid);
|
|
21854
|
+
return start === null || start === marker.pidStart;
|
|
21855
|
+
};
|
|
21856
|
+
var sameLead = (marker, owner) => marker.session === owner.session || owner.leads.some((lead) => lead === marker.session || marker.leads.includes(lead));
|
|
21857
|
+
var sameOwner = (marker, owner, machine) => {
|
|
21858
|
+
if (sameLead(marker, owner))
|
|
21859
|
+
return true;
|
|
21860
|
+
if (marker.machine !== machine)
|
|
21861
|
+
return false;
|
|
21862
|
+
const pid = owner.pid();
|
|
21863
|
+
if (pid === null || marker.pid === null || marker.pidStart === null)
|
|
21864
|
+
return familyOf(marker.host) === familyOf(owner.host);
|
|
21865
|
+
return marker.pid === pid;
|
|
21866
|
+
};
|
|
21867
|
+
var liveConflict = (target, owner, deps = {}) => {
|
|
21868
|
+
try {
|
|
21869
|
+
const checkout = checkoutOf(target);
|
|
21870
|
+
if (!checkout)
|
|
21871
|
+
return null;
|
|
21872
|
+
const file = markerFile2(checkout);
|
|
21873
|
+
const marker = readMarker(file);
|
|
21874
|
+
if (!marker || sameLead(marker, owner) || !isLive(marker, deps))
|
|
21875
|
+
return null;
|
|
21876
|
+
if (sameOwner(marker, owner, deps.machine ?? os6.hostname()))
|
|
21877
|
+
return null;
|
|
21878
|
+
return { marker, file, ageMs: ageOf2(marker, deps.now ?? new Date), checkout };
|
|
21879
|
+
} catch {
|
|
21880
|
+
return null;
|
|
21881
|
+
}
|
|
21882
|
+
};
|
|
21883
|
+
var claimLiveSession = (target, owner, deps = {}) => {
|
|
21884
|
+
try {
|
|
21885
|
+
const checkout = checkoutOf(target);
|
|
21886
|
+
if (!checkout)
|
|
21887
|
+
return;
|
|
21888
|
+
const file = markerFile2(checkout);
|
|
21889
|
+
const now = deps.now ?? new Date;
|
|
21890
|
+
const machine = deps.machine ?? os6.hostname();
|
|
21891
|
+
const current = readMarker(file);
|
|
21892
|
+
if (current) {
|
|
21893
|
+
const age = ageOf2(current, now);
|
|
21894
|
+
if (sameLead(current, owner) && age >= 0 && age < HEARTBEAT_EVERY_MS)
|
|
21895
|
+
return;
|
|
21896
|
+
if (!sameLead(current, owner) && isLive(current, deps) && !sameOwner(current, owner, machine))
|
|
21897
|
+
return;
|
|
21898
|
+
}
|
|
21899
|
+
const pid = owner.pid();
|
|
21900
|
+
const marker = {
|
|
21901
|
+
version: 1,
|
|
21902
|
+
session: owner.session,
|
|
21903
|
+
leads: owner.leads,
|
|
21904
|
+
host: owner.host,
|
|
21905
|
+
pid,
|
|
21906
|
+
pidStart: pid === null ? null : liveProbe.start(pid),
|
|
21907
|
+
machine,
|
|
21908
|
+
checkout,
|
|
21909
|
+
heartbeatAt: now.toISOString()
|
|
21910
|
+
};
|
|
21911
|
+
fs18.mkdirSync(path26.dirname(file), { recursive: true });
|
|
21912
|
+
const temp = `${file}.${process.pid}.tmp`;
|
|
21913
|
+
fs18.writeFileSync(temp, JSON.stringify(marker));
|
|
21914
|
+
fs18.renameSync(temp, file);
|
|
21915
|
+
} catch {}
|
|
21916
|
+
};
|
|
21917
|
+
|
|
21918
|
+
// packages/workit-core/src/hooks/write-gate.ts
|
|
21688
21919
|
var NONE3 = { kind: "none" };
|
|
21689
21920
|
var CASE_INSENSITIVE = process.platform === "darwin" || process.platform === "win32";
|
|
21690
21921
|
var canonical2 = (file) => {
|
|
21691
21922
|
let rest = "";
|
|
21692
|
-
for (let dir =
|
|
21923
|
+
for (let dir = path27.resolve(file);; dir = path27.dirname(dir)) {
|
|
21693
21924
|
try {
|
|
21694
|
-
return
|
|
21925
|
+
return path27.join(realpathSync6(dir), rest);
|
|
21695
21926
|
} catch {
|
|
21696
|
-
if (
|
|
21697
|
-
return
|
|
21698
|
-
rest = rest ?
|
|
21927
|
+
if (path27.dirname(dir) === dir)
|
|
21928
|
+
return path27.resolve(file);
|
|
21929
|
+
rest = rest ? path27.join(path27.basename(dir), rest) : path27.basename(dir);
|
|
21699
21930
|
}
|
|
21700
21931
|
}
|
|
21701
21932
|
};
|
|
21702
21933
|
var exempt = (cwd, root, file, plans) => {
|
|
21703
21934
|
const fold = (value) => CASE_INSENSITIVE ? value.toLowerCase() : value;
|
|
21704
|
-
const relative =
|
|
21705
|
-
if (relative.startsWith("..") ||
|
|
21935
|
+
const relative = path27.relative(fold(canonical2(root)), fold(canonical2(path27.resolve(cwd, file))));
|
|
21936
|
+
if (relative.startsWith("..") || path27.isAbsolute(relative))
|
|
21706
21937
|
return true;
|
|
21707
|
-
const posix = relative.split(
|
|
21938
|
+
const posix = relative.split(path27.sep).join("/");
|
|
21708
21939
|
const segments = posix.split("/");
|
|
21709
21940
|
return /\.mdx?$/i.test(posix) || segments[0] === "docs" || segments.includes("plans") || [...plans].some((plan) => fold(plan) === posix);
|
|
21710
21941
|
};
|
|
@@ -21802,7 +22033,7 @@ var shellWrites = (command) => {
|
|
|
21802
22033
|
return { writes: true, targets: unknown ? null : targets };
|
|
21803
22034
|
};
|
|
21804
22035
|
var cache2 = new Map;
|
|
21805
|
-
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(
|
|
22036
|
+
var planKeyOf = (root, plans) => [...plans].map((file) => fileSignature(path27.resolve(root, file)) ?? "-").join("\x00");
|
|
21806
22037
|
var gateFor = (cwd) => {
|
|
21807
22038
|
const store = new TaskStore(cwd);
|
|
21808
22039
|
const listed = store.listTaskIndex();
|
|
@@ -21840,7 +22071,218 @@ var gateFor = (cwd) => {
|
|
|
21840
22071
|
cache2.delete(cache2.keys().next().value);
|
|
21841
22072
|
return gate;
|
|
21842
22073
|
};
|
|
21843
|
-
|
|
22074
|
+
var MUTATING_GIT = new Set([
|
|
22075
|
+
"add",
|
|
22076
|
+
"am",
|
|
22077
|
+
"apply",
|
|
22078
|
+
"checkout",
|
|
22079
|
+
"cherry-pick",
|
|
22080
|
+
"clean",
|
|
22081
|
+
"commit",
|
|
22082
|
+
"merge",
|
|
22083
|
+
"mv",
|
|
22084
|
+
"pull",
|
|
22085
|
+
"rebase",
|
|
22086
|
+
"reset",
|
|
22087
|
+
"restore",
|
|
22088
|
+
"revert",
|
|
22089
|
+
"rm",
|
|
22090
|
+
"stash",
|
|
22091
|
+
"switch"
|
|
22092
|
+
]);
|
|
22093
|
+
var DRY_N = new Set(["add", "clean", "rm", "mv"]);
|
|
22094
|
+
var MUTATING_WORKIT_GIT = new Set(["commit", "branch"]);
|
|
22095
|
+
var DIR_TARGET = ".workit-live-target";
|
|
22096
|
+
var OVERRIDE = "WORKIT_LIVE_SESSION=override";
|
|
22097
|
+
var inert = (args, sub) => args.some((arg) => arg === "--help" || arg === "-h" || arg === "--dry-run" || DRY_N.has(sub) && /^-[a-zA-Z]*n[a-zA-Z]*$/.test(arg) && !arg.startsWith("--"));
|
|
22098
|
+
var gitMutationDirs = (cwd, command, dialect) => {
|
|
22099
|
+
let invocations;
|
|
22100
|
+
try {
|
|
22101
|
+
invocations = rawInvocations(command, dialect);
|
|
22102
|
+
} catch {
|
|
22103
|
+
return [];
|
|
22104
|
+
}
|
|
22105
|
+
return invocations.flatMap((invocation) => {
|
|
22106
|
+
if (invocation.tool !== "git" || invocation.dir === undefined)
|
|
22107
|
+
return [];
|
|
22108
|
+
const at = invocation.args.findIndex((arg) => !arg.startsWith("-"));
|
|
22109
|
+
const sub = invocation.args[at] ?? "";
|
|
22110
|
+
const rest = invocation.args.slice(at + 1);
|
|
22111
|
+
if (!MUTATING_GIT.has(sub) || inert(rest, sub))
|
|
22112
|
+
return [];
|
|
22113
|
+
if (sub === "stash" && ["list", "show"].includes(rest[0] ?? ""))
|
|
22114
|
+
return [];
|
|
22115
|
+
return [path27.resolve(cwd, invocation.dir ?? ".")];
|
|
22116
|
+
});
|
|
22117
|
+
};
|
|
22118
|
+
var CD2 = new Set(["cd", "chdir", "set-location", "sl"]);
|
|
22119
|
+
var PUSHD2 = new Set(["pushd", "push-location"]);
|
|
22120
|
+
var POPD2 = new Set(["popd", "pop-location"]);
|
|
22121
|
+
var cdInto = (dir, words) => {
|
|
22122
|
+
const target = words.slice(1).find((word) => !word.startsWith("-") || word === "-");
|
|
22123
|
+
if (dir === undefined || target === "-" || target && /^~|[$`*?]/.test(target))
|
|
22124
|
+
return;
|
|
22125
|
+
return target === undefined ? os7.homedir() : path27.resolve(dir, target);
|
|
22126
|
+
};
|
|
22127
|
+
var quoteWord = (word) => /^[\w@%+=:,./-]+$/.test(word) ? word : `'${word.replaceAll("'", "'\\''")}'`;
|
|
22128
|
+
var segmentTargets = (cwd, command, dialect) => {
|
|
22129
|
+
const targets = [];
|
|
22130
|
+
let dir = cwd;
|
|
22131
|
+
let stack = [];
|
|
22132
|
+
const groups = [];
|
|
22133
|
+
const at = (file) => path27.isAbsolute(file) ? file : dir === undefined ? null : path27.resolve(dir, file);
|
|
22134
|
+
for (const segment of segmentsOf(command, dialect)) {
|
|
22135
|
+
if (segment.group === "open") {
|
|
22136
|
+
groups.push({ dir, stack: [...stack] });
|
|
22137
|
+
continue;
|
|
22138
|
+
}
|
|
22139
|
+
if (segment.group === "close") {
|
|
22140
|
+
const saved = groups.pop();
|
|
22141
|
+
if (saved)
|
|
22142
|
+
({ dir, stack } = saved);
|
|
22143
|
+
continue;
|
|
22144
|
+
}
|
|
22145
|
+
const lead = segment.words.findIndex((word) => !/^\w+=/.test(word));
|
|
22146
|
+
const words = lead < 0 ? [] : segment.words.slice(lead);
|
|
22147
|
+
const name = (words[0]?.split(/[\\/]/).at(-1) ?? "").toLowerCase();
|
|
22148
|
+
if (CD2.has(name)) {
|
|
22149
|
+
dir = cdInto(dir, words);
|
|
22150
|
+
continue;
|
|
22151
|
+
}
|
|
22152
|
+
if (PUSHD2.has(name)) {
|
|
22153
|
+
stack.push(dir);
|
|
22154
|
+
dir = cdInto(dir, words);
|
|
22155
|
+
continue;
|
|
22156
|
+
}
|
|
22157
|
+
if (POPD2.has(name)) {
|
|
22158
|
+
dir = stack.length ? stack.pop() : undefined;
|
|
22159
|
+
continue;
|
|
22160
|
+
}
|
|
22161
|
+
for (const file of segment.redirects)
|
|
22162
|
+
if (!/^\/dev\/(?:null|stdout|stderr|tty)$/.test(file)) {
|
|
22163
|
+
const resolved = at(file);
|
|
22164
|
+
if (resolved)
|
|
22165
|
+
targets.push(resolved);
|
|
22166
|
+
}
|
|
22167
|
+
const edits = editsOf(segment.words);
|
|
22168
|
+
if (edits === null) {
|
|
22169
|
+
if (dir !== undefined)
|
|
22170
|
+
targets.push(path27.join(dir, DIR_TARGET));
|
|
22171
|
+
} else
|
|
22172
|
+
for (const file of edits) {
|
|
22173
|
+
const resolved = at(file);
|
|
22174
|
+
if (resolved)
|
|
22175
|
+
targets.push(resolved);
|
|
22176
|
+
}
|
|
22177
|
+
if (dir !== undefined && workitInvocations(segment.words.map(quoteWord).join(" "), "posix").some((args) => args[0] === "git" && MUTATING_WORKIT_GIT.has(args[1] ?? "") && !inert(args.slice(2), "")))
|
|
22178
|
+
targets.push(path27.join(dir, DIR_TARGET));
|
|
22179
|
+
}
|
|
22180
|
+
return targets;
|
|
22181
|
+
};
|
|
22182
|
+
var overridden = (command, dialect) => {
|
|
22183
|
+
const segments = segmentsOf(command, dialect);
|
|
22184
|
+
if (segments.length !== 1 || segments[0].group || segments[0].subs?.length)
|
|
22185
|
+
return false;
|
|
22186
|
+
const words = segments[0].words;
|
|
22187
|
+
const lead = words.findIndex((word) => !/^\w+=/.test(word));
|
|
22188
|
+
return lead > 0 && words.slice(0, lead).includes(OVERRIDE);
|
|
22189
|
+
};
|
|
22190
|
+
var HOST_LABELS = {
|
|
22191
|
+
claude_code: "Claude Code",
|
|
22192
|
+
opencode: "OpenCode",
|
|
22193
|
+
codex_cli: "Codex CLI",
|
|
22194
|
+
codex_desktop: "Codex Desktop",
|
|
22195
|
+
cursor: "Cursor",
|
|
22196
|
+
pi: "Pi"
|
|
22197
|
+
};
|
|
22198
|
+
var ageText = (ms) => {
|
|
22199
|
+
const seconds = Math.max(0, Math.round(ms / 1000));
|
|
22200
|
+
return seconds < 120 ? `${seconds}s` : `${Math.round(seconds / 60)} min`;
|
|
22201
|
+
};
|
|
22202
|
+
var liveDeny = ({ marker, checkout, ageMs, file }) => {
|
|
22203
|
+
const unblock = `Work in your own worktree (\`git worktree add <path> -b <branch>\`, or \`workit fanout worktree create <slice>\`). Only if the user confirms that session is finished: release it with \`rm '${file}'\`, or prefix one command with ${OVERRIDE}.`;
|
|
22204
|
+
return {
|
|
22205
|
+
kind: "deny",
|
|
22206
|
+
reason: `workit live-session guard: ${checkout} belongs to another live agent session, ${HOST_LABELS[marker.host] ?? marker.host} session ${marker.session}${marker.pid === null ? "" : ` (pid ${marker.pid})`}, last active ${ageText(ageMs)} ago. Do not edit a checkout another session is working in; read-only commands stay allowed. ${unblock}`,
|
|
22207
|
+
unblock
|
|
22208
|
+
};
|
|
22209
|
+
};
|
|
22210
|
+
var guarded = (targets) => {
|
|
22211
|
+
const seen = new Set;
|
|
22212
|
+
return targets.filter((target) => {
|
|
22213
|
+
const dir = path27.dirname(target);
|
|
22214
|
+
if (seen.has(dir))
|
|
22215
|
+
return false;
|
|
22216
|
+
seen.add(dir);
|
|
22217
|
+
return true;
|
|
22218
|
+
});
|
|
22219
|
+
};
|
|
22220
|
+
var liveGate = (owner, targets, deps) => {
|
|
22221
|
+
try {
|
|
22222
|
+
for (const target of guarded(targets)) {
|
|
22223
|
+
const conflict = liveConflict(target, owner, deps);
|
|
22224
|
+
if (conflict && workspaceLiveSessionGuard(conflict.checkout))
|
|
22225
|
+
return liveDeny(conflict);
|
|
22226
|
+
}
|
|
22227
|
+
} catch {}
|
|
22228
|
+
return NONE3;
|
|
22229
|
+
};
|
|
22230
|
+
var claimAll = (owner, targets, deps) => {
|
|
22231
|
+
for (const target of guarded(targets))
|
|
22232
|
+
claimLiveSession(target, owner, deps);
|
|
22233
|
+
};
|
|
22234
|
+
var liveTargets = (cwd, paths) => paths?.length ? paths.map((file) => path27.resolve(cwd, file)) : [path27.join(cwd, DIR_TARGET)];
|
|
22235
|
+
function writeGate(cwd, paths, owner, deps) {
|
|
22236
|
+
if (!owner)
|
|
22237
|
+
return beforeWriteGate(cwd, paths);
|
|
22238
|
+
const targets = liveTargets(cwd, paths);
|
|
22239
|
+
const live = liveGate(owner, targets, deps);
|
|
22240
|
+
if (live.kind !== "none")
|
|
22241
|
+
return live;
|
|
22242
|
+
const decision = beforeWriteGate(cwd, paths);
|
|
22243
|
+
if (decision.kind === "none")
|
|
22244
|
+
claimAll(owner, targets, deps);
|
|
22245
|
+
return decision;
|
|
22246
|
+
}
|
|
22247
|
+
function shellGate(cwd, command, owner, dialect = "posix", deps) {
|
|
22248
|
+
const writes = shellWrites(command);
|
|
22249
|
+
let targets = [];
|
|
22250
|
+
if (owner) {
|
|
22251
|
+
try {
|
|
22252
|
+
targets = [
|
|
22253
|
+
...segmentTargets(cwd, command, dialect),
|
|
22254
|
+
...gitMutationDirs(cwd, command, dialect).map((dir) => path27.join(dir, DIR_TARGET))
|
|
22255
|
+
];
|
|
22256
|
+
} catch {
|
|
22257
|
+
targets = [];
|
|
22258
|
+
}
|
|
22259
|
+
}
|
|
22260
|
+
if (owner && targets.length) {
|
|
22261
|
+
const live = liveGate(owner, targets, deps);
|
|
22262
|
+
if (live.kind !== "none") {
|
|
22263
|
+
if (!overridden(command, dialect))
|
|
22264
|
+
return live;
|
|
22265
|
+
noteOverride(cwd, owner, command);
|
|
22266
|
+
}
|
|
22267
|
+
}
|
|
22268
|
+
const decision = writes.writes ? beforeWriteGate(cwd, writes.targets) : NONE3;
|
|
22269
|
+
if (owner && targets.length && decision.kind === "none")
|
|
22270
|
+
claimAll(owner, targets, deps);
|
|
22271
|
+
return decision;
|
|
22272
|
+
}
|
|
22273
|
+
var noteOverride = (cwd, owner, command) => {
|
|
22274
|
+
try {
|
|
22275
|
+
if (!inWorkitWorkspace(cwd))
|
|
22276
|
+
return;
|
|
22277
|
+
appendHookObserved(cwd, {
|
|
22278
|
+
type: "live_session.override",
|
|
22279
|
+
actor: { host: owner.host, session: owner.session, agentId: null },
|
|
22280
|
+
session: owner.session,
|
|
22281
|
+
command: command.slice(0, 200)
|
|
22282
|
+
});
|
|
22283
|
+
} catch {}
|
|
22284
|
+
};
|
|
22285
|
+
function beforeWriteGate(cwd, paths) {
|
|
21844
22286
|
try {
|
|
21845
22287
|
const gate = gateFor(cwd);
|
|
21846
22288
|
if (!gate || gate.blockers.length === 0)
|
|
@@ -21858,7 +22300,7 @@ function writeGate(cwd, paths) {
|
|
|
21858
22300
|
}
|
|
21859
22301
|
}
|
|
21860
22302
|
// packages/workit-core/src/hooks/stop.ts
|
|
21861
|
-
import { spawnSync as
|
|
22303
|
+
import { spawnSync as spawnSync18 } from "node:child_process";
|
|
21862
22304
|
var NONE4 = { kind: "none" };
|
|
21863
22305
|
var QUESTION = /[??؟](?=[\s*_`"')\]>]*(?:\s|$))|¿/;
|
|
21864
22306
|
var OFFER = /\b(?:let me know|want me to|should I|shall I|would you like|do you want|if you(?:'d| would) like|may I|can I go ahead)\b/i;
|
|
@@ -21879,7 +22321,7 @@ var asksUser = (message) => {
|
|
|
21879
22321
|
};
|
|
21880
22322
|
var short = (sha) => (sha ?? "?").slice(0, 12);
|
|
21881
22323
|
var gitList = (cwd, args) => {
|
|
21882
|
-
const run =
|
|
22324
|
+
const run = spawnSync18("git", args, {
|
|
21883
22325
|
cwd,
|
|
21884
22326
|
encoding: "utf8",
|
|
21885
22327
|
timeout: 3000,
|
|
@@ -22341,11 +22783,11 @@ var OPENCODE_DESCRIPTOR = {
|
|
|
22341
22783
|
]
|
|
22342
22784
|
};
|
|
22343
22785
|
// packages/workit-opencode/src/shared/init-apply.ts
|
|
22344
|
-
import
|
|
22786
|
+
import path32 from "node:path";
|
|
22345
22787
|
|
|
22346
22788
|
// packages/workit-core/src/core/gitignore.ts
|
|
22347
22789
|
import { existsSync as existsSync9, readFileSync as readFileSync6, writeFileSync as writeFileSync4 } from "node:fs";
|
|
22348
|
-
import
|
|
22790
|
+
import path28 from "node:path";
|
|
22349
22791
|
var GITIGNORE_ENTRIES = [
|
|
22350
22792
|
"# workit: SDD working state (never commit)",
|
|
22351
22793
|
"docs/*/sdd/",
|
|
@@ -22365,7 +22807,7 @@ var GITIGNORE_ENTRIES = [
|
|
|
22365
22807
|
var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
22366
22808
|
if (!confirmed)
|
|
22367
22809
|
return { ok: false, error: "confirmed: true required" };
|
|
22368
|
-
const file =
|
|
22810
|
+
const file = path28.join(workspaceRoot, ".gitignore");
|
|
22369
22811
|
const existing = existsSync9(file) ? readFileSync6(file, "utf8") : "";
|
|
22370
22812
|
const existingLines = new Set(existing.split(`
|
|
22371
22813
|
`).map((l) => l.trim()).filter(Boolean));
|
|
@@ -22393,25 +22835,25 @@ var ensureProjectGitignore = (workspaceRoot, confirmed) => {
|
|
|
22393
22835
|
|
|
22394
22836
|
// packages/workit-core/src/core/hygiene.ts
|
|
22395
22837
|
import { existsSync as existsSync10, readFileSync as readFileSync7, readdirSync as readdirSync5, writeFileSync as writeFileSync5 } from "node:fs";
|
|
22396
|
-
import
|
|
22838
|
+
import path29 from "node:path";
|
|
22397
22839
|
var repoRoot3 = assetRoot();
|
|
22398
|
-
var templatesDir = () =>
|
|
22840
|
+
var templatesDir = () => path29.join(repoRoot3, "templates", "hygiene");
|
|
22399
22841
|
var packageJson = (root) => {
|
|
22400
|
-
if (!existsSync10(
|
|
22842
|
+
if (!existsSync10(path29.join(root, "package.json")))
|
|
22401
22843
|
return null;
|
|
22402
22844
|
try {
|
|
22403
|
-
return JSON.parse(readFileSync7(
|
|
22845
|
+
return JSON.parse(readFileSync7(path29.join(root, "package.json"), "utf8"));
|
|
22404
22846
|
} catch {
|
|
22405
22847
|
return null;
|
|
22406
22848
|
}
|
|
22407
22849
|
};
|
|
22408
22850
|
var isOpenSource = (root) => {
|
|
22409
|
-
if (existsSync10(
|
|
22851
|
+
if (existsSync10(path29.join(root, "LICENSE")))
|
|
22410
22852
|
return true;
|
|
22411
22853
|
const pkg = packageJson(root);
|
|
22412
22854
|
if (pkg && !pkg.private)
|
|
22413
22855
|
return true;
|
|
22414
|
-
return
|
|
22856
|
+
return path29.basename(root) === "workflow-toolkit";
|
|
22415
22857
|
};
|
|
22416
22858
|
var licenseHolder = (root) => {
|
|
22417
22859
|
const pkg = packageJson(root);
|
|
@@ -22434,13 +22876,13 @@ var planHygieneFiles = (root, opts = {}) => {
|
|
|
22434
22876
|
files.push("LICENSE", "CONTRIBUTING.md");
|
|
22435
22877
|
const planned = [];
|
|
22436
22878
|
for (const file of files) {
|
|
22437
|
-
if (existsSync10(
|
|
22879
|
+
if (existsSync10(path29.join(root, file)))
|
|
22438
22880
|
continue;
|
|
22439
|
-
const tpl =
|
|
22881
|
+
const tpl = path29.join(templatesDir(), file);
|
|
22440
22882
|
if (!existsSync10(tpl))
|
|
22441
22883
|
continue;
|
|
22442
|
-
const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g,
|
|
22443
|
-
planned.push({ path:
|
|
22884
|
+
const content = readFileSync7(tpl, "utf8").replace(/<PROJECT>/g, path29.basename(root)).replace(/<YEAR>/g, String(new Date().getFullYear())).replace(/<HOLDER>\s*/g, licenseHolder(root));
|
|
22885
|
+
planned.push({ path: path29.join(root, file), content });
|
|
22444
22886
|
}
|
|
22445
22887
|
return planned;
|
|
22446
22888
|
};
|
|
@@ -22450,19 +22892,19 @@ var ensureHygieneFiles = (root, opts) => {
|
|
|
22450
22892
|
const created = [];
|
|
22451
22893
|
for (const planned of planHygieneFiles(root, opts)) {
|
|
22452
22894
|
writeFileSync5(planned.path, planned.content, "utf8");
|
|
22453
|
-
created.push(
|
|
22895
|
+
created.push(path29.basename(planned.path));
|
|
22454
22896
|
}
|
|
22455
22897
|
return { ok: true, created };
|
|
22456
22898
|
};
|
|
22457
22899
|
|
|
22458
22900
|
// packages/workit-core/src/core/init.ts
|
|
22459
|
-
import
|
|
22460
|
-
import
|
|
22461
|
-
import { spawnSync as
|
|
22901
|
+
import fs19 from "node:fs";
|
|
22902
|
+
import path31 from "node:path";
|
|
22903
|
+
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
22462
22904
|
|
|
22463
22905
|
// packages/workit-core/src/core/workspace-branch-policy.ts
|
|
22464
22906
|
import { mkdirSync as mkdirSync4, readFileSync as readFileSync8, renameSync as renameSync3, rmSync as rmSync2, writeFileSync as writeFileSync6 } from "node:fs";
|
|
22465
|
-
import
|
|
22907
|
+
import path30 from "node:path";
|
|
22466
22908
|
import { isDeepStrictEqual } from "node:util";
|
|
22467
22909
|
var readFileSafe = (p) => {
|
|
22468
22910
|
try {
|
|
@@ -22473,7 +22915,7 @@ var readFileSafe = (p) => {
|
|
|
22473
22915
|
};
|
|
22474
22916
|
function applyWorkspaceBranchPolicy(opts) {
|
|
22475
22917
|
const { workspace_root, env = process.env } = opts;
|
|
22476
|
-
const dir =
|
|
22918
|
+
const dir = path30.join(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
22477
22919
|
const {
|
|
22478
22920
|
status,
|
|
22479
22921
|
path: wsPath,
|
|
@@ -22485,7 +22927,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22485
22927
|
if (status === "malformed" || status === "invalid")
|
|
22486
22928
|
return { ok: false, error: workspacesError ?? `invalid workspaces.json: ${wsPath}` };
|
|
22487
22929
|
const detection = detectBranchPolicy(workspace_root);
|
|
22488
|
-
const name = String(env.WORKFLOW_BP_NAME ??
|
|
22930
|
+
const name = String(env.WORKFLOW_BP_NAME ?? path30.basename(workspace_root));
|
|
22489
22931
|
const integration = env.WORKFLOW_BP_INTEGRATION ?? detection.integration;
|
|
22490
22932
|
const policy = {
|
|
22491
22933
|
preset: detection.preset,
|
|
@@ -22534,7 +22976,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22534
22976
|
ok: false,
|
|
22535
22977
|
error: `workspaces.json changed while applying the branch policy: ${wsPath}`
|
|
22536
22978
|
};
|
|
22537
|
-
mkdirSync4(
|
|
22979
|
+
mkdirSync4(path30.dirname(wsPath), { recursive: true });
|
|
22538
22980
|
const tmp = `${wsPath}.${process.pid}.tmp`;
|
|
22539
22981
|
try {
|
|
22540
22982
|
writeFileSync6(tmp, JSON.stringify({ ...document, workspaces: next }, null, 2) + `
|
|
@@ -22569,7 +23011,7 @@ function applyWorkspaceBranchPolicy(opts) {
|
|
|
22569
23011
|
var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
|
|
22570
23012
|
var readJson = (p) => {
|
|
22571
23013
|
try {
|
|
22572
|
-
const parsed = JSON.parse(
|
|
23014
|
+
const parsed = JSON.parse(fs19.readFileSync(p, "utf8"));
|
|
22573
23015
|
return isConfigObject(parsed) ? parsed : null;
|
|
22574
23016
|
} catch {
|
|
22575
23017
|
return null;
|
|
@@ -22581,7 +23023,7 @@ var resolveInitProvider = (cwd) => {
|
|
|
22581
23023
|
return env.toLowerCase();
|
|
22582
23024
|
const root = cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
|
|
22583
23025
|
try {
|
|
22584
|
-
const out =
|
|
23026
|
+
const out = spawnSync19("git", ["remote", "get-url", "origin"], {
|
|
22585
23027
|
cwd: root,
|
|
22586
23028
|
encoding: "utf8"
|
|
22587
23029
|
});
|
|
@@ -22599,7 +23041,7 @@ var youtrackJsonContent = (dir) => {
|
|
|
22599
23041
|
const meetingIssue = process.env.WORKFLOW_YT_MEETING_ISSUE;
|
|
22600
23042
|
return {
|
|
22601
23043
|
baseUrl: process.env.WORKFLOW_YT_BASE_URL ?? "https://youtrack.example.com",
|
|
22602
|
-
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ??
|
|
23044
|
+
tokenFile: process.env.WORKFLOW_YT_TOKEN_FILE ?? path31.join(dir, "youtrack.token"),
|
|
22603
23045
|
...meetingIssue ? {
|
|
22604
23046
|
meetingIssue,
|
|
22605
23047
|
meetingIssues: { general: { issue: meetingIssue, label: "General meetings" } }
|
|
@@ -22629,19 +23071,19 @@ var vcsJsonContent = () => {
|
|
|
22629
23071
|
};
|
|
22630
23072
|
function initApplyData(action, env = process.env) {
|
|
22631
23073
|
const dir = String(env.WORKFLOW_TOOLKIT_CONFIG ?? configDir());
|
|
22632
|
-
|
|
23074
|
+
fs19.mkdirSync(dir, { recursive: true });
|
|
22633
23075
|
switch (action) {
|
|
22634
23076
|
case "youtrack_json": {
|
|
22635
|
-
const out =
|
|
22636
|
-
|
|
23077
|
+
const out = path31.join(dir, "youtrack.json");
|
|
23078
|
+
fs19.writeFileSync(out, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
22637
23079
|
`, "utf8");
|
|
22638
23080
|
return { action, ok: true, path: out };
|
|
22639
23081
|
}
|
|
22640
23082
|
case "youtrack_token_placeholder": {
|
|
22641
|
-
const p =
|
|
23083
|
+
const p = path31.join(dir, "youtrack.token");
|
|
22642
23084
|
const preserved = writeFileExclusive(p, TOKEN_PLACEHOLDER2 + `
|
|
22643
23085
|
`, 384) === "preserved";
|
|
22644
|
-
const abs =
|
|
23086
|
+
const abs = path31.resolve(p);
|
|
22645
23087
|
return {
|
|
22646
23088
|
action,
|
|
22647
23089
|
ok: true,
|
|
@@ -22653,14 +23095,14 @@ function initApplyData(action, env = process.env) {
|
|
|
22653
23095
|
};
|
|
22654
23096
|
}
|
|
22655
23097
|
case "youtrack_scaffold": {
|
|
22656
|
-
const jsonOut =
|
|
22657
|
-
const tokenOut =
|
|
22658
|
-
|
|
23098
|
+
const jsonOut = path31.join(dir, "youtrack.json");
|
|
23099
|
+
const tokenOut = path31.join(dir, "youtrack.token");
|
|
23100
|
+
fs19.writeFileSync(jsonOut, JSON.stringify(youtrackJsonContent(dir), null, 2) + `
|
|
22659
23101
|
`, "utf8");
|
|
22660
23102
|
const preserved = writeFileExclusive(tokenOut, TOKEN_PLACEHOLDER2 + `
|
|
22661
23103
|
`, 384) === "preserved";
|
|
22662
|
-
const configPath =
|
|
22663
|
-
const tokenPath =
|
|
23104
|
+
const configPath = path31.resolve(jsonOut);
|
|
23105
|
+
const tokenPath = path31.resolve(tokenOut);
|
|
22664
23106
|
const prev = process.env.WORKFLOW_YOUTRACK_CONFIG;
|
|
22665
23107
|
process.env.WORKFLOW_YOUTRACK_CONFIG = configPath;
|
|
22666
23108
|
let tokenCreate = {};
|
|
@@ -22712,10 +23154,10 @@ function initApplyData(action, env = process.env) {
|
|
|
22712
23154
|
}
|
|
22713
23155
|
}
|
|
22714
23156
|
case "vcs_scaffold": {
|
|
22715
|
-
const jsonOut =
|
|
22716
|
-
|
|
23157
|
+
const jsonOut = path31.join(dir, "vcs.json");
|
|
23158
|
+
fs19.writeFileSync(jsonOut, JSON.stringify(vcsJsonContent(), null, 2) + `
|
|
22717
23159
|
`, "utf8");
|
|
22718
|
-
const configPath =
|
|
23160
|
+
const configPath = path31.resolve(jsonOut);
|
|
22719
23161
|
const prev = process.env.WORKFLOW_VCS_CONFIG;
|
|
22720
23162
|
process.env.WORKFLOW_VCS_CONFIG = configPath;
|
|
22721
23163
|
try {
|
|
@@ -22834,7 +23276,7 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
22834
23276
|
protectedNames: args.branch_policy_protected
|
|
22835
23277
|
}, current);
|
|
22836
23278
|
writeConfig(next);
|
|
22837
|
-
return output(ok({ action: "config", path:
|
|
23279
|
+
return output(ok({ action: "config", path: path32.join(configDir(), "config.json"), ...next }));
|
|
22838
23280
|
}
|
|
22839
23281
|
const env = Object.fromEntries(Object.entries({
|
|
22840
23282
|
WORKFLOW_YT_BASE_URL: args.base_url,
|
|
@@ -22851,14 +23293,14 @@ var executeInitApply = (args, directory, runtime) => {
|
|
|
22851
23293
|
|
|
22852
23294
|
// packages/workit-opencode/src/shared/session.ts
|
|
22853
23295
|
import { realpathSync as realpathSync7 } from "node:fs";
|
|
22854
|
-
import
|
|
23296
|
+
import path33 from "node:path";
|
|
22855
23297
|
var sameWorkspace = (expected, observed) => {
|
|
22856
23298
|
if (typeof observed !== "string" || !observed)
|
|
22857
23299
|
return false;
|
|
22858
23300
|
try {
|
|
22859
23301
|
return realpathSync7(expected) === realpathSync7(observed);
|
|
22860
23302
|
} catch {
|
|
22861
|
-
return
|
|
23303
|
+
return path33.resolve(expected) === path33.resolve(observed);
|
|
22862
23304
|
}
|
|
22863
23305
|
};
|
|
22864
23306
|
|
|
@@ -22930,7 +23372,7 @@ var workitFamilyOf = (toolName) => {
|
|
|
22930
23372
|
|
|
22931
23373
|
// packages/workit-opencode/src/tools/context.ts
|
|
22932
23374
|
import { realpathSync as realpathSync8 } from "node:fs";
|
|
22933
|
-
import
|
|
23375
|
+
import path34 from "node:path";
|
|
22934
23376
|
var createContextTool = () => ({
|
|
22935
23377
|
description: "Read Git, pull request, YouTrack, issue, changelog, release, or affected-file context.",
|
|
22936
23378
|
execute: async (args, context) => {
|
|
@@ -22940,7 +23382,7 @@ var createContextTool = () => ({
|
|
|
22940
23382
|
let root;
|
|
22941
23383
|
try {
|
|
22942
23384
|
const cwd = parsed.data.cwd;
|
|
22943
|
-
root = realpathSync8(cwd ?
|
|
23385
|
+
root = realpathSync8(cwd ? path34.resolve(context.directory, cwd) : context.directory);
|
|
22944
23386
|
} catch {
|
|
22945
23387
|
return JSON.stringify(failure2("invalid_input", "context target directory cannot be resolved"), null, 2);
|
|
22946
23388
|
}
|
|
@@ -23484,8 +23926,9 @@ var evaluateShellPermission = (root, event, workdirs = new Map) => {
|
|
|
23484
23926
|
event.message = reason;
|
|
23485
23927
|
};
|
|
23486
23928
|
const resources = event.resources.filter((resource) => typeof resource === "string");
|
|
23929
|
+
const owner = liveOwner({ host: "opencode", session: { id: event.sessionID ?? "", parentId: null } }, "in-process-plugin");
|
|
23487
23930
|
if (WRITE_ACTIONS.has(event.action)) {
|
|
23488
|
-
const decision = writeGate(root, resources);
|
|
23931
|
+
const decision = writeGate(root, resources, owner);
|
|
23489
23932
|
if (decision.kind === "deny")
|
|
23490
23933
|
deny(decision.reason);
|
|
23491
23934
|
return;
|
|
@@ -23507,9 +23950,8 @@ var evaluateShellPermission = (root, event, workdirs = new Map) => {
|
|
|
23507
23950
|
}, resource, { nudge: false });
|
|
23508
23951
|
if (raw.kind === "deny")
|
|
23509
23952
|
return deny(raw.reason);
|
|
23510
|
-
const
|
|
23511
|
-
|
|
23512
|
-
if (gated?.kind === "deny")
|
|
23953
|
+
const gated = shellGate(cwd, resource, owner);
|
|
23954
|
+
if (gated.kind === "deny")
|
|
23513
23955
|
return deny(gated.reason);
|
|
23514
23956
|
}
|
|
23515
23957
|
};
|
|
@@ -23533,7 +23975,7 @@ var childTaskMissing = (store, input) => {
|
|
|
23533
23975
|
var childDenial = (identity) => `OpenCode child session has no validated Workit worker: as ${identity} it may only record evidence or findings (workit_evidence/workit_finding action "record") on an existing task; for anything else the lead assigns it a worker with workit_worker before launching it`;
|
|
23534
23976
|
|
|
23535
23977
|
// packages/workit-opencode/src/v2/shell.ts
|
|
23536
|
-
import
|
|
23978
|
+
import path35 from "node:path";
|
|
23537
23979
|
var commandOf = (input) => {
|
|
23538
23980
|
if (input === null || typeof input !== "object")
|
|
23539
23981
|
return null;
|
|
@@ -23542,7 +23984,7 @@ var commandOf = (input) => {
|
|
|
23542
23984
|
};
|
|
23543
23985
|
var hookInput = (root, sessionID, id, shell) => ({
|
|
23544
23986
|
host: "opencode",
|
|
23545
|
-
cwd: shell.workdir ?
|
|
23987
|
+
cwd: shell.workdir ? path35.resolve(root, shell.workdir) : root,
|
|
23546
23988
|
session: { id: sessionID, agentId: null, agentType: null, parentId: null },
|
|
23547
23989
|
permissionMode: null,
|
|
23548
23990
|
transcriptPath: null,
|
|
@@ -23727,37 +24169,37 @@ var createStopControl = (root, port) => {
|
|
|
23727
24169
|
|
|
23728
24170
|
// packages/workit-opencode/src/v2/registry.ts
|
|
23729
24171
|
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "node:fs";
|
|
23730
|
-
import
|
|
24172
|
+
import path37 from "node:path";
|
|
23731
24173
|
|
|
23732
24174
|
// packages/workit-opencode/src/shared/assets.ts
|
|
23733
24175
|
import { existsSync as existsSync11, realpathSync as realpathSync9 } from "node:fs";
|
|
23734
|
-
import
|
|
24176
|
+
import path36 from "node:path";
|
|
23735
24177
|
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
23736
24178
|
var moduleDir = () => {
|
|
23737
24179
|
try {
|
|
23738
|
-
return realpathSync9(
|
|
24180
|
+
return realpathSync9(path36.dirname(fileURLToPath2(import.meta.url)));
|
|
23739
24181
|
} catch {
|
|
23740
|
-
return
|
|
24182
|
+
return path36.dirname(fileURLToPath2(import.meta.url));
|
|
23741
24183
|
}
|
|
23742
24184
|
};
|
|
23743
24185
|
var packageRoot2 = () => {
|
|
23744
24186
|
const dir = moduleDir();
|
|
23745
24187
|
for (const candidate of [
|
|
23746
|
-
|
|
23747
|
-
|
|
23748
|
-
|
|
24188
|
+
path36.resolve(dir, ".."),
|
|
24189
|
+
path36.resolve(dir, "..", ".."),
|
|
24190
|
+
path36.resolve(dir, "..", "..", "..")
|
|
23749
24191
|
]) {
|
|
23750
|
-
if (existsSync11(
|
|
24192
|
+
if (existsSync11(path36.join(candidate, "package.json")))
|
|
23751
24193
|
return candidate;
|
|
23752
24194
|
}
|
|
23753
|
-
return
|
|
24195
|
+
return path36.resolve(dir, "..");
|
|
23754
24196
|
};
|
|
23755
24197
|
var skillsRoot = () => {
|
|
23756
24198
|
const root = packageRoot2();
|
|
23757
|
-
const canonical =
|
|
23758
|
-
if (existsSync11(
|
|
24199
|
+
const canonical = path36.resolve(root, "..", "workit-core", "skills");
|
|
24200
|
+
if (existsSync11(path36.join(root, "src")) && existsSync11(canonical))
|
|
23759
24201
|
return canonical;
|
|
23760
|
-
return
|
|
24202
|
+
return path36.join(root, "assets", "skills");
|
|
23761
24203
|
};
|
|
23762
24204
|
|
|
23763
24205
|
// packages/workit-opencode/src/v2/registry.ts
|
|
@@ -23784,11 +24226,11 @@ var registerSkills = async (ctx) => {
|
|
|
23784
24226
|
if (!existsSync12(skillsDir))
|
|
23785
24227
|
return new Set;
|
|
23786
24228
|
const listed = await ctx.skill.list().catch(() => ({ data: [] }));
|
|
23787
|
-
const own = (skill) => typeof skill.path === "string" &&
|
|
24229
|
+
const own = (skill) => typeof skill.path === "string" && path37.resolve(skill.path).startsWith(skillsDir + path37.sep);
|
|
23788
24230
|
const userSkills = new Set((listed?.data ?? []).filter((skill) => !own(skill)).map((skill) => skill.id));
|
|
23789
24231
|
const packaged = new Map;
|
|
23790
24232
|
for (const id of WORKIT_METHOD_SKILLS) {
|
|
23791
|
-
const file =
|
|
24233
|
+
const file = path37.join(skillsDir, id, "SKILL.md");
|
|
23792
24234
|
if (!userSkills.has(id) && existsSync12(file))
|
|
23793
24235
|
packaged.set(id, file);
|
|
23794
24236
|
}
|
|
@@ -23836,22 +24278,22 @@ var registerCommands = async (ctx, registeredSkills) => {
|
|
|
23836
24278
|
|
|
23837
24279
|
// packages/workit-opencode/src/stale-sources.ts
|
|
23838
24280
|
import { existsSync as existsSync13, readdirSync as readdirSync6 } from "node:fs";
|
|
23839
|
-
import
|
|
24281
|
+
import path38 from "node:path";
|
|
23840
24282
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
23841
24283
|
var typescriptFiles = (directory) => {
|
|
23842
24284
|
if (!existsSync13(directory))
|
|
23843
24285
|
return [];
|
|
23844
24286
|
return readdirSync6(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
23845
|
-
const file =
|
|
24287
|
+
const file = path38.join(directory, entry.name);
|
|
23846
24288
|
return entry.isDirectory() ? typescriptFiles(file) : entry.isFile() && file.endsWith(".ts") ? [file] : [];
|
|
23847
24289
|
});
|
|
23848
24290
|
};
|
|
23849
24291
|
var pluginSourceFiles = [
|
|
23850
24292
|
...new Set([
|
|
23851
24293
|
fileURLToPath3(import.meta.url),
|
|
23852
|
-
...typescriptFiles(
|
|
23853
|
-
|
|
23854
|
-
...typescriptFiles(
|
|
24294
|
+
...typescriptFiles(path38.join(packageRoot2(), "src")),
|
|
24295
|
+
path38.join(packageRoot2(), "..", "workit-core", "src", "core.ts"),
|
|
24296
|
+
...typescriptFiles(path38.join(packageRoot2(), "..", "workit-core", "src", "core"))
|
|
23855
24297
|
])
|
|
23856
24298
|
].filter((file) => existsSync13(file)).toSorted();
|
|
23857
24299
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-opencode",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.14.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit OpenCode plugin — native tools, adaptive policy, and sixteen method skills",
|
|
6
6
|
"keywords": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"typecheck": "tsc --noEmit"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@brainervirus/workit-core": "^8.
|
|
42
|
+
"@brainervirus/workit-core": "^8.14.0",
|
|
43
43
|
"@opencode/plugin": "2.0.18"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|