@brainervirus/workit-codex 1.2.0 → 1.3.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.
@@ -6621,13 +6621,13 @@ var require_dist = __commonJS(function(exports, module) {
6621
6621
  });
6622
6622
 
6623
6623
  // packages/workit-codex/scripts/launch-mcp.ts
6624
- import { existsSync as existsSync8, realpathSync as realpathSync8, statSync as statSync2 } from "node:fs";
6625
- import path15 from "node:path";
6624
+ import { existsSync as existsSync8, realpathSync as realpathSync9, statSync as statSync3 } from "node:fs";
6625
+ import path16 from "node:path";
6626
6626
 
6627
6627
  // packages/workit-core/src/core.ts
6628
6628
  import { spawnSync as spawnSync3 } from "node:child_process";
6629
- import { existsSync as existsSync5, realpathSync as realpathSync6 } from "node:fs";
6630
- import path10 from "node:path";
6629
+ import { existsSync as existsSync5, realpathSync as realpathSync7 } from "node:fs";
6630
+ import path11 from "node:path";
6631
6631
 
6632
6632
  // packages/workit-core/src/core/task-contract.ts
6633
6633
  import { createHash, randomUUID } from "node:crypto";
@@ -11934,6 +11934,17 @@ function _array(Class, element, params) {
11934
11934
  ...normalizeParams(params)
11935
11935
  });
11936
11936
  }
