@brainervirus/workit-mcp 8.9.0 → 8.9.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +155 -28
  2. package/package.json +2 -2
package/dist/index.js CHANGED
@@ -19403,6 +19403,38 @@ var isNewerVersion = (candidate, current) => {
19403
19403
  return false;
19404
19404
  };
19405
19405
  var validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value);
19406
+ var readText = (file) => {
19407
+ try {
19408
+ return fs7.readFileSync(file, "utf8");
19409
+ } catch {
19410
+ return "";
19411
+ }
19412
+ };
19413
+ var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
19414
+ var branchStart = (location, branch, aliases) => {
19415
+ const first = readText(path6.join(path6.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
19416
+ `, 1)[0];
19417
+ const created = REFLOG_LINE.exec(first);
19418
+ const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
19419
+ if (!created || !start)
19420
+ return null;
19421
+ if (start !== "HEAD")
19422
+ return start;
19423
+ if (!location.git)
19424
+ return null;
19425
+ for (const line of readText(path6.join(location.git.gitDir, "logs", "HEAD")).split(`
19426
+ `)) {
19427
+ const entry = REFLOG_LINE.exec(line);
19428
+ const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
19429
+ const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
19430
+ if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
19431
+ return moved[1];
19432
+ }
19433
+ return null;
19434
+ };
19435
+ var refExists = (common, ref) => fs7.existsSync(path6.join(common, ref)) || readText(path6.join(common, "packed-refs")).includes(` ${ref}
19436
+ `);
19437
+ var origins = new Map;
19406
19438
  var sameDirectoryIdentity = (left, right) => {
19407
19439
  if (!path6.isAbsolute(left) || !path6.isAbsolute(right))
19408
19440
  return false;
@@ -19655,6 +19687,66 @@ class TaskStore {
19655
19687
  return bound;
19656
19688
  return success(null, null, { key: key.data, bound: bound.data });
19657
19689
  }
19690
+ taskForWorker(workerId) {
19691
+ if (typeof workerId !== "string" || !validId(workerId))
19692
+ return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
19693
+ const listed = this.listTaskIndex();
19694
+ if (!listed.ok)
19695
+ return listed;
19696
+ for (const entry of listed.data) {
19697
+ if (entry.status === "closed")
19698
+ continue;
19699
+ const loaded = this.load(entry.id);
19700
+ if (!loaded.ok)
19701
+ return loaded;
19702
+ if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
19703
+ return success(null, null, entry.id);
19704
+ }
19705
+ return success(null, null, null);
19706
+ }
19707
+ originTaskEntry() {
19708
+ const key = this.currentKey();
19709
+ const location = this.location();
19710
+ const branch = key.ok ? key.data.branch : null;
19711
+ if (!branch || !location.ok)
19712
+ return success(null, null, null);
19713
+ const common = path6.dirname(location.data.dir);
19714
+ const heads = path6.join(common, "logs", "refs", "heads");
19715
+ const headLog = location.data.git ? path6.join(location.data.git.gitDir, "logs", "HEAD") : "";
19716
+ const signatures = [
19717
+ path6.join(heads, branch),
19718
+ headLog,
19719
+ heads,
19720
+ path6.join(common, "packed-refs")
19721
+ ].map((file) => fileSignature(file) ?? "-");
19722
+ const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
19723
+ let resolved = origins.get(slot);
19724
+ if (resolved?.signature !== signatures.join("|")) {
19725
+ const renames = branchRenames(location.data);
19726
+ const aliases = [branch];
19727
+ for (const rename of renames.toReversed())
19728
+ if (rename.to === aliases.at(-1))
19729
+ aliases.push(rename.from);
19730
+ const start = branchStart(location.data, branch, aliases);
19731
+ const slash = start?.indexOf("/") ?? -1;
19732
+ const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path6.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
19733
+ const name = start && (remote ? start.slice(slash + 1) : start);
19734
+ resolved = {
19735
+ signature: signatures.join("|"),
19736
+ name: name && !aliases.includes(name) ? name : null,
19737
+ renames
19738
+ };
19739
+ origins.set(slot, resolved);
19740
+ }
19741
+ const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
19742
+ if (entry && !entry.ok)
19743
+ return entry;
19744
+ return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
19745
+ }
19746
+ readStoreTask(taskId) {
19747
+ const loaded = this.load(taskId);
19748
+ return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
19749
+ }
19658
19750
  create(input, provenance, expectedWorkspaceRevision) {
19659
19751
  const value = "intent" in input ? input : {
19660
19752
  intent: input,
@@ -20324,12 +20416,12 @@ class TaskStore {
20324
20416
  return 0;
20325
20417
  }
20326
20418
  }
20327
- openTasksForKey(key) {
20419
+ openTasksForKey(key, known) {
20328
20420
  const all = this.listStoreIndex();
20329
20421
  if (!all.ok)
20330
20422
  return all;
20331
- const location = this.location();
20332
- const renames = location.ok ? branchRenames(location.data) : [];
20423
+ const location = known ? null : this.location();
20424
+ const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
20333
20425
  return success(null, null, all.data.filter((entry) => entry.key !== null && entry.status !== "closed").filter((entry) => (renames.length ? followRenames(entry.key, entry.boundAt ?? entry.createdAt, renames) : entry.key) === key).toSorted(oldestFirst));
20334
20426
  }
20335
20427
  openTaskForKey(key) {
@@ -24415,38 +24507,45 @@ var compactReference = (ref) => {
24415
24507
  };
24416
24508
  return { kind: ref.kind };
24417
24509
  };
24418
- function compactTaskContext(view) {
24419
- const decisions = view.task.decisions.slice().toSorted((left, right) => {
24420
- const leftAt = compactTimestamp(left.recordedAt);
24421
- const rightAt = compactTimestamp(right.recordedAt);
24422
- if (leftAt !== rightAt)
24423
- return leftAt > rightAt ? -1 : 1;
24424
- return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
24425
- }).slice(0, COMPACT_MAX_ITEMS).map((entry) => ({
24426
- id: entry.id,
24427
- purpose: entry.data.purpose,
24428
- status: entry.data.response,
24429
- digest: entry.data.digest,
24430
- choice: compactDecisionChoice(entry) || `task.inspect taskId=${view.task.id} view=full; decision=${entry.id}`,
24431
- references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
24432
- }));
24433
- const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
24510
+ var newestDecisionFirst = (left, right) => {
24511
+ const leftAt = compactTimestamp(left.recordedAt);
24512
+ const rightAt = compactTimestamp(right.recordedAt);
24513
+ if (leftAt !== rightAt)
24514
+ return leftAt > rightAt ? -1 : 1;
24515
+ return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
24516
+ };
24517
+ var compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
24518
+ id: entry.id,
24519
+ purpose: entry.data.purpose,
24520
+ status: entry.data.response,
24521
+ digest: entry.data.digest,
24522
+ choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
24523
+ references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
24524
+ origin: source.origin ?? undefined
24525
+ })) : []).slice(0, COMPACT_MAX_ITEMS);
24526
+ function compactTaskContext(view, inherited = null) {
24527
+ const decisions = compactDecisions([
24528
+ view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
24529
+ inherited
24530
+ ]);
24531
+ const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
24434
24532
  id: method.id,
24435
24533
  assurance: method.assurance,
24436
24534
  reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
24437
24535
  })) : [];
24536
+ const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
24438
24537
  const gaps = Array.from(new Set([
24439
- ...view.evidence.filter((entry) => entry.status === "stale").map((entry) => `stale evidence: ${entry.evidenceId}`),
24440
- ...view.requirements.filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
24441
- ...view.task.progress.blockers.map((entry) => entry.reason)
24538
+ ...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
24539
+ ...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
24540
+ ...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
24442
24541
  ])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
24443
24542
  const context = {
24444
- objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
24445
- status: view.task.status,
24543
+ objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
24544
+ status: view?.task.status ?? null,
24446
24545
  decisions,
24447
24546
  methods,
24448
24547
  gaps,
24449
- nextAction: compactText(view.task.progress.nextAction, COMPACT_TEXT_BYTES)
24548
+ nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
24450
24549
  };
24451
24550
  let encoded = JSON.stringify(context);
24452
24551
  while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
@@ -25051,6 +25150,13 @@ var TASK_SCOPED_ACTIONS = new Set([
25051
25150
  "worker.cancel",
25052
25151
  "state.export"
25053
25152
  ]);
25153
+ var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
25154
+ var workerCommand = (taskId, request) => {
25155
+ const { action, workerId, reason, report } = request;
25156
+ const rest = JSON.stringify({ reason, report });
25157
+ const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
25158
+ return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
25159
+ };
25054
25160
  var CREATING_ACTIONS = new Set([
25055
25161
  "task.revise",
25056
25162
  "task.progress",
@@ -25301,6 +25407,17 @@ class WorkitCore {
25301
25407
  const root = this.contextRootError();
25302
25408
  if (!root.ok)
25303
25409
  return root;
25410
+ const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
25411
+ const workerId = addressed ? value.workerId : this.context.workerId ?? null;
25412
+ if (workerId !== null) {
25413
+ const owner = this.store.taskForWorker(workerId);
25414
+ if (!owner.ok)
25415
+ return owner;
25416
+ if (owner.data)
25417
+ return success(null, null, { ...request, taskId: owner.data });
25418
+ if (addressed)
25419
+ return failure2("not_found", `worker ${workerId} is on no open task; find its task with \`workit task status --all\`, then run \`${workerCommand("<task-id>", request)}\``);
25420
+ }
25304
25421
  const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
25305
25422
  const found = this.store.implicitTask({
25306
25423
  provenance: provenance(this.context),
@@ -26007,7 +26124,9 @@ class WorkitCore {
26007
26124
  if (!resolved.ok)
26008
26125
  return resolved;
26009
26126
  const value = resolved.data;
26010
- return this.retryOmittedRevisions(value, () => this.workerOnce(value));
26127
+ const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
26128
+ const { action, taskId } = value ?? {};
26129
+ return result.ok || request?.taskId !== undefined || typeof taskId !== "string" || !WORKER_ADDRESSED_ACTIONS.has(`worker.${String(action)}`) ? result : { ...result, error: `${result.error}; run \`${workerCommand(taskId, value)}\`` };
26011
26130
  }
26012
26131
  workerOnce(request) {
26013
26132
  const root = this.contextRootError();
@@ -26212,10 +26331,18 @@ class WorkitCore {
26212
26331
  requirements
26213
26332
  });
26214
26333
  }
26215
- compactContext(taskId, freshness) {
26334
+ compactContext(taskId, freshness, origin = null) {
26216
26335
  const root = this.contextRootError();
26217
26336
  if (!root.ok)
26218
26337
  return root;
26338
+ const base = origin && this.store.readStoreTask(origin.taskId);
26339
+ const inherited = base?.ok && base.data ? {
26340
+ origin: origin.origin,
26341
+ taskId: base.data.id,
26342
+ decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
26343
+ } : null;
26344
+ if (taskId === null)
26345
+ return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
26219
26346
  const task = this.store.readTask(taskId);
26220
26347
  if (!task.ok)
26221
26348
  return task;
@@ -26225,7 +26352,7 @@ class WorkitCore {
26225
26352
  const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
26226
26353
  if (!view.ok)
26227
26354
  return view;
26228
- return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
26355
+ return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
26229
26356
  }
26230
26357
  view(task, capture = true, freshness = treeFreshness(this.store.root)) {
26231
26358
  const workspace = this.store.readWorkspace();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-mcp",
3
- "version": "8.9.0",
3
+ "version": "8.9.1",
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.9.0",
45
+ "@brainervirus/workit-core": "^8.9.1",
46
46
  "@modelcontextprotocol/sdk": "1.32.0",
47
47
  "zod": "4.6.5"
48
48
  },