@brainervirus/workit-opencode 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/assets/skills/workit-ship/SKILL.md +2 -2
- package/dist/plugin.js +108 -23
- package/package.json +2 -2
|
@@ -33,8 +33,8 @@ that repeats because the base keeps moving, or after 3 failed fix attempts on
|
|
|
33
33
|
the same check.
|
|
34
34
|
|
|
35
35
|
1. **Open.** `workit git push`, then `workit pr create --title "<title>"
|
|
36
|
-
--body-file <f|->` (
|
|
37
|
-
`workit pr edit`). A stack: `workit stack plan <bottom> ... <top>`, one
|
|
36
|
+
--body-file <f|->` plus `--issue <#n|ID>` per issue (`--refs`: no close; idempotent;
|
|
37
|
+
body: `references/pr-body.md`; edits: `workit pr edit`). A stack: `workit stack plan <bottom> ... <top>`, one
|
|
38
38
|
`workit pr create --base <parent> --fill` per branch, then
|
|
39
39
|
`workit stack sync`. Finish the whole stack before babysitting any PR.
|
|
40
40
|
2. **Read state** with `workit pr status --json`. `REVIEW` alone is a pending
|
package/dist/plugin.js
CHANGED
|
@@ -7772,6 +7772,8 @@ var text = string2().check((ctx) => {
|
|
|
7772
7772
|
ctx.issues.push({ code: "custom", input: ctx.value, message: "invalid Unicode", path: [] });
|
|
7773
7773
|
});
|
|
7774
7774
|
var id = text.regex(uuidPattern, "expected lowercase UUID");
|
|
7775
|
+
var idRefPattern = /^[0-9a-f][0-9a-f-]{7,35}$/;
|
|
7776
|
+
var idRef = text.regex(idRefPattern, "expected a UUID or an id prefix of 8+ characters");
|
|
7775
7777
|
var digest = text.regex(digestPattern, "expected lowercase SHA-256 digest");
|
|
7776
7778
|
var revision = text.regex(uuidPattern, "expected UUID revision");
|
|
7777
7779
|
var validUtc = (value) => {
|
|
@@ -8149,7 +8151,7 @@ var workerReportSchema = object({
|
|
|
8149
8151
|
outcome: _enum(["completed", "failed", "cancelled"]),
|
|
8150
8152
|
summary: text,
|
|
8151
8153
|
evidenceIds: array(id),
|
|
8152
|
-
findingIds: array(
|
|
8154
|
+
findingIds: array(idRef)
|
|
8153
8155
|
}).strict();
|
|
8154
8156
|
var workerSchema = object({
|
|
8155
8157
|
assignment: assignmentSchema,
|
|
@@ -8395,7 +8397,7 @@ var findingOperations = {
|
|
|
8395
8397
|
action: literal("resolve"),
|
|
8396
8398
|
...taskId,
|
|
8397
8399
|
expectedRevision: revision.optional(),
|
|
8398
|
-
findingId:
|
|
8400
|
+
findingId: idRef,
|
|
8399
8401
|
disposition: _enum(["open", "fixed", "dismissed", "deferred"]),
|
|
8400
8402
|
reason: text,
|
|
8401
8403
|
evidenceIds: array(id),
|
|
@@ -8436,7 +8438,7 @@ var workerOperations = {
|
|
|
8436
8438
|
...taskId,
|
|
8437
8439
|
expectedRevision: revision.optional(),
|
|
8438
8440
|
expectedWorkspaceRevision: revision.optional(),
|
|
8439
|
-
workerId:
|
|
8441
|
+
workerId: idRef,
|
|
8440
8442
|
report: workerReportSchema
|
|
8441
8443
|
}),
|
|
8442
8444
|
cancel: operation({
|
|
@@ -8444,7 +8446,7 @@ var workerOperations = {
|
|
|
8444
8446
|
...taskId,
|
|
8445
8447
|
...revisions,
|
|
8446
8448
|
expectedWorkspaceRevision: revision.optional(),
|
|
8447
|
-
workerId:
|
|
8449
|
+
workerId: idRef,
|
|
8448
8450
|
reason: text
|
|
8449
8451
|
})
|
|
8450
8452
|
};
|
|
@@ -8494,6 +8496,16 @@ var success = (revisionValue, workspaceRevisionValue, data) => ({
|
|
|
8494
8496
|
data
|
|
8495
8497
|
});
|
|
8496
8498
|
var failure2 = (code, error, details = {}) => ({ ok: false, schemaVersion: 1, code, error, details });
|
|
8499
|
+
function resolveIdRef(kind, ref, ids) {
|
|
8500
|
+
if (ids.includes(ref))
|
|
8501
|
+
return success(null, null, ref);
|
|
8502
|
+
const matches = ids.filter((candidate) => candidate.startsWith(ref));
|
|
8503
|
+
if (matches.length === 1)
|
|
8504
|
+
return success(null, null, matches[0]);
|
|
8505
|
+
if (matches.length === 0)
|
|
8506
|
+
return failure2("not_found", `${kind} not found`);
|
|
8507
|
+
return failure2("invalid_input", `${kind} prefix ${ref} is ambiguous; candidates: ${matches.join(", ")}`);
|
|
8508
|
+
}
|
|
8497
8509
|
function parseOperation(family, input) {
|
|
8498
8510
|
if (typeof input === "object" && input !== null && "schemaVersion" in input && input.schemaVersion !== SCHEMA_VERSION)
|
|
8499
8511
|
return failure2("unsupported_version", "unsupported schema version", { operation: family });
|
|
@@ -11003,20 +11015,26 @@ class TaskStore {
|
|
|
11003
11015
|
return success(null, null, { key: key.data, bound: bound.data });
|
|
11004
11016
|
}
|
|
11005
11017
|
taskForWorker(workerId) {
|
|
11006
|
-
if (typeof workerId !== "string" || !
|
|
11018
|
+
if (typeof workerId !== "string" || !idRefPattern.test(workerId))
|
|
11007
11019
|
return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
|
|
11008
11020
|
const listed = this.listTaskIndex();
|
|
11009
11021
|
if (!listed.ok)
|
|
11010
11022
|
return listed;
|
|
11023
|
+
const owners = new Map;
|
|
11011
11024
|
for (const entry of listed.data) {
|
|
11012
11025
|
if (entry.status === "closed")
|
|
11013
11026
|
continue;
|
|
11014
11027
|
const loaded = this.load(entry.id);
|
|
11015
11028
|
if (!loaded.ok)
|
|
11016
11029
|
return loaded;
|
|
11017
|
-
|
|
11018
|
-
|
|
11030
|
+
for (const worker of loaded.data?.record.workers ?? [])
|
|
11031
|
+
owners.set(worker.id, entry.id);
|
|
11019
11032
|
}
|
|
11033
|
+
const resolved = resolveIdRef("worker", workerId, [...owners.keys()]);
|
|
11034
|
+
if (resolved.ok)
|
|
11035
|
+
return success(null, null, owners.get(resolved.data) ?? null);
|
|
11036
|
+
if (resolved.code !== "not_found")
|
|
11037
|
+
return resolved;
|
|
11020
11038
|
return success(null, null, null);
|
|
11021
11039
|
}
|
|
11022
11040
|
originTaskEntry() {
|
|
@@ -13168,20 +13186,19 @@ var workspaceConfigSchema = object({
|
|
|
13168
13186
|
union([boolean2(), literal("verified")]).optional()
|
|
13169
13187
|
]))).passthrough().optional(),
|
|
13170
13188
|
youtrack: object({ baseUrl: nonBlank.optional(), link_issues: boolean2().optional() }).passthrough().optional(),
|
|
13171
|
-
issues: object({
|
|
13189
|
+
issues: object({
|
|
13190
|
+
provider: _enum(["github", "gitlab", "youtrack"]).optional(),
|
|
13191
|
+
link: boolean2().optional(),
|
|
13192
|
+
relation: unknown().optional(),
|
|
13193
|
+
backlink: boolean2().optional(),
|
|
13194
|
+
link_on_pr: boolean2().optional()
|
|
13195
|
+
}).passthrough().optional(),
|
|
13172
13196
|
branchPolicy: workspaceBranchPolicySchema.optional(),
|
|
13173
13197
|
commitPolicy: workspaceCommitPolicySchema.optional(),
|
|
13174
13198
|
defaultProfile: nonBlank.optional(),
|
|
13175
13199
|
profiles: record(nonBlank, workspaceProfileSchema).optional(),
|
|
13176
13200
|
releaseTracks: record(nonBlank, releaseTrackSchema).optional()
|
|
13177
13201
|
}).passthrough().superRefine((workspace, context) => {
|
|
13178
|
-
if (workspace.issues && workspace.vcs?.provider !== "github") {
|
|
13179
|
-
context.addIssue({
|
|
13180
|
-
code: "custom",
|
|
13181
|
-
path: ["issues"],
|
|
13182
|
-
message: `GitHub issue linking requires the github provider, got ${workspace.vcs?.provider ?? "unset"}`
|
|
13183
|
-
});
|
|
13184
|
-
}
|
|
13185
13202
|
if (workspace.defaultProfile !== undefined && !Object.hasOwn(workspace.profiles ?? {}, workspace.defaultProfile)) {
|
|
13186
13203
|
context.addIssue({
|
|
13187
13204
|
code: "custom",
|
|
@@ -14048,6 +14065,44 @@ var readVcsConfig = () => {
|
|
|
14048
14065
|
}
|
|
14049
14066
|
return { status: "malformed", path: file, config: {}, error: `${file} is not a JSON object` };
|
|
14050
14067
|
};
|
|
14068
|
+
var TRACKERS = new Set(["github", "gitlab", "youtrack"]);
|
|
14069
|
+
var asRecord = (value) => value && typeof value === "object" && !Array.isArray(value) ? value : {};
|
|
14070
|
+
var flag = (value) => typeof value === "boolean" ? value : null;
|
|
14071
|
+
var text2 = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
|
|
14072
|
+
function issueTrackerOf(issues, youtrack) {
|
|
14073
|
+
const iss = asRecord(issues);
|
|
14074
|
+
const yt = asRecord(youtrack);
|
|
14075
|
+
const warnings = [];
|
|
14076
|
+
const named = typeof iss.provider === "string" && TRACKERS.has(iss.provider.toLowerCase()) ? iss.provider.toLowerCase() : null;
|
|
14077
|
+
if (iss.relation !== undefined && iss.relation !== "closes" && iss.relation !== "refs")
|
|
14078
|
+
warnings.push(`issues.relation ${JSON.stringify(iss.relation)} is not closes or refs; read as closes`);
|
|
14079
|
+
for (const key of ["repo", "host"])
|
|
14080
|
+
if (iss[key] !== undefined && text2(iss[key]) === null)
|
|
14081
|
+
warnings.push(`issues.${key} is not a non-empty string; ignored`);
|
|
14082
|
+
const common = {
|
|
14083
|
+
relation: iss.relation === "refs" ? "refs" : "closes",
|
|
14084
|
+
backlink: iss.backlink === true,
|
|
14085
|
+
baseUrl: text2(yt.baseUrl),
|
|
14086
|
+
repo: text2(iss.repo)?.replace(/^\/+|\/+$/gu, "") ?? null,
|
|
14087
|
+
host: text2(iss.host)?.toLowerCase() ?? null,
|
|
14088
|
+
warnings
|
|
14089
|
+
};
|
|
14090
|
+
const legacyGithub = named === "github" && flag(iss.link) === null;
|
|
14091
|
+
if (flag(yt.link_issues) === true && (named === null || legacyGithub))
|
|
14092
|
+
return { provider: "youtrack", link: true, ...common, source: "youtrack.link_issues" };
|
|
14093
|
+
if (named) {
|
|
14094
|
+
const link = flag(iss.link) ?? flag(iss.link_on_pr) ?? (named === "youtrack" ? flag(yt.link_issues) : null) ?? false;
|
|
14095
|
+
return {
|
|
14096
|
+
provider: named,
|
|
14097
|
+
link,
|
|
14098
|
+
...common,
|
|
14099
|
+
source: flag(iss.link) === null && flag(iss.link_on_pr) !== null ? "issues.link_on_pr" : "issues"
|
|
14100
|
+
};
|
|
14101
|
+
}
|
|
14102
|
+
if (common.baseUrl)
|
|
14103
|
+
return { provider: "youtrack", link: false, ...common, source: "youtrack.baseUrl" };
|
|
14104
|
+
return null;
|
|
14105
|
+
}
|
|
14051
14106
|
var trackSummary = (resolution) => resolution.status === "resolved" ? {
|
|
14052
14107
|
name: resolution.track?.name ?? null,
|
|
14053
14108
|
source: resolution.source,
|
|
@@ -14068,6 +14123,7 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
14068
14123
|
const rawProvider = wsVcs.provider ?? (determinateRoot ? remoteProvider(root) : null) ?? cfg.provider ?? null;
|
|
14069
14124
|
const provider = rawProvider === null || rawProvider === undefined ? null : String(rawProvider).toLowerCase() || null;
|
|
14070
14125
|
const workspaceDefault = workspaceDefaultTarget(root, cfg.defaultTargetBranch);
|
|
14126
|
+
const issueTracker = issueTrackerOf(wsIssues, wsYt);
|
|
14071
14127
|
const linkIssues = typeof wsYt.link_issues === "boolean" ? wsYt.link_issues : null;
|
|
14072
14128
|
const youtrackBaseUrl = typeof wsYt.baseUrl === "string" ? wsYt.baseUrl : null;
|
|
14073
14129
|
let issuesProvider = null;
|
|
@@ -14100,7 +14156,8 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
14100
14156
|
link_issues: linkIssues,
|
|
14101
14157
|
youtrack_base_url: youtrackBaseUrl,
|
|
14102
14158
|
issues_provider: issuesProvider,
|
|
14103
|
-
link_on_pr: linkOnPr
|
|
14159
|
+
link_on_pr: linkOnPr,
|
|
14160
|
+
issue_tracker: issueTracker
|
|
14104
14161
|
};
|
|
14105
14162
|
}
|
|
14106
14163
|
if (cfgStatus === "malformed") {
|
|
@@ -14126,7 +14183,8 @@ function vcsConfig(mode, cwd, options = {}) {
|
|
|
14126
14183
|
link_issues: linkIssues,
|
|
14127
14184
|
youtrack_base_url: youtrackBaseUrl,
|
|
14128
14185
|
issues_provider: issuesProvider,
|
|
14129
|
-
link_on_pr: linkOnPr
|
|
14186
|
+
link_on_pr: linkOnPr,
|
|
14187
|
+
issue_tracker: issueTracker
|
|
14130
14188
|
};
|
|
14131
14189
|
if (provider === "gitlab") {
|
|
14132
14190
|
out.gitlab = {
|
|
@@ -14230,7 +14288,12 @@ var MAX_LINE_BYTES = 4096;
|
|
|
14230
14288
|
var PASSING = new Set(["verified", "tests-verified", "type-check-only"]);
|
|
14231
14289
|
var FAILING = new Set(["failed", "blocked"]);
|
|
14232
14290
|
var STRONG_RESULTS = new Set(["verified", "tests-verified"]);
|
|
14233
|
-
var PR_ROW_TYPES = new Set([
|
|
14291
|
+
var PR_ROW_TYPES = new Set([
|
|
14292
|
+
"pr.created",
|
|
14293
|
+
"pr.status",
|
|
14294
|
+
"pr.merged",
|
|
14295
|
+
"pr.linked"
|
|
14296
|
+
]);
|
|
14234
14297
|
var err = (code, error, unblock) => ({
|
|
14235
14298
|
ok: false,
|
|
14236
14299
|
code,
|
|
@@ -18515,7 +18578,7 @@ var RISK = {
|
|
|
18515
18578
|
};
|
|
18516
18579
|
var YES = new Set(["true", "yes", "y", "1", "on"]);
|
|
18517
18580
|
var NO = new Set(["false", "no", "n", "0", "off", "none"]);
|
|
18518
|
-
var
|
|
18581
|
+
var flag2 = (value, unknownAs) => {
|
|
18519
18582
|
if (typeof value === "boolean")
|
|
18520
18583
|
return { ok: true, value };
|
|
18521
18584
|
if (typeof value === "number" && (value === 0 || value === 1))
|
|
@@ -18581,12 +18644,12 @@ function normalizeJudgment(raw, previous) {
|
|
|
18581
18644
|
const items = Array.isArray(value) ? value : [value];
|
|
18582
18645
|
judgment.refs.push(...items.filter((item) => item !== null && item !== "").map(refOf));
|
|
18583
18646
|
} else {
|
|
18584
|
-
if (name === "needsPlan" && typeof value === "string" && !
|
|
18647
|
+
if (name === "needsPlan" && typeof value === "string" && !flag2(value, false).ok && value.trim()) {
|
|
18585
18648
|
judgment.needsPlan = true;
|
|
18586
18649
|
judgment.refs.push(refOf(value.trim()));
|
|
18587
18650
|
continue;
|
|
18588
18651
|
}
|
|
18589
|
-
const parsed =
|
|
18652
|
+
const parsed = flag2(value, name === "behaviorChange");
|
|
18590
18653
|
if (parsed.ok)
|
|
18591
18654
|
judgment[name] = parsed.value;
|
|
18592
18655
|
else
|
|
@@ -18966,6 +19029,8 @@ var TASK_SCOPED_ACTIONS = new Set([
|
|
|
18966
19029
|
"worker.cancel",
|
|
18967
19030
|
"state.export"
|
|
18968
19031
|
]);
|
|
19032
|
+
var ONLY_OWN_REPORT = "workers can submit only their own report";
|
|
19033
|
+
var NO_HELPER_CONTROL = "helpers cannot cancel or control workers";
|
|
18969
19034
|
var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
|
|
18970
19035
|
var workerCommand = (taskId, request) => {
|
|
18971
19036
|
const { action, workerId, reason, report } = request;
|
|
@@ -19863,6 +19928,10 @@ class WorkitCore {
|
|
|
19863
19928
|
}
|
|
19864
19929
|
if (helper?.ok)
|
|
19865
19930
|
return failure2("permission_denied", "helpers cannot resolve findings");
|
|
19931
|
+
const findingRef = resolveIdRef("finding", input.findingId, task.data.findings.map((entry) => entry.id));
|
|
19932
|
+
if (!findingRef.ok)
|
|
19933
|
+
return findingRef;
|
|
19934
|
+
input.findingId = findingRef.data;
|
|
19866
19935
|
const finding = task.data.findings.find((entry) => entry.id === input.findingId);
|
|
19867
19936
|
if (!finding)
|
|
19868
19937
|
return failure2("not_found", "finding not found");
|
|
@@ -20014,15 +20083,31 @@ class WorkitCore {
|
|
|
20014
20083
|
return changed;
|
|
20015
20084
|
return success(changed.data.task.revision, changed.data.workspace.revision, changed.data.task.workers.at(-1));
|
|
20016
20085
|
}
|
|
20086
|
+
const workerRef = resolveIdRef("worker", input.workerId, helperId === null ? task.data.workers.map((candidate) => candidate.id) : [helperId]);
|
|
20087
|
+
if (!workerRef.ok)
|
|
20088
|
+
return helperId === null ? workerRef : failure2("permission_denied", input.action === "report" ? ONLY_OWN_REPORT : NO_HELPER_CONTROL);
|
|
20089
|
+
input.workerId = workerRef.data;
|
|
20017
20090
|
const entry = task.data.workers.find((candidate) => candidate.id === input.workerId);
|
|
20018
20091
|
if (!entry)
|
|
20019
20092
|
return failure2("not_found", "worker not found");
|
|
20020
20093
|
if (input.action === "report") {
|
|
20021
20094
|
if (helperId === null || helperId !== input.workerId)
|
|
20022
|
-
return failure2("permission_denied",
|
|
20095
|
+
return failure2("permission_denied", ONLY_OWN_REPORT);
|
|
20023
20096
|
const helper = this.helperEntry(task.data, true, true);
|
|
20024
20097
|
if (!helper.ok)
|
|
20025
20098
|
return helper;
|
|
20099
|
+
const ownFindings = task.data.findings.filter((candidate) => candidate.provenance.workerId === helperId).map((candidate) => candidate.id);
|
|
20100
|
+
const reported = [];
|
|
20101
|
+
for (const findingRef of input.report.findingIds) {
|
|
20102
|
+
const found = resolveIdRef("finding", findingRef, ownFindings);
|
|
20103
|
+
if (found.ok)
|
|
20104
|
+
reported.push(found.data);
|
|
20105
|
+
else if (found.code === "not_found")
|
|
20106
|
+
reported.push(findingRef);
|
|
20107
|
+
else
|
|
20108
|
+
return found;
|
|
20109
|
+
}
|
|
20110
|
+
input.report.findingIds = reported;
|
|
20026
20111
|
const evidence = task.data.evidence.filter((candidate) => input.report.evidenceIds.includes(candidate.id));
|
|
20027
20112
|
const findings = task.data.findings.filter((candidate) => input.report.findingIds.includes(candidate.id));
|
|
20028
20113
|
if (evidence.length !== input.report.evidenceIds.length || findings.length !== input.report.findingIds.length)
|
|
@@ -20050,7 +20135,7 @@ class WorkitCore {
|
|
|
20050
20135
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during report");
|
|
20051
20136
|
}
|
|
20052
20137
|
if (helperId !== null)
|
|
20053
|
-
return failure2("permission_denied",
|
|
20138
|
+
return failure2("permission_denied", NO_HELPER_CONTROL);
|
|
20054
20139
|
if (task.data.status === "closed")
|
|
20055
20140
|
return failure2("invalid_transition", "closed task cannot cancel workers");
|
|
20056
20141
|
if (entry.data.state === "stopped") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-opencode",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.15.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit OpenCode plugin — native tools, adaptive policy, and sixteen method skills",
|
|
6
6
|
"keywords": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"typecheck": "tsc --noEmit"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@brainervirus/workit-core": "^8.
|
|
42
|
+
"@brainervirus/workit-core": "^8.15.0",
|
|
43
43
|
"@opencode/plugin": "2.0.18"
|
|
44
44
|
},
|
|
45
45
|
"engines": {
|