@brainervirus/workit-cursor 1.0.12 → 1.1.1

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.
@@ -7655,7 +7655,13 @@ var init_task_contract = __esm(() => {
7655
7655
  id
7656
7656
  }).strict(),
7657
7657
  object({ kind: literal("host"), host: hostSchema, handle: nonEmpty }).strict(),
7658
- object({ kind: literal("external"), url: text.url() }).strict()
7658
+ object({ kind: literal("external"), url: text.url() }).strict(),
7659
+ object({
7660
+ kind: literal("standing"),
7661
+ workspace: nonEmpty,
7662
+ class: nonEmpty,
7663
+ configDigest: nullableDigest
7664
+ }).strict()
7659
7665
  ]);
7660
7666
  provenanceSchema = object({
7661
7667
  kind: _enum(["host_observed", "agent_reported", "imported"]),
@@ -7876,7 +7882,8 @@ var init_task_contract = __esm(() => {
7876
7882
  approvedContent: text,
7877
7883
  displayed: text.optional(),
7878
7884
  contentRefs: array(refSchema),
7879
- statedChoice: object({ ref: text, text }).strict().optional()
7885
+ statedChoice: object({ ref: text, text }).strict().optional(),
7886
+ standing: object({ workspace: nonEmpty, class: nonEmpty }).strict().optional()
7880
7887
  }).strict(),
7881
7888
  digest,
7882
7889
  response: _enum(["approved", "rejected", "stated"]),
@@ -9794,10 +9801,360 @@ var init_workers = __esm(() => {
9794
9801
  UNCERTAIN_WORKER_STATES = ["dispatching", "running", "cancelling", "unknown"];
9795
9802
  });
9796
9803
 
9797
- // packages/workit-core/src/core/authority.ts
9804
+ // packages/workit-core/src/core/boundary.ts
9805
+ var EVENT, errorDetail = (err) => {
9806
+ if (err instanceof Error) {
9807
+ return { error_name: err.name, error: err.message };
9808
+ }
9809
+ return { error_name: "unknown", error: String(err) };
9810
+ };
9811
+ var init_boundary = __esm(() => {
9812
+ EVENT = {
9813
+ initialization: "initialization",
9814
+ provenance: "provenance",
9815
+ assets: "assets",
9816
+ configurationSource: "configuration_source",
9817
+ hooks: "hooks",
9818
+ mcpConnection: "mcp_connection",
9819
+ migration: "migration",
9820
+ installSteps: "install_steps",
9821
+ uncaughtFailure: "uncaught_failure",
9822
+ toolsFailed: "tools_failed",
9823
+ doctor: "doctor"
9824
+ };
9825
+ });
9826
+
9827
+ // packages/workit-core/src/core/config.ts
9828
+ import {
9829
+ copyFileSync,
9830
+ cpSync,
9831
+ existsSync as existsSync3,
9832
+ mkdirSync as mkdirSync2,
9833
+ readFileSync as readFileSync2,
9834
+ readdirSync as readdirSync2,
9835
+ writeFileSync as writeFileSync2
9836
+ } from "node:fs";
9837
+ import os from "node:os";
9838
+ import path5 from "node:path";
9839
+ var diagnosticLogger, PRESETS, resolveConfigDir = () => process.env.WORKFLOW_TOOLKIT_CONFIG ?? process.env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path5.join(process.env.XDG_CONFIG_HOME || path5.join(os.homedir(), ".config"), "workit"), migratedDir = null, migrationFailed = false, ensureConfigDir = (dir = resolveConfigDir()) => {
9840
+ if (migratedDir === dir)
9841
+ return dir;
9842
+ if (process.env.WORKFLOW_TOOLKIT_CONFIG || process.env.WORKFLOW_TOOLKIT_CONFIG_DIR) {
9843
+ migratedDir = dir;
9844
+ return dir;
9845
+ }
9846
+ const legacy = path5.join(process.env.XDG_CONFIG_HOME || path5.join(os.homedir(), ".config"), "workflow-toolkit");
9847
+ if (!existsSync3(legacy)) {
9848
+ migratedDir = dir;
9849
+ return dir;
9850
+ }
9851
+ if (!migrationFailed && existsSync3(dir)) {
9852
+ migratedDir = dir;
9853
+ return dir;
9854
+ }
9855
+ migrationFailed = false;
9856
+ mkdirSync2(dir, { recursive: true });
9857
+ diagnosticLogger?.info(EVENT.migration, { from: legacy, to: dir });
9858
+ for (const entry of readdirSync2(legacy, { withFileTypes: true })) {
9859
+ const src = path5.join(legacy, entry.name);
9860
+ const dest = path5.join(dir, entry.name);
9861
+ if (existsSync3(dest))
9862
+ continue;
9863
+ try {
9864
+ if (entry.isDirectory())
9865
+ cpSync(src, dest, { recursive: true });
9866
+ else if (entry.isFile())
9867
+ copyFileSync(src, dest);
9868
+ } catch (err) {
9869
+ migrationFailed = true;
9870
+ diagnosticLogger?.warn(EVENT.migration, { from: src, ok: false, ...errorDetail(err) });
9871
+ console.warn(`[workit] config migration: a file could not be copied to ${dir}; it will be retried on the next run`);
9872
+ }
9873
+ }
9874
+ if (!migrationFailed)
9875
+ migratedDir = dir;
9876
+ return dir;
9877
+ }, configDir = () => ensureConfigDir(), DEFAULTS, isConfigObject = (value) => value !== null && typeof value === "object" && !Array.isArray(value);
9878
+ var init_config = __esm(() => {
9879
+ init_boundary();
9880
+ PRESETS = {
9881
+ gitflow: {
9882
+ allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
9883
+ protected: ["main", "develop", "master", "prod", "production"]
9884
+ },
9885
+ "github-flow": { allowed: ["*"], protected: ["main"] },
9886
+ "trunk-based": { allowed: ["*"], protected: ["main"] },
9887
+ custom: { allowed: [], protected: [] }
9888
+ };
9889
+ DEFAULTS = {
9890
+ locale: "en",
9891
+ localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
9892
+ timezone: "America/Santiago",
9893
+ branchPolicy: {
9894
+ preset: "gitflow",
9895
+ allowed: [...PRESETS.gitflow.allowed],
9896
+ protected: [...PRESETS.gitflow.protected]
9897
+ },
9898
+ commitPolicy: { preset: "conventional" }
9899
+ };
9900
+ });
9901
+
9902
+ // packages/workit-core/src/core/workspaces.ts
9903
+ import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
9904
+ import path6 from "node:path";
9905
+ var readWorkspacesResult = (dir = configDir()) => {
9906
+ const file = path6.join(dir, "workspaces.json");
9907
+ let raw;
9908
+ try {
9909
+ raw = readFileSync3(file, "utf8");
9910
+ } catch {
9911
+ return { status: "missing", path: file, entries: [] };
9912
+ }
9913
+ let parsed;
9914
+ try {
9915
+ parsed = JSON.parse(raw);
9916
+ } catch {
9917
+ return { status: "malformed", path: file, entries: [], error: `${file} is not valid JSON` };
9918
+ }
9919
+ if (!isConfigObject(parsed)) {
9920
+ return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
9921
+ }
9922
+ const list = parsed.workspaces;
9923
+ return {
9924
+ status: "valid",
9925
+ path: file,
9926
+ entries: Array.isArray(list) ? list : []
9927
+ };
9928
+ }, loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries, globToRegExp = (glob) => {
9929
+ let out = "";
9930
+ for (let i = 0;i < glob.length; i++) {
9931
+ const c = glob[i];
9932
+ if (c === "*") {
9933
+ if (glob[i + 1] === "*") {
9934
+ if (glob[i + 2] === "/") {
9935
+ if (out === "")
9936
+ out += "/?";
9937
+ out += "(?:[^/]+/)*";
9938
+ i += 2;
9939
+ } else {
9940
+ if (i + 2 >= glob.length) {
9941
+ if (out.endsWith("/")) {
9942
+ out = out.slice(0, -1);
9943
+ out += "(?:/.*)?";
9944
+ } else {
9945
+ out += ".*";
9946
+ }
9947
+ } else {
9948
+ out += ".*";
9949
+ }
9950
+ i++;
9951
+ }
9952
+ } else {
9953
+ out += "[^/]*";
9954
+ }
9955
+ } else {
9956
+ out += c.replace(/[.+?^${}()|[\]\\]/g, "\\$&");
9957
+ }
9958
+ }
9959
+ return new RegExp(`^${out}$`);
9960
+ }, matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/")), realpathOf = (p) => {
9961
+ const real = realpathSync2.native ?? realpathSync2;
9962
+ try {
9963
+ return real(p);
9964
+ } catch {
9965
+ return p;
9966
+ }
9967
+ }, canonicalGlob = (glob) => {
9968
+ const m = /[*?[\]{]/.exec(glob);
9969
+ const prefix = m ? glob.slice(0, m.index) : glob;
9970
+ const rest = m ? glob.slice(m.index) : "";
9971
+ if (!prefix)
9972
+ return glob;
9973
+ const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
9974
+ return real + (prefix.endsWith("/") ? "/" : "") + rest;
9975
+ }, resolveWorkspaceFrom = (cwd, dir) => {
9976
+ const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
9977
+ for (const entry of loadWorkspacesFrom(dir)) {
9978
+ if (!entry || typeof entry !== "object")
9979
+ continue;
9980
+ const ws = entry;
9981
+ if (typeof ws.glob !== "string" || !ws.glob)
9982
+ continue;
9983
+ const glob = ws.glob.replaceAll("\\", "/");
9984
+ const canonical = canonicalGlob(glob);
9985
+ for (const target of targets) {
9986
+ if (matchWorkspace(glob, target))
9987
+ return ws;
9988
+ if (canonical !== glob && matchWorkspace(canonical, target))
9989
+ return ws;
9990
+ }
9991
+ }
9992
+ return null;
9993
+ };
9994
+ var init_workspaces = __esm(() => {
9995
+ init_config();
9996
+ });
9997
+
9998
+ // packages/workit-core/src/core/git.ts
9999
+ var init_git = () => {};
10000
+
10001
+ // packages/workit-core/src/core/vcs-config.ts
10002
+ var init_vcs_config = __esm(() => {
10003
+ init_config();
10004
+ init_workspaces();
10005
+ init_branch();
10006
+ });
10007
+
10008
+ // packages/workit-core/src/core/branch.ts
10009
+ import { execFileSync } from "node:child_process";
10010
+ var verifyPushIdentity = (root, provider, account) => {
10011
+ if (!account || typeof account !== "string")
10012
+ return { ok: false, error: "push identity requires a configured area account" };
10013
+ const probe = provider === "gitlab" ? ["api", "user", "--jq", ".username"] : ["api", "user", "--jq", ".login"];
10014
+ const bin = provider === "gitlab" ? "glab" : "gh";
10015
+ let login;
10016
+ try {
10017
+ login = execFileSync(bin, [...probe], {
10018
+ cwd: root,
10019
+ encoding: "utf8",
10020
+ stdio: ["pipe", "pipe", "pipe"]
10021
+ }).trim();
10022
+ } catch {
10023
+ return { ok: false, error: `${bin} identity could not be resolved` };
10024
+ }
10025
+ if (!login || login.toLowerCase() !== account.toLowerCase())
10026
+ return {
10027
+ ok: false,
10028
+ error: `push identity ${login || "(unknown)"} does not match area account ${account}`
10029
+ };
10030
+ return { ok: true };
10031
+ };
10032
+ var init_branch = __esm(() => {
10033
+ init_git();
10034
+ init_config();
10035
+ init_workspaces();
10036
+ init_vcs_config();
10037
+ });
10038
+
10039
+ // packages/workit-core/src/core/auto-approval.ts
9798
10040
  import { createHash as createHash3 } from "node:crypto";
10041
+ import { readFileSync as readFileSync4 } from "node:fs";
10042
+ var AUTO_CLASSES, OPERATION_CLASS, operationAutoClass = (operation) => typeof operation === "string" ? OPERATION_CLASS[operation] ?? null : null, digestOf = (configDirPath) => {
10043
+ try {
10044
+ const raw = readFileSync4(`${configDirPath}/workspaces.json`, "utf8");
10045
+ return createHash3("sha256").update(raw).digest("hex");
10046
+ } catch {
10047
+ return null;
10048
+ }
10049
+ }, normalizeClasses = (value) => {
10050
+ if (value === true)
10051
+ return [...AUTO_CLASSES];
10052
+ if (!Array.isArray(value))
10053
+ return [];
10054
+ const seen = new Set;
10055
+ for (const item of value) {
10056
+ if (typeof item === "string" && AUTO_CLASSES.includes(item))
10057
+ seen.add(item);
10058
+ }
10059
+ return [...seen];
10060
+ }, resolveAutoApproval = (root, dir = configDir()) => {
10061
+ let match;
10062
+ try {
10063
+ match = resolveWorkspaceFrom(root, dir);
10064
+ } catch {
10065
+ return { status: "off" };
10066
+ }
10067
+ if (!match)
10068
+ return { status: "off" };
10069
+ const classes = normalizeClasses(match.autoApprove);
10070
+ if (classes.length === 0)
10071
+ return { status: "off" };
10072
+ return {
10073
+ status: "on",
10074
+ workspace: match.name,
10075
+ classes,
10076
+ account: typeof match.vcs?.account === "string" && match.vcs.account ? match.vcs.account : null,
10077
+ configDigest: digestOf(dir)
10078
+ };
10079
+ }, isStandingReceipt = (value) => typeof value === "object" && value !== null && value.kind === "standing", standingApprovalLive = (root, receipt, cls, dir) => {
10080
+ if (!isStandingReceipt(receipt))
10081
+ return false;
10082
+ if (typeof cls !== "string" || !AUTO_CLASSES.includes(cls))
10083
+ return false;
10084
+ if (receipt.class !== cls)
10085
+ return false;
10086
+ const resolved = resolveAutoApproval(root, dir);
10087
+ if (resolved.status !== "on")
10088
+ return false;
10089
+ if (resolved.workspace !== receipt.workspace)
10090
+ return false;
10091
+ return resolved.classes.includes(cls);
10092
+ }, standingReceiptFor = (root, workspace, cls, dir) => {
10093
+ const resolved = resolveAutoApproval(root, dir);
10094
+ return {
10095
+ kind: "standing",
10096
+ workspace,
10097
+ class: cls,
10098
+ configDigest: resolved.status === "on" && resolved.workspace === workspace ? resolved.configDigest : null
10099
+ };
10100
+ }, verifyStandingApproval = (root, task, caller, binding) => {
10101
+ if (task.status !== "active")
10102
+ return failure("invalid_transition", "only active tasks can authorize actions");
10103
+ const session = task.intent?.provenance?.session;
10104
+ if (!session || session.kind !== "host" || session.host !== caller.host || session.handle !== caller.actor)
10105
+ return failure("permission_denied", "standing approval requires the lead session");
10106
+ const standing = binding.standing;
10107
+ if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
10108
+ return failure("invalid_input", "standing approval needs a workspace and class");
10109
+ let operation;
10110
+ try {
10111
+ operation = typeof binding.approvedContent === "string" ? JSON.parse(binding.approvedContent).operation : null;
10112
+ } catch {
10113
+ operation = null;
10114
+ }
10115
+ const cls = operationAutoClass(operation);
10116
+ if (!cls || cls !== standing.class)
10117
+ return failure("permission_denied", "standing approval does not cover this operation");
10118
+ const resolved = resolveAutoApproval(root);
10119
+ if (resolved.status !== "on" || resolved.workspace !== standing.workspace || !resolved.classes.includes(cls))
10120
+ return failure("permission_denied", "standing auto-approval is not live for this operation");
10121
+ if (cls === "push") {
10122
+ const workspace = resolveWorkspaceFrom(root, configDir());
10123
+ const provider = workspace?.vcs?.provider;
10124
+ const account = resolved.account;
10125
+ if (!provider || !account)
10126
+ return failure("permission_denied", "auto-push requires a workspace provider and area account");
10127
+ const identity = verifyPushIdentity(root, provider, account);
10128
+ if (!identity.ok)
10129
+ return failure("permission_denied", identity.error);
10130
+ }
10131
+ return success(null, null, {
10132
+ kind: "host_observed",
10133
+ host: caller.host,
10134
+ session: { kind: "host", host: caller.host, handle: caller.actor },
10135
+ workerId: null,
10136
+ receipts: [standingReceiptFor(root, standing.workspace, cls)]
10137
+ });
10138
+ };
10139
+ var init_auto_approval = __esm(() => {
10140
+ init_workspaces();
10141
+ init_config();
10142
+ init_branch();
10143
+ init_task_contract();
10144
+ AUTO_CLASSES = ["branch", "commit", "push", "pr", "merge"];
10145
+ OPERATION_CLASS = {
10146
+ "git.branch_setup": "branch",
10147
+ "git.commit": "commit",
10148
+ "git.push": "push",
10149
+ "hosting.pull_request": "pr",
10150
+ "hosting.merge": "merge"
10151
+ };
10152
+ });
10153
+
10154
+ // packages/workit-core/src/core/authority.ts
10155
+ import { createHash as createHash4 } from "node:crypto";
9799
10156
  import * as fs5 from "node:fs";
9800
- import path5 from "node:path";
10157
+ import path7 from "node:path";
9801
10158
  function applicableDecision(task, purpose, binding, checkoutRoot) {
9802
10159
  if (binding.taskId !== task.id || binding.workspaceId !== task.workspaceId)
9803
10160
  return [];
@@ -10199,7 +10556,7 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
10199
10556
  const record = verifiedAuthorities.get(authority);
10200
10557
  verifiedAuthorities.delete(authority);
10201
10558
  return record?.provenance ?? null;
10202
- }, authorityMatches = (authority, expected) => authority.kind === "action" && authority.taskId === expected.taskId && authority.workspaceId === expected.workspaceId && authority.decisionId === expected.decisionId && sameRef(authority.actionRef, expected.actionRef) && authority.taskRevision === expected.taskRevision && authority.workspaceRevision === expected.workspaceRevision && authority.outcome === expected.outcome, authorityDecisionMatches = (authority, decision) => authority.purpose === decision.purpose && authority.response === decision.response && authority.digest === decision.digest && canonicalJson(authority.binding) === canonicalJson(decision.binding) && canonicalJson(authority.requirementIds) === canonicalJson(decision.requirementIds), provenanceMatches = (left, right) => left.kind === "host_observed" && right.kind === "host_observed" && left.host === right.host && canonicalJson(left.session) === canonicalJson(right.session), digestBytes2 = (bytes) => createHash3("sha256").update(bytes).digest("hex"), verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
10559
+ }, authorityMatches = (authority, expected) => authority.kind === "action" && authority.taskId === expected.taskId && authority.workspaceId === expected.workspaceId && authority.decisionId === expected.decisionId && sameRef(authority.actionRef, expected.actionRef) && authority.taskRevision === expected.taskRevision && authority.workspaceRevision === expected.workspaceRevision && authority.outcome === expected.outcome, authorityDecisionMatches = (authority, decision) => authority.purpose === decision.purpose && authority.response === decision.response && authority.digest === decision.digest && canonicalJson(authority.binding) === canonicalJson(decision.binding) && canonicalJson(authority.requirementIds) === canonicalJson(decision.requirementIds), provenanceMatches = (left, right) => left.kind === "host_observed" && right.kind === "host_observed" && left.host === right.host && canonicalJson(left.session) === canonicalJson(right.session), digestBytes2 = (bytes) => createHash4("sha256").update(bytes).digest("hex"), verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
10203
10560
  let root;
10204
10561
  try {
10205
10562
  root = fs5.realpathSync(checkoutRoot);
@@ -10211,23 +10568,23 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
10211
10568
  continue;
10212
10569
  if (!reference.digest)
10213
10570
  return failure("invalid_input", "document references require a byte digest");
10214
- const target = path5.resolve(root, reference.path);
10215
- if (target !== root && !target.startsWith(`${root}${path5.sep}`))
10571
+ const target = path7.resolve(root, reference.path);
10572
+ if (target !== root && !target.startsWith(`${root}${path7.sep}`))
10216
10573
  return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
10217
10574
  fields: [
10218
10575
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
10219
10576
  ]
10220
10577
  });