11937
+ function _custom(Class, fn, _params) {
11938
+ const norm = normalizeParams(_params);
11939
+ norm.abort ?? (norm.abort = true);
11940
+ const schema = new Class({
11941
+ type: "custom",
11942
+ check: "custom",
11943
+ fn,
11944
+ ...norm
11945
+ });
11946
+ return schema;
11947
+ }
11937
11948
  function _refine(Class, fn, _params) {
11938
11949
  const schema = new Class({
11939
11950
  type: "custom",
@@ -14087,6 +14098,9 @@ var ZodCustom = /* @__PURE__ */ $constructor("ZodCustom", (inst, def) => {
14087
14098
  ZodType.init(inst, def);
14088
14099
  inst._zod.processJSONSchema = (ctx, json, params) => customProcessor(inst, ctx, json, params);
14089
14100
  });
14101
+ function custom(fn, _params) {
14102
+ return _custom(ZodCustom, fn ?? (() => true), _params);
14103
+ }
14090
14104
  function refine(fn, _params = {}) {
14091
14105
  return _refine(ZodCustom, fn, _params);
14092
14106
  }
@@ -14520,7 +14534,8 @@ var decisionSchema = object({
14520
14534
  consumption: object({
14521
14535
  state: _enum(["reserved", "consumed", "uncertain"]),
14522
14536
  at: utc,
14523
- actionRef: refSchema
14537
+ actionRef: refSchema,
14538
+ historicalActionId: text.optional()
14524
14539
  }).strict().nullable()
14525
14540
  }).strict();
14526
14541
  var findingSchema = object({
@@ -14551,6 +14566,7 @@ var workerSchema = object({
14551
14566
  assignment: assignmentSchema,
14552
14567
  state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
14553
14568
  session: refSchema.nullable(),
14569
+ coordinator: refSchema.optional(),
14554
14570
  report: workerReportSchema.nullable()
14555
14571
  }).strict();
14556
14572
  var ownerSchema = object({
@@ -14662,6 +14678,7 @@ var taskSummarySchema = object({
14662
14678
  requirements: array(requirementEvaluationSchema),
14663
14679
  writer: workspaceRecordSchema.shape.writer
14664
14680
  }).strict();
14681
+ var taskListItemSchema = taskSummarySchema.omit({ policy: true, requirements: true }).extend({ source: provenanceSchema.pick({ host: true, kind: true }) }).strict();
14665
14682
  var exportBundleSchema = object({
14666
14683
  schemaVersion: literal(1),
14667
14684
  exportedAt: utc,
@@ -14683,11 +14700,16 @@ var taskOperations = {
14683
14700
  expectedWorkspaceRevision: revision.nullable().optional(),
14684
14701
  intent: intentSchema
14685
14702
  }),
14686
- list: operation({ action: literal("list") }),
14703
+ list: operation({
14704
+ action: literal("list"),
14705
+ status: _enum(["open", "closed", "all"]).optional(),
14706
+ query: string2().trim().min(1).max(200).optional(),
14707
+ limit: number2().int().min(1).max(50).optional()
14708
+ }),
14687
14709
  inspect: operation({
14688
14710
  action: literal("inspect"),
14689
14711
  ...taskId,
14690
- view: _enum(["summary", "full"])
14712
+ view: _enum(["summary", "full"]).optional()
14691
14713
  }),
14692
14714
  revise: operation({
14693
14715
  action: literal("revise"),
@@ -14715,7 +14737,7 @@ var taskOperations = {
14715
14737
  ...taskId,
14716
14738
  ...revisions,
14717
14739
  expectedWorkspaceRevision: revision.optional(),
14718
- authorityRefs: array(refSchema)
14740
+ authorityRefs: array(refSchema).optional()
14719
14741
  }),
14720
14742
  close: operation({
14721
14743
  action: literal("close"),
@@ -15029,6 +15051,7 @@ function requirementId(input) {
15029
15051
  }
15030
15052
  // packages/workit-core/src/core/task-store.ts
15031
15053
  import { createHash as createHash2, randomUUID as randomUUID2 } from "node:crypto";
15054
+ import { AsyncLocalStorage } from "node:async_hooks";
15032
15055
  import * as fs4 from "node:fs";
15033
15056
  import { hostname } from "node:os";
15034
15057
  import path4 from "node:path";
@@ -15664,7 +15687,8 @@ var metadataLockSchema = object({
15664
15687
  pid: number2().int().nonnegative().safe(),
15665
15688
  processStart: string2().nullable(),
15666
15689
  host: string2().min(1),
15667
- nonce: string2().min(1)
15690
+ nonce: string2().min(1),
15691
+ externalAction: literal(true).optional()
15668
15692
  }).strict();
15669
15693
  var now = () => new Date().toISOString().replace(/\.\d{3}Z$/, "Z");
15670
15694
  var cachedRuntimeVersion = null;
@@ -15716,6 +15740,28 @@ var sameMetadataLock = (left, right) => {
15716
15740
  const parsed = metadataLockSchema.safeParse(left);
15717
15741
  return parsed.success && canonicalJson(parsed.data) === canonicalJson(right);
15718
15742
  };
15743
+ var sameDirectoryIdentity = (left, right) => {
15744
+ if (!path4.isAbsolute(left) || !path4.isAbsolute(right))
15745
+ return false;
15746
+ const normalizedLeft = path4.resolve(left);
15747
+ const normalizedRight = path4.resolve(right);
15748
+ try {
15749
+ const a = fs4.statSync(normalizedLeft, { bigint: true });
15750
+ const b = fs4.statSync(normalizedRight, { bigint: true });
15751
+ if (!a.isDirectory() || !b.isDirectory())
15752
+ return false;
15753
+ if (process.platform === "win32" ? normalizedLeft.toLowerCase() === normalizedRight.toLowerCase() : normalizedLeft === normalizedRight)
15754
+ return true;
15755
+ const canonicalLeft = fs4.realpathSync(normalizedLeft);
15756
+ const canonicalRight = fs4.realpathSync(normalizedRight);
15757
+ if (process.platform === "win32" ? canonicalLeft.toLowerCase() === canonicalRight.toLowerCase() : canonicalLeft === canonicalRight)
15758
+ return true;
15759
+ return a.dev === b.dev && a.ino !== 0n && a.ino === b.ino;
15760
+ } catch {
15761
+ return false;
15762
+ }
15763
+ };
15764
+ var externalActionLockRoots = new AsyncLocalStorage;
15719
15765
 
15720
15766
  class TaskStore {
15721
15767
  root;
@@ -15776,7 +15822,7 @@ class TaskStore {
15776
15822
  const item = this.readRecord(this.workspacePath, workspaceRecordSchema);
15777
15823
  if (!item.exists)
15778
15824
  return success(null, null, null);
15779
- if (item.result.ok && item.result.data.root !== this.root)
15825
+ if (item.result.ok && item.result.data.root !== this.root && !sameDirectoryIdentity(item.result.data.root, this.root))
15780
15826
  return failure("recovery_required", "workspace root binding is invalid", {
15781
15827
  path: this.workspacePath
15782
15828
  });
@@ -15867,6 +15913,29 @@ class TaskStore {
15867
15913
  const current = this.readWorkspace();
15868
15914
  if (!current.ok)
15869
15915
  return current;
15916
+ const origin = input.task.origin;
15917
+ if (current.data && origin) {
15918
+ if (origin.workspaceId === current.data.id) {
15919
+ const source = this.readTask(origin.taskId);
15920
+ if (source.ok)
15921
+ return success(source.data.revision, current.data.revision, source.data);
15922
+ if (source.code !== "not_found")
15923
+ return source;
15924
+ return failure("not_found", "source task is not present in this shared workspace");
15925
+ }
15926
+ const tasks = this.listTasks();
15927
+ if (!tasks.ok)
15928
+ return tasks;
15929
+ const prior = tasks.data.filter((task) => task.origin?.workspaceId === origin.workspaceId && task.origin?.taskId === origin.taskId);
15930
+ if (prior.length > 1)
15931
+ return failure("recovery_required", `source task has multiple imported mappings: ${prior.map((task) => task.id).join(", ")}`);
15932
+ if (prior.length === 1) {
15933
+ const existing = prior[0];
15934
+ if (existing.origin.exportDigest !== origin.exportDigest)
15935
+ return failure("revision_conflict", "source task export changed; reconcile the mapped destination task before importing", { taskId: existing.id });
15936
+ return success(existing.revision, current.data.revision, existing);
15937
+ }
15938
+ }
15870
15939
  if (current.data ? input.expectedWorkspaceRevision !== current.data.revision : input.expectedWorkspaceRevision !== null)
15871
15940
  return failure("revision_conflict", "workspace revision does not match; omit expectedWorkspaceRevision to use the current record", {
15872
15941
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
@@ -16255,6 +16324,73 @@ class TaskStore {
16255
16324
  })
16256
16325
  };
16257
16326
  }
16327
+ externalActionLockOptions() {
16328
+ const options = this.metadataLockOptions();
16329
+ return { ...options, payload: () => ({ ...options.payload(), externalAction: true }) };
16330
+ }
16331
+ async withExternalActionLock(operation, reentrant = false) {
16332
+ const activeRoots = externalActionLockRoots.getStore();
16333
+ if (activeRoots && [...activeRoots].some((root) => root === this.root || sameDirectoryIdentity(root, this.root)))
16334
+ return operation();
16335
+ try {
16336
+ this.initializeMutationStorage();
16337
+ } catch (error) {
16338
+ return failure("storage_error", `unable to initialize store: ${String(error)}`, {
16339
+ path: this.workitDir
16340
+ });
16341
+ }
16342
+ let handle;
16343
+ try {
16344
+ handle = acquireFileLockSync(this.workspacePath, this.externalActionLockOptions());
16345
+ } catch (error) {
16346
+ return this.lockFailure(error);
16347
+ }
16348
+ let result;
16349
+ try {
16350
+ if (!handle.verifyStillHeld())
16351
+ result = failure("recovery_required", "metadata lock was compromised", {
16352
+ path: this.lockPath
16353
+ });
16354
+ else {
16355
+ try {
16356
+ const run = () => operation();
16357
+ result = await (reentrant ? externalActionLockRoots.run(new Set([...activeRoots ?? [], this.root]), run) : run());
16358
+ } catch {
16359
+ result = failure("external_outcome_unknown", "external action outcome is unknown", {
16360
+ outcome: "unknown"
16361
+ });
16362
+ }
16363
+ try {
16364
+ if (!handle.verifyStillHeld())
16365
+ result = failure("external_outcome_unknown", "metadata lock was compromised", {
16366
+ outcome: "unknown",
16367
+ path: this.lockPath
16368
+ });
16369
+ } catch {
16370
+ result = failure("external_outcome_unknown", "metadata lock could not be verified", {
16371
+ outcome: "unknown",
16372
+ path: this.lockPath
16373
+ });
16374
+ }
16375
+ }
16376
+ } catch {
16377
+ result = failure("external_outcome_unknown", "external action lock operation failed", {
16378
+ outcome: "unknown",
16379
+ path: this.lockPath
16380
+ });
16381
+ }
16382
+ try {
16383
+ handle.release();
16384
+ } catch (error) {
16385
+ const released = this.lockFailure(error);
16386
+ const releaseError = released.ok ? "metadata lock release failed" : released.error;
16387
+ return result.ok ? failure("external_outcome_unknown", "external action metadata lock release is uncertain", {
16388
+ outcome: "unknown",
16389
+ path: this.lockPath
16390
+ }) : failure("recovery_required", `${result.error}; ${releaseError}`, result.details);
16391
+ }
16392
+ return result;
16393
+ }
16258
16394
  recoveryLockOptions(observed, evidence, gate) {
16259
16395
  const options = this.metadataLockOptions();
16260
16396
  options.staleRecovery = "remove-if-unchanged";
@@ -16324,6 +16460,14 @@ class TaskStore {
16324
16460
  lockFailure(error) {
16325
16461
  const value = error;
16326
16462
  const code = typeof value?.code === "string" ? value.code : "";
16463
+ if (code === "EEXIST" || code === "file_lock_timeout") {
16464
+ const lock = this.readLockSnapshot();
16465
+ if (lock.ok && lock.data?.data.externalAction)
16466
+ return failure("writer_conflict", "workspace is reserved by a managed external action", {
16467
+ outcome: "not_started",
16468
+ path: this.lockPath
16469
+ });
16470
+ }
16327
16471
  const recovery = code === "EEXIST" || code === "file_lock_timeout" || code === "file_lock_stale" || code === "metadata_lock_invalid" || code === "not-file" || /metadata lock|file lock|reclaim/i.test(String(value?.message ?? error));
16328
16472
  return failure(recovery ? "recovery_required" : "storage_error", `metadata lock operation failed: ${String(error)}`, { path: this.lockPath });
16329
16473
  }
@@ -16548,6 +16692,10 @@ var validPath = (value) => typeof value === "string" && value.length > 0 && ![..
16548
16692
  }) && !value.split(/[\\/]/).includes("..");
16549
16693
  var callerValue = (value) => ({ host: value.host, actor: value.actor });
16550
16694
  var workerIdOf = (value) => value.workerId ?? null;
16695
+ var currentWriterOwnsTask = (task, workspace, caller) => {
16696
+ const owner = workspace.writer?.owner;
16697
+ return task.status === "active" && task.workspaceId === workspace.id && owner?.taskId === task.id && owner.session.host === caller.host && owner.session.handle === caller.actor;
16698
+ };
16551
16699
  function assertProductWriteAllowed(input) {
16552
16700
  if (!Array.isArray(input.paths) || input.paths.some((path) => !validPath(path)))
16553
16701
  return failure("invalid_input", "invalid product write path");
@@ -16587,21 +16735,23 @@ function assertProductWriteAllowed(input) {
16587
16735
  }
16588
16736
 
16589
16737
  // packages/workit-core/src/core/task-evaluation.ts
16590
- import { createHash as createHash5 } from "node:crypto";
16738
+ import { createHash as createHash6 } from "node:crypto";
16591
16739
  import * as fs7 from "node:fs";
16592
16740
  import { spawnSync as spawnSync2 } from "node:child_process";
16593
- import path9 from "node:path";
16741
+ import path10 from "node:path";
16594
16742
 
16595
16743
  // packages/workit-core/src/core/authority.ts
16596
- import { createHash as createHash4 } from "node:crypto";
16744
+ import { createHash as createHash5 } from "node:crypto";
16597
16745
  import * as fs6 from "node:fs";
16598
- import path8 from "node:path";
16746
+ import path9 from "node:path";
16599
16747
 
16600
16748
  // packages/workit-core/src/core/auto-approval.ts
16601
- import { createHash as createHash3 } from "node:crypto";
16602
- import { readFileSync as readFileSync4 } from "node:fs";
16749
+ import { createHash as createHash4 } from "node:crypto";
16750
+ import { readFileSync as readFileSync4, realpathSync as realpathSync3 } from "node:fs";
16751
+ import path8 from "node:path";
16603
16752
 
16604
16753
  // packages/workit-core/src/core/workspaces.ts
16754
+ import { createHash as createHash3 } from "node:crypto";
16605
16755
  import { readFileSync as readFileSync3, realpathSync as realpathSync2 } from "node:fs";
16606
16756
  import path6 from "node:path";
16607
16757
 
@@ -16619,7 +16769,7 @@ import os from "node:os";
16619
16769
  import path5 from "node:path";
16620
16770
 
16621
16771
  // packages/workit-core/src/core/boundary.ts
16622
- import { statSync } from "node:fs";
16772
+ import { statSync as statSync2 } from "node:fs";
16623
16773
  var EVENT = {
16624
16774
  initialization: "initialization",
16625
16775
  provenance: "provenance",
@@ -16647,7 +16797,7 @@ var changedSourcesSinceLoad = (marker) => {
16647
16797
  const changed = [];
16648
16798
  for (const file of marker.files) {
16649
16799
  try {
16650
- if (statSync(file).mtimeMs > marker.loadedAt)
16800
+ if (statSync2(file).mtimeMs > marker.loadedAt)
16651
16801
  changed.push(file);
16652
16802
  } catch {}
16653
16803
  }
@@ -16806,33 +16956,197 @@ var COMMIT_PRESETS = [
16806
16956
  "custom",
16807
16957
  "auto"
16808
16958
  ];
16959
+ var resolveCommitPolicy = (config, workspace) => {
16960
+ const wp = workspace?.commitPolicy ?? {};
16961
+ if (COMMIT_PRESETS.includes(String(wp.preset ?? ""))) {
16962
+ return {
16963
+ preset: wp.preset,
16964
+ ...typeof wp.pattern === "string" ? { pattern: wp.pattern } : {}
16965
+ };
16966
+ }
16967
+ return config.commitPolicy ?? { preset: "conventional" };
16968
+ };
16809
16969
 
16810
16970
  // packages/workit-core/src/core/workspaces.ts
16971
+ var nonBlank = string2().refine((value) => value.trim().length > 0, "must not be blank");
16972
+ var branchPreset = custom((value) => typeof value === "string" && Object.hasOwn(PRESETS, value), "unsupported branch preset");
16973
+ var commitPreset = custom((value) => typeof value === "string" && COMMIT_PRESETS.includes(value), "unsupported commit preset");
16974
+ var workspaceBranchPolicySchema = object({
16975
+ preset: branchPreset,
16976
+ developBranch: nonBlank.optional(),
16977
+ prefixes: object({
16978
+ feature: nonBlank,
16979
+ bugfix: nonBlank,
16980
+ release: nonBlank,
16981
+ hotfix: nonBlank
16982
+ }).passthrough().optional(),
16983
+ allowed: array(nonBlank).optional(),
16984
+ protected: array(nonBlank).optional(),
16985
+ integration: _enum(["pr", "merge"]).optional()
16986
+ }).strict();
16987
+ var workspaceCommitPolicySchema = object({ preset: commitPreset, pattern: string2().optional() }).strict();
16988
+ var workspaceProfileSchema = object({
16989
+ branchPolicy: workspaceBranchPolicySchema.optional(),
16990
+ commitPolicy: workspaceCommitPolicySchema.optional()
16991
+ }).strict().refine((profile) => profile.branchPolicy !== undefined || profile.commitPolicy !== undefined, {
16992
+ message: "a profile must define branchPolicy or commitPolicy"
16993
+ });
16994
+ var versionSourceSchema = discriminatedUnion("kind", [
16995
+ object({
16996
+ kind: literal("package-json"),
16997
+ path: nonBlank,
16998
+ field: nonBlank
16999
+ }).strict(),
17000
+ object({ kind: literal("git-tag") }).strict(),
17001
+ object({ kind: literal("manual") }).strict()
17002
+ ]);
17003
+ var releaseTrackSchema = object({
17004
+ strategy: branchPreset,
17005
+ productionBranch: nonBlank,
17006
+ integrationBranch: nonBlank,
17007
+ naming: object({ feature: nonBlank, release: nonBlank, hotfix: nonBlank }).strict(),
17008
+ baseBranch: nonBlank,
17009
+ mergeBackBranches: array(nonBlank),
17010
+ pullRequestTarget: nonBlank,
17011
+ tagNamespace: string2(),
17012
+ versionSource: versionSourceSchema,
17013
+ requiredChecks: array(nonBlank)
17014
+ }).strict().superRefine((track, context) => {
17015
+ for (const [field, values] of [
17016
+ ["mergeBackBranches", track.mergeBackBranches],
17017
+ ["requiredChecks", track.requiredChecks]
17018
+ ]) {
17019
+ if (new Set(values).size !== values.length) {
17020
+ context.addIssue({ code: "custom", path: [field], message: "entries must be unique" });
17021
+ }
17022
+ }
17023
+ if (track.versionSource.kind === "package-json" && (path6.posix.isAbsolute(track.versionSource.path.replaceAll("\\", "/")) || /^[A-Za-z]:\//u.test(track.versionSource.path.replaceAll("\\", "/")) || track.versionSource.path.replaceAll("\\", "/").split("/").includes(".."))) {
17024
+ context.addIssue({
17025
+ code: "custom",
17026
+ path: ["versionSource", "path"],
17027
+ message: "must stay inside the repository"
17028
+ });
17029
+ }
17030
+ });
17031
+ var workspaceConfigSchema = object({
17032
+ name: nonBlank,
17033
+ glob: nonBlank,
17034
+ vcs: object({
17035
+ provider: _enum(["gitlab", "github"]),
17036
+ defaultTargetBranch: nonBlank.optional(),
17037
+ tokenFile: nonBlank.optional(),
17038
+ account: nonBlank.optional()
17039
+ }).passthrough().optional(),
17040
+ autoApprove: union([boolean2(), array(nonBlank)]).optional(),
17041
+ youtrack: object({ baseUrl: nonBlank.optional(), link_issues: boolean2().optional() }).passthrough().optional(),
17042
+ issues: object({ provider: literal("github").optional(), link_on_pr: boolean2().optional() }).passthrough().optional(),
17043
+ branchPolicy: workspaceBranchPolicySchema.optional(),
17044
+ commitPolicy: workspaceCommitPolicySchema.optional(),
17045
+ defaultProfile: nonBlank.optional(),
17046
+ profiles: record(nonBlank, workspaceProfileSchema).optional(),
17047
+ releaseTracks: record(nonBlank, releaseTrackSchema).optional()
17048
+ }).passthrough().superRefine((workspace, context) => {
17049
+ if (workspace.youtrack && workspace.vcs?.provider !== "gitlab") {
17050
+ context.addIssue({
17051
+ code: "custom",
17052
+ path: ["youtrack"],
17053
+ message: `YouTrack issue linking requires the gitlab provider, got ${workspace.vcs?.provider ?? "unset"}`
17054
+ });
17055
+ }
17056
+ if (workspace.issues && workspace.vcs?.provider !== "github") {
17057
+ context.addIssue({
17058
+ code: "custom",
17059
+ path: ["issues"],
17060
+ message: `GitHub issue linking requires the github provider, got ${workspace.vcs?.provider ?? "unset"}`
17061
+ });
17062
+ }
17063
+ if (workspace.defaultProfile !== undefined && !Object.hasOwn(workspace.profiles ?? {}, workspace.defaultProfile)) {
17064
+ context.addIssue({
17065
+ code: "custom",
17066
+ path: ["defaultProfile"],
17067
+ message: `does not name a configured profile`
17068
+ });
17069
+ }
17070
+ });
17071
+ var workspacesFileSchema = object({ workspaces: array(workspaceConfigSchema).optional() }).passthrough().superRefine((file, context) => {
17072
+ const names = new Set;
17073
+ for (const [index, workspace] of (file.workspaces ?? []).entries()) {
17074
+ if (names.has(workspace.name)) {
17075
+ context.addIssue({
17076
+ code: "custom",
17077
+ path: ["workspaces", index, "name"],
17078
+ message: `duplicate workspace name ${JSON.stringify(workspace.name)}`
17079
+ });
17080
+ }
17081
+ names.add(workspace.name);
17082
+ }
17083
+ });
17084
+ var workspacesRevision = (contents) => createHash3("sha256").update(contents ?? "<missing>").digest("hex");
17085
+ var validateWorkspacesDocument = (parsed, file) => {
17086
+ if (!isConfigObject(parsed)) {
17087
+ return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
17088
+ }
17089
+ const validated = workspacesFileSchema.safeParse(parsed, { reportInput: true });
17090
+ if (!validated.success) {
17091
+ const detail = validated.error.issues.map((issue) => {
17092
+ const field = String(issue.path[issue.path.length - 1] ?? "");
17093
+ const attempted = ["provider", "preset", "strategy"].includes(field) && typeof issue.input === "string" ? ` (${JSON.stringify(issue.input)})` : "";
17094
+ return `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}${attempted}`;
17095
+ }).join("; ");
17096
+ return {
17097
+ status: "invalid",
17098
+ path: file,
17099
+ entries: [],
17100
+ error: `${file} has invalid workspace configuration: ${detail}`
17101
+ };
17102
+ }
17103
+ for (const [index, workspace] of (validated.data.workspaces ?? []).entries()) {
17104
+ const glob = validateWorkspaceGlob(workspace.glob);
17105
+ if (!glob.ok) {
17106
+ return {
17107
+ status: "invalid",
17108
+ path: file,
17109
+ entries: [],
17110
+ error: `${file} workspaces.${index}.glob: ${glob.error}`
17111
+ };
17112
+ }
17113
+ }
17114
+ return {
17115
+ status: "valid",
17116
+ path: file,
17117
+ entries: validated.data.workspaces ?? [],
17118
+ document: parsed
17119
+ };
17120
+ };
16811
17121
  var readWorkspacesResult = (dir = configDir()) => {
16812
17122
  const file = path6.join(dir, "workspaces.json");
16813
17123
  let raw;
16814
17124
  try {
16815
17125
  raw = readFileSync3(file, "utf8");
16816
- } catch {
16817
- return { status: "missing", path: file, entries: [] };
17126
+ } catch (error) {
17127
+ if (error.code === "ENOENT")
17128
+ return { status: "missing", path: file, entries: [], revision: workspacesRevision(null) };
17129
+ return {
17130
+ status: "malformed",
17131
+ path: file,
17132
+ entries: [],
17133
+ error: `${file} could not be read: ${error instanceof Error ? error.message : String(error)}`
17134
+ };
16818
17135
  }
16819
17136
  let parsed;
16820
17137
  try {
16821
17138
  parsed = JSON.parse(raw);
16822
17139
  } catch {
16823
- return { status: "malformed", path: file, entries: [], error: `${file} is not valid JSON` };
16824
- }
16825
- if (!isConfigObject(parsed)) {
16826
- return { status: "malformed", path: file, entries: [], error: `${file} is not a JSON object` };
17140
+ return {
17141
+ status: "malformed",
17142
+ path: file,
17143
+ entries: [],
17144
+ revision: workspacesRevision(raw),
17145
+ error: `${file} is not valid JSON`
17146
+ };
16827
17147
  }
16828
- const list = parsed.workspaces;
16829
- return {
16830
- status: "valid",
16831
- path: file,
16832
- entries: Array.isArray(list) ? list : []
16833
- };
17148
+ return { ...validateWorkspacesDocument(parsed, file), revision: workspacesRevision(raw) };
16834
17149
  };
16835
- var loadWorkspacesFrom = (dir) => readWorkspacesResult(dir).entries;
16836
17150
  var globToRegExp = (glob) => {
16837
17151
  let out = "";
16838
17152
  for (let i = 0;i < glob.length; i++) {
@@ -16867,6 +17181,30 @@ var globToRegExp = (glob) => {
16867
17181
  return new RegExp(`^${out}$`);
16868
17182
  };
16869
17183
  var matchWorkspace = (glob, target) => globToRegExp(glob.replaceAll("\\", "/")).test(target.replaceAll("\\", "/"));
17184
+ var UNSUPPORTED_GLOB = /[?[\]{}]/;
17185
+ var UNSUPPORTED_EXTGLOB = /[@+*]\(|!/;
17186
+ var validateWorkspaceGlob = (glob) => {
17187
+ const trimmed = glob.trim();
17188
+ if (!trimmed)
17189
+ return { ok: false, error: "workspace pattern is required" };
17190
+ const m = UNSUPPORTED_GLOB.exec(trimmed);
17191
+ if (m) {
17192
+ return {
17193
+ ok: false,
17194
+ error: `unsupported glob character ${JSON.stringify(m[0])} in workspace pattern ${JSON.stringify(glob)} — the matcher supports * and ** only (e.g. /work/**)`
17195
+ };
17196
+ }
17197
+ const ext = UNSUPPORTED_EXTGLOB.exec(trimmed);
17198
+ if (ext) {
17199
+ const token = ext[0];
17200
+ const kind = token === "!" ? "negation (!)" : `extglob (${token})`;
17201
+ return {
17202
+ ok: false,
17203
+ error: `unsupported glob ${kind} in workspace pattern ${JSON.stringify(glob)} — the matcher supports * and ** only (e.g. /work/**)`
17204
+ };
17205
+ }
17206
+ return { ok: true };
17207
+ };
16870
17208
  var realpathOf = (p) => {
16871
17209
  const real = realpathSync2.native ?? realpathSync2;
16872
17210
  try {
@@ -16884,29 +17222,137 @@ var canonicalGlob = (glob) => {
16884
17222
  const real = realpathOf(prefix.replace(/\/+$/, "") || "/");
16885
17223
  return real + (prefix.endsWith("/") ? "/" : "") + rest;
16886
17224
  };
16887
- var resolveWorkspaceFrom = (cwd, dir) => {
17225
+ var resolveWorkspaceFrom = (cwd, dir, workspaceName) => {
17226
+ const result = readWorkspacesResult(dir);
17227
+ if (result.status === "malformed" || result.status === "invalid")
17228
+ throw new Error(result.error);
16888
17229
  const targets = [cwd, realpathOf(cwd)].map((p) => p.replaceAll("\\", "/"));
16889
- for (const entry of loadWorkspacesFrom(dir)) {
16890
- if (!entry || typeof entry !== "object")
16891
- continue;
16892
- const ws = entry;
16893
- if (typeof ws.glob !== "string" || !ws.glob)
16894
- continue;
17230
+ const matches = [];
17231
+ for (const ws of result.entries) {
16895
17232
  const glob = ws.glob.replaceAll("\\", "/");
16896
17233
  const canonical = canonicalGlob(glob);
17234
+ let matched = false;
16897
17235
  for (const target of targets) {
16898
- if (matchWorkspace(glob, target))
16899
- return ws;
16900
- if (canonical !== glob && matchWorkspace(canonical, target))
16901
- return ws;
17236
+ if (matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target)) {
17237
+ matched = true;
17238
+ break;
17239
+ }
16902
17240
  }
17241
+ if (matched)
17242
+ matches.push(ws);
16903
17243
  }
16904
- return null;
17244
+ if (workspaceName !== undefined) {
17245
+ const selected = matches.find((ws) => ws.name === workspaceName);
17246
+ if (!selected && matches.length > 0) {
17247
+ const matchingNames = matches.map((ws) => ws.name);
17248
+ throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matchingNames.join(", ")}`);
17249
+ }
17250
+ if (selected)
17251
+ return selected;
17252
+ throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
17253
+ }
17254
+ if (matches.length > 1) {
17255
+ throw new Error(`ambiguous workspace for ${cwd}; choose one of: ${matches.map((ws) => ws.name).join(", ")}`);
17256
+ }
17257
+ return matches[0] ?? null;
17258
+ };
17259
+ var runtimeWorkspacePolicySchema = (kind) => {
17260
+ const key = kind === "branch" ? "branchPolicy" : kind === "commit" ? "commitPolicy" : "vcs";
17261
+ const policySchema = kind === "branch" ? workspaceBranchPolicySchema : kind === "commit" ? workspaceCommitPolicySchema : object({
17262
+ provider: _enum(["gitlab", "github"]),
17263
+ defaultTargetBranch: nonBlank.optional(),
17264
+ account: nonBlank.optional()
17265
+ }).passthrough();
17266
+ const profileSchema = object({ [key]: policySchema.optional() }).passthrough();
17267
+ const entrySchema = object({
17268
+ name: nonBlank,
17269
+ glob: nonBlank,
17270
+ [key]: policySchema.optional(),
17271
+ defaultProfile: nonBlank.optional(),
17272
+ profiles: record(nonBlank, profileSchema).optional()
17273
+ }).passthrough();
17274
+ return entrySchema;
17275
+ };
17276
+ var resolveRuntimeWorkspaceVcs = (cwd) => resolveRuntimeWorkspaceCandidate(cwd, "vcs");
17277
+ var runtimeWorkspaceIndexSchema = object({
17278
+ workspaces: array(object({ name: nonBlank, glob: nonBlank }).passthrough()).optional()
17279
+ }).passthrough();
17280
+ var resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
17281
+ const file = path6.join(configDir(), "workspaces.json");
17282
+ let raw;
17283
+ try {
17284
+ raw = readFileSync3(file, "utf8");
17285
+ } catch (error) {
17286
+ if (error.code === "ENOENT")
17287
+ return null;
17288
+ throw new Error(`${file} could not be read: ${error instanceof Error ? error.message : String(error)}`);
17289
+ }
17290
+ let parsed;
17291
+ try {
17292
+ parsed = JSON.parse(raw);
17293
+ } catch {
17294
+ throw new Error(`${file} is not valid JSON`);
17295
+ }
17296
+ const validated = runtimeWorkspaceIndexSchema.safeParse(parsed);
17297
+ if (!validated.success) {
17298
+ const detail = validated.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspaces"}: ${issue.message}`).join("; ");
17299
+ throw new Error(`${file} has invalid workspace matching data: ${detail}`);
17300
+ }
17301
+ const entries = validated.data.workspaces;
17302
+ for (const entry of entries ?? []) {
17303
+ const glob = validateWorkspaceGlob(entry.glob);
17304
+ if (!glob.ok)
17305
+ throw new Error(`${file} workspace ${entry.name}: ${glob.error}`);
17306
+ }
17307
+ const targets = [cwd, realpathOf(cwd)].map((value) => value.replaceAll("\\", "/"));
17308
+ const matches = (entries ?? []).filter((entry) => {
17309
+ const glob = entry.glob.replaceAll("\\", "/");
17310
+ const canonical = canonicalGlob(glob);
17311
+ return targets.some((target) => matchWorkspace(glob, target) || canonical !== glob && matchWorkspace(canonical, target));
17312
+ });
17313
+ const workspaceName = process.env.WORKFLOW_WORKSPACE_NAME?.trim() || undefined;
17314
+ let selected;
17315
+ if (workspaceName) {
17316
+ const named = matches.filter((entry) => entry.name === workspaceName);
17317
+ if (named.length === 1)
17318
+ selected = named[0];
17319
+ else if (named.length > 1)
17320
+ throw new Error(`ambiguous workspace ${JSON.stringify(workspaceName)} for ${cwd}`);
17321
+ else if (matches.length)
17322
+ throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}; matching choices: ${matches.map((entry) => entry.name).join(", ")}`);
17323
+ else
17324
+ throw new Error(`workspace ${JSON.stringify(workspaceName)} does not match ${cwd}`);
17325
+ } else if (matches.length > 1)
17326
+ throw new Error(`ambiguous workspace for ${cwd}; choose one of: ${matches.map((entry) => entry.name).join(", ")}`);
17327
+ else
17328
+ selected = matches[0];
17329
+ if (!selected)
17330
+ return null;
17331
+ const policy = runtimeWorkspacePolicySchema(kind).safeParse(selected);
17332
+ if (!policy.success) {
17333
+ const detail = policy.error.issues.map((issue) => `${issue.path.map(String).join(".") || "workspace"}: ${issue.message}`).join("; ");
17334
+ throw new Error(`${file} has invalid ${kind} policy configuration: ${detail}`);
17335
+ }
17336
+ return policy.data;
17337
+ };
17338
+ var resolveRuntimeWorkspacePolicy = (cwd, kind, requestedProfile) => {
17339
+ const candidate = resolveRuntimeWorkspaceCandidate(cwd, kind);
17340
+ if (!candidate)
17341
+ return { policy: undefined, source: "user-default" };
17342
+ const key = kind === "branch" ? "branchPolicy" : "commitPolicy";
17343
+ const profileName = requestedProfile?.trim() || candidate.defaultProfile;
17344
+ if (candidate.defaultProfile && !Object.hasOwn(candidate.profiles ?? {}, candidate.defaultProfile))
17345
+ throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(candidate.defaultProfile)}`);
17346
+ const profile = profileName ? candidate.profiles?.[profileName] : undefined;
17347
+ if (profileName && !profile)
17348
+ throw new Error(`workspace ${JSON.stringify(candidate.name)} has no profile ${JSON.stringify(profileName)}`);
17349
+ const profilePolicy = profile?.[key];
17350
+ const workspacePolicy = candidate[key];
17351
+ return {
17352
+ policy: profilePolicy ?? workspacePolicy,
17353
+ source: profilePolicy ? `profile:${profileName}` : workspacePolicy ? `workspace:${candidate.name}` : "user-default"
17354
+ };
16905
17355
  };
16906
- var resolveWorkspace = (cwd) => resolveWorkspaceFrom(cwd, configDir());
16907
-
16908
- // packages/workit-core/src/core/branch.ts
16909
- import { execFileSync as execFileSync2 } from "node:child_process";
16910
17356
 
16911
17357
  // packages/workit-core/src/core/git.ts
16912
17358
  import { execFileSync } from "node:child_process";
@@ -16980,7 +17426,6 @@ var gitContext = (workspaceRoot, paths = []) => {
16980
17426
  import fs5 from "node:fs";
16981
17427
  import path7 from "node:path";
16982
17428
  import { spawnSync } from "node:child_process";
16983
- var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
16984
17429
  var vcsConfigPath = () => process.env.WORKFLOW_VCS_CONFIG ?? path7.join(configDir(), "vcs.json");
16985
17430
  var vcsCwd = (cwd) => cwd ?? process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd();
16986
17431
  var remoteProvider = (cwd) => {
@@ -16994,6 +17439,79 @@ var remoteProvider = (cwd) => {
16994
17439
  return "gitlab";
16995
17440
  return null;
16996
17441
  };
17442
+ var remoteEndpoint = (remote) => {
17443
+ const raw = remote.trim();
17444
+ if (!raw)
17445
+ return null;
17446
+ const scp = /^(?:[^@\s]+@)?([^:]+):(.+)$/u.exec(raw);
17447
+ if (scp && !raw.includes("://"))
17448
+ return {
17449
+ host: scp[1].toLowerCase(),
17450
+ path: scp[2].replace(/^\//u, "").replace(/\.git$/u, ""),
17451
+ protocol: "ssh:",
17452
+ port: ""
17453
+ };
17454
+ try {
17455
+ const url = new URL(raw);
17456
+ return {
17457
+ host: url.hostname.toLowerCase(),
17458
+ path: url.pathname.replace(/^\//u, "").replace(/\.git$/u, "").replace(/\/$/u, ""),
17459
+ protocol: url.protocol,
17460
+ port: url.port
17461
+ };
17462
+ } catch {
17463
+ return null;
17464
+ }
17465
+ };
17466
+ var configuredSshEndpoint = (host) => {
17467
+ if (host.startsWith("-"))
17468
+ return null;
17469
+ const resolved = spawnSync("ssh", ["-G", host], {
17470
+ encoding: "utf8",
17471
+ timeout: 5000,
17472
+ maxBuffer: 1024 * 1024,
17473
+ env: { ...process.env, PATH: process.env.PATH ?? "" }
17474
+ });
17475
+ if (resolved.status !== 0)
17476
+ return null;
17477
+ const output = resolved.stdout ?? "";
17478
+ const hostname = /^hostname\s+(\S+)/imu.exec(output)?.[1]?.toLowerCase();
17479
+ if (!hostname)
17480
+ return null;
17481
+ return { host: hostname, port: /^port\s+(\d+)/imu.exec(output)?.[1] ?? "22" };
17482
+ };
17483
+ var sshApiHostAliases = {
17484
+ "github.com": [{ host: "ssh.github.com", port: "443" }],
17485
+ "gitlab.com": [{ host: "altssh.gitlab.com", port: "443" }]
17486
+ };
17487
+ var hostingApiHostMatches = (remote, apiHost, resolveSshEndpoint = configuredSshEndpoint) => {
17488
+ const endpoint = remoteEndpoint(remote);
17489
+ if (!endpoint)
17490
+ return false;
17491
+ let expected;
17492
+ try {
17493
+ const url = new URL(`https://${apiHost}`);
17494
+ if (url.pathname !== "/" || url.search || url.hash || url.username || url.password)
17495
+ return false;
17496
+ expected = {
17497
+ host: url.hostname.toLowerCase().replace(/\.$/u, ""),
17498
+ port: url.port
17499
+ };
17500
+ } catch {
17501
+ return false;
17502
+ }
17503
+ if (endpoint.protocol !== "ssh:") {
17504
+ return endpoint.host.replace(/\.$/u, "") === expected.host && endpoint.port === expected.port;
17505
+ }
17506
+ const resolved = resolveSshEndpoint(endpoint.host);
17507
+ if (!resolved)
17508
+ return false;
17509
+ const hostname = resolved.host.toLowerCase().replace(/\.$/u, "");
17510
+ const port = endpoint.port || resolved.port;
17511
+ if (hostname === expected.host)
17512
+ return port === (expected.port || "22");
17513
+ return Boolean(sshApiHostAliases[expected.host]?.some((alias) => alias.host === hostname && alias.port === port && (!expected.port || expected.port === port)));
17514
+ };
16997
17515
  var readVcsConfig = () => {
16998
17516
  const file = vcsConfigPath();
16999
17517
  let raw;
@@ -17014,7 +17532,7 @@ var readVcsConfig = () => {
17014
17532
  return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
17015
17533
  };
17016
17534
  function vcsConfig(mode, cwd) {
17017
- const ws = resolveWorkspace(vcsCwd(cwd));
17535
+ const ws = resolveRuntimeWorkspaceVcs(vcsCwd(cwd));
17018
17536
  const wsVcs = ws?.vcs ?? {};
17019
17537
  const wsYt = ws?.youtrack ?? {};
17020
17538
  const wsIssues = ws?.issues ?? {};
@@ -17027,8 +17545,8 @@ function vcsConfig(mode, cwd) {
17027
17545
  const hasWorkspacePolicy = typeof wp.preset === "string" && Object.hasOwn(PRESETS, wp.preset);
17028
17546
  const policyDefault = resolveBranchPolicyFor(root).defaultTargetBranch;
17029
17547
  const defaultTarget = String(wsVcs.defaultTargetBranch ?? (hasWorkspacePolicy ? policyDefault : cfg.defaultTargetBranch ?? policyDefault) ?? "develop");
17030
- const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
17031
- const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
17548
+ const linkIssues = provider === "gitlab" && typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
17549
+ const youtrackBaseUrl = provider === "gitlab" && typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
17032
17550
  let issuesProvider = null;
17033
17551
  let linkOnPr = null;
17034
17552
  if (provider === "github" && typeof wsIssues.provider === "string" && wsIssues.provider.toLowerCase() === "github") {
@@ -17052,9 +17570,8 @@ function vcsConfig(mode, cwd) {
17052
17570
  if (cfgStatus === "malformed") {
17053
17571
  return { ok: false, error: cfgError, configPath: cfgPath };
17054
17572
  }
17055
- if (cfgStatus === "missing") {
17573
+ if (cfgStatus === "missing" && provider === null)
17056
17574
  return { ok: false, error: `vcs.json is missing: ${cfgPath}`, configPath: cfgPath };
17057
- }
17058
17575
  if (provider === null) {
17059
17576
  return {
17060
17577
  ok: false,
@@ -17063,24 +17580,12 @@ function vcsConfig(mode, cwd) {
17063
17580
  };
17064
17581
  }
17065
17582
  const prov = cfg[provider] ?? {};
17066
- const wsTokenFile = typeof wsVcs.tokenFile === "string" && wsVcs.tokenFile.trim() !== "" ? wsVcs.tokenFile : null;
17067
- const tokenFile = String(wsTokenFile ?? prov.tokenFile ?? path7.join(configDir(), `${provider}.token`));
17068
- const tokenPath = path7.resolve(tokenFile);
17069
- let tokenOk = false;
17070
- if (fs5.existsSync(tokenPath)) {
17071
- const token = fs5.readFileSync(tokenPath, "utf8").trim();
17072
- const placeholder = !token || token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER);
17073
- tokenOk = !placeholder;
17074
- }
17075
17583
  const out = {
17076
17584
  ok: true,
17077
17585
  configPath: path7.resolve(cfgPath),
17078
17586
  provider,
17079
17587
  defaultTargetBranch: defaultTarget,
17080
17588
  pr: cfg.pr ?? {},
17081
- tokenPath,
17082
- tokenPresent: fs5.existsSync(tokenPath),
17083
- tokenReady: tokenOk,
17084
17589
  workspace_name: ws?.name ?? null,
17085
17590
  link_issues: linkIssues,
17086
17591
  youtrack_base_url: youtrackBaseUrl,
@@ -17095,16 +17600,58 @@ function vcsConfig(mode, cwd) {
17095
17600
  } else if (provider === "github") {
17096
17601
  out.github = { host: prov.host ?? "github.com" };
17097
17602
  }
17098
- if (mode === "summary")
17099
- delete out.tokenPath;
17100
17603
  return out;
17101
17604
  }
17605
+ function vcsCliIdentity(cwd) {
17606
+ const root = vcsCwd(cwd);
17607
+ const cfg = vcsConfig("load", root);
17608
+ if (!cfg.ok)
17609
+ return { ok: false, error: cfg.error ?? "vcs config not ready" };
17610
+ const provider = cfg.provider;
17611
+ if (provider !== "gitlab" && provider !== "github")
17612
+ return { ok: false, error: `unsupported provider: ${provider}` };
17613
+ const bin = provider === "gitlab" ? "glab" : "gh";
17614
+ const host = String(provider === "github" ? cfg.github?.host ?? "github.com" : cfg.gitlab?.host ?? "gitlab.com").toLowerCase();
17615
+ const env = {
17616
+ ...process.env,
17617
+ PATH: process.env.PATH ?? "",
17618
+ ...provider === "github" ? { GH_HOST: host } : { GITLAB_HOST: host }
17619
+ };
17620
+ const result = spawnSync(bin, ["api", "user"], {
17621
+ cwd: root,
17622
+ encoding: "utf8",
17623
+ env
17624
+ });
17625
+ if (result.status !== 0)
17626
+ return { ok: false, provider, error: `${bin} is missing or not authenticated` };
17627
+ try {
17628
+ const user = JSON.parse(result.stdout ?? "");
17629
+ const username = provider === "gitlab" ? user.username : user.login;
17630
+ if (typeof username !== "string" || !username)
17631
+ return { ok: false, provider, error: `invalid ${bin} identity response` };
17632
+ const expected = resolveRuntimeWorkspaceVcs(root)?.vcs?.account;
17633
+ if (expected && username.toLowerCase() !== expected.toLowerCase())
17634
+ return {
17635
+ ok: false,
17636
+ provider,
17637
+ error: `${bin} account ${username} does not match ${expected}`
17638
+ };
17639
+ return { ok: true, provider, host, username, name: user.name };
17640
+ } catch {
17641
+ return { ok: false, provider, error: `invalid JSON from ${bin} api user` };
17642
+ }
17643
+ }
17102
17644
  function mergedPrStyle(limit = 6, cwd) {
17103
17645
  const cfg = vcsConfig("load", cwd);
17104
- if (!cfg.ok || !cfg.tokenReady)
17646
+ if (!cfg.ok)
17105
17647
  return { ok: false, error: "vcs not configured" };
17106
17648
  const provider = cfg.provider;
17107
- const token = fs5.readFileSync(cfg.tokenPath, "utf8").trim();
17649
+ const host = String(provider === "github" ? cfg.github?.host ?? "github.com" : cfg.gitlab?.host ?? "gitlab.com").toLowerCase();
17650
+ const env = {
17651
+ ...process.env,
17652
+ PATH: process.env.PATH ?? "",
17653
+ ...provider === "github" ? { GH_HOST: host } : { GITLAB_HOST: host }
17654
+ };
17108
17655
  const examples = [];
17109
17656
  const descInfo = (desc, caseInsensitiveNotes = false) => ({
17110
17657
  hasNotesSection: caseInsensitiveNotes ? /##\s*notes/i.test(desc) : /##\s*Notes/.test(desc),
@@ -17112,23 +17659,30 @@ function mergedPrStyle(limit = 6, cwd) {
17112
17659
  `).filter((l) => l.startsWith("## ")).map((l) => l.trim()),
17113
17660
  descriptionPreview: desc.slice(0, 600)
17114
17661
  });
17662
+ const remoteResult = spawnSync("git", ["remote", "get-url", "--push", "--all", "origin"], {
17663
+ cwd,
17664
+ encoding: "utf8"
17665
+ });
17666
+ const remotes = (remoteResult.stdout ?? "").split(/\r?\n/u).map((value) => value.trim()).filter(Boolean);
17667
+ if (remotes.length === 0)
17668
+ return { ok: false, error: "no origin remote" };
17669
+ if (remotes.length !== 1)
17670
+ return { ok: false, error: "origin has ambiguous push destinations" };
17671
+ const remote = remotes[0];
17672
+ const endpoint = remoteEndpoint(remote);
17673
+ if (!endpoint?.path)
17674
+ return { ok: false, error: "hosting repository could not be resolved" };
17675
+ if (!hostingApiHostMatches(remote, host))
17676
+ return { ok: false, error: "PR style target does not match the provider host" };
17115
17677
  if (provider === "gitlab") {
17116
- const remote = spawnSync("git", ["remote", "get-url", "origin"], { cwd, encoding: "utf8" });
17117
- if (remote.status !== 0)
17118
- return { ok: false, error: "no origin remote" };
17119
- const url = (remote.stdout ?? "").trim();
17120
- const m = /gitlab\.com[:/](.+?)(?:\.git)?$/.exec(url);
17121
- if (!m)
17122
- return { ok: false, error: "not a gitlab.com origin" };
17123
- const project = m[1];
17124
- const env = { ...process.env, GITLAB_TOKEN: token };
17125
- const run = (args) => spawnSync("glab", ["api", ...args], { cwd, encoding: "utf8", env });
17678
+ const run = (args) => spawnSync("glab", ["api", ...args], {
17679
+ cwd,
17680
+ encoding: "utf8",
17681
+ env
17682
+ });
17126
17683
  let r = run([
17127
- `projects/${project.replaceAll("/", "%2F")}/merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`
17684
+ `projects/${endpoint.path.replaceAll("/", "%2F")}/merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`
17128
17685
  ]);
17129
- if (r.status !== 0) {
17130
- r = run([`merge_requests?state=merged&per_page=${limit}&order_by=updated_at&sort=desc`]);
17131
- }
17132
17686
  if (r.status !== 0)
17133
17687
  return { ok: false, error: "could not list merge requests" };
17134
17688
  for (const mr of JSON.parse(r.stdout ?? "[]")) {
@@ -17141,7 +17695,18 @@ function mergedPrStyle(limit = 6, cwd) {
17141
17695
  });
17142
17696
  }
17143
17697
  } else if (provider === "github") {
17144
- const r = spawnSync("gh", ["pr", "list", "--state", "merged", "--limit", String(limit), "--json", "title,url,body"], { cwd, encoding: "utf8", env: { ...process.env, GH_TOKEN: token } });
17698
+ const r = spawnSync("gh", [
17699
+ "pr",
17700
+ "list",
17701
+ "--repo",
17702
+ `${host}/${endpoint.path}`,
17703
+ "--state",
17704
+ "merged",
17705
+ "--limit",
17706
+ String(limit),
17707
+ "--json",
17708
+ "title,url,body"
17709
+ ], { cwd, encoding: "utf8", env });
17145
17710
  if (r.status !== 0)
17146
17711
  return { ok: false, error: "could not list pull requests" };
17147
17712
  for (const pr of JSON.parse(r.stdout ?? "[]")) {
@@ -17163,26 +17728,28 @@ function mergedPrStyle(limit = 6, cwd) {
17163
17728
  }
17164
17729
 
17165
17730
  // packages/workit-core/src/core/branch.ts
17166
- var resolveBranchPolicyFor = (workspaceRoot) => resolveBranchPolicy(readConfig(), resolveWorkspace(workspaceRoot));
17731
+ function effectiveWorkspacePolicy(workspaceRoot, kind, profileName) {
17732
+ const selected = resolveRuntimeWorkspacePolicy(workspaceRoot, kind, profileName?.trim() || process.env.WORKFLOW_PROFILE?.trim() || undefined);
17733
+ const config = readConfig();
17734
+ return kind === "branch" ? {
17735
+ branchPolicy: resolveBranchPolicy(config, selected.policy ? { branchPolicy: selected.policy } : null),
17736
+ provenance: { branchPolicy: selected.source }
17737
+ } : {
17738
+ commitPolicy: resolveCommitPolicy(config, selected.policy ? { commitPolicy: selected.policy } : null),
17739
+ provenance: { commitPolicy: selected.source }
17740
+ };
17741
+ }
17742
+ var resolveBranchPolicyFor = (workspaceRoot, profileName) => effectiveWorkspacePolicy(workspaceRoot, "branch", profileName).branchPolicy;
17167
17743
  var verifyPushIdentity = (root, provider, account) => {
17168
17744
  if (!account || typeof account !== "string")
17169
17745
  return { ok: false, error: "push identity requires a configured area account" };
17170
- const probe = provider === "gitlab" ? ["api", "user", "--jq", ".username"] : ["api", "user", "--jq", ".login"];
17171
- const bin = provider === "gitlab" ? "glab" : "gh";
17172
- let login;
17173
- try {
17174
- login = execFileSync2(bin, [...probe], {
17175
- cwd: root,
17176
- encoding: "utf8",
17177
- stdio: ["pipe", "pipe", "pipe"]
17178
- }).trim();
17179
- } catch {
17180
- return { ok: false, error: `${bin} identity could not be resolved` };
17181
- }
17182
- if (!login || login.toLowerCase() !== account.toLowerCase())
17746
+ const identity = vcsCliIdentity(root);
17747
+ if (!identity.ok || identity.provider !== provider)
17748
+ return { ok: false, error: identity.error ?? "hosting CLI identity could not be resolved" };
17749
+ if (identity.username?.toLowerCase() !== account.toLowerCase())
17183
17750
  return {
17184
17751
  ok: false,
17185
- error: `push identity ${login || "(unknown)"} does not match area account ${account}`
17752
+ error: `push identity ${identity.username || "(unknown)"} does not match area account ${account}`
17186
17753
  };
17187
17754
  return { ok: true };
17188
17755
  };
@@ -17200,7 +17767,7 @@ var operationAutoClass = (operation) => typeof operation === "string" ? OPERATIO
17200
17767
  var digestOf = (configDirPath) => {
17201
17768
  try {
17202
17769
  const raw = readFileSync4(`${configDirPath}/workspaces.json`, "utf8");
17203
- return createHash3("sha256").update(raw).digest("hex");
17770
+ return createHash4("sha256").update(raw).digest("hex");
17204
17771
  } catch {
17205
17772
  return null;
17206
17773
  }
@@ -17238,14 +17805,30 @@ var resolveAutoApproval = (root, dir = configDir()) => {
17238
17805
  };
17239
17806
  };
17240
17807
  var isStandingReceipt = (value) => typeof value === "object" && value !== null && value.kind === "standing";
17241
- var standingApprovalLive = (root, receipt, cls, dir) => {
17808
+ var targetOf = (root, descriptor) => {
17809
+ try {
17810
+ const parsed = typeof descriptor === "string" ? JSON.parse(descriptor) : descriptor;
17811
+ const cwd = parsed?.payload?.cwd;
17812
+ if (cwd === undefined)
17813
+ return root;
17814
+ if (typeof cwd !== "string" || !path8.isAbsolute(cwd))
17815
+ return null;
17816
+ return realpathSync3(cwd);
17817
+ } catch {
17818
+ return null;
17819
+ }
17820
+ };
17821
+ var standingApprovalLive = (root, receipt, cls, dir, descriptor) => {
17242
17822
  if (!isStandingReceipt(receipt))
17243
17823
  return false;
17244
17824
  if (typeof cls !== "string" || !AUTO_CLASSES.includes(cls))
17245
17825
  return false;
17246
17826
  if (receipt.class !== cls)
17247
17827
  return false;
17248
- const resolved = resolveAutoApproval(root, dir);
17828
+ const target = targetOf(root, descriptor);
17829
+ if (!target)
17830
+ return false;
17831
+ const resolved = resolveAutoApproval(target, dir);
17249
17832
  if (resolved.status !== "on")
17250
17833
  return false;
17251
17834
  if (resolved.workspace !== receipt.workspace)
@@ -17264,9 +17847,11 @@ var standingReceiptFor = (root, workspace, cls, dir) => {
17264
17847
  var verifyStandingApproval = (root, task, caller, binding) => {
17265
17848
  if (task.status !== "active")
17266
17849
  return failure("invalid_transition", "only active tasks can authorize actions");
17267
- const session = task.intent?.provenance?.session;
17268
- if (!session || session.kind !== "host" || session.host !== caller.host || session.handle !== caller.actor)
17269
- return failure("permission_denied", "standing approval requires the lead session");
17850
+ const workspace = new TaskStore(root).readWorkspace();
17851
+ if (!workspace.ok)
17852
+ return workspace;
17853
+ if (!workspace.data || !currentWriterOwnsTask(task, workspace.data, caller))
17854
+ return failure("permission_denied", "standing approval requires the current writer session");
17270
17855
  const standing = binding.standing;
17271
17856
  if (!standing || typeof standing.workspace !== "string" || typeof standing.class !== "string")
17272
17857
  return failure("invalid_input", "standing approval needs a workspace and class");
@@ -17279,16 +17864,19 @@ var verifyStandingApproval = (root, task, caller, binding) => {
17279
17864
  const cls = operationAutoClass(operation);
17280
17865
  if (!cls || cls !== standing.class)
17281
17866
  return failure("permission_denied", "standing approval does not cover this operation");
17282
- const resolved = resolveAutoApproval(root);
17867
+ const target = targetOf(root, binding.approvedContent);
17868
+ if (!target)
17869
+ return failure("permission_denied", "standing action target is invalid");
17870
+ const resolved = resolveAutoApproval(target);
17283
17871
  if (resolved.status !== "on" || resolved.workspace !== standing.workspace || !resolved.classes.includes(cls))
17284
17872
  return failure("permission_denied", "standing auto-approval is not live for this operation");
17285
17873
  if (cls === "push") {
17286
- const workspace = resolveWorkspaceFrom(root, configDir());
17874
+ const workspace = resolveWorkspaceFrom(target, configDir());
17287
17875
  const provider = workspace?.vcs?.provider;
17288
17876
  const account = resolved.account;
17289
17877
  if (!provider || !account)
17290
17878
  return failure("permission_denied", "auto-push requires a workspace provider and area account");
17291
- const identity = verifyPushIdentity(root, provider, account);
17879
+ const identity = verifyPushIdentity(target, provider, account);
17292
17880
  if (!identity.ok)
17293
17881
  return failure("permission_denied", identity.error);
17294
17882
  }
@@ -17297,7 +17885,7 @@ var verifyStandingApproval = (root, task, caller, binding) => {
17297
17885
  host: caller.host,
17298
17886
  session: { kind: "host", host: caller.host, handle: caller.actor },
17299
17887
  workerId: null,
17300
- receipts: [standingReceiptFor(root, standing.workspace, cls)]
17888
+ receipts: [standingReceiptFor(target, standing.workspace, cls)]
17301
17889
  });
17302
17890
  };
17303
17891
 
@@ -17448,7 +18036,7 @@ function applicableDecision(task, purpose, binding, checkoutRoot) {
17448
18036
  return [];
17449
18037
  return task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, entry.data.binding).ok : entry.data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map((entry) => entry.data);
17450
18038
  }
17451
- var digestBytes2 = (bytes) => createHash4("sha256").update(bytes).digest("hex");
18039
+ var digestBytes2 = (bytes) => createHash5("sha256").update(bytes).digest("hex");
17452
18040
  var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
17453
18041
  let root;
17454
18042
  try {
@@ -17461,23 +18049,23 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
17461
18049
  continue;
17462
18050
  if (!reference.digest)
17463
18051
  return failure("invalid_input", "document references require a byte digest");
17464
- const target = path8.resolve(root, reference.path);
17465
- if (target !== root && !target.startsWith(`${root}${path8.sep}`))
18052
+ const target = path9.resolve(root, reference.path);
18053
+ if (target !== root && !target.startsWith(`${root}${path9.sep}`))
17466
18054
  return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
17467
18055
  fields: [
17468
18056
  { path: "contentRefs.path", reason: `${reference.path} is outside the checkout` }
17469
18057
  ]
17470
18058
  });
17471
18059
  try {
17472
- const relative = path8.relative(root, target);
18060
+ const relative = path9.relative(root, target);
17473
18061
  let current = root;
17474
- for (const segment of relative.split(path8.sep)) {
18062
+ for (const segment of relative.split(path9.sep)) {
17475
18063
  if (!segment)
17476
18064
  continue;
17477
- current = path8.join(current, segment);
18065
+ current = path9.join(current, segment);
17478
18066
  const stat = fs6.lstatSync(current);
17479
18067
  const real = fs6.realpathSync(current);
17480
- if (real !== root && !real.startsWith(`${root}${path8.sep}`))
18068
+ if (real !== root && !real.startsWith(`${root}${path9.sep}`))
17481
18069
  return failure("invalid_input", "document reference escapes checkout; cite it as an external file:// reference instead", {
17482
18070
  fields: [
17483
18071
  {
@@ -17503,6 +18091,20 @@ var verifyDecisionContentAtRoot = (checkoutRoot, binding) => {
17503
18091
  };
17504
18092
  var verifyContentRefs = (store, binding) => verifyDecisionContentAtRoot(store.root, binding);
17505
18093
  var storedDecisionApplicable = (store, task, purpose, binding) => task.decisions.filter((entry) => entry.provenance.kind !== "imported" && decisionMatches(entry, purpose, binding) && verifyDecisionContentAtRoot(store.root, entry.data.binding).ok);
18094
+ var approvalAuthorityMatches = (store, task, approval, current) => {
18095
+ if (approval.kind !== "host_observed" || approval.receipts.length === 0)
18096
+ return false;
18097
+ if (provenanceMatches(approval, current))
18098
+ return true;
18099
+ const session = current.kind === "host_observed" ? current.session : null;
18100
+ if (!session || session.kind !== "host")
18101
+ return false;
18102
+ const workspace = store.readWorkspace();
18103
+ return workspace.ok && workspace.data !== null && currentWriterOwnsTask(task, workspace.data, {
18104
+ host: session.host,
18105
+ actor: session.handle
18106
+ });
18107
+ };
17506
18108
  var validateAction = (store, task, workspaceId, input, authority) => {
17507
18109
  if (task.status !== "active")
17508
18110
  return failure("invalid_transition", "only active tasks can authorize actions");
@@ -17528,7 +18130,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
17528
18130
  return failure("permission_denied", "native reservation authority does not match decision");
17529
18131
  if (decision.response !== "approved")
17530
18132
  return failure("permission_denied", "decision was rejected");
17531
- if (entry.provenance.kind !== "host_observed" || entry.provenance.receipts.length === 0 || !provenanceMatches(entry.provenance, authority.provenance))
18133
+ if (!approvalAuthorityMatches(store, task, entry.provenance, authority.provenance))
17532
18134
  return failure("permission_denied", "action approval lacks native receipt assurance");
17533
18135
  const receipts = entry.provenance.receipts;
17534
18136
  const standing = receipts.filter((receipt) => typeof receipt === "object" && receipt !== null && receipt.kind === "standing");
@@ -17541,7 +18143,7 @@ var validateAction = (store, task, workspaceId, input, authority) => {
17541
18143
  operation = null;
17542
18144
  }
17543
18145
  const cls = operationAutoClass(operation);
17544
- if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls)))
18146
+ if (!cls || !standing.some((receipt) => standingApprovalLive(store.root, receipt, cls, undefined, decision.binding.approvedContent)))
17545
18147
  return failure("permission_denied", "standing auto-approval is not live for this operation");
17546
18148
  }
17547
18149
  if (decision.revoked)
@@ -17695,7 +18297,7 @@ function settleAction(input) {
17695
18297
  return failure("not_found", "decision not found");
17696
18298
  if (!authorityDecisionMatches(authority, entry.data))
17697
18299
  return failure("permission_denied", "native settlement authority does not match decision");
17698
- if (!provenanceMatches(entry.provenance, authority.provenance))
18300
+ if (!approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
17699
18301
  return failure("permission_denied", "native settlement caller does not match approval");
17700
18302
  if (entry.data.purpose !== "action" || entry.data.digest !== decisionDigest(entry.data))
17701
18303
  return failure("permission_denied", "decision binding is invalid");
@@ -17794,7 +18396,7 @@ function reconcileAction(input) {
17794
18396
  const entry = current.decisions.find((candidate) => candidate.id === input.decisionId);
17795
18397
  if (!entry || entry.data.purpose !== "action")
17796
18398
  return failure("not_found", "decision not found");
17797
- if (!authorityDecisionMatches(authority, entry.data) || !provenanceMatches(entry.provenance, authority.provenance))
18399
+ if (!authorityDecisionMatches(authority, entry.data) || !approvalAuthorityMatches(input.store, current, entry.provenance, authority.provenance))
17798
18400
  return failure("permission_denied", "native reconciliation caller does not match approval");
17799
18401
  if (entry.data.response !== "approved" || entry.data.revoked !== null || entry.data.digest !== decisionDigest(entry.data) || entry.data.binding.taskId !== current.id || entry.data.binding.workspaceId !== current.workspaceId || entry.data.binding.workspaceId !== workspace.data.id)
17800
18402
  return failure("permission_denied", "reconciliation decision binding is invalid");
@@ -17843,15 +18445,15 @@ var bindingCovers = scopeCovers;
17843
18445
  var verifyDecisionContent = verifyContentRefs;
17844
18446
 
17845
18447
  // packages/workit-core/src/core/task-evaluation.ts
17846
- var digestBytes3 = (value) => createHash5("sha256").update(value).digest("hex");
17847
- var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path9.sep}`);
17848
- var relative = (root, candidate) => path9.relative(root, candidate).split(path9.sep).join("/") || ".";
18448
+ var digestBytes3 = (value) => createHash6("sha256").update(value).digest("hex");
18449
+ var inside = (root, candidate) => candidate === root || candidate.startsWith(`${root}${path10.sep}`);
18450
+ var relative = (root, candidate) => path10.relative(root, candidate).split(path10.sep).join("/") || ".";
17849
18451
  var canonicalPath = (value) => {
17850
18452
  if (typeof value !== "string" || !value)
17851
18453
  return null;
17852
18454
  if (value.includes("\\") || value.split("/").includes(".."))
17853
18455
  return null;
17854
- const normalized = path9.posix.normalize(value);
18456
+ const normalized = path10.posix.normalize(value);
17855
18457
  if (normalized === ".." || normalized.startsWith("../") || normalized.startsWith("/"))
17856
18458
  return null;
17857
18459
  return normalized === "." ? "." : normalized.replace(/\/$/, "");
@@ -17899,14 +18501,14 @@ var gitPaths = (root) => {
17899
18501
  git: /not a git repository/i.test(stderr) ? false : true
17900
18502
  };
17901
18503
  }
17902
- const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path9.sep).join("/")) : [];
18504
+ const paths = result.stdout ? result.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")) : [];
17903
18505
  const stagedDeleted = spawnSync2("git", ["diff", "--cached", "--name-only", "--diff-filter=D", "-z"], { cwd: root, encoding: "buffer" });
17904
18506
  if (stagedDeleted.status !== 0) {
17905
18507
  const stderr = stagedDeleted.stderr?.toString("utf8") ?? "";
17906
18508
  if (!/not a git repository/i.test(stderr))
17907
18509
  return { paths, uncertain: true, git: true };
17908
18510
  } else if (stagedDeleted.stdout) {
17909
- paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path9.sep).join("/")));
18511
+ paths.push(...stagedDeleted.stdout.toString("utf8").split("\x00").filter(Boolean).map((item) => item.split(path10.sep).join("/")));
17910
18512
  }
17911
18513
  return { paths: [...new Set(paths)], uncertain: false, git: true };
17912
18514
  };
@@ -17921,7 +18523,7 @@ var walk = (root, directory, output) => {
17921
18523
  for (const name of names) {
17922
18524
  if (name === ".git" || name === ".workit")
17923
18525
  continue;
17924
- const target = path9.join(directory, name);
18526
+ const target = path10.join(directory, name);
17925
18527
  let stat;
17926
18528
  try {
17927
18529
  stat = fs7.lstatSync(target);
@@ -17939,12 +18541,12 @@ var walk = (root, directory, output) => {
17939
18541
  var scopeRoots = (root, scope) => {
17940
18542
  const roots = [];
17941
18543
  for (const item of scope.paths.length ? scope.paths : ["."]) {
17942
- const target = path9.resolve(root, item);
18544
+ const target = path10.resolve(root, item);
17943
18545
  if (!inside(root, target))
17944
18546
  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` }] });
17945
18547
  let ancestor = target;
17946
18548
  while (!fs7.existsSync(ancestor) && ancestor !== root)
17947
- ancestor = path9.dirname(ancestor);
18549
+ ancestor = path10.dirname(ancestor);
17948
18550
  try {
17949
18551
  if (!inside(root, fs7.realpathSync(ancestor)))
17950
18552
  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` }] });
@@ -17996,7 +18598,7 @@ function captureCandidate(root, scope, environment = []) {
17996
18598
  for (const item of [...names].sort()) {
17997
18599
  if (!scopeMatches(item, normalizedScope))
17998
18600
  continue;
17999
- const target = path9.join(checkout, item);
18601
+ const target = path10.join(checkout, item);
18000
18602
  try {
18001
18603
  const stat = fs7.lstatSync(target);
18002
18604
  if (stat.isSymbolicLink()) {
@@ -18131,7 +18733,7 @@ var resolveBinding = (evidence, pin, currentCandidateId) => {
18131
18733
  candidateId: evidence.candidateId ?? fallback
18132
18734
  };
18133
18735
  };
18134
- var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId === findingCandidateId);
18736
+ var findingVerificationPasses = (findingCandidateId, evidence) => evidence.status === "passed" && (evidence.kind === "check" || evidence.kind === "review") && ((findingCandidateId ?? null) === null || evidence.candidateId != null);
18135
18737
  var applicableDecision2 = (task, workspace, requirement, checkoutRoot) => task.decisions.filter((entry) => entry.provenance.kind !== "imported").map((entry) => entry.data).filter((decision) => decision.purpose === "limitation" && decision.response === "approved" && decision.revoked === null && decision.binding.taskId === task.id && decision.binding.workspaceId === workspace.id && decision.requirementIds.includes(requirement.id) && decision.binding.scope && scopeCovers2(decision.binding.scope, requirement.scope) && decision.digest === decisionDigest(decision) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, decision.binding).ok : decision.binding.contentRefs.every((reference) => reference.kind !== "file")));
18136
18738
  var applicableRequirementDecision = (task, workspace, requirement, checkoutRoot) => task.decisions.filter(({ data, provenance }) => provenance.kind !== "imported" && data.purpose !== "limitation" && (data.response === "approved" || data.response === "stated") && data.revoked === null && data.binding.taskId === task.id && data.binding.workspaceId === workspace.id && data.requirementIds.includes(requirement.id) && scopeCovers2(data.binding.scope, requirement.scope) && data.digest === decisionDigest(data) && (checkoutRoot ? verifyDecisionContentAtRoot(checkoutRoot, data.binding).ok : data.binding.contentRefs.every((reference) => reference.kind !== "file"))).map(({ id }) => ({ id }));
18137
18739
  var evidenceMatchesRequirement = (kind, evidenceKind, dimension, ruleId) => {
@@ -18366,6 +18968,11 @@ var exportDigest = (bundle) => sha256({
18366
18968
  task: bundle.task
18367
18969
  });
18368
18970
  // packages/workit-core/src/core/external-action.ts
18971
+ var chainStepSchema = union([
18972
+ string2().min(1),
18973
+ object({ branch: string2().min(1) }).strict(),
18974
+ object({ pr: literal(true) }).strict()
18975
+ ]);
18369
18976
  var externalActionSchema = discriminatedUnion("operation", [
18370
18977
  object({
18371
18978
  operation: literal("git.branch_setup"),
@@ -18373,20 +18980,22 @@ var externalActionSchema = discriminatedUnion("operation", [
18373
18980
  action: _enum(["setup", "reapply_stash"]).optional(),
18374
18981
  sdd_dir: string2().optional(),
18375
18982
  target_branch: string2().optional(),
18376
- stash: _enum(["yes", "no"]).optional()
18983
+ stash: _enum(["yes", "no"]).optional(),
18984
+ cwd: string2().min(1).optional()
18377
18985
  }).strict()
18378
18986
  }).strict(),
18379
18987
  object({
18380
18988
  operation: literal("git.commit"),
18381
18989
  payload: object({
18382
18990
  message: string2().min(1).optional(),
18383
- plan_steps: array(string2().min(1)).optional(),
18384
- plan_branch: string2().min(1).optional()
18991
+ plan_steps: array(chainStepSchema).min(1).max(32).optional(),
18992
+ plan_branch: string2().min(1).optional(),
18993
+ cwd: string2().min(1).optional()
18385
18994
  }).strict()
18386
18995
  }).strict(),
18387
18996
  object({
18388
18997
  operation: literal("git.push"),
18389
- payload: object({ branch: string2().min(1).optional() }).strict()
18998
+ payload: object({ branch: string2().min(1).optional(), cwd: string2().min(1).optional() }).strict()
18390
18999
  }).strict(),
18391
19000
  object({
18392
19001
  operation: literal("hosting.pull_request"),
@@ -18395,16 +19004,22 @@ var externalActionSchema = discriminatedUnion("operation", [
18395
19004
  body: string2().optional(),
18396
19005
  draft: boolean2().optional(),
18397
19006
  target_branch: string2().optional(),
18398
- babysit: boolean2().optional()
19007
+ babysit: boolean2().optional(),
19008
+ cwd: string2().min(1).optional()
18399
19009
  }).strict()
18400
19010
  }).strict(),
18401
19011
  object({
18402
19012
  operation: literal("hosting.merge"),
18403
19013
  payload: object({
18404
19014
  target_branch: string2().min(1).optional(),
18405
- source_branch: string2().min(1).optional()
19015
+ source_branch: string2().min(1).optional(),
19016
+ cwd: string2().min(1).optional()
18406
19017
  }).strict()
18407
19018
  }).strict(),
19019
+ object({
19020
+ operation: literal("hosting.delete_branch"),
19021
+ payload: object({ branch: string2().min(1), cwd: string2().min(1).optional() }).strict()
19022
+ }).strict(),
18408
19023
  object({
18409
19024
  operation: literal("youtrack.update"),
18410
19025
  payload: object({
@@ -18460,12 +19075,13 @@ var externalActionSchema = discriminatedUnion("operation", [
18460
19075
  issueRef: string2().optional(),
18461
19076
  mode: string2().optional(),
18462
19077
  specPath: string2().optional(),
18463
- planPath: string2().optional()
19078
+ planPath: string2().optional(),
19079
+ cwd: string2().min(1).optional()
18464
19080
  }).strict()
18465
19081
  }).strict()
18466
19082
  ]);
18467
19083
  // packages/workit-core/src/core/task-engine.ts
18468
- import { realpathSync as realpathSync5 } from "node:fs";
19084
+ import { realpathSync as realpathSync6 } from "node:fs";
18469
19085
 
18470
19086
  // packages/workit-core/src/core/policy-resolver.ts
18471
19087
  var resolverInputSchema = object({
@@ -18866,6 +19482,7 @@ var verifyNativeWorker = (verifier, input, binding) => {
18866
19482
  expectedWorkspaceRevision: expected.expectedWorkspaceRevision,
18867
19483
  state: expected.state,
18868
19484
  session: expected.session,
19485
+ report: expected.report ?? null,
18869
19486
  provenance: result.data,
18870
19487
  ...binding
18871
19488
  });
@@ -18876,7 +19493,7 @@ var takeWorker = (token, binding, expected) => {
18876
19493
  verifiedWorkers.delete(token);
18877
19494
  if (!value)
18878
19495
  return null;
18879
- if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session))
19496
+ if (value.owner !== binding.owner || value.store !== binding.store || value.root !== binding.root || value.root !== value.store.root || !sameValue(value.caller, binding.caller) || value.taskId !== expected.taskId || value.workerId !== expected.workerId || value.expectedRevision !== expected.expectedRevision || value.expectedWorkspaceRevision !== expected.expectedWorkspaceRevision || value.state !== expected.state || !sameValue(value.session, expected.session) || !sameValue(value.report, expected.report ?? null))
18880
19497
  return null;
18881
19498
  return value;
18882
19499
  };
@@ -18917,6 +19534,8 @@ var applyWorkerLifecycle = (input) => {
18917
19534
  return failure("invalid_transition", "worker already has an observed session");
18918
19535
  if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
18919
19536
  return failure("invalid_transition", "worker launch was not claimed");
19537
+ if (input.report && input.state !== "stopped")
19538
+ return failure("invalid_input", "worker reports require a stopped worker");
18920
19539
  if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
18921
19540
  return failure("invalid_input", "running worker observations require a session");
18922
19541
  if (entry.data.session && !sameValue(entry.data.session, input.session))
@@ -18933,12 +19552,13 @@ var applyWorkerLifecycle = (input) => {
18933
19552
  data: {
18934
19553
  ...entry.data,
18935
19554
  state: terminalCancel ? "cancelling" : input.state,
18936
- session: input.session
19555
+ session: input.session,
19556
+ report: entry.data.report ?? input.report ?? null
18937
19557
  }
18938
19558
  };
18939
19559
  const shouldClear = input.state === "stopped" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
18940
19560
  const shouldUncertain = input.state === "unknown" && workspace.data.writer !== null && workspace.data.writer.owner.taskId === task.data.id && workspace.data.writer.owner.workerId === input.workerId;
18941
- if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && !shouldClear && !shouldUncertain)
19561
+ if (entry.data.state === nextEntry.data.state && sameValue(entry.data.session, input.session) && sameValue(entry.data.report, nextEntry.data.report) && !shouldClear && !shouldUncertain)
18942
19562
  return success(task.data.revision, workspace.data.revision, entry);
18943
19563
  const changed = input.store.mutateTaskAndWorkspace({
18944
19564
  taskId: task.data.id,
@@ -19006,6 +19626,18 @@ var portableTask = (task) => {
19006
19626
  };
19007
19627
  for (const entry of clone.assessments)
19008
19628
  demote(entry.data.facts, entry.data.signals, entry.data.consequences);
19629
+ const sourceDecisions = new Map(task.decisions.map((entry) => [entry.id, entry]));
19630
+ for (const entry of clone.decisions) {
19631
+ const consumption = sourceDecisions.get(entry.id)?.data.consumption;
19632
+ if (!consumption)
19633
+ continue;
19634
+ entry.data.consumption = {
19635
+ ...consumption,
19636
+ state: consumption.state === "reserved" ? "uncertain" : consumption.state,
19637
+ actionRef: { kind: "record", collection: "decisions", id: entry.id },
19638
+ ...consumption.historicalActionId ? { historicalActionId: consumption.historicalActionId } : consumption.actionRef.kind === "host" ? { historicalActionId: consumption.actionRef.handle } : {}
19639
+ };
19640
+ }
19009
19641
  clone.candidates = [];
19010
19642
  return clone;
19011
19643
  };
@@ -19131,7 +19763,11 @@ var importedTask = (source, bundle, destinationWorkspaceId, context, timestamp)
19131
19763
  taskId: "",
19132
19764
  workspaceId: destinationWorkspaceId
19133
19765
  },
19134
- consumption: null
19766
+ consumption: data.consumption ? {
19767
+ ...data.consumption,
19768
+ state: data.consumption.state === "reserved" ? "uncertain" : data.consumption.state,
19769
+ actionRef: { kind: "record", collection: "decisions", id: entry.id }
19770
+ } : null
19135
19771
  }
19136
19772
  };
19137
19773
  }),
@@ -19172,7 +19808,7 @@ class WorkitCore {
19172
19808
  }
19173
19809
  contextRootError() {
19174
19810
  try {
19175
- if (realpathSync5(this.context.root) !== this.store.root)
19811
+ if (realpathSync6(this.context.root) !== this.store.root)
19176
19812
  return failure("invalid_input", "operation context root does not match the task store root");
19177
19813
  } catch {
19178
19814
  return failure("invalid_input", "operation context root cannot be resolved");
@@ -19331,7 +19967,7 @@ class WorkitCore {
19331
19967
  return parsed;
19332
19968
  const input = parsed.data;
19333
19969
  if ((this.context.workerId ?? null) !== null) {
19334
- if (input.action !== "inspect")
19970
+ if (input.action !== "inspect" && input.action !== "list")
19335
19971
  return failure("permission_denied", "helpers cannot control task lifecycle or scope");
19336
19972
  }
19337
19973
  switch (input.action) {
@@ -19356,14 +19992,45 @@ class WorkitCore {
19356
19992
  const listed = this.store.listTasks();
19357
19993
  if (!listed.ok)
19358
19994
  return listed;
19359
- const summaries = [];
19360
- for (const task of listed.data) {
19361
- const summary = this.summary(task);
19362
- if (!summary.ok)
19363
- return summary;
19364
- summaries.push(summary.data);
19365
- }
19366
- return success(null, null, summaries);
19995
+ const status = input.status ?? "open";
19996
+ const limit = input.limit ?? 20;
19997
+ const query = input.query?.toLowerCase().split(/\s+/);
19998
+ const selected = listed.data.filter((task) => status === "all" ? true : status === "closed" ? task.status === "closed" : task.status !== "closed").filter((task) => !query || query.every((term) => JSON.stringify({
19999
+ id: task.id,
20000
+ workspaceId: task.workspaceId,
20001
+ origin: task.origin,
20002
+ createdAt: task.createdAt,
20003
+ updatedAt: task.updatedAt,
20004
+ source: task.intent.provenance,
20005
+ objective: task.intent.data.objective,
20006
+ progress: task.progress,
20007
+ decisions: task.decisions.map(({ data }) => data)
20008
+ }).toLowerCase().includes(term))).sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)).slice(0, limit);
20009
+ if (selected.length === 0)
20010
+ return success(null, null, []);
20011
+ const workspace = this.store.readWorkspace();
20012
+ if (!workspace.ok)
20013
+ return workspace;
20014
+ if (!workspace.data)
20015
+ return failure("not_found", "workspace not found");
20016
+ const tasks = selected.map((task) => ({
20017
+ id: task.id,
20018
+ revision: task.revision,
20019
+ workspaceRevision: workspace.data.revision,
20020
+ createdAt: task.createdAt,
20021
+ updatedAt: task.updatedAt,
20022
+ runtime: task.runtime ?? null,
20023
+ objective: task.intent.data.objective,
20024
+ status: task.status,
20025
+ closure: task.closure,
20026
+ progress: task.progress,
20027
+ writer: task.status === "closed" ? null : workspace.data.writer,
20028
+ source: {
20029
+ host: task.intent.provenance.host,
20030
+ kind: task.intent.provenance.kind
20031
+ }
20032
+ }));
20033
+ return success(null, null, tasks);
19367
20034
  }
19368
20035
  case "inspect": {
19369
20036
  const task = this.store.readTask(input.taskId);
@@ -19372,7 +20039,7 @@ class WorkitCore {
19372
20039
  const helper = this.helperTaskGuard(task.data, true);
19373
20040
  if (!helper.ok)
19374
20041
  return helper;
19375
- if (input.view === "summary")
20042
+ if ((input.view ?? "summary") === "summary")
19376
20043
  return this.summary(task.data);
19377
20044
  return this.view(task.data);
19378
20045
  }
@@ -19716,7 +20383,7 @@ class WorkitCore {
19716
20383
  const evaluations = evaluateEvidence(task.data, current.data);
19717
20384
  const verified = evidence.some((entry) => {
19718
20385
  const evaluation = evaluations.find((item) => item.evidenceId === entry.id);
19719
- return findingVerificationPasses(finding.data.candidateId, {
20386
+ return entry.recordedAt >= finding.recordedAt && findingVerificationPasses(finding.data.candidateId, {
19720
20387
  kind: entry.data.kind,
19721
20388
  candidateId: entry.data.candidateId,
19722
20389
  status: evaluation?.status ?? "missing"
@@ -19796,7 +20463,18 @@ class WorkitCore {
19796
20463
  if (task.data.status !== "active")
19797
20464
  return failure("invalid_transition", "paused or closed tasks cannot assign workers");
19798
20465
  if (!bindingCovers(task.data.intent.data.scope, input.assignment.scope))
19799
- return failure("permission_denied", "worker assignment is outside task scope");
20466
+ return failure("permission_denied", "worker assignment is outside task scope", {
20467
+ fields: [
20468
+ {
20469
+ path: "assignment.scope",
20470
+ reason: `paths ${JSON.stringify(input.assignment.scope.paths)} must be contained by lead paths ${JSON.stringify(task.data.intent.data.scope.paths)}`
20471
+ },
20472
+ {
20473
+ path: "task.intent.scope",
20474
+ reason: `lead paths are ${JSON.stringify(task.data.intent.data.scope.paths)}`
20475
+ }
20476
+ ]
20477
+ });
19800
20478
  if (input.assignment.decisionIds.some((id) => !task.data.decisions.some((entry) => entry.id === id)))
19801
20479
  return failure("invalid_input", "worker assignment references an unknown decision");
19802
20480
  if (input.assignment.requirementIds.some((id) => !task.data.policy?.requirements.some((requirement) => requirement.id === id)))
@@ -20003,7 +20681,15 @@ class WorkitCore {
20003
20681
  ...item,
20004
20682
  recordedAt: mutation.now,
20005
20683
  provenance: attested.data,
20006
- data: { ...item.data, state: "dispatching" }
20684
+ data: {
20685
+ ...item.data,
20686
+ state: "dispatching",
20687
+ coordinator: {
20688
+ kind: "host",
20689
+ host: this.context.caller.host,
20690
+ handle: this.context.caller.actor
20691
+ }
20692
+ }
20007
20693
  } : item)
20008
20694
  });
20009
20695
  }
@@ -20079,6 +20765,7 @@ class WorkitCore {
20079
20765
  expectedWorkspaceRevision: input.expectedWorkspaceRevision,
20080
20766
  state: "stopped",
20081
20767
  session: null,
20768
+ report: null,
20082
20769
  provenance: attested.data,
20083
20770
  owner: this.authorityOwner,
20084
20771
  store: this.store,
@@ -20386,10 +21073,12 @@ class WorkitCore {
20386
21073
  return workspace;
20387
21074
  if (!workspace.data)
20388
21075
  return failure("not_found", "workspace not found");
20389
- const current = captureCandidate(this.store.root, task.intent.data.scope, environment());
21076
+ const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
21077
+ const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
20390
21078
  if (!current.ok)
20391
21079
  return current;
20392
- const requirements = evaluateRequirements(task, workspace.data, this.context.capabilities, current.data, this.store.root, this.context.caller);
21080
+ const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
21081
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
20393
21082
  return success(task.revision, workspace.data.revision, {
20394
21083
  id: task.id,
20395
21084
  revision: task.revision,
@@ -20403,7 +21092,7 @@ class WorkitCore {
20403
21092
  progress: task.progress,
20404
21093
  policy: task.policy,
20405
21094
  requirements,
20406
- writer: workspace.data.writer
21095
+ writer: evaluationWorkspace.writer
20407
21096
  });
20408
21097
  }
20409
21098
  view(task) {
@@ -20412,13 +21101,15 @@ class WorkitCore {
20412
21101
  return workspace;
20413
21102
  if (!workspace.data)
20414
21103
  return failure("not_found", "workspace not found");
20415
- const current = captureCandidate(this.store.root, task.intent.data.scope, environment());
21104
+ const historical = task.status === "closed" ? task.candidates.at(-1) : undefined;
21105
+ const current = historical ? success(null, null, historical) : captureCandidate(this.store.root, task.intent.data.scope, environment());
20416
21106
  if (!current.ok)
20417
21107
  return current;
20418
- const requirements = evaluateRequirements(task, workspace.data, this.context.capabilities, current.data, this.store.root, this.context.caller);
21108
+ const evaluationWorkspace = task.status === "closed" ? { ...workspace.data, writer: null } : workspace.data;
21109
+ const requirements = evaluateRequirements(task, evaluationWorkspace, this.context.capabilities, current.data, this.store.root, this.context.caller);
20419
21110
  return success(task.revision, workspace.data.revision, {
20420
21111
  task,
20421
- workspace: workspace.data,
21112
+ workspace: evaluationWorkspace,
20422
21113
  capabilities: this.context.capabilities,
20423
21114
  requirements,
20424
21115
  evidence: evaluateEvidence(task, current.data)
@@ -20433,7 +21124,7 @@ class WorkitCore {
20433
21124
  if (view.task.workspaceId !== view.workspace.id)
20434
21125
  return failure("invalid_input", "task and workspace bindings are invalid");
20435
21126
  try {
20436
- if (realpathSync5(view.workspace.root) !== this.store.root)
21127
+ if (realpathSync6(view.workspace.root) !== this.store.root)
20437
21128
  return failure("invalid_input", "task view root does not match the task store");
20438
21129
  } catch {
20439
21130
  return failure("invalid_input", "task view root cannot be resolved");
@@ -20509,8 +21200,6 @@ class WorkitCore {
20509
21200
  return helper;
20510
21201
  if (status === "paused" && task.data.status !== "active" || status === "active" && task.data.status !== "paused")
20511
21202
  return failure("invalid_transition", `cannot transition ${task.data.status} to ${status}`);
20512
- if (status === "active" && input.authorityRefs.length === 0)
20513
- return failure("permission_denied", "resuming a task requires authority references");
20514
21203
  const workspace = this.store.readWorkspace();
20515
21204
  if (!workspace.ok)
20516
21205
  return workspace;
@@ -20540,7 +21229,7 @@ class WorkitCore {
20540
21229
  return failure("needs_input", "imported task requires fresh destination evidence");
20541
21230
  if (current.data.blockers.some((blocker) => blocker.dependentAction === "resume" || blocker.reason === "worker state requires reconciliation"))
20542
21231
  return failure("recovery_required", "imported task requires resume reconciliation");
20543
- if (!validResumeApproval(task.data, workspace.data, input.authorityRefs, this.context))
21232
+ if (!validResumeApproval(task.data, workspace.data, input.authorityRefs ?? [], this.context))
20544
21233
  return failure("permission_denied", "imported resume requires native destination approval");
20545
21234
  resumeCandidate = current.data.candidate;
20546
21235
  }
@@ -20670,7 +21359,7 @@ class WorkitCore {
20670
21359
  task: (value, context) => success(context.revision, null, {
20671
21360
  ...value,
20672
21361
  status: "closed",
20673
- candidates: value.candidates.some((item) => item.id === withCandidate.id) ? value.candidates : [...value.candidates, withCandidate],
21362
+ candidates: taskForView.candidates,
20674
21363
  closure: {
20675
21364
  outcome: closure.data.outcome,
20676
21365
  at: context.now,
@@ -20712,22 +21401,22 @@ function resolveGitRevision(root, value) {
20712
21401
  }
20713
21402
  }
20714
21403
  function resolveInside(root, candidate) {
20715
- const base = realpathSync6(root);
20716
- const target = path10.resolve(base, candidate);
20717
- if (target !== base && !target.startsWith(base + path10.sep)) {
21404
+ const base = realpathSync7(root);
21405
+ const target = path11.resolve(base, candidate);
21406
+ if (target !== base && !target.startsWith(base + path11.sep)) {
20718
21407
  throw new Error("path must stay inside repository root");
20719
21408
  }
20720
21409
  let ancestor = target;
20721
21410
  while (!existsSync5(ancestor))
20722
- ancestor = path10.dirname(ancestor);
20723
- const canonicalAncestor = realpathSync6(ancestor);
20724
- if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path10.sep)) {
21411
+ ancestor = path11.dirname(ancestor);
21412
+ const canonicalAncestor = realpathSync7(ancestor);
21413
+ if (canonicalAncestor !== base && !canonicalAncestor.startsWith(base + path11.sep)) {
20725
21414
  throw new Error("path must stay inside repository root");
20726
21415
  }
20727
21416
  return target;
20728
21417
  }
20729
21418
  function run2(root, executable, args, env = {}) {
20730
- const cwd = realpathSync6(root);
21419
+ const cwd = realpathSync7(root);
20731
21420
  const result = spawnSync3(executable, args, {
20732
21421
  cwd,
20733
21422
  encoding: "utf8",
@@ -20751,8 +21440,8 @@ var SUPPORT_MATRIX = {
20751
21440
  };
20752
21441
 
20753
21442
  // packages/workit-mcp/src/server.ts
20754
- import { existsSync as existsSync7, readFileSync as readFileSync8, realpathSync as realpathSync7 } from "node:fs";
20755
- import path14 from "node:path";
21443
+ import { existsSync as existsSync7, readFileSync as readFileSync8, realpathSync as realpathSync8 } from "node:fs";
21444
+ import path15 from "node:path";
20756
21445
 
20757
21446
  // node_modules/@modelcontextprotocol/sdk/node_modules/zod/v4/core/core.js
20758
21447
  var NEVER2 = Object.freeze({
@@ -24279,7 +24968,7 @@ function check(fn) {
24279
24968
  ch._zod.check = fn;
24280
24969
  return ch;
24281
24970
  }
24282
- function custom(fn, _params) {
24971
+ function custom2(fn, _params) {
24283
24972
  return _custom2(ZodCustom2, fn ?? (() => true), _params);
24284
24973
  }
24285
24974
  function refine2(fn, _params = {}) {
@@ -24316,7 +25005,7 @@ var LATEST_PROTOCOL_VERSION = "2025-11-25";
24316
25005
  var SUPPORTED_PROTOCOL_VERSIONS = [LATEST_PROTOCOL_VERSION, "2025-06-18", "2025-03-26", "2024-11-05", "2024-10-07"];
24317
25006
  var RELATED_TASK_META_KEY = "io.modelcontextprotocol/related-task";
24318
25007
  var JSONRPC_VERSION = "2.0";
24319
- var AssertObjectSchema = custom((v) => v !== null && (typeof v === "object" || typeof v === "function"));
25008
+ var AssertObjectSchema = custom2((v) => v !== null && (typeof v === "object" || typeof v === "function"));
24320
25009
  var ProgressTokenSchema = union2([string4(), number4().int()]);
24321
25010
  var CursorSchema = string4();
24322
25011
  var TaskCreationParamsSchema = looseObject({
@@ -26662,18 +27351,19 @@ var redactSecrets = (value) => applyPatterns(value);
26662
27351
 
26663
27352
  // packages/workit-core/src/core/pr-create.ts
26664
27353
  import fs8 from "node:fs";
26665
- import path11 from "node:path";
27354
+ import path12 from "node:path";
27355
+ import { spawnSync as spawnSync4 } from "node:child_process";
26666
27356
  function parseGhIssue(value) {
26667
27357
  const m = /issues\/(\d+)/.exec(value);
26668
27358
  return m ? m[1] : String(value).trim().replace(/^#/, "");
26669
27359
  }
26670
27360
  function whichOnPath(tool) {
26671
27361
  const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
26672
- for (const dir of (process.env.PATH ?? "").split(path11.delimiter)) {
27362
+ for (const dir of (process.env.PATH ?? "").split(path12.delimiter)) {
26673
27363
  if (!dir)
26674
27364
  continue;
26675
27365
  for (const name of names) {
26676
- const candidate = path11.join(dir, name);
27366
+ const candidate = path12.join(dir, name);
26677
27367
  try {
26678
27368
  fs8.accessSync(candidate, fs8.constants.X_OK);
26679
27369
  return candidate;
@@ -26683,11 +27373,21 @@ function whichOnPath(tool) {
26683
27373
  return null;
26684
27374
  }
26685
27375
  var hostingCliAvailable = (provider) => whichOnPath(provider === "gitlab" ? "glab" : "gh") !== null;
27376
+ var pushRemote = (cwd) => {
27377
+ const result = spawnSync4("git", ["remote", "get-url", "--push", "--all", "origin"], {
27378
+ cwd,
27379
+ encoding: "utf8"
27380
+ });
27381
+ if (result.status !== 0)
27382
+ return null;
27383
+ const urls = (result.stdout ?? "").split(/\r?\n/u).map((url) => url.trim()).filter(Boolean);
27384
+ return urls.length === 1 ? urls[0] : null;
27385
+ };
26686
27386
 
26687
27387
  // packages/workit-core/src/core/repo-context.ts
26688
- import { spawnSync as spawnSync4 } from "node:child_process";
27388
+ import { spawnSync as spawnSync5 } from "node:child_process";
26689
27389
  import { existsSync as existsSync6, readFileSync as readFileSync7, readdirSync as readdirSync4 } from "node:fs";
26690
- import path12 from "node:path";
27390
+ import path13 from "node:path";
26691
27391
  var isSafeContextRange = (value) => value.length <= 4096 && (() => {
26692
27392
  try {
26693
27393
  gitRevisionParts(value);
@@ -26714,7 +27414,7 @@ var invalidRange = (cwd) => ({
26714
27414
  cwd
26715
27415
  });
26716
27416
  var runGit = (cwd, args) => {
26717
- const result = spawnSync4("git", args, { cwd, encoding: "utf8" });
27417
+ const result = spawnSync5("git", args, { cwd, encoding: "utf8" });
26718
27418
  return {
26719
27419
  stdout: (result.stdout ?? "").trimEnd(),
26720
27420
  stderr: (result.stderr ?? "").trimEnd(),
@@ -26821,7 +27521,7 @@ var findPrTemplate = (cwd) => {
26821
27521
  ]) {
26822
27522
  const parts = rel.split("/");
26823
27523
  const base = parts[parts.length - 1];
26824
- const dir = parts.length > 1 ? path12.join(cwd, ...parts.slice(0, -1)) : cwd;
27524
+ const dir = parts.length > 1 ? path13.join(cwd, ...parts.slice(0, -1)) : cwd;
26825
27525
  const actual = probeCaseInsensitive(dir, base);
26826
27526
  if (actual)
26827
27527
  return [...parts.slice(0, -1), actual].join("/");
@@ -26852,7 +27552,7 @@ var diffStatForRange = (cwd, range) => {
26852
27552
  return runGit(cwd, ["diff", "--stat", "--"]).stdout;
26853
27553
  };
26854
27554
  var packageScripts = (cwd, keys) => {
26855
- const pkgPath = path12.join(cwd, "package.json");
27555
+ const pkgPath = path13.join(cwd, "package.json");
26856
27556
  if (!existsSync6(pkgPath))
26857
27557
  return [];
26858
27558
  try {
@@ -26878,9 +27578,9 @@ var documentationFiles = (cwd) => {
26878
27578
  if (entry.isDirectory()) {
26879
27579
  if (excluded.has(entry.name))
26880
27580
  continue;
26881
- walk(path12.join(dir, entry.name), depth + 1);
27581
+ walk(path13.join(dir, entry.name), depth + 1);
26882
27582
  } else if (entry.isFile() && (entry.name === "README.md" || entry.name.endsWith(".md"))) {
26883
- matches.push("./" + path12.relative(cwd, path12.join(dir, entry.name)).replaceAll("\\", "/"));
27583
+ matches.push("./" + path13.relative(cwd, path13.join(dir, entry.name)).replaceAll("\\", "/"));
26884
27584
  }
26885
27585
  }
26886
27586
  };
@@ -26896,7 +27596,7 @@ var readTrimmed = (file, maxLines) => {
26896
27596
  `);
26897
27597
  };
26898
27598
  function prReadyContext(root, range) {
26899
- const cwd = path12.resolve(repoRoot(root));
27599
+ const cwd = path13.resolve(repoRoot(root));
26900
27600
  if (range !== undefined && !isResolvableContextRange(cwd, range))
26901
27601
  return invalidRange(cwd);
26902
27602
  let stdout = "";
@@ -26954,7 +27654,7 @@ ${status.stderr}` : "");
26954
27654
  stdout += `template_path: ${template}
26955
27655
 
26956
27656
  `;
26957
- stdout += readTrimmed(path12.join(cwd, template), 220);
27657
+ stdout += readTrimmed(path13.join(cwd, template), 220);
26958
27658
  } else {
26959
27659
  stdout += `template_path: none
26960
27660
 
@@ -26964,7 +27664,7 @@ ${status.stderr}` : "");
26964
27664
  stdout += `
26965
27665
  `;
26966
27666
  stdout += printSection("Recent Validation Signals");
26967
- if (existsSync6(path12.join(cwd, "package.json"))) {
27667
+ if (existsSync6(path13.join(cwd, "package.json"))) {
26968
27668
  stdout += `package.json detected. Common scripts:
26969
27669
  `;
26970
27670
  const found = packageScripts(cwd, ["lint", "format:check", "test", "build"]);
@@ -26973,7 +27673,7 @@ ${status.stderr}` : "");
26973
27673
  `) + `
26974
27674
  `;
26975
27675
  }
26976
- if (existsSync6(path12.join(cwd, "Cargo.toml")) || existsSync6(path12.join(cwd, "src-tauri/Cargo.toml"))) {
27676
+ if (existsSync6(path13.join(cwd, "Cargo.toml")) || existsSync6(path13.join(cwd, "src-tauri/Cargo.toml"))) {
26977
27677
  stdout += `Rust project detected.
26978
27678
  `;
26979
27679
  }
@@ -27006,7 +27706,7 @@ var CHANGELOG_RULES = `- Use an [Unreleased] section.
27006
27706
  - Apply with the native workit_changelog_apply tool only (not hand-edits under Unreleased).
27007
27707
  - If Unreleased already has duplicate category headings, normalize_only first.`;
27008
27708
  function changelogContext(root, range) {
27009
- const cwd = path12.resolve(repoRoot(root));
27709
+ const cwd = path13.resolve(repoRoot(root));
27010
27710
  if (range !== undefined && !isResolvableContextRange(cwd, range))
27011
27711
  return invalidRange(cwd);
27012
27712
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -27024,7 +27724,7 @@ function changelogContext(root, range) {
27024
27724
  stdout += CHANGELOG_RULES + `
27025
27725
  `;
27026
27726
  stdout += printSection("Existing CHANGELOG.md");
27027
- const changelogPath = path12.join(cwd, "CHANGELOG.md");
27727
+ const changelogPath = path13.join(cwd, "CHANGELOG.md");
27028
27728
  if (existsSync6(changelogPath)) {
27029
27729
  stdout += readTrimmed(changelogPath, 260) + `
27030
27730
  `;
@@ -27044,7 +27744,7 @@ function changelogContext(root, range) {
27044
27744
  return { stdout, stderr: "", exitCode: 0, cwd };
27045
27745
  }
27046
27746
  function docsRefreshContext(root, range) {
27047
- const cwd = path12.resolve(repoRoot(root));
27747
+ const cwd = path13.resolve(repoRoot(root));
27048
27748
  if (range !== undefined && !isResolvableContextRange(cwd, range))
27049
27749
  return invalidRange(cwd);
27050
27750
  const resolvedRange = rangeArgOrDefault(range, cwd);
@@ -27066,7 +27766,7 @@ function docsRefreshContext(root, range) {
27066
27766
  `) + `
27067
27767
  `;
27068
27768
  stdout += printSection("README Preview");
27069
- const readme = path12.join(cwd, "README.md");
27769
+ const readme = path13.join(cwd, "README.md");
27070
27770
  if (existsSync6(readme)) {
27071
27771
  stdout += readTrimmed(readme, 220) + `
27072
27772
  `;
@@ -27075,7 +27775,7 @@ function docsRefreshContext(root, range) {
27075
27775
  `;
27076
27776
  }
27077
27777
  stdout += printSection("Package Scripts");
27078
- const pkgPath = path12.join(cwd, "package.json");
27778
+ const pkgPath = path13.join(cwd, "package.json");
27079
27779
  if (existsSync6(pkgPath)) {
27080
27780
  try {
27081
27781
  const pkg = JSON.parse(readFileSync7(pkgPath, "utf8"));
@@ -27089,7 +27789,7 @@ function docsRefreshContext(root, range) {
27089
27789
  return { stdout, stderr: "", exitCode: 0, cwd };
27090
27790
  }
27091
27791
  function releaseNotesContext(root, rangeOrTag) {
27092
- const cwd = path12.resolve(repoRoot(root));
27792
+ const cwd = path13.resolve(repoRoot(root));
27093
27793
  if (!rangeOrTag) {
27094
27794
  return { stdout: "", stderr: `ERROR: release tag or range required
27095
27795
  `, exitCode: 1, cwd };
@@ -27127,7 +27827,7 @@ function releaseNotesContext(root, rangeOrTag) {
27127
27827
  `;
27128
27828
  stdout += printSection("Existing Release Files");
27129
27829
  for (const rel of ["CHANGELOG.md", "RELEASE_NOTES.md", ".github/releases.md"]) {
27130
- if (existsSync6(path12.join(cwd, rel)))
27830
+ if (existsSync6(path13.join(cwd, rel)))
27131
27831
  stdout += rel + `
27132
27832
  `;
27133
27833
  }
@@ -27155,19 +27855,7 @@ function releaseNotesContext(root, rangeOrTag) {
27155
27855
  }
27156
27856
 
27157
27857
  // packages/workit-core/src/core/tracker-issues.ts
27158
- import fs9 from "node:fs";
27159
- import { spawnSync as spawnSync5 } from "node:child_process";
27160
- var trackerRequest = async (url, init) => {
27161
- try {
27162
- const res = await fetch(url, { method: init.method, headers: init.headers });
27163
- const text = await res.text();
27164
- if (!res.ok)
27165
- return { status: res.status, stdout: "", stderr: text.slice(0, 200) };
27166
- return { status: 0, stdout: text, stderr: "" };
27167
- } catch (err) {
27168
- return { status: 1, stdout: "", stderr: err instanceof Error ? err.message : "network error" };
27169
- }
27170
- };
27858
+ import { spawnSync as spawnSync6 } from "node:child_process";
27171
27859
  var repoPathFromRemote = (remote) => {
27172
27860
  let rest = (remote || "").trim().replace(/\/+$/, "");
27173
27861
  if (!rest)
@@ -27188,7 +27876,11 @@ var defaultCli = (bin, available) => {
27188
27876
  if (!available)
27189
27877
  return null;
27190
27878
  return (args, root) => {
27191
- const out = spawnSync5(bin, args, { cwd: root, encoding: "utf8" });
27879
+ const out = spawnSync6(bin, args, {
27880
+ cwd: root,
27881
+ encoding: "utf8",
27882
+ env: { ...process.env, PATH: process.env.PATH ?? "" }
27883
+ });
27192
27884
  return Promise.resolve({
27193
27885
  status: out.status ?? 1,
27194
27886
  stdout: out.stdout ?? "",
@@ -27196,49 +27888,22 @@ var defaultCli = (bin, available) => {
27196
27888
  });
27197
27889
  };
27198
27890
  };
27199
- var tryCliTriple = async (cli, args, root, id, idKeys, bodyKeys) => {
27891
+ var tryCliTriple = async (cli, name, args, root, id, idKeys, bodyKeys) => {
27200
27892
  if (!cli)
27201
- return null;
27893
+ return { error: `${name} CLI is unavailable`, kind: "creds" };
27202
27894
  const run = cli(args, root);
27203
27895
  if (!run)
27204
- return null;
27896
+ return { error: `${name} CLI is unavailable`, kind: "creds" };
27205
27897
  const out = await run;
27206
27898
  if (out.status !== 0)
27207
- return null;
27899
+ return { error: `${name} CLI is not authenticated or the issue is unavailable`, kind: "creds" };
27208
27900
  try {
27209
- return parseTriple(JSON.parse(out.stdout), id, idKeys, bodyKeys);
27901
+ const triple = parseTriple(JSON.parse(out.stdout), id, idKeys, bodyKeys);
27902
+ return triple ? { data: triple } : { error: `unexpected ${name} issue response`, kind: "request" };
27210
27903
  } catch {
27211
- return null;
27904
+ return { error: `invalid ${name} issue response`, kind: "request" };
27212
27905
  }
27213
27906
  };
27214
- var originRemote = (root) => {
27215
- const out = spawnSync5("git", ["remote", "get-url", "origin"], { cwd: root, encoding: "utf8" });
27216
- return out.status === 0 ? (out.stdout ?? "").trim() || null : null;
27217
- };
27218
- var trackerCreds = (root, provider) => {
27219
- const cfg = vcsConfig("load", root);
27220
- if (!cfg || cfg.provider !== provider || typeof cfg.tokenPath !== "string" || !cfg.tokenPath)
27221
- return { error: `unconfigured ${provider} provider` };
27222
- let token = "";
27223
- try {
27224
- token = fs9.readFileSync(cfg.tokenPath, "utf8").trim();
27225
- } catch {
27226
- return { error: `missing ${provider} token` };
27227
- }
27228
- if (!token)
27229
- return { error: `empty ${provider} token file` };
27230
- if (provider === "gitlab") {
27231
- const gitlab = cfg.gitlab;
27232
- const apiUrl = String(gitlab?.apiUrl ?? "https://gitlab.com/api/v4").replace(/\/+$/, "");
27233
- return { token, api: apiUrl };
27234
- }
27235
- const github = cfg.github;
27236
- const host = String(github?.host ?? "github.com").toLowerCase();
27237
- return {
27238
- token,
27239
- api: host === "github.com" ? "https://api.github.com" : `https://${host}/api/v3`
27240
- };
27241
- };
27242
27907
  var parseTriple = (parsed, id, idKeys, bodyKeys) => {
27243
27908
  const title = parsed.title;
27244
27909
  if (typeof title !== "string")
@@ -27261,62 +27926,36 @@ var fetchGitHubIssueBody = async (ref, root, deps = {}) => {
27261
27926
  const id = parseGhIssue(ref);
27262
27927
  if (!/^\d+$/.test(id))
27263
27928
  return { error: `invalid GitHub issue ref "${ref}"`, kind: "input" };
27264
- const repo = repoPathFromRemote((deps.remote ?? originRemote)(root) ?? "");
27929
+ const remote = (deps.remote ?? pushRemote)(root);
27930
+ const repo = repoPathFromRemote(remote ?? "");
27265
27931
  if (!repo)
27266
27932
  return { error: "no origin remote to resolve the repository", kind: "input" };
27267
- const viaCli = await tryCliTriple(deps.cli ?? defaultCli("gh", hostingCliAvailable("github")), ["issue", "view", id, "--repo", repo, "--json", "number,title,body,state"], root, id, ["number"], ["body"]);
27268
- if (viaCli)
27269
- return { data: viaCli };
27270
- const credentials = (deps.creds ?? ((r) => trackerCreds(r, "github")))(root);
27271
- if ("error" in credentials)
27272
- return { ...credentials, kind: "creds" };
27273
- const out = await (deps.request ?? trackerRequest)(`${credentials.api}/repos/${repo}/issues/${id}`, {
27274
- method: "GET",
27275
- headers: {
27276
- Authorization: `Bearer ${credentials.token}`,
27277
- Accept: "application/vnd.github+json"
27278
- }
27279
- });
27280
- if (out.status !== 0)
27281
- return { error: out.stderr || "GitHub issue fetch failed", kind: "request" };
27282
- try {
27283
- const triple = parseTriple(JSON.parse(out.stdout), id, ["number"], ["body"]);
27284
- if (!triple)
27285
- return { error: "unexpected GitHub issue shape", kind: "request" };
27286
- return { data: triple };
27287
- } catch {
27288
- return { error: "invalid GitHub issue response", kind: "request" };
27289
- }
27933
+ const cfg = vcsConfig("load", root);
27934
+ const host = (deps.host ?? (cfg.ok && cfg.provider === "github" ? cfg.github?.host : null) ?? "github.com").toLowerCase();
27935
+ if (!remote || !hostingApiHostMatches(remote, host))
27936
+ return { error: "hosting API host does not match the push destination", kind: "input" };
27937
+ const repoArg = host === "github.com" ? repo : `${host}/${repo}`;
27938
+ return tryCliTriple(deps.cli ?? defaultCli("gh", hostingCliAvailable("github")), "gh", ["issue", "view", id, "--repo", repoArg, "--json", "number,title,body,state"], root, id, ["number"], ["body"]);
27290
27939
  };
27291
27940
  var fetchGitLabIssueBody = async (ref, root, deps = {}) => {
27292
27941
  const id = parseGhIssue(ref);
27293
27942
  if (!/^\d+$/.test(id))
27294
27943
  return { error: `invalid GitLab issue ref "${ref}"`, kind: "input" };
27295
- const repo = repoPathFromRemote((deps.remote ?? originRemote)(root) ?? "");
27944
+ const remote = (deps.remote ?? pushRemote)(root);
27945
+ const repo = repoPathFromRemote(remote ?? "");
27296
27946
  if (!repo)
27297
27947
  return { error: "no origin remote to resolve the project", kind: "input" };
27298
- const viaCli = await tryCliTriple(deps.cli ?? defaultCli("glab", hostingCliAvailable("gitlab")), ["issue", "view", id, "-R", repo, "-F", "json"], root, id, ["iid"], ["description"]);
27299
- if (viaCli)
27300
- return { data: viaCli };
27301
- const credentials = (deps.creds ?? ((r) => trackerCreds(r, "gitlab")))(root);
27302
- if ("error" in credentials)
27303
- return { ...credentials, kind: "creds" };
27304
- const out = await (deps.request ?? trackerRequest)(`${credentials.api}/projects/${encodeURIComponent(repo)}/issues/${id}`, { method: "GET", headers: { "PRIVATE-TOKEN": credentials.token } });
27305
- if (out.status !== 0)
27306
- return { error: out.stderr || "GitLab issue fetch failed", kind: "request" };
27307
- try {
27308
- const triple = parseTriple(JSON.parse(out.stdout), id, ["iid"], ["description"]);
27309
- if (!triple)
27310
- return { error: "unexpected GitLab issue shape", kind: "request" };
27311
- return { data: triple };
27312
- } catch {
27313
- return { error: "invalid GitLab issue response", kind: "request" };
27314
- }
27948
+ const cfg = vcsConfig("load", root);
27949
+ const host = (deps.host ?? (cfg.ok && cfg.provider === "gitlab" ? cfg.gitlab?.host : null) ?? "gitlab.com").toLowerCase();
27950
+ if (!remote || !hostingApiHostMatches(remote, host))
27951
+ return { error: "hosting API host does not match the push destination", kind: "input" };
27952
+ const repoArg = host === "gitlab.com" ? repo : `https://${host}/${repo}`;
27953
+ return tryCliTriple(deps.cli ?? defaultCli("glab", hostingCliAvailable("gitlab")), "glab", ["issue", "view", id, "-R", repoArg, "-F", "json"], root, id, ["iid"], ["description"]);
27315
27954
  };
27316
27955
 
27317
27956
  // packages/workit-core/src/core/youtrack.ts
27318
- import fs10 from "node:fs";
27319
- import path13 from "node:path";
27957
+ import fs9 from "node:fs";
27958
+ import path14 from "node:path";
27320
27959
 
27321
27960
  // packages/workit-core/src/core/templates.ts
27322
27961
  var repoRoot2 = assetRoot();
@@ -27328,23 +27967,23 @@ var resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
27328
27967
 
27329
27968
  // packages/workit-core/src/core/youtrack.ts
27330
27969
  var ISSUE_RE = /^[A-Z]+-\d+$/;
27331
- var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE";
27332
- var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path13.join(configDir(), "youtrack.json");
27333
- var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path13.join(resolveConfigDir(), "youtrack.json");
27970
+ var TOKEN_PLACEHOLDER = "YOUR_TOKEN_HERE";
27971
+ var youTrackConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path14.join(configDir(), "youtrack.json");
27972
+ var youTrackReadOnlyConfigPath = () => process.env.WORKFLOW_YOUTRACK_CONFIG ?? path14.join(resolveConfigDir(), "youtrack.json");
27334
27973
  var youTrackTokenModeOk = (p) => {
27335
27974
  if (process.platform === "win32")
27336
27975
  return true;
27337
- const mode = fs10.statSync(p).mode & 511;
27976
+ const mode = fs9.statSync(p).mode & 511;
27338
27977
  return mode === 384;
27339
27978
  };
27340
27979
  function readYouTrackConfig(required) {
27341
27980
  const cfgPath = youTrackConfigPath();
27342
- if (!fs10.existsSync(cfgPath)) {
27981
+ if (!fs9.existsSync(cfgPath)) {
27343
27982
  return required ? { error: "ERROR: missing youtrack.json", path: cfgPath } : { config: {}, path: cfgPath };
27344
27983
  }
27345
27984
  let parsed;
27346
27985
  try {
27347
- parsed = JSON.parse(fs10.readFileSync(cfgPath, "utf8"));
27986
+ parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
27348
27987
  } catch {
27349
27988
  return { error: `${cfgPath} is not valid JSON`, path: cfgPath };
27350
27989
  }
@@ -27355,11 +27994,11 @@ function readYouTrackConfig(required) {
27355
27994
  }
27356
27995
  var readYouTrackContextConfig = () => {
27357
27996
  const cfgPath = youTrackReadOnlyConfigPath();
27358
- if (!fs10.existsSync(cfgPath))
27997
+ if (!fs9.existsSync(cfgPath))
27359
27998
  return { data: {} };
27360
27999
  let parsed;
27361
28000
  try {
27362
- parsed = JSON.parse(fs10.readFileSync(cfgPath, "utf8"));
28001
+ parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
27363
28002
  } catch {
27364
28003
  return { error: "YouTrack configuration is unavailable" };
27365
28004
  }
@@ -27384,12 +28023,12 @@ function youTrackGreeting(configOverride) {
27384
28023
  const cfgPath = configOverride ?? youTrackConfigPath();
27385
28024
  let parsed;
27386
28025
  try {
27387
- parsed = JSON.parse(fs10.readFileSync(cfgPath, "utf8"));
28026
+ parsed = JSON.parse(fs9.readFileSync(cfgPath, "utf8"));
27388
28027
  } catch {
27389
28028
  return {
27390
28029
  stdout: "",
27391
28030
  exitCode: 1,
27392
- stderr: fs10.existsSync(cfgPath) ? `${cfgPath} is not valid JSON` : `missing youtrack.json: ${cfgPath}`
28031
+ stderr: fs9.existsSync(cfgPath) ? `${cfgPath} is not valid JSON` : `missing youtrack.json: ${cfgPath}`
27393
28032
  };
27394
28033
  }
27395
28034
  if (!isConfigObject(parsed)) {
@@ -27435,15 +28074,15 @@ var youTrackToken = () => {
27435
28074
  if ("error" in loaded)
27436
28075
  return loaded;
27437
28076
  const tokenFile = String(loaded.config.tokenFile ?? "");
27438
- const tokenPath = tokenFile ? path13.isAbsolute(tokenFile) ? path13.resolve(tokenFile) : path13.resolve(process.cwd(), tokenFile) : "";
27439
- if (!tokenPath || !fs10.existsSync(tokenPath))
28077
+ const tokenPath = tokenFile ? path14.isAbsolute(tokenFile) ? path14.resolve(tokenFile) : path14.resolve(process.cwd(), tokenFile) : "";
28078
+ if (!tokenPath || !fs9.existsSync(tokenPath))
27440
28079
  return { error: "missing youtrack.token" };
27441
28080
  if (!youTrackTokenModeOk(tokenPath))
27442
28081
  return { error: "youtrack.token mode must be 0600" };
27443
- const token = fs10.readFileSync(tokenPath, "utf8").trim();
28082
+ const token = fs9.readFileSync(tokenPath, "utf8").trim();
27444
28083
  if (!token)
27445
28084
  return { error: "empty token file" };
27446
- if (token === TOKEN_PLACEHOLDER2 || token.startsWith(TOKEN_PLACEHOLDER2)) {
28085
+ if (token === TOKEN_PLACEHOLDER || token.startsWith(TOKEN_PLACEHOLDER)) {
27447
28086
  return {
27448
28087
  error: "token file still has placeholder YOUR_TOKEN_HERE — edit the file locally, then run /wk-status"
27449
28088
  };
@@ -27506,9 +28145,9 @@ function resolveYouTrackFromPaths(spec_path, plan_path, workspace_root) {
27506
28145
  } catch {
27507
28146
  continue;
27508
28147
  }
27509
- if (!fs10.existsSync(full))
28148
+ if (!fs9.existsSync(full))
27510
28149
  continue;
27511
- const text = fs10.readFileSync(full, "utf8");
28150
+ const text = fs9.readFileSync(full, "utf8");
27512
28151
  const m = text.match(/^\*\*YouTrack:\*\*\s*`?([A-Z]+-\d+)`?/m);
27513
28152
  if (m)
27514
28153
  return m[1];
@@ -27742,6 +28381,7 @@ var readExternalContext = async (root, payload) => {
27742
28381
  }
27743
28382
  }
27744
28383
  };
28384
+ var targetWriterActionLease = Symbol("target-writer-action-lease");
27745
28385
 
27746
28386
  // packages/workit-mcp/src/server.ts
27747
28387
  class McpCapabilityUnavailableError extends Error {
@@ -27786,13 +28426,13 @@ var rootVariants = (workspaceRoot) => {
27786
28426
  if (!workspaceRoot)
27787
28427
  return [];
27788
28428
  const roots = new Set;
27789
- for (const candidate of [workspaceRoot, path14.resolve(workspaceRoot)]) {
27790
- if (path14.isAbsolute(candidate) && candidate !== path14.parse(candidate).root)
28429
+ for (const candidate of [workspaceRoot, path15.resolve(workspaceRoot)]) {
28430
+ if (path15.isAbsolute(candidate) && candidate !== path15.parse(candidate).root)
27791
28431
  roots.add(candidate);
27792
28432
  }
27793
28433
  try {
27794
- const canonical = realpathSync7(workspaceRoot);
27795
- if (canonical !== path14.parse(canonical).root)
28434
+ const canonical = realpathSync8(workspaceRoot);
28435
+ if (canonical !== path15.parse(canonical).root)
27796
28436
  roots.add(canonical);
27797
28437
  } catch {}
27798
28438
  return [...roots].sort((left, right) => right.length - left.length);
@@ -27893,11 +28533,11 @@ var thrownResult = (tool, error, workspaceRoot) => {
27893
28533
  };
27894
28534
  function createMcpServer(host, contextProvider) {
27895
28535
  assertMcpHost(host);
27896
- const here = path14.dirname(fileURLToPath2(import.meta.url));
28536
+ const here = path15.dirname(fileURLToPath2(import.meta.url));
27897
28537
  const sourceMarker = markSourcesLoaded([
27898
- path14.join(here, "server.ts"),
27899
- path14.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
27900
- path14.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
28538
+ path15.join(here, "server.ts"),
28539
+ path15.join(here, "..", "..", "workit-core", "src", "core", "task-contract.ts"),
28540
+ path15.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
27901
28541
  ].filter((file) => existsSync7(file)));
27902
28542
  let staleWarned = false;
27903
28543
  const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
@@ -28178,6 +28818,7 @@ var allowedKeys = {
28178
28818
  "stop_hook_active"
28179
28819
  ])
28180
28820
  };
28821
+ var historyOfferSessions = new Set;
28181
28822
  if (false)
28182
28823
  ;
28183
28824
 
@@ -28190,14 +28831,14 @@ var codexQualification = (host) => host === "codex_cli" ? { surface: host, cli:
28190
28831
  var resolveCodexWorkspaceRoot = (pluginRoot, env) => {
28191
28832
  const explicit = env.WORKFLOW_WORKSPACE_ROOT;
28192
28833
  const inherited = env.PWD;
28193
- const candidate = explicit ?? (inherited && path15.resolve(inherited) !== path15.resolve(pluginRoot) ? inherited : undefined);
28194
- if (!candidate || !path15.isAbsolute(candidate) || !existsSync8(candidate))
28834
+ const candidate = explicit ?? (inherited && path16.resolve(inherited) !== path16.resolve(pluginRoot) ? inherited : undefined);
28835
+ if (!candidate || !path16.isAbsolute(candidate) || !existsSync8(candidate))
28195
28836
  return null;
28196
28837
  try {
28197
- if (!statSync2(candidate).isDirectory())
28838
+ if (!statSync3(candidate).isDirectory())
28198
28839
  return null;
28199
- const canonical = realpathSync8(candidate);
28200
- if (canonical === realpathSync8(pluginRoot)) {
28840
+ const canonical = realpathSync9(candidate);
28841
+ if (canonical === realpathSync9(pluginRoot)) {
28201
28842
  if (!explicit)
28202
28843
  return null;
28203
28844
  const workspace = new TaskStore(canonical).readWorkspace();