@brainervirus/workit-pi 8.24.0 → 8.26.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/worker.js CHANGED
@@ -6669,6 +6669,41 @@ var ENVELOPE = new Set([
6669
6669
  "judgment"
6670
6670
  ]);
6671
6671
  // packages/workit-core/src/core/config.ts
6672
+ import {
6673
+ copyFileSync,
6674
+ cpSync,
6675
+ existsSync,
6676
+ mkdirSync,
6677
+ readFileSync,
6678
+ readdirSync,
6679
+ writeFileSync
6680
+ } from "node:fs";
6681
+ import os from "node:os";
6682
+ import path from "node:path";
6683
+
6684
+ // packages/workit-core/src/core/boundary.ts
6685
+ var EVENT = {
6686
+ initialization: "initialization",
6687
+ provenance: "provenance",
6688
+ assets: "assets",
6689
+ configurationSource: "configuration_source",
6690
+ hooks: "hooks",
6691
+ mcpConnection: "mcp_connection",
6692
+ migration: "migration",
6693
+ installSteps: "install_steps",
6694
+ uncaughtFailure: "uncaught_failure",
6695
+ toolsFailed: "tools_failed",
6696
+ doctor: "doctor"
6697
+ };
6698
+ var errorDetail = (err) => {
6699
+ if (err instanceof Error) {
6700
+ return { error_name: err.name, error: err.message };
6701
+ }
6702
+ return { error_name: "unknown", error: String(err) };
6703
+ };
6704
+
6705
+ // packages/workit-core/src/core/config.ts
6706
+ var diagnosticLogger;
6672
6707
  var PRESETS = {
6673
6708
  gitflow: {
6674
6709
  allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
@@ -6678,6 +6713,49 @@ var PRESETS = {
6678
6713
  "trunk-based": { allowed: ["*"], protected: ["main"] },
6679
6714
  custom: { allowed: [], protected: [] }
6680
6715
  };
6716
+ var resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "workit");
6717
+ var migratedDir = null;
6718
+ var migrationFailed = false;
6719
+ var ensureConfigDir = (dir = resolveConfigDir()) => {
6720
+ if (migratedDir === dir)
6721
+ return dir;
6722
+ if (process.env.WORKFLOW_TOOLKIT_CONFIG || process.env.WORKFLOW_TOOLKIT_CONFIG_DIR) {
6723
+ migratedDir = dir;
6724
+ return dir;
6725
+ }
6726
+ const legacy = path.join(process.env.XDG_CONFIG_HOME || path.join(os.homedir(), ".config"), "workflow-toolkit");
6727
+ if (!existsSync(legacy)) {
6728
+ migratedDir = dir;
6729
+ return dir;
6730
+ }
6731
+ if (!migrationFailed && existsSync(dir)) {
6732
+ migratedDir = dir;
6733
+ return dir;
6734
+ }
6735
+ migrationFailed = false;
6736
+ mkdirSync(dir, { recursive: true });
6737
+ diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
6738
+ for (const entry of readdirSync(legacy, { withFileTypes: true })) {
6739
+ const src = path.join(legacy, entry.name);
6740
+ const dest = path.join(dir, entry.name);
6741
+ if (existsSync(dest))
6742
+ continue;
6743
+ try {
6744
+ if (entry.isDirectory())
6745
+ cpSync(src, dest, { recursive: true });
6746
+ else if (entry.isFile())
6747
+ copyFileSync(src, dest);
6748
+ } catch (err) {
6749
+ migrationFailed = true;
6750
+ diagnosticLogger?.warn(EVENT.migration, { from: src, ok: false, ...errorDetail(err) });
6751
+ console.warn(`[workit] config migration: a file could not be copied to ${dir}; it will be retried on the next run`);
6752
+ }
6753
+ }
6754
+ if (!migrationFailed)
6755
+ migratedDir = dir;
6756
+ return dir;
6757
+ };
6758
+ var configDir = () => ensureConfigDir();
6681
6759
  var DEFAULTS = {
6682
6760
  locale: "en",
6683
6761
  localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
@@ -6699,7 +6777,9 @@ var COMMIT_PRESETS = [
6699
6777
  ];
6700
6778
 
6701
6779
  // packages/workit-core/src/core/workspaces.ts
6702
- import path from "node:path";
6780
+ import { spawnSync } from "node:child_process";
6781
+ import { readFileSync as readFileSync2, realpathSync } from "node:fs";
6782
+ import path2 from "node:path";
6703
6783
  var nonBlank = string2().refine((value) => value.trim().length > 0, "must not be blank");
6704
6784
  var branchPreset = custom((value) => typeof value === "string" && Object.hasOwn(PRESETS, value), "unsupported branch preset");
6705
6785
  var commitPreset = custom((value) => typeof value === "string" && COMMIT_PRESETS.includes(value), "unsupported commit preset");
@@ -6757,7 +6837,7 @@ var releaseTrackSchema = object({
6757
6837
  context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
6758
6838
  }
6759
6839
  }
6760
- if (track.versionSource?.kind === "package-json" && (path.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
6840
+ if (track.versionSource?.kind === "package-json" && (path2.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
6761
6841
  context.addIssue({
6762
6842
  code: "custom",
6763
6843
  path: ["versionSource", "path"],
@@ -6781,6 +6861,7 @@ var workspaceConfigSchema = object({
6781
6861
  trailers: boolean2().optional(),
6782
6862
  liveSessionGuard: boolean2().optional(),
6783
6863
  fanout: object({ tierModels: unknown().optional() }).passthrough().optional(),
6864
+ models: object({ roles: unknown().optional() }).passthrough().optional(),
6784
6865
  autonomy: object(Object.fromEntries(["push", "pr", "merge", "release", "rerun"].map((kind) => [
6785
6866
  kind,
6786
6867
  union([boolean2(), literal("verified")]).optional()
@@ -6807,7 +6888,10 @@ var workspaceConfigSchema = object({
6807
6888
  });
6808
6889
  }
6809
6890
  });
6810
- var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).optional() }).passthrough().superRefine((file, context) => {
6891
+ var workspacesFileSchema = object({
6892
+ workspaces: array(workspaceConfigSchema).optional(),
6893
+ models: object({ roles: unknown().optional() }).passthrough().optional()
6894
+ }).passthrough().superRefine((file, context) => {
6811
6895
  const names = new Set;
6812
6896
  for (const [index, workspace] of (file.workspaces ?? []).entries()) {
6813
6897
  if (names.has(workspace.name)) {
@@ -6820,10 +6904,213 @@ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).opt
6820
6904
  names.add(workspace.name);
6821
6905
  }
6822
6906
  });
6907
+ var globToRegExp = (glob) => {
6908
+ let out = "";
6909
+ for (let i = 0;i < glob.length; i++) {
6910
+ const c = glob[i];
6911
+ if (c === "*") {
6912
+ if (glob[i + 1] === "*") {
6913
+ if (glob[i + 2] === "/") {
6914
+ if (out === "")
6915
+ out += "/?";
6916
+ out += "(?:[^/]+/)*";
6917
+ i += 2;
6918
+ } else {
6919
+ if (i + 2 >= glob.length) {
6920
+ if (out.endsWith("/")) {
6921
+ out = out.slice(0, -1);
6922
+ out += "(?:/.*)?";
6923
+ } else {
6924
+ out += ".*";
6925
+ }
6926
+ } else {
6927
+ out += ".*";
6928
+ }
6929
+ i++;
6930
+ }
6931
+ } else {
6932
+ out += "[^/]*";
6933
+ }
6934
+ } else {
6935
+ out += c.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
6936
+ }
6937
+ }
6938
+ return new RegExp(`^${out}$`);
6939
+ };
6940
+ var matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/"));
6941
+ var UNSUPPORTED_GLOB = /[?[\]{}]/;
6942
+ var UNSUPPORTED_EXTGLOB = /[@+*]\(|!/;
6943
+ var validateWorkspaceGlob = (glob) => {
6944
+ const trimmed = glob.trim();
6945
+ if (!trimmed)
6946
+ return { ok: false, error: "workspace pattern is required" };
6947
+ const m = UNSUPPORTED_GLOB.exec(trimmed);
6948
+ if (m) {
6949
+ return {
6950
+ ok: false,
6951
+ error: `unsupported glob character ${JSON.stringify(m[0])} in workspace pattern ${JSON.stringify(glob)} — the matcher supports * and ** only (e.g. /work/**)`
6952
+ };
6953
+ }
6954
+ const ext = UNSUPPORTED_EXTGLOB.exec(trimmed);
6955
+ if (ext) {
6956
+ const token = ext[0];
6957
+ const kind = token === "!" ? "negation (!)" : `extglob (${token})`;
6958
+ return {
6959
+ ok: false,
6960
+ error: `unsupported glob ${kind} in workspace pattern ${JSON.stringify(glob)} — the matcher supports * and ** only (e.g. /work/**)`
6961
+ };
6962
+ }
6963
+ return { ok: true };
6964
+ };
6965
+
6966
+ class WorkspaceConfigError extends Error {
6967
+ name = "WorkspaceConfigError";
6968
+ }
6823
6969
  var mainCheckouts = new Map;
6970
+ var mainCheckoutOf = (cwd) => {
6971
+ const cached = mainCheckouts.get(cwd);
6972
+ if (cached !== undefined)
6973
+ return cached;
6974
+ const env = { ...process.env };
6975
+ for (const name of ["GIT_DIR", "GIT_WORK_TREE", "GIT_COMMON_DIR", "GIT_INDEX_FILE"])
6976
+ delete env[name];
6977
+ const git = (args) => {
6978
+ const run = spawnSync("git", args, {
6979
+ cwd,
6980
+ env,
6981
+ encoding: "utf8",
6982
+ timeout: 5000,
6983
+ windowsHide: true
6984
+ });
6985
+ return run.status === 0 ? run.stdout : null;
6986
+ };
6987
+ let main = null;
6988
+ const dirs = git(["rev-parse", "--path-format=absolute", "--git-dir", "--git-common-dir"]);
6989
+ const [gitDir, common] = (dirs ?? "").trim().split(/\r?\n/u);
6990
+ if (gitDir && common && path2.resolve(gitDir) !== path2.resolve(common)) {
6991
+ const listed = /^worktree (.+)$/mu.exec(git(["worktree", "list", "--porcelain"]) ?? "")?.[1];
6992
+ main = listed?.trim() || (path2.basename(common) === ".git" ? path2.dirname(common) : common);
6993
+ }
6994
+ mainCheckouts.set(cwd, main);
6995
+ return main;
6996
+ };
6997
+ var matchingEntries = (cwd, entries) => {
6998
+ const match = (where) => {
6999
+ const targets = [where, realpathOf(where)].map((value) => value.replaceAll("\\", "/"));
7000
+ return entries.filter((entry) => {
7001
+ const glob = entry.glob.replaceAll("\\", "/");
7002
+ const canonical = canonicalGlob(glob);
7003
+ return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
7004
+ });
7005
+ };
7006
+ const direct = match(cwd);
7007
+ if (direct.length)
7008
+ return direct;
7009
+ const main = mainCheckoutOf(cwd);
7010
+ return main ? match(main) : direct;
7011
+ };
7012
+ var realpathOf = (p) => {
7013
+ const real = realpathSync.native ?? realpathSync;
7014
+ try {
7015
+ return real(p);
7016
+ } catch {
7017
+ return p;
7018
+ }
7019
+ };
7020
+ var canonicalGlob = (glob) => {
7021
+ const m = /[*?[\]{]/.exec(glob);
7022
+ const prefix = m ? glob.slice(0, m.index) : glob;
7023
+ const rest = m ? glob.slice(m.index) : "";
7024
+ if (!prefix)
7025
+ return glob;
7026
+ const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
7027
+ return real + (prefix.endsWith("/") ? "/" : "") + rest;
7028
+ };
7029
+ var globSpecificity = (glob) => glob.replaceAll("\\", "/").split("/").filter((segment) => segment.length > 0 && !/[*?[\]{]/.test(segment)).length;
7030
+ var selectWorkspaceMatch = (matches, cwd, workspaceName) => {
7031
+ if (workspaceName !== undefined) {
7032
+ const named = matches.filter((entry) => entry.name === workspaceName);
7033
+ if (named.length === 1)
7034
+ return named[0];
7035
+ if (named.length > 1)
7036
+ throw new WorkspaceConfigError(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
7037
+ if (matches.length > 0) {
7038
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
7039
+ }
7040
+ throw new WorkspaceConfigError(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
7041
+ }
7042
+ if (matches.length < 2)
7043
+ return matches[0];
7044
+ const highestSpecificity = Math.max(...matches.map((entry) => globSpecificity(entry.glob)));
7045
+ const mostSpecific = matches.filter((entry) => globSpecificity(entry.glob) === highestSpecificity);
7046
+ if (mostSpecific.length > 1) {
7047
+ throw new WorkspaceConfigError(`ambiguous workspace for ${cwd}; choose one of: ${mostSpecific.map((entry) => entry.name).join(", ")}`);
7048
+ }
7049
+ return mostSpecific[0];
7050
+ };
6824
7051
  var runtimeWorkspaceIndexSchema = object({
6825
7052
  workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
6826
7053
  }).passthrough();
7054
+ var matchRuntimeWorkspace = (cwd, dir = configDir()) => {
7055
+ const file = path2.join(dir, "workspaces.json");
7056
+ let raw;
7057
+ try {
7058
+ raw = readFileSync2(file, "utf8");
7059
+ } catch (error) {
7060
+ if (error.code === "ENOENT")
7061
+ return null;
7062
+ throw new WorkspaceConfigError(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`, { cause: error });
7063
+ }
7064
+ let parsed;
7065
+ try {
7066
+ parsed = JSON.parse(raw);
7067
+ } catch {
7068
+ throw new WorkspaceConfigError(`${file} is not valid JSON`);
7069
+ }
7070
+ const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
7071
+ if (!validated.success) {
7072
+ const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
7073
+ throw new WorkspaceConfigError(`${file} has invalid workspace matching data: ${detail}`);
7074
+ }
7075
+ const entries = validated.data.workspaces;
7076
+ for (const entry of entries ?? []) {
7077
+ const glob = validateWorkspaceGlob(entry.glob);
7078
+ if (!glob.ok)
7079
+ throw new WorkspaceConfigError(`${file} workspace ${entry.name}: ${glob.error}`);
7080
+ }
7081
+ const matches = matchingEntries(cwd, entries ?? []);
7082
+ const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
7083
+ const selected = selectWorkspaceMatch(matches, cwd, workspaceName);
7084
+ return selected ? { file, selected, matches } : null;
7085
+ };
7086
+ var modelSources = (cwd, dir) => {
7087
+ const out = [];
7088
+ try {
7089
+ const matched = matchRuntimeWorkspace(cwd, dir);
7090
+ if (matched) {
7091
+ const enclosing = matched.matches.filter((entry) => entry !== matched.selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
7092
+ for (const entry of [matched.selected, ...enclosing])
7093
+ out.push({
7094
+ name: entry.name,
7095
+ global: false,
7096
+ models: entry.models,
7097
+ tierModels: entry.fanout?.tierModels
7098
+ });
7099
+ }
7100
+ } catch {}
7101
+ try {
7102
+ const document = JSON.parse(readFileSync2(path2.join(dir ?? configDir(), "workspaces.json"), "utf8"));
7103
+ if (document && typeof document === "object" && !Array.isArray(document))
7104
+ out.push({
7105
+ name: "global",
7106
+ global: true,
7107
+ models: document.models,
7108
+ tierModels: undefined
7109
+ });
7110
+ } catch {}
7111
+ return out;
7112
+ };
7113
+ var hostFamily = (host) => host.replace(/_(?:cli|desktop)$/u, "");
6827
7114
  var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
6828
7115
  var TIER_HOSTS = new Set([
6829
7116
  "claude_code",
@@ -6837,11 +7124,11 @@ var TIER_HOSTS = new Set([
6837
7124
  ]);
6838
7125
 
6839
7126
  // packages/workit-core/src/git/rev.ts
6840
- import { spawnSync } from "node:child_process";
7127
+ import { spawnSync as spawnSync2 } from "node:child_process";
6841
7128
  var MAX_BUFFER = 256 * 1024 * 1024;
6842
7129
  var GIT_TIMEOUTS = { local: 20000, worktree: 120000, network: 15000 };
6843
7130
  var git = (cwd, args, options = {}) => {
6844
- const result = spawnSync("git", args, {
7131
+ const result = spawnSync2("git", args, {
6845
7132
  cwd,
6846
7133
  encoding: "utf8",
6847
7134
  env: options.env ?? process.env,
@@ -6890,7 +7177,7 @@ var memo2 = new Map;
6890
7177
  var TRACKERS = new Set(["github", "gitlab", "youtrack"]);
6891
7178
 
6892
7179
  // packages/workit-core/src/core/logger.ts
6893
- import os from "node:os";
7180
+ import os2 from "node:os";
6894
7181
  var SENSITIVE_WORDS = new Set([
6895
7182
  "token",
6896
7183
  "secret",
@@ -6929,15 +7216,15 @@ var SENSITIVE_WORDS = new Set([
6929
7216
  ]);
6930
7217
  var STACK_WORDS = new Set(["stack", "stacktrace", "trace"]);
6931
7218
  var escapeRegExp = (value) => value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6932
- var homePattern = new RegExp(`${escapeRegExp(os.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
7219
+ var homePattern = new RegExp(`${escapeRegExp(os2.homedir())}(?=[/\\\\]|$)|\\$HOME(?=[/\\\\]|$)`, "g");
6933
7220
 
6934
7221
  // packages/workit-core/src/store/paths.ts
6935
- import { spawnSync as spawnSync2 } from "node:child_process";
7222
+ import { spawnSync as spawnSync3 } from "node:child_process";
6936
7223
  import fs from "node:fs";
6937
- import path2 from "node:path";
7224
+ import path3 from "node:path";
6938
7225
  var NOT_A_REPO = /not a git repository \(or any (of the parent directories|parent up to)/u;
6939
7226
  var gitRun = (cwd, args) => {
6940
- const run = spawnSync2("git", args, {
7227
+ const run = spawnSync3("git", args, {
6941
7228
  cwd,
6942
7229
  encoding: "utf8",
6943
7230
  timeout: 20000,
@@ -6975,14 +7262,14 @@ var DISCOVERY_ENV = [
6975
7262
  ];
6976
7263
  var isGitDir = (dir) => {
6977
7264
  try {
6978
- return fs.statSync(path2.join(dir, "HEAD")).isFile() && fs.statSync(path2.join(dir, "objects")).isDirectory() && fs.statSync(path2.join(dir, "refs")).isDirectory();
7265
+ return fs.statSync(path3.join(dir, "HEAD")).isFile() && fs.statSync(path3.join(dir, "objects")).isDirectory() && fs.statSync(path3.join(dir, "refs")).isDirectory();
6979
7266
  } catch {
6980
7267
  return false;
6981
7268
  }
6982
7269
  };
6983
7270
  var reftableRefs = (common) => {
6984
7271
  try {
6985
- return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs.readFileSync(path2.join(common, "config"), "utf8"));
7272
+ return /^\s*refstorage\s*=\s*reftable\s*$/imu.test(fs.readFileSync(path3.join(common, "config"), "utf8"));
6986
7273
  } catch {
6987
7274
  return false;
6988
7275
  }
@@ -7002,7 +7289,7 @@ function fastGit(root) {
7002
7289
  if (device !== null && stat.dev !== device)
7003
7290
  return { kind: "none" };
7004
7291
  device = stat.dev;
7005
- const dotGit = path2.join(dir, ".git");
7292
+ const dotGit = path3.join(dir, ".git");
7006
7293
  let entry = null;
7007
7294
  try {
7008
7295
  entry = fs.statSync(dotGit);
@@ -7023,19 +7310,19 @@ function fastGit(root) {
7023
7310
  const match = /^gitdir: (.+)$/mu.exec(text);
7024
7311
  if (!match)
7025
7312
  return null;
7026
- gitDir = path2.resolve(dir, match[1].trim());
7313
+ gitDir = path3.resolve(dir, match[1].trim());
7027
7314
  } else
7028
7315
  return null;
7029
- if (!fs.existsSync(path2.join(gitDir, "HEAD")))
7316
+ if (!fs.existsSync(path3.join(gitDir, "HEAD")))
7030
7317
  return null;
7031
7318
  } else if (isGitDir(dir))
7032
7319
  gitDir = dir;
7033
7320
  if (gitDir) {
7034
7321
  let common = gitDir;
7035
7322
  try {
7036
- common = path2.resolve(gitDir, fs.readFileSync(path2.join(gitDir, "commondir"), "utf8").trim());
7323
+ common = path3.resolve(gitDir, fs.readFileSync(path3.join(gitDir, "commondir"), "utf8").trim());
7037
7324
  } catch {}
7038
- if (!fs.existsSync(path2.join(common, "objects")))
7325
+ if (!fs.existsSync(path3.join(common, "objects")))
7039
7326
  return null;
7040
7327
  return {
7041
7328
  kind: "repo",
@@ -7045,26 +7332,26 @@ function fastGit(root) {
7045
7332
  reftable: reftableRefs(common)
7046
7333
  };
7047
7334
  }
7048
- const parent = path2.dirname(dir);
7335
+ const parent = path3.dirname(dir);
7049
7336
  if (parent === dir)
7050
7337
  return { kind: "none" };
7051
7338
  dir = parent;
7052
7339
  }
7053
7340
  }
7054
7341
  function resolveStore(root) {
7055
- root = path2.resolve(root);
7342
+ root = path3.resolve(root);
7056
7343
  let directory = false;
7057
7344
  try {
7058
7345
  directory = fs.statSync(root).isDirectory();
7059
7346
  } catch {}
7060
7347
  if (!directory)
7061
- return { dir: path2.join(root, ".workit"), shared: false, top: null };
7348
+ return { dir: path3.join(root, ".workit"), shared: false, top: null };
7062
7349
  const fast = fastGit(root);
7063
7350
  if (fast?.kind === "none")
7064
- return { dir: path2.join(root, ".workit"), shared: false, top: null };
7351
+ return { dir: path3.join(root, ".workit"), shared: false, top: null };
7065
7352
  if (fast?.kind === "repo")
7066
7353
  return {
7067
- dir: path2.join(fast.common, "workit"),
7354
+ dir: path3.join(fast.common, "workit"),
7068
7355
  shared: true,
7069
7356
  top: fast.top,
7070
7357
  git: { gitDir: fast.gitDir, top: fast.top, reftable: fast.reftable }
@@ -7074,13 +7361,13 @@ function resolveStore(root) {
7074
7361
  if (run.ok && common) {
7075
7362
  const top = gitRun(root, ["rev-parse", "--path-format=absolute", "--show-toplevel"]);
7076
7363
  return {
7077
- dir: path2.join(real(path2.resolve(root, common)), "workit"),
7364
+ dir: path3.join(real(path3.resolve(root, common)), "workit"),
7078
7365
  shared: true,
7079
7366
  top: top.ok && top.stdout.trim() ? real(top.stdout.trim()) : null
7080
7367
  };
7081
7368
  }
7082
7369
  if (run.missing || !run.ok && NOT_A_REPO.test(run.stderr))
7083
- return { dir: path2.join(root, ".workit"), shared: false, top: null };
7370
+ return { dir: path3.join(root, ".workit"), shared: false, top: null };
7084
7371
  return new Error(`cannot locate the workit store: git rev-parse failed (${run.stderr.trim().split(`
7085
7372
  `)[0] || "no output"}); fix the repository (git status should work here)`);
7086
7373
  }
@@ -7095,22 +7382,22 @@ function childSessionId(lead, child) {
7095
7382
  }
7096
7383
 
7097
7384
  // packages/workit-core/src/core/package-root.ts
7098
- import { existsSync } from "node:fs";
7099
- import path3 from "node:path";
7385
+ import { existsSync as existsSync2 } from "node:fs";
7386
+ import path4 from "node:path";
7100
7387
  import { fileURLToPath } from "node:url";
7101
7388
  var packageRoot = () => {
7102
- let dir = path3.dirname(fileURLToPath(import.meta.url));
7389
+ let dir = path4.dirname(fileURLToPath(import.meta.url));
7103
7390
  while (true) {
7104
- const parent = path3.dirname(dir);
7105
- if (existsSync(path3.join(dir, "package.json")) || parent === dir)
7391
+ const parent = path4.dirname(dir);
7392
+ if (existsSync2(path4.join(dir, "package.json")) || parent === dir)
7106
7393
  return dir;
7107
7394
  dir = parent;
7108
7395
  }
7109
7396
  };
7110
7397
  var assetRoot = () => {
7111
7398
  const root = packageRoot();
7112
- const assets = path3.join(root, "assets");
7113
- return existsSync(assets) ? assets : root;
7399
+ const assets = path4.join(root, "assets");
7400
+ return existsSync2(assets) ? assets : root;
7114
7401
  };
7115
7402
 
7116
7403
  // packages/workit-core/src/core/store-lock.ts
@@ -7162,41 +7449,139 @@ var STALE_TEMPORARY_MS = 60 * 60000;
7162
7449
  var origins = new Map;
7163
7450
  var heldInProcess = new Map;
7164
7451
  // packages/workit-core/src/ledger.ts
7165
- import { spawnSync as spawnSync3 } from "node:child_process";
7452
+ import { spawnSync as spawnSync4 } from "node:child_process";
7166
7453
  import { createHash as createHash3, randomBytes } from "node:crypto";
7167
7454
  import fs3 from "node:fs";
7168
- import path5 from "node:path";
7455
+ import path6 from "node:path";
7456
+
7457
+ // packages/workit-core/src/core/policy/substance.ts
7458
+ var SLASH_BLOCK = [["/*", "*/"]];
7459
+ var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
7460
+ var SYNTAXES = Object.fromEntries([
7461
+ ...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
7462
+ ...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
7463
+ ext,
7464
+ { ...C_LIKE, triple: true }
7465
+ ]),
7466
+ ["go", { ...C_LIKE, rawBacktick: true }],
7467
+ ...["css", "scss", "less"].map((ext) => [
7468
+ ext,
7469
+ { line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
7470
+ ]),
7471
+ ...["py", "pyi"].map((ext) => [
7472
+ ext,
7473
+ { line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
7474
+ ]),
7475
+ ["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
7476
+ ...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
7477
+ ext,
7478
+ { line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
7479
+ ])
7480
+ ]);
7481
+ var REGEX_KEYWORDS = new Set([
7482
+ "return",
7483
+ "typeof",
7484
+ "instanceof",
7485
+ "case",
7486
+ "do",
7487
+ "else",
7488
+ "in",
7489
+ "of",
7490
+ "new",
7491
+ "delete",
7492
+ "void",
7493
+ "throw",
7494
+ "yield",
7495
+ "await"
7496
+ ]);
7497
+ var decoder = new TextDecoder("utf-8", { fatal: true });
7498
+
7499
+ // packages/workit-core/src/core/policy/lane.ts
7500
+ var RISK_TOKENS = new Set([
7501
+ "auth",
7502
+ "authn",
7503
+ "authz",
7504
+ "oauth",
7505
+ "security",
7506
+ "permission",
7507
+ "permissions",
7508
+ "secret",
7509
+ "secrets",
7510
+ "crypto",
7511
+ "gate",
7512
+ "gates",
7513
+ "policy",
7514
+ "policies",
7515
+ "hook",
7516
+ "hooks",
7517
+ "husky",
7518
+ "lefthook",
7519
+ "release",
7520
+ "releases",
7521
+ "migration",
7522
+ "migrations",
7523
+ "migrate",
7524
+ "workflows"
7525
+ ]);
7526
+ var RISK_FILES = new Set([
7527
+ ".gitlab-ci.yml",
7528
+ "Jenkinsfile",
7529
+ "azure-pipelines.yml",
7530
+ ".pre-commit-config.yaml",
7531
+ ".workit/risk-paths"
7532
+ ]);
7533
+ var RISK_NAMES = new Set([".gitattributes"]);
7534
+ var LOCKFILES = new Set([
7535
+ "bun.lock",
7536
+ "bun.lockb",
7537
+ "package-lock.json",
7538
+ "npm-shrinkwrap.json",
7539
+ "yarn.lock",
7540
+ "pnpm-lock.yaml",
7541
+ "Cargo.lock",
7542
+ "go.sum",
7543
+ "poetry.lock",
7544
+ "uv.lock",
7545
+ "Pipfile.lock",
7546
+ "Gemfile.lock",
7547
+ "composer.lock"
7548
+ ]);
7549
+ var globCache = new Map;
7550
+ var REGULAR = new Set(["100644", "100755"]);
7551
+
7552
+ // packages/workit-core/src/core/policy/carry.ts
7553
+ var TEST_ONLY_KINDS = new Set(["doc", "cosmetic", "generated", "test"]);
7169
7554
 
7170
7555
  // packages/workit-core/src/hooks/standing-signal.ts
7171
7556
  import { createHash as createHash2 } from "node:crypto";
7172
7557
  import fs2 from "node:fs";
7173
- import path4 from "node:path";
7558
+ import path5 from "node:path";
7174
7559
  var gitStoreDir = (cwd) => {
7175
7560
  try {
7176
- for (let dir = path4.resolve(cwd);; dir = path4.dirname(dir)) {
7177
- const dotGit = path4.join(dir, ".git");
7561
+ for (let dir = path5.resolve(cwd);; dir = path5.dirname(dir)) {
7562
+ const dotGit = path5.join(dir, ".git");
7178
7563
  if (fs2.existsSync(dotGit)) {
7179
7564
  let gitDir = dotGit;
7180
7565
  if (fs2.statSync(dotGit).isFile()) {
7181
7566
  const target = /^gitdir:\s*(.+)$/mu.exec(fs2.readFileSync(dotGit, "utf8"))?.[1]?.trim();
7182
7567
  if (!target)
7183
7568
  return null;
7184
- gitDir = path4.resolve(dir, target);
7185
- const common = path4.join(gitDir, "commondir");
7569
+ gitDir = path5.resolve(dir, target);
7570
+ const common = path5.join(gitDir, "commondir");
7186
7571
  if (fs2.existsSync(common))
7187
- gitDir = path4.resolve(gitDir, fs2.readFileSync(common, "utf8").trim());
7572
+ gitDir = path5.resolve(gitDir, fs2.readFileSync(common, "utf8").trim());
7188
7573
  }
7189
- return path4.join(gitDir, "workit");
7574
+ return path5.join(gitDir, "workit");
7190
7575
  }
7191
- if (path4.dirname(dir) === dir)
7576
+ if (path5.dirname(dir) === dir)
7192
7577
  return null;
7193
7578
  }
7194
7579
  } catch {
7195
7580
  return null;
7196
7581
  }
7197
7582
  };
7198
- var standingRevFile = (store) => path4.join(store, "standing", "rev");
7199
- var standingSeenFile = (store, session, agentId) => path4.join(store, "standing", "seen", `${createHash2("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`);
7583
+ var standingRevFile = (store) => path5.join(store, "standing", "rev");
7584
+ var standingSeenFile = (store, session, agentId) => path5.join(store, "standing", "seen", `${createHash2("sha256").update(`${session}\x00${agentId ?? ""}`).digest("hex").slice(0, 32)}.json`);
7200
7585
  var readStandingRev = (store) => {
7201
7586
  try {
7202
7587
  return fs2.readFileSync(standingRevFile(store), "utf8").trim() || null;
@@ -7206,7 +7591,7 @@ var readStandingRev = (store) => {
7206
7591
  };
7207
7592
  var writeStandingSeen = (file, seen) => {
7208
7593
  try {
7209
- fs2.mkdirSync(path4.dirname(file), { recursive: true });
7594
+ fs2.mkdirSync(path5.dirname(file), { recursive: true });
7210
7595
  fs2.writeFileSync(file, JSON.stringify(seen));
7211
7596
  } catch {}
7212
7597
  };
@@ -7229,7 +7614,7 @@ var err = (code, error, unblock) => ({
7229
7614
  ...unblock ? { unblock } : {}
7230
7615
  });
7231
7616
  var gitRun2 = (cwd, args, timeoutMs = GIT_TIMEOUTS.local) => {
7232
- const run = spawnSync3("git", args, {
7617
+ const run = spawnSync4("git", args, {
7233
7618
  cwd,
7234
7619
  encoding: "utf8",
7235
7620
  timeout: timeoutMs,
@@ -7257,14 +7642,14 @@ var git2 = (cwd, args) => {
7257
7642
  };
7258
7643
  var safeRef = (value) => value.length > 0 && !value.startsWith("-");
7259
7644
  function storeRoot(cwd) {
7260
- const location = resolveStore(path5.resolve(cwd));
7645
+ const location = resolveStore(path6.resolve(cwd));
7261
7646
  if (location instanceof Error)
7262
7647
  return err("unavailable", location.message.replace(/; fix the repository.*$/u, ""), "fix the repository (git status should work here) or run from a non-git directory");
7263
7648
  return { ok: true, value: { root: location.dir, shared: location.shared } };
7264
7649
  }
7265
7650
  function ledgerPath(cwd) {
7266
7651
  const root = storeRoot(cwd);
7267
- return root.ok ? { ok: true, value: path5.join(root.value.root, "ledger", "ledger.jsonl") } : root;
7652
+ return root.ok ? { ok: true, value: path6.join(root.value.root, "ledger", "ledger.jsonl") } : root;
7268
7653
  }
7269
7654
  var NETWORK_FS_TYPES = new Set([
7270
7655
  26985,
@@ -7506,6 +7891,7 @@ var SHELL_VOUCHED_HOSTS = new Set([
7506
7891
  "codex_cli",
7507
7892
  "codex_desktop"
7508
7893
  ]);
7894
+ var restackExact = new WeakMap;
7509
7895
  var fromLead = (row, leads) => leads === null || row.actor.session !== null && leads.includes(row.actor.session);
7510
7896
  function activeStanding(rows, fanout, leads = null) {
7511
7897
  const active = [];
@@ -7534,109 +7920,6 @@ var OBSERVED_ONLY = new Set([
7534
7920
  "policy.judged",
7535
7921
  ...PR_ROW_TYPES
7536
7922
  ]);
7537
- // packages/workit-core/src/forge/redact.ts
7538
- var KEY = String.raw`[A-Za-z0-9_.-]*?(?:token|secret|password|passwd|pwd|api[_-]?key|access[_-]?key|account[_-]?key|private[_-]?key|client[_-]?secret|credentials?|authorization|cookie|signature)[A-Za-z0-9_.-]*`;
7539
- var ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*["']?)((?:bearer|basic|token|digest)\s+)?([^\s"',;&]+)`, "giu");
7540
- var QUOTED_ASSIGNMENT = new RegExp(String.raw`(?<![A-Za-z0-9])(["']?${KEY}["']?\s*[:=]\s*)(["'])([^"'\n]*[^\s"'][^"'\n]*)\2`, "giu");
7541
- var ANSI = new RegExp(String.raw`\u001b\[[0-9;?]*[ -/]*[@-~]|\u001b\][^\u0007]*\u0007`, "gu");
7542
-
7543
- // packages/workit-core/src/git/ops.ts
7544
- var COMMIT_TIMEOUT_MS = 10 * 60000;
7545
-
7546
- // packages/workit-core/src/core/policy/substance.ts
7547
- var SLASH_BLOCK = [["/*", "*/"]];
7548
- var C_LIKE = { line: ["//"], block: SLASH_BLOCK, quotes: `"'` };
7549
- var SYNTAXES = Object.fromEntries([
7550
- ...["ts", "mts", "cts", "js", "mjs", "cjs"].map((ext) => [ext, { ...C_LIKE, js: true }]),
7551
- ...["java", "kt", "kts", "scala", "swift", "dart", "groovy"].map((ext) => [
7552
- ext,
7553
- { ...C_LIKE, triple: true }
7554
- ]),
7555
- ["go", { ...C_LIKE, rawBacktick: true }],
7556
- ...["css", "scss", "less"].map((ext) => [
7557
- ext,
7558
- { line: [], block: SLASH_BLOCK, quotes: `"'`, cssUrl: true }
7559
- ]),
7560
- ...["py", "pyi"].map((ext) => [
7561
- ext,
7562
- { line: ["#"], block: [], quotes: `"'`, triple: true, indented: true, fstring: true }
7563
- ]),
7564
- ["toml", { line: ["#"], block: [], quotes: `"`, literal: "'", triple: true }],
7565
- ...["html", "htm", "xml", "svg", "xhtml"].map((ext) => [
7566
- ext,
7567
- { line: [], block: [["<!--", "-->"]], quotes: "", indented: true }
7568
- ])
7569
- ]);
7570
- var REGEX_KEYWORDS = new Set([
7571
- "return",
7572
- "typeof",
7573
- "instanceof",
7574
- "case",
7575
- "do",
7576
- "else",
7577
- "in",
7578
- "of",
7579
- "new",
7580
- "delete",
7581
- "void",
7582
- "throw",
7583
- "yield",
7584
- "await"
7585
- ]);
7586
- var decoder = new TextDecoder("utf-8", { fatal: true });
7587
-
7588
- // packages/workit-core/src/core/policy/lane.ts
7589
- var RISK_TOKENS = new Set([
7590
- "auth",
7591
- "authn",
7592
- "authz",
7593
- "oauth",
7594
- "security",
7595
- "permission",
7596
- "permissions",
7597
- "secret",
7598
- "secrets",
7599
- "crypto",
7600
- "gate",
7601
- "gates",
7602
- "policy",
7603
- "policies",
7604
- "hook",
7605
- "hooks",
7606
- "husky",
7607
- "lefthook",
7608
- "release",
7609
- "releases",
7610
- "migration",
7611
- "migrations",
7612
- "migrate",
7613
- "workflows"
7614
- ]);
7615
- var RISK_FILES = new Set([
7616
- ".gitlab-ci.yml",
7617
- "Jenkinsfile",
7618
- "azure-pipelines.yml",
7619
- ".pre-commit-config.yaml",
7620
- ".workit/risk-paths"
7621
- ]);
7622
- var RISK_NAMES = new Set([".gitattributes"]);
7623
- var LOCKFILES = new Set([
7624
- "bun.lock",
7625
- "bun.lockb",
7626
- "package-lock.json",
7627
- "npm-shrinkwrap.json",
7628
- "yarn.lock",
7629
- "pnpm-lock.yaml",
7630
- "Cargo.lock",
7631
- "go.sum",
7632
- "poetry.lock",
7633
- "uv.lock",
7634
- "Pipfile.lock",
7635
- "Gemfile.lock",
7636
- "composer.lock"
7637
- ]);
7638
- var globCache = new Map;
7639
- var REGULAR = new Set(["100644", "100755"]);
7640
7923
  // packages/workit-core/src/hooks/raw-git.ts
7641
7924
  var SHELLS = new Set(["sh", "bash", "zsh", "dash", "ksh", "ash", "mksh", "yash", "fish"]);
7642
7925
  var POWERSHELLS = new Set(["pwsh", "powershell"]);
@@ -7825,11 +8108,97 @@ var contextReadSchema = object({
7825
8108
  planPath: string2().optional(),
7826
8109
  cwd: string2().min(1).optional()
7827
8110
  }).strict();
8111
+ // packages/workit-core/src/core/role-models.ts
8112
+ var MODEL_ROLES = [
8113
+ "decider",
8114
+ "implementer",
8115
+ "verifier",
8116
+ "reviewer",
8117
+ "explore",
8118
+ "babysit"
8119
+ ];
8120
+ var MODEL_TIERS = ["mundane", "standard", "hard"];
8121
+ var HOST_KEYS = new Set([
8122
+ "claude_code",
8123
+ "opencode",
8124
+ "codex",
8125
+ "codex_cli",
8126
+ "codex_desktop",
8127
+ "cursor",
8128
+ "pi",
8129
+ "default"
8130
+ ]);
8131
+ var BUILT_IN_CLAUDE_CODE = {
8132
+ decider: { model: "fable", effort: "high" },
8133
+ "implementer.hard": { model: "opus" },
8134
+ "implementer.standard": { model: "sonnet" },
8135
+ "implementer.mundane": { model: "haiku" },
8136
+ verifier: { model: "sonnet" },
8137
+ reviewer: { model: "opus" },
8138
+ explore: { model: "haiku" },
8139
+ babysit: { model: "haiku" }
8140
+ };
8141
+ var isObject2 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
8142
+ var name = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
8143
+ var leaf = (value) => {
8144
+ const text = name(value);
8145
+ if (text)
8146
+ return { model: text, effort: null };
8147
+ if (!isObject2(value))
8148
+ return null;
8149
+ const model = name(value.model);
8150
+ return model ? { model, effort: name(value.effort) } : null;
8151
+ };
8152
+ var isFlat = (value) => typeof value === "string" || isObject2(value) && Object.hasOwn(value, "model");
8153
+ var forHost = (value, host) => {
8154
+ if (isFlat(value))
8155
+ return leaf(value);
8156
+ if (!isObject2(value))
8157
+ return null;
8158
+ const family = host ? hostFamily(host) : null;
8159
+ for (const key of [family, host, "default"])
8160
+ if (key && Object.hasOwn(value, key)) {
8161
+ const found = leaf(value[key]);
8162
+ if (found)
8163
+ return found;
8164
+ }
8165
+ return null;
8166
+ };
8167
+ var rolesOf = (models) => isObject2(models) && isObject2(models.roles) ? models.roles : null;
8168
+ var resolveRoleModel = (cwd, selector, host, dir) => {
8169
+ const { role, tier } = selector;
8170
+ for (const source of modelSources(cwd, dir)) {
8171
+ const where = source.global ? "the global default" : `workspace ${source.name}`;
8172
+ const roles = rolesOf(source.models);
8173
+ const configured = roles && Object.hasOwn(roles, role) ? roles[role] : undefined;
8174
+ const fromRoles = role === "implementer" ? tier && isObject2(configured) && Object.hasOwn(configured, tier) ? forHost(configured[tier], host) : null : forHost(configured, host);
8175
+ if (fromRoles)
8176
+ return { ...fromRoles, source: `models.roles in ${where}` };
8177
+ if (role === "implementer" && tier && isObject2(source.tierModels)) {
8178
+ const fromTiers = Object.hasOwn(source.tierModels, tier) ? forHost(source.tierModels[tier], host) : null;
8179
+ if (fromTiers)
8180
+ return { model: fromTiers.model, effort: null, source: `fanout.tierModels in ${where}` };
8181
+ }
8182
+ }
8183
+ if (host && hostFamily(host) === "claude_code") {
8184
+ const builtIn = BUILT_IN_CLAUDE_CODE[tier ? `${role}.${tier}` : role];
8185
+ if (builtIn)
8186
+ return { model: builtIn.model, effort: builtIn.effort ?? null, source: "built-in" };
8187
+ }
8188
+ return null;
8189
+ };
8190
+ var uniformImplementerModel = (cwd, host, dir) => {
8191
+ const tiers = MODEL_TIERS.map((tier) => resolveRoleModel(cwd, { role: "implementer", tier }, host, dir));
8192
+ const [first] = tiers;
8193
+ return first && tiers.every((t) => t?.model === first.model && t.effort === first.effort) ? first : null;
8194
+ };
8195
+ var ROLE_SELECTORS = MODEL_ROLES.flatMap((role) => role === "implementer" ? MODEL_TIERS.map((tier) => ({ role, tier })) : [{ role }]);
8196
+
7828
8197
  // packages/workit-core/src/hooks/standing.ts
7829
8198
  import fs4 from "node:fs";
7830
- import path6 from "node:path";
8199
+ import path7 from "node:path";
7831
8200
  var readPlans = (store) => {
7832
- const dir = path6.join(store, "fanouts");
8201
+ const dir = path7.join(store, "fanouts");
7833
8202
  let names;
7834
8203
  try {
7835
8204
  names = fs4.readdirSync(dir).filter((name) => name.endsWith(".json"));
@@ -7839,7 +8208,7 @@ var readPlans = (store) => {
7839
8208
  const plans = [];
7840
8209
  for (const name of names) {
7841
8210
  try {
7842
- const raw = JSON.parse(fs4.readFileSync(path6.join(dir, name), "utf8"));
8211
+ const raw = JSON.parse(fs4.readFileSync(path7.join(dir, name), "utf8"));
7843
8212
  if (typeof raw.name !== "string" || !Array.isArray(raw.slices))
7844
8213
  continue;
7845
8214
  const texts = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string" && item !== "") : [];
@@ -7966,7 +8335,9 @@ var roleTools = (role) => {
7966
8335
  return [...readOnly, "write", "edit", ...familyTools];
7967
8336
  return [...readOnly, ...familyTools];
7968
8337
  };
8338
+ var roleModel = (cwd, role) => role === "implementer" ? uniformImplementerModel(cwd, "pi") : resolveRoleModel(cwd, { role: role === "investigator" ? "explore" : "reviewer" }, "pi");
7969
8339
  function workerCommand(runtime, assignment) {
8340
+ const configured = roleModel(runtime.root, assignment.role);
7970
8341
  return {
7971
8342
  command: runtime.node,
7972
8343
  args: [
@@ -7977,6 +8348,8 @@ function workerCommand(runtime, assignment) {
7977
8348
  ...runtime.sessionId ? ["--session-id", runtime.sessionId] : [],
7978
8349
  "--approve",
7979
8350
  "--offline",
8351
+ ...configured ? ["--model", configured.model] : [],
8352
+ ...configured?.effort ? ["--thinking", configured.effort] : [],
7980
8353
  "--no-context-files",
7981
8354
  "--tools",
7982
8355
  roleTools(assignment.role).join(","),