10221
10578
  try {
10222
- const relative = path5.relative(root, target);
10579
+ const relative = path7.relative(root, target);
10223
10580
  let current = root;
10224
- for (const segment of relative.split(path5.sep)) {
10581
+ for (const segment of relative.split(path7.sep)) {
10225
10582
  if (!segment)
10226
10583
  continue;
10227
- current = path5.join(current, segment);
10584
+ current = path7.join(current, segment);
10228
10585
  const stat = fs5.lstatSync(current);
10229
10586
  const real = fs5.realpathSync(current);
10230
- if (real !== root && !real.startsWith(`${root}${path5.sep}`))
10587
+ if (real !== root && !real.startsWith(`${root}${path7.sep}`))
10231
10588
  return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
10232
10589
  fields: [
10233
10590
  {
@@ -10277,6 +10634,20 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
10277
10634
  return failure("permission_denied", "decision was rejected");
10278
10635
  if (entry.provenance.kind !== "host_observed" || entry.provenance.receipts.length === 0 || !provenanceMatches(entry.provenance, authority.provenance))
10279
10636
  return failure("permission_denied", "action approval lacks native receipt assurance");
10637
+ const receipts = entry.provenance.receipts;
10638
+ const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
10639
+ const questionBacked = receipts.some((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "host");
10640
+ if (!questionBacked && standing.length > 0) {
10641
+ let operation;
10642
+ try {
10643
+ operation = JSON.parse(decision.binding.approvedContent).operation;
10644
+ } catch {
10645
+ operation = null;
10646
+ }
10647
+ const cls = operationAutoClass(operation);
10648
+ if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls)))
10649
+ return failure("permission_denied", "standing auto-approval is not live for this operation");
10650
+ }
10280
10651
  if (decision.revoked)
10281
10652
  return failure("permission_denied", "decision is revoked");
10282
10653
  if (decision.digest !== decisionDigest(decision))
@@ -10300,6 +10671,7 @@ var verifiedAuthorities, trustedAuthority = (kind, provenance, expected, binding
10300
10671
  return success(null, null, { entry, workflow });
10301
10672
  }, bindingCovers, verifyDecisionContent;
10302
10673
  var init_authority = __esm(() => {
10674
+ init_auto_approval();
10303
10675
  init_task_contract();
10304
10676
  verifiedAuthorities = new WeakMap;
10305
10677
  bindingCovers = scopeCovers;
@@ -10307,10 +10679,10 @@ var init_authority = __esm(() => {
10307
10679
  });
10308
10680
 
10309
10681
  // packages/workit-core/src/core/task-evaluation.ts
10310
- import { createHash as createHash4 } from "node:crypto";
10682
+ import { createHash as createHash5 } from "node:crypto";
10311
10683
  import * as fs6 from "node:fs";
10312
10684
  import { spawnSync } from "node:child_process";
10313
- import path6 from "node:path";
10685
+ import path8 from "node:path";
10314
10686
  function captureCandidate(root, scope, environment = []) {
10315
10687
  let checkout;
10316
10688
  try {
@@ -10352,7 +10724,7 @@ function captureCandidate(root, scope, environment = []) {
10352
10724
  for (const item of [...names].sort()) {
10353
10725
  if (!scopeMatches(item, normalizedScope))
10354
10726
  continue;
10355
- const target = path6.join(checkout, item);
10727
+ const target = path8.join(checkout, item);
10356
10728
  try {
10357
10729
  const stat = fs6.lstatSync(target);
10358
10730
  if (stat.isSymbolicLink()) {
@@ -10623,12 +10995,12 @@ function evaluateClosure(requestedOutcome, view) {
10623
10995
  requirementIds: evaluations.map((item) => item.requirementId)
10624
10996
  });
10625
10997
  }
10626
- var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path6.sep}`), relative = (root, candidate) => path6.relative(root, candidate).split(path6.sep).join("/") || ".", canonicalPath = (value) => {
10998
+ var digestBytes3 = (value) => createHash5("sha256").update(value).digest("hex"), inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path8.sep}`), relative = (root, candidate) => path8.relative(root, candidate).split(path8.sep).join("/") || ".", canonicalPath = (value) => {
10627
10999
  if (typeof value !== "string" || !value)
10628
11000
  return null;
10629
11001
  if (value.includes("\\") || value.split("/").includes(".."))
10630
11002
  return null;
10631
- const normalized = path6.posix.normalize(value);
11003
+ const normalized = path8.posix.normalize(value);
10632
11004
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
10633
11005
  return null;
10634
11006
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -10670,14 +11042,14 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
10670
11042
  git: /not a git repository/i.test(stderr) ? false : true
10671
11043
  };
10672
11044
  }
10673
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path6.sep).join("/")) : [];
11045
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path8.sep).join("/")) : [];
10674
11046
  const stagedDeleted = spawnSync("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
10675
11047
  if (stagedDeleted.status !== 0) {
10676
11048
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
10677
11049
  if (!/not a git repository/i.test(stderr))
10678
11050
  return { paths, uncertain: true, git: true };
10679
11051
  } else if (stagedDeleted.stdout) {
10680
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path6.sep).join("/")));
11052
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path8.sep).join("/")));
10681
11053
  }
10682
11054
  return { paths: [...new Set(paths)], uncertain: false, git: true };
10683
11055
  }, walk = (root, directory, output) => {
@@ -10691,7 +11063,7 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
10691
11063
  for (const name of names) {
10692
11064
  if (name === ".git" || name === ".workit")
10693
11065
  continue;
10694
- const target = path6.join(directory, name);
11066
+ const target = path8.join(directory, name);
10695
11067
  let stat;
10696
11068
  try {
10697
11069
  stat = fs6.lstatSync(target);
@@ -10708,12 +11080,12 @@ var digestBytes3 = (value) => createHash4("sha256").update(value).digest("hex"),
10708
11080
  }, scopeRoots = (root, scope) => {
10709
11081
  const roots = [];
10710
11082
  for (const item of scope.paths.length ? scope.paths : ["."]) {
10711
- const target = path6.resolve(root, item);
11083
+ const target = path8.resolve(root, item);
10712
11084
  if (!inside(root, target))
10713
11085
  return failure("invalid_input", `candidate scope escapes checkout: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} is outside the checkout` }] });
10714
11086
  let ancestor = target;
10715
11087
  while (!fs6.existsSync(ancestor) && ancestor !== root)
10716
- ancestor = path6.dirname(ancestor);
11088
+ ancestor = path8.dirname(ancestor);
10717
11089
  try {
10718
11090
  if (!inside(root, fs6.realpathSync(ancestor)))
10719
11091
  return failure("invalid_input", `candidate scope escapes checkout through a symlink: ${item} — keep one task per repository and coordinate a separate linked task in the other checkout`, { fields: [{ path: "scope.paths", reason: `${item} resolves outside the checkout` }] });
@@ -10991,58 +11363,6 @@ var init_route_intent = __esm(() => {
10991
11363
  init_task_store();
10992
11364
  });
10993
11365
 
10994
- // packages/workit-core/src/core/git.ts
10995
- var init_git = () => {};
10996
-
10997
- // packages/workit-core/src/core/boundary.ts
10998
- var init_boundary = () => {};
10999
-
11000
- // packages/workit-core/src/core/config.ts
11001
- var PRESETS, DEFAULTS;
11002
- var init_config = __esm(() => {
11003
- init_boundary();
11004
- PRESETS = {
11005
- gitflow: {
11006
- allowed: ["feature/*", "bugfix/*", "hotfix/*", "release/*"],
11007
- protected: ["main", "develop", "master", "prod", "production"]
11008
- },
11009
- "github-flow": { allowed: ["*"], protected: ["main"] },
11010
- "trunk-based": { allowed: ["*"], protected: ["main"] },
11011
- custom: { allowed: [], protected: [] }
11012
- };
11013
- DEFAULTS = {
11014
- locale: "en",
11015
- localeOptions: ["en", "es-CL", "es-MX", "es-AR", "pt-BR"],
11016
- timezone: "America/Santiago",
11017
- branchPolicy: {
11018
- preset: "gitflow",
11019
- allowed: [...PRESETS.gitflow.allowed],
11020
- protected: [...PRESETS.gitflow.protected]
11021
- },
11022
- commitPolicy: { preset: "conventional" }
11023
- };
11024
- });
11025
-
11026
- // packages/workit-core/src/core/workspaces.ts
11027
- var init_workspaces = __esm(() => {
11028
- init_config();
11029
- });
11030
-
11031
- // packages/workit-core/src/core/vcs-config.ts
11032
- var init_vcs_config = __esm(() => {
11033
- init_config();
11034
- init_workspaces();
11035
- init_branch();
11036
- });
11037
-
11038
- // packages/workit-core/src/core/branch.ts
11039
- var init_branch = __esm(() => {
11040
- init_git();
11041
- init_config();
11042
- init_workspaces();
11043
- init_vcs_config();
11044
- });
11045
-
11046
11366
  // packages/workit-core/src/core/runtime-identity.ts
11047
11367
  var init_runtime_identity = () => {};
11048
11368
 
@@ -11084,6 +11404,13 @@ var init_external_action = __esm(() => {
11084
11404
  babysit: boolean2().optional()
11085
11405
  }).strict()
11086
11406
  }).strict(),
