@brainervirus/workit-mcp 8.14.0 → 8.15.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.
Files changed (2) hide show
  1. package/dist/index.js +108 -23
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -16611,6 +16611,8 @@ var text = string2().check((ctx) => {
16611
16611
  ctx.issues.push({ code: "custom", input: ctx.value, message: "invalid Unicode", path: [] });
16612
16612
  });
16613
16613
  var id = text.regex(uuidPattern, "expected lowercase UUID");
16614
+ var idRefPattern = /^[0-9a-f][0-9a-f-]{7,35}$/;
16615
+ var idRef = text.regex(idRefPattern, "expected a UUID or an id prefix of 8+ characters");
16614
16616
  var digest = text.regex(digestPattern, "expected lowercase SHA-256 digest");
16615
16617
  var revision = text.regex(uuidPattern, "expected UUID revision");
16616
16618
  var validUtc = (value) => {
@@ -16988,7 +16990,7 @@ var workerReportSchema = object2({
16988
16990
  outcome: _enum(["completed", "failed", "cancelled"]),
16989
16991
  summary: text,
16990
16992
  evidenceIds: array(id),
16991
- findingIds: array(id)
16993
+ findingIds: array(idRef)
16992
16994
  }).strict();
16993
16995
  var workerSchema = object2({
16994
16996
  assignment: assignmentSchema,
@@ -17234,7 +17236,7 @@ var findingOperations = {
17234
17236
  action: literal("resolve"),
17235
17237
  ...taskId,
17236
17238
  expectedRevision: revision.optional(),
17237
- findingId: id,
17239
+ findingId: idRef,
17238
17240
  disposition: _enum(["open", "fixed", "dismissed", "deferred"]),
17239
17241
  reason: text,
17240
17242
  evidenceIds: array(id),
@@ -17275,7 +17277,7 @@ var workerOperations = {
17275
17277
  ...taskId,
17276
17278
  expectedRevision: revision.optional(),
17277
17279
  expectedWorkspaceRevision: revision.optional(),
17278
- workerId: id,
17280
+ workerId: idRef,
17279
17281
  report: workerReportSchema
17280
17282
  }),
17281
17283
  cancel: operation({
@@ -17283,7 +17285,7 @@ var workerOperations = {
17283
17285
  ...taskId,
17284
17286
  ...revisions,
17285
17287
  expectedWorkspaceRevision: revision.optional(),
17286
- workerId: id,
17288
+ workerId: idRef,
17287
17289
  reason: text
17288
17290
  })
17289
17291
  };
@@ -17333,6 +17335,16 @@ var success = (revisionValue, workspaceRevisionValue, data) => ({
17333
17335
  data
17334
17336
  });
17335
17337
  var failure2 = (code, error, details = {}) => ({ ok: false, schemaVersion: 1, code, error, details });
17338
+ function resolveIdRef(kind, ref, ids) {
17339
+ if (ids.includes(ref))
17340
+ return success(null, null, ref);
17341
+ const matches = ids.filter((candidate) => candidate.startsWith(ref));
17342
+ if (matches.length === 1)
17343
+ return success(null, null, matches[0]);
17344
+ if (matches.length === 0)
17345
+ return failure2("not_found", `${kind} not found`);
17346
+ return failure2("invalid_input", `${kind} prefix ${ref} is ambiguous; candidates: ${matches.join(", ")}`);
17347
+ }
17336
17348
  function parseOperation(family, input) {
17337
17349
  if (typeof input === "object" && input !== null && "schemaVersion" in input && input.schemaVersion !== SCHEMA_VERSION)
17338
17350
  return failure2("unsupported_version", "unsupported schema version", { operation: family });
@@ -19851,20 +19863,26 @@ class TaskStore {
19851
19863
  return success(null, null, { key: key.data, bound: bound.data });
19852
19864
  }
19853
19865
  taskForWorker(workerId) {
19854
- if (typeof workerId !== "string" || !validId(workerId))
19866
+ if (typeof workerId !== "string" || !idRefPattern.test(workerId))
19855
19867
  return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
19856
19868
  const listed = this.listTaskIndex();
19857
19869
  if (!listed.ok)
19858
19870
  return listed;
19871
+ const owners = new Map;
19859
19872
  for (const entry of listed.data) {
19860
19873
  if (entry.status === "closed")
19861
19874
  continue;
19862
19875
  const loaded = this.load(entry.id);
19863
19876
  if (!loaded.ok)
19864
19877
  return loaded;
19865
- if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
19866
- return success(null, null, entry.id);
19878
+ for (const worker of loaded.data?.record.workers ?? [])
19879
+ owners.set(worker.id, entry.id);
19867
19880
  }
19881
+ const resolved = resolveIdRef("worker", workerId, [...owners.keys()]);
19882
+ if (resolved.ok)
19883
+ return success(null, null, owners.get(resolved.data) ?? null);
19884
+ if (resolved.code !== "not_found")
19885
+ return resolved;
19868
19886
  return success(null, null, null);
19869
19887
  }
19870
19888
  originTaskEntry() {
@@ -21984,20 +22002,19 @@ var workspaceConfigSchema = object2({
21984
22002
  union([boolean2(), literal("verified")]).optional()
21985
22003
  ]))).passthrough().optional(),
21986
22004
  youtrack: object2({ baseUrl: nonBlank.optional(), link_issues: boolean2().optional() }).passthrough().optional(),
21987
- issues: object2({ provider: literal("github").optional(), link_on_pr: boolean2().optional() }).passthrough().optional(),
22005
+ issues: object2({
22006
+ provider: _enum(["github", "gitlab", "youtrack"]).optional(),
22007
+ link: boolean2().optional(),
22008
+ relation: unknown().optional(),
22009
+ backlink: boolean2().optional(),
22010
+ link_on_pr: boolean2().optional()
22011
+ }).passthrough().optional(),
21988
22012
  branchPolicy: workspaceBranchPolicySchema.optional(),
21989
22013
  commitPolicy: workspaceCommitPolicySchema.optional(),
21990
22014
  defaultProfile: nonBlank.optional(),
21991
22015
  profiles: record(nonBlank, workspaceProfileSchema).optional(),
21992
22016
  releaseTracks: record(nonBlank, releaseTrackSchema).optional()
21993
22017
  }).passthrough().superRefine((workspace, context) => {
21994
- if (workspace.issues && workspace.vcs?.provider !== "github") {
21995
- context.addIssue({
21996
- code: "custom",
21997
- path: ["issues"],
21998
- message: `GitHub issue linking requires the github provider, got ${workspace.vcs?.provider ?? "unset"}`
21999
- });
22000
- }
22001
22018
  if (workspace.defaultProfile !== undefined && !Object.hasOwn(workspace.profiles ?? {}, workspace.defaultProfile)) {
22002
22019
  context.addIssue({
22003
22020
  code: "custom",
@@ -22837,6 +22854,44 @@ var readVcsConfig = () => {
22837
22854
  }
22838
22855
  return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
22839
22856
  };
22857
+ var TRACKERS = new Set(["github", "gitlab", "youtrack"]);
22858
+ var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
22859
+ var flag = (value) => typeof value === "boolean" ? value : null;
22860
+ var text2 = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
22861
+ function issueTrackerOf(issues, youtrack) {
22862
+ const iss = asRecord(issues);
22863
+ const yt = asRecord(youtrack);
22864
+ const warnings = [];
22865
+ const named = typeof iss.provider === "string" && TRACKERS.has(iss.provider.toLowerCase()) ? iss.provider.toLowerCase() : null;
22866
+ if (iss.relation !== undefined && iss.relation !== "closes" && iss.relation !== "refs")
22867
+ warnings.push(`issues.relation ${JSON.stringify(iss.relation)} is not closes or refs; read as closes`);
22868
+ for (const key of ["repo", "host"])
22869
+ if (iss[key] !== undefined && text2(iss[key]) === null)
22870
+ warnings.push(`issues.${key} is not a non-empty string; ignored`);
22871
+ const common = {
22872
+ relation: iss.relation === "refs" ? "refs" : "closes",
22873
+ backlink: iss.backlink === true,
22874
+ baseUrl: text2(yt.baseUrl),
22875
+ repo: text2(iss.repo)?.replace(/^\/+|\/+$/gu, "") ?? null,
22876
+ host: text2(iss.host)?.toLowerCase() ?? null,
22877
+ warnings
22878
+ };
22879
+ const legacyGithub = named === "github" && flag(iss.link) === null;
22880
+ if (flag(yt.link_issues) === true && (named === null || legacyGithub))
22881
+ return { provider: "youtrack", link: true, ...common, source: "youtrack.link_issues" };
22882
+ if (named) {
22883
+ const link = flag(iss.link) ?? flag(iss.link_on_pr) ?? (named === "youtrack" ? flag(yt.link_issues) : null) ?? false;
22884
+ return {
22885
+ provider: named,
22886
+ link,
22887
+ ...common,
22888
+ source: flag(iss.link) === null && flag(iss.link_on_pr) !== null ? "issues.link_on_pr" : "issues"
22889
+ };
22890
+ }
22891
+ if (common.baseUrl)
22892
+ return { provider: "youtrack", link: false, ...common, source: "youtrack.baseUrl" };
22893
+ return null;
22894
+ }
22840
22895
  var trackSummary = (resolution) => resolution.status === "resolved" ? {
22841
22896
  name: resolution.track?.name ?? null,
22842
22897
  source: resolution.source,
@@ -22857,6 +22912,7 @@ function vcsConfig(mode, cwd, options = {}) {
22857
22912
  const rawProvider = wsVcs.provider ?? (determinateRoot ? remoteProvider(root) : null) ?? cfg.provider ?? null;
22858
22913
  const provider = rawProvider === null || rawProvider === undefined ? null : String(rawProvider).toLowerCase() || null;
22859
22914
  const workspaceDefault = workspaceDefaultTarget(root, cfg.defaultTargetBranch);
22915
+ const issueTracker = issueTrackerOf(wsIssues, wsYt);
22860
22916
  const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
22861
22917
  const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
22862
22918
  let issuesProvider = null;
@@ -22889,7 +22945,8 @@ function vcsConfig(mode, cwd, options = {}) {
22889
22945
  link_issues: linkIssues,
22890
22946
  youtrack_base_url: youtrackBaseUrl,
22891
22947
  issues_provider: issuesProvider,
22892
- link_on_pr: linkOnPr
22948
+ link_on_pr: linkOnPr,
22949
+ issue_tracker: issueTracker
22893
22950
  };
22894
22951
  }
22895
22952
  if (cfgStatus === "malformed") {
@@ -22915,7 +22972,8 @@ function vcsConfig(mode, cwd, options = {}) {
22915
22972
  link_issues: linkIssues,
22916
22973
  youtrack_base_url: youtrackBaseUrl,
22917
22974
  issues_provider: issuesProvider,
22918
- link_on_pr: linkOnPr
22975
+ link_on_pr: linkOnPr,
22976
+ issue_tracker: issueTracker
22919
22977
  };
22920
22978
  if (provider === "gitlab") {
22921
22979
  out.gitlab = {
@@ -23019,7 +23077,12 @@ var MAX_LINE_BYTES = 4096;
23019
23077
  var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
23020
23078
  var FAILING = new Set(["failed", "blocked"]);
23021
23079
  var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
23022
- var PR_ROW_TYPES = new Set(["pr.created", "pr.status", "pr.merged"]);
23080
+ var PR_ROW_TYPES = new Set([
23081
+ "pr.created",
23082
+ "pr.status",
23083
+ "pr.merged",
23084
+ "pr.linked"
23085
+ ]);
23023
23086
  var err = (code, error, unblock) => ({
23024
23087
  ok: false,
23025
23088
  code,
@@ -25156,7 +25219,7 @@ var RISK = {
25156
25219
  };
25157
25220
  var YES = new Set(["true", "yes", "y", "1", "on"]);
25158
25221
  var NO = new Set(["false", "no", "n", "0", "off", "none"]);
25159
- var flag = (value, unknownAs) => {
25222
+ var flag2 = (value, unknownAs) => {
25160
25223
  if (typeof value === "boolean")
25161
25224
  return { ok: true, value };
25162
25225
  if (typeof value === "number" && (value === 0 || value === 1))
@@ -25222,12 +25285,12 @@ function normalizeJudgment(raw, previous) {
25222
25285
  const items = Array.isArray(value) ? value : [value];
25223
25286
  judgment.refs.push(...items.filter((item) => item !== null && item !== "").map(refOf));
25224
25287
  } else {
25225
- if (name === "needsPlan" && typeof value === "string" && !flag(value, false).ok && value.trim()) {
25288
+ if (name === "needsPlan" && typeof value === "string" && !flag2(value, false).ok && value.trim()) {
25226
25289
  judgment.needsPlan = true;
25227
25290
  judgment.refs.push(refOf(value.trim()));
25228
25291
  continue;
25229
25292
  }
25230
- const parsed = flag(value, name === "behaviorChange");
25293
+ const parsed = flag2(value, name === "behaviorChange");
25231
25294
  if (parsed.ok)
25232
25295
  judgment[name] = parsed.value;
25233
25296
  else
@@ -25582,6 +25645,8 @@ var TASK_SCOPED_ACTIONS = new Set([
25582
25645
  "worker.cancel",
25583
25646
  "state.export"
25584
25647
  ]);
25648
+ var ONLY_OWN_REPORT = "workers can submit only their own report";
25649
+ var NO_HELPER_CONTROL = "helpers cannot cancel or control workers";
25585
25650
  var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
25586
25651
  var workerCommand = (taskId, request) => {
25587
25652
  const { action, workerId, reason, report } = request;
@@ -26479,6 +26544,10 @@ class WorkitCore {
26479
26544
  }
26480
26545
  if (helper?.ok)
26481
26546
  return failure2("permission_denied", "helpers cannot resolve findings");
26547
+ const findingRef = resolveIdRef("finding", input.findingId, task.data.findings.map((entry) => entry.id));
26548
+ if (!findingRef.ok)
26549
+ return findingRef;
26550
+ input.findingId = findingRef.data;
26482
26551
  const finding = task.data.findings.find((entry) => entry.id === input.findingId);
26483
26552
  if (!finding)
26484
26553
  return failure2("not_found", "finding not found");
@@ -26630,15 +26699,31 @@ class WorkitCore {
26630
26699
  return changed;
26631
26700
  return success(changed.data.task.revision, changed.data.workspace.revision, changed.data.task.workers.at(-1));
26632
26701
  }
26702
+ const workerRef = resolveIdRef("worker", input.workerId, helperId === null ? task.data.workers.map((candidate) => candidate.id) : [helperId]);
26703
+ if (!workerRef.ok)
26704
+ return helperId === null ? workerRef : failure2("permission_denied", input.action === "report" ? ONLY_OWN_REPORT : NO_HELPER_CONTROL);
26705
+ input.workerId = workerRef.data;
26633
26706
  const entry = task.data.workers.find((candidate) => candidate.id === input.workerId);
26634
26707
  if (!entry)
26635
26708
  return failure2("not_found", "worker not found");
26636
26709
  if (input.action === "report") {
26637
26710
  if (helperId === null || helperId !== input.workerId)
26638
- return failure2("permission_denied", "workers can submit only their own report");
26711
+ return failure2("permission_denied", ONLY_OWN_REPORT);
26639
26712
  const helper = this.helperEntry(task.data, true, true);
26640
26713
  if (!helper.ok)
26641
26714
  return helper;
26715
+ const ownFindings = task.data.findings.filter((candidate) => candidate.provenance.workerId === helperId).map((candidate) => candidate.id);
26716
+ const reported = [];
26717
+ for (const findingRef of input.report.findingIds) {
26718
+ const found = resolveIdRef("finding", findingRef, ownFindings);
26719
+ if (found.ok)
26720
+ reported.push(found.data);
26721
+ else if (found.code === "not_found")
26722
+ reported.push(findingRef);
26723
+ else
26724
+ return found;
26725
+ }
26726
+ input.report.findingIds = reported;
26642
26727
  const evidence = task.data.evidence.filter((candidate) => input.report.evidenceIds.includes(candidate.id));
26643
26728
  const findings = task.data.findings.filter((candidate) => input.report.findingIds.includes(candidate.id));
26644
26729
  if (evidence.length !== input.report.evidenceIds.length || findings.length !== input.report.findingIds.length)
@@ -26666,7 +26751,7 @@ class WorkitCore {
26666
26751
  return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during report");
26667
26752
  }
26668
26753
  if (helperId !== null)
26669
- return failure2("permission_denied", "helpers cannot cancel or control workers");
26754
+ return failure2("permission_denied", NO_HELPER_CONTROL);
26670
26755
  if (task.data.status === "closed")
26671
26756
  return failure2("invalid_transition", "closed task cannot cancel workers");
26672
26757
  if (entry.data.state === "stopped") {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-mcp",
3
- "version": "8.14.0",
3
+ "version": "8.15.0",
4
4
  "private": false,
5
5
  "description": "Workit shared MCP transport for the eight task and policy operation families",
6
6
  "keywords": [
@@ -42,7 +42,7 @@
42
42
  "build": "bun scripts/build.ts"
43
43
  },
44
44
  "dependencies": {
45
- "@brainervirus/workit-core": "^8.14.0",
45
+ "@brainervirus/workit-core": "^8.15.0",
46
46
  "@modelcontextprotocol/sdk": "1.32.0",
47
47
  "zod": "4.6.5"
48
48
  },