@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.
- package/dist/index.js +108 -23
- 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(
|
|
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:
|
|
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:
|
|
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:
|
|
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" || !
|
|
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
|
-
|
|
19866
|
-
|
|
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({
|
|
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([
|
|
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
|
|
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" && !
|
|
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 =
|
|
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",
|
|
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",
|
|
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.
|
|
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.
|
|
45
|
+
"@brainervirus/workit-core": "^8.15.0",
|
|
46
46
|
"@modelcontextprotocol/sdk": "1.32.0",
|
|
47
47
|
"zod": "4.6.5"
|
|
48
48
|
},
|