11407
+ object({
11408
+ operation: literal("hosting.merge"),
11409
+ payload: object({
11410
+ target_branch: string2().min(1).optional(),
11411
+ source_branch: string2().min(1).optional()
11412
+ }).strict()
11413
+ }).strict(),
11087
11414
  object({
11088
11415
  operation: literal("youtrack.update"),
11089
11416
  payload: object({
@@ -11464,7 +11791,7 @@ var init_policy_resolver = __esm(() => {
11464
11791
  });
11465
11792
 
11466
11793
  // packages/workit-core/src/core/task-engine.ts
11467
- import { realpathSync as realpathSync4 } from "node:fs";
11794
+ import { realpathSync as realpathSync5 } from "node:fs";
11468
11795
 
11469
11796
  class WorkitCore {
11470
11797
  store;
@@ -11479,7 +11806,7 @@ class WorkitCore {
11479
11806
  }
11480
11807
  contextRootError() {
11481
11808
  try {
11482
- if (realpathSync4(this.context.root) !== this.store.root)
11809
+ if (realpathSync5(this.context.root) !== this.store.root)
11483
11810
  return failure("invalid_input", "operation context root does not match the task store root");
11484
11811
  } catch {
11485
11812
  return failure("invalid_input", "operation context root cannot be resolved");
@@ -11825,7 +12152,10 @@ class WorkitCore {
11825
12152
  observeDecision(request, observation) {
11826
12153
  return this.recordDecision(request, observation, true);
11827
12154
  }
11828
- recordDecision(request, nativeObservation, nativeRequired = false) {
12155
+ observeStandingDecision(request) {
12156
+ return this.recordDecision(request, undefined, true, true);
12157
+ }
12158
+ recordDecision(request, nativeObservation, nativeRequired = false, standing = false) {
11829
12159
  const root = this.contextRootError();
11830
12160
  if (!root.ok)
11831
12161
  return root;
@@ -11840,8 +12170,10 @@ class WorkitCore {
11840
12170
  return failure("permission_denied", "stated choices cannot authorize mutating actions");
11841
12171
  if (stated && !input.binding?.statedChoice)
11842
12172
  return failure("invalid_input", "stated choices require binding.statedChoice");
11843
- if (nativeRequired && nativeObservation === undefined && !stated)
12173
+ if (nativeRequired && nativeObservation === undefined && !stated && !standing)
11844
12174
  return failure("permission_denied", "native decision observation is required");
12175
+ if (standing && (!input.binding?.standing || input.purpose !== "action" || input.response !== "approved"))
12176
+ return failure("invalid_input", "standing approvals need an approved action binding with a standing rule");
11845
12177
  const task = this.store.readTask(input.taskId);
11846
12178
  if (!task.ok)
11847
12179
  return task;
@@ -11873,7 +12205,10 @@ class WorkitCore {
11873
12205
  consumption: null
11874
12206
  };
11875
12207
  const data = { ...base, digest: decisionDigest(base) };
11876
- const native = nativeRequired && !stated ? verifyNativeDecision(this.context.nativeAuthority, {
12208
+ const standingApproval = standing && !stated ? verifyStandingApproval(this.store.root, task.data, this.context.caller, input.binding) : null;
12209
+ if (standingApproval && !standingApproval.ok)
12210
+ return standingApproval;
12211
+ const native = nativeRequired && !stated && !standingApproval ? verifyNativeDecision(this.context.nativeAuthority, {
11877
12212
  observation: nativeObservation,
11878
12213
  expected: {
11879
12214
  taskId: task.data.id,
@@ -11904,7 +12239,7 @@ class WorkitCore {
11904
12239
  const entry = {
11905
12240
  id: newId(),
11906
12241
  recordedAt: mutation.now,
11907
- provenance: nativeProvenance ?? provenance(this.context, "agent_reported"),
12242
+ provenance: standingApproval?.ok === true ? standingApproval.data : nativeProvenance ?? provenance(this.context, "agent_reported"),
11908
12243
  data
11909
12244
  };
11910
12245
  return success(mutation.revision, null, {
@@ -12732,7 +13067,7 @@ class WorkitCore {
12732
13067
  if (view.task.workspaceId !== view.workspace.id)
12733
13068
  return failure("invalid_input", "task and workspace bindings are invalid");
12734
13069
  try {
12735
- if (realpathSync4(view.workspace.root) !== this.store.root)
13070
+ if (realpathSync5(view.workspace.root) !== this.store.root)
12736
13071
  return failure("invalid_input", "task view root does not match the task store");
12737
13072
  } catch {
12738
13073
  return failure("invalid_input", "task view root cannot be resolved");
@@ -13330,6 +13665,7 @@ var init_task_engine = __esm(() => {
13330
13665
  init_task_contract();
13331
13666
  init_authority();
13332
13667
  init_policy_resolver();
13668
+ init_auto_approval();
13333
13669
  init_task_context();
13334
13670
  init_workers();
13335
13671
  init_task_evaluation();
@@ -13345,6 +13681,7 @@ var init_core3 = __esm(() => {
13345
13681
  init_methods();
13346
13682
  init_route_intent();
13347
13683
  init_branch();
13684
+ init_auto_approval();
13348
13685
  init_runtime_identity();
13349
13686
  init_authority();
13350
13687
  init_authority();
@@ -13356,8 +13693,8 @@ var init_core3 = __esm(() => {
13356
13693
  });
13357
13694
 
13358
13695
  // packages/workit-cursor/hooks/workit-hook.ts
13359
- import { existsSync as existsSync4, realpathSync as realpathSync5 } from "node:fs";
13360
- import path7 from "node:path";
13696
+ import { existsSync as existsSync5, realpathSync as realpathSync6 } from "node:fs";
13697
+ import path9 from "node:path";
13361
13698
  var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapabilities = (availability = {}) => {
13362
13699
  const has = (name) => availability[name] === true;
13363
13700
  return [
@@ -13418,7 +13755,7 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
13418
13755
  if (!Array.isArray(roots) || roots.length !== 1 || !roots.every(nonEmpty2))
13419
13756
  return { ok: false, error: "exactly one workspace root is required" };
13420
13757
  const root = roots[0];
13421
- if (!path7.isAbsolute(root) || !existsSync4(root))
13758
+ if (!path9.isAbsolute(root) || !existsSync5(root))
13422
13759
  return { ok: false, error: "workspace root must be an existing absolute path" };
13423
13760
  const event = value.hook_event_name;
13424
13761
  const conversationId = nonEmpty2(value.conversation_id) ? value.conversation_id : undefined;
@@ -13553,7 +13890,7 @@ var hostRef = (handle) => ({ kind: "host", host: "cursor", handle }), cursorCapa
13553
13890
  return isRecord2(raw) && raw.hook_event_name === "sessionStart" ? {} : deny(parsed.error);
13554
13891
  }
13555
13892
  const input = parsed.data;
13556
- const root = realpathSync5(input.workspace_roots[0]);
13893
+ const root = realpathSync6(input.workspace_roots[0]);
13557
13894
  if (input.hook_event_name === "sessionStart") {
13558
13895
  const actor = input.session_id ?? input.conversation_id;
13559
13896
  let compact = "";