@brainervirus/workit-cursor 1.0.8 → 1.0.9
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/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +116 -55
- package/dist/mcp-server.js +13 -8
- package/dist/workit-hook.js +112 -54
- package/package.json +3 -3
- package/skills/workit-babysit/SKILL.md +3 -1
|
@@ -7832,7 +7832,7 @@ var init_task_contract = __esm(() => {
|
|
|
7832
7832
|
}).strict();
|
|
7833
7833
|
workerSchema = object({
|
|
7834
7834
|
assignment: assignmentSchema,
|
|
7835
|
-
state: _enum(["assigned", "running", "cancelling", "stopped", "unknown"]),
|
|
7835
|
+
state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
7836
7836
|
session: refSchema.nullable(),
|
|
7837
7837
|
report: workerReportSchema.nullable()
|
|
7838
7838
|
}).strict();
|
|
@@ -10478,6 +10478,95 @@ var init_task_evaluation = __esm(() => {
|
|
|
10478
10478
|
scopeCovers2 = scopeCovers;
|
|
10479
10479
|
});
|
|
10480
10480
|
|
|
10481
|
+
// packages/workit-core/src/core/methods.ts
|
|
10482
|
+
function selectMethods(policy, capabilities) {
|
|
10483
|
+
return Object.entries(METHODS).flatMap(([id, definition]) => {
|
|
10484
|
+
const requirements = policy.requirements.filter((requirement) => methodMatches(id, definition, requirement));
|
|
10485
|
+
if (requirements.length === 0)
|
|
10486
|
+
return [];
|
|
10487
|
+
const capability = assuranceFor(id, definition, capabilities);
|
|
10488
|
+
const reasons = [
|
|
10489
|
+
...new Set(requirements.map((requirement) => requirement.reason)),
|
|
10490
|
+
...capability.reasons
|
|
10491
|
+
];
|
|
10492
|
+
if (capability.assurance === "unavailable")
|
|
10493
|
+
reasons.unshift("required capability is unavailable");
|
|
10494
|
+
return [{ id, reason: reasons.join("; "), assurance: capability.assurance }];
|
|
10495
|
+
});
|
|
10496
|
+
}
|
|
10497
|
+
var METHODS, methodMatches = (id, definition, requirement) => definition.dimensions?.includes(requirement.dimension) === true && !(id === "workit-review" && requirement.ruleId === "self-review") || definition.ruleIds?.includes(requirement.ruleId) === true, assuranceFor = (id, definition, capabilities) => {
|
|
10498
|
+
const keys = new Set([
|
|
10499
|
+
id,
|
|
10500
|
+
...definition.dimensions ?? [],
|
|
10501
|
+
...definition.ruleIds ?? []
|
|
10502
|
+
]);
|
|
10503
|
+
const matches = capabilities.filter((capability) => keys.has(capability.name) || keys.has(capability.surface));
|
|
10504
|
+
if (id === "workit-review" && matches.length === 0)
|
|
10505
|
+
return { assurance: "unavailable", reasons: ["independent review capability is unavailable"] };
|
|
10506
|
+
if (matches.some((capability) => capability.assurance === "unavailable"))
|
|
10507
|
+
return {
|
|
10508
|
+
assurance: "unavailable",
|
|
10509
|
+
reasons: matches.filter((capability) => capability.assurance === "unavailable").map((capability) => capability.reason)
|
|
10510
|
+
};
|
|
10511
|
+
if (matches.some((capability) => capability.assurance === "enforced"))
|
|
10512
|
+
return { assurance: "enforced", reasons: matches.map((capability) => capability.reason) };
|
|
10513
|
+
return { assurance: "agent_guided", reasons: matches.map((capability) => capability.reason) };
|
|
10514
|
+
}, invariantBootstrap = () => `
|
|
10515
|
+
Workit keeps one accountable lead and one shared task state. Inspect current task
|
|
10516
|
+
state before acting. An empty task.list means no session yet, not permission to
|
|
10517
|
+
skip Workit; for user-requested product, debug, or behavior work with no active
|
|
10518
|
+
or paused task, run task.start then policy.assess before other product mutations.
|
|
10519
|
+
Omitted expectedRevision, expectedWorkspaceRevision, and writer workerId default
|
|
10520
|
+
to the current records; explicit values are still concurrency-checked, so never
|
|
10521
|
+
copy revisions between calls. Happy path: task.list, task.start {intent} with no
|
|
10522
|
+
revisions, policy.assess {assessment: {facts, signals, consequences,
|
|
10523
|
+
verification}} where facts are inferred or observed with file refs,
|
|
10524
|
+
writer.acquire {taskId} before product writes, evidence.record {evidence} where
|
|
10525
|
+
check and review kinds auto-bind the current tree and need no digests. Record
|
|
10526
|
+
self-review requirements with kind review and reviewContext matching your own
|
|
10527
|
+
host session; the lead may also record checks. Fresh-context review requires a
|
|
10528
|
+
session distinct from the task creator and every other evidence recorder. Record
|
|
10529
|
+
verification evidence last: later evidence or commits can reopen fixed findings
|
|
10530
|
+
for re-verification, so verify, resolve, then close with nothing in between.
|
|
10531
|
+
decision.record {binding with taskId/workspaceId from inspect plus presented and
|
|
10532
|
+
approvedContent} only through a native approval question asked receipt-shaped:
|
|
10533
|
+
header Workit decision: <purpose>, the same label repeated in the question
|
|
10534
|
+
text (some host UIs do not render headers), exactly two options approved and
|
|
10535
|
+
rejected; ask once, never re-ask to mint a receipt. task.close {outcome,
|
|
10536
|
+
summary, decisionIds}. A completed native subagent run stops its bound worker
|
|
10537
|
+
by itself; if a worker strands in cancelling with its run verifiably over,
|
|
10538
|
+
repeat worker.cancel {reason} to confirm the stop. Check and review evidence
|
|
10539
|
+
without a bound candidate goes stale; findings close only as fixed with passing
|
|
10540
|
+
verification, dismissed with supporting evidence, or deferred under an approved
|
|
10541
|
+
limitation. Use only the
|
|
10542
|
+
shared operations for task, policy, evidence, finding, decision, worker, writer,
|
|
10543
|
+
and state changes. Authority is bounded by the requested scope, current
|
|
10544
|
+
revision, caller/session provenance, and observed capabilities. Never claim host
|
|
10545
|
+
enforcement or evidence that the host cannot provide. Shell-executed writes are
|
|
10546
|
+
unattested agent-guided work even when a writer is held. Preserve unresolved
|
|
10547
|
+
requirements, gaps, and uncertain workers. Skill routing: slash aliases /wk-*
|
|
10548
|
+
always load on demand. Model-invoked: new instructions, interruptions, or
|
|
10549
|
+
forgotten items mid-task → load workit-steer before responding; about to open
|
|
10550
|
+
a PR or fresh from implementing → load workit-deslop; CI red → load
|
|
10551
|
+
workit-green-run; small-looking change before close or merge → load
|
|
10552
|
+
workit-blast-radius; ambiguous, consequential, or disputed proposal before
|
|
10553
|
+
recommending → load workit-challenge; spec or plan needs a flow diagram →
|
|
10554
|
+
load workit-diagram, a UI sketch → load workit-mockup. Load the skill; never
|
|
10555
|
+
act from memory of it.
|
|
10556
|
+
`.trim();
|
|
10557
|
+
var init_methods = __esm(() => {
|
|
10558
|
+
METHODS = {
|
|
10559
|
+
"workit-challenge": { dimensions: ["challenge", "decisions"] },
|
|
10560
|
+
"workit-behavioral-tdd": { dimensions: ["testing"] },
|
|
10561
|
+
"workit-review": { dimensions: ["review"] },
|
|
10562
|
+
"workit-plan": { dimensions: ["artifacts", "continuity"] },
|
|
10563
|
+
"workit-implement": { dimensions: ["delegation"] },
|
|
10564
|
+
"workit-debug": { ruleIds: ["root-cause-investigation"] },
|
|
10565
|
+
"workit-handoff": { ruleIds: ["durable-handoff"] },
|
|
10566
|
+
"workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
|
|
10567
|
+
};
|
|
10568
|
+
});
|
|
10569
|
+
|
|
10481
10570
|
// packages/workit-core/src/core/task-context.ts
|
|
10482
10571
|
function reconcileResume(view, observations = []) {
|
|
10483
10572
|
if (observations.length > 0)
|
|
@@ -10487,7 +10576,7 @@ function reconcileResume(view, observations = []) {
|
|
|
10487
10576
|
return candidate;
|
|
10488
10577
|
const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
|
|
10489
10578
|
const blockers = [...view.task.progress.blockers];
|
|
10490
|
-
if (view.task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
10579
|
+
if (view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
10491
10580
|
blockers.push({
|
|
10492
10581
|
reason: "worker state requires reconciliation",
|
|
10493
10582
|
dependentAction: "resume",
|
|
@@ -10509,6 +10598,11 @@ function compactTaskContext(view) {
|
|
|
10509
10598
|
digest: entry.data.digest,
|
|
10510
10599
|
references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
|
|
10511
10600
|
}));
|
|
10601
|
+
const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
|
|
10602
|
+
id: method.id,
|
|
10603
|
+
assurance: method.assurance,
|
|
10604
|
+
reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
|
|
10605
|
+
})) : [];
|
|
10512
10606
|
const gaps = Array.from(new Set([
|
|
10513
10607
|
...view.evidence.filter((entry) => entry.status === "stale").map((entry) => `stale evidence: ${entry.evidenceId}`),
|
|
10514
10608
|
...view.requirements.filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
|
|
@@ -10518,15 +10612,18 @@ function compactTaskContext(view) {
|
|
|
10518
10612
|
objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
|
|
10519
10613
|
status: view.task.status,
|
|
10520
10614
|
decisions,
|
|
10615
|
+
methods,
|
|
10521
10616
|
gaps,
|
|
10522
10617
|
nextAction: compactText(view.task.progress.nextAction, COMPACT_TEXT_BYTES)
|
|
10523
10618
|
};
|
|
10524
10619
|
let encoded = JSON.stringify(context);
|
|
10525
|
-
while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.gaps.length)) {
|
|
10620
|
+
while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
|
|
10526
10621
|
if (context.gaps.length)
|
|
10527
10622
|
context.gaps.pop();
|
|
10528
|
-
else
|
|
10623
|
+
else if (context.decisions.length)
|
|
10529
10624
|
context.decisions.pop();
|
|
10625
|
+
else
|
|
10626
|
+
context.methods.pop();
|
|
10530
10627
|
encoded = JSON.stringify(context);
|
|
10531
10628
|
}
|
|
10532
10629
|
if (compactBytes(encoded) > COMPACT_MAX_BYTES) {
|
|
@@ -10564,56 +10661,14 @@ var COMPACT_MAX_BYTES = 4096, COMPACT_MAX_ITEMS = 8, COMPACT_TEXT_BYTES = 128, s
|
|
|
10564
10661
|
var init_task_context = __esm(() => {
|
|
10565
10662
|
init_task_contract();
|
|
10566
10663
|
init_task_evaluation();
|
|
10664
|
+
init_methods();
|
|
10567
10665
|
sensitiveUrl = /\bhttps?:\/\/[^/\s:@]+:[^@\s]+@/gi;
|
|
10568
10666
|
sensitiveValue = /(?:token|secret|password|passwd|api[-_ ]?key|private[-_ ]?key|authorization)\s*[:=]\s*(?:bearer\s+)?\S+|\bbearer\s+\S+/gi;
|
|
10569
10667
|
privateKey = /-----BEGIN [^-]*PRIVATE KEY-----[\s\S]*?-----END [^-]*PRIVATE KEY-----/gi;
|
|
10570
10668
|
});
|
|
10571
10669
|
|
|
10572
|
-
// packages/workit-core/src/core/
|
|
10573
|
-
var
|
|
10574
|
-
Workit keeps one accountable lead and one shared task state. Inspect current task
|
|
10575
|
-
state before acting. An empty task.list means no session yet, not permission to
|
|
10576
|
-
skip Workit; for user-requested product, debug, or behavior work with no active
|
|
10577
|
-
or paused task, run task.start then policy.assess before other product mutations.
|
|
10578
|
-
Omitted expectedRevision, expectedWorkspaceRevision, and writer workerId default
|
|
10579
|
-
to the current records; explicit values are still concurrency-checked, so never
|
|
10580
|
-
copy revisions between calls. Happy path: task.list, task.start {intent} with no
|
|
10581
|
-
revisions, policy.assess {assessment: {facts, signals, consequences,
|
|
10582
|
-
verification}} where facts are inferred or observed with file refs,
|
|
10583
|
-
writer.acquire {taskId} before product writes, evidence.record {evidence} where
|
|
10584
|
-
check and review kinds auto-bind the current tree and need no digests. Record
|
|
10585
|
-
self-review requirements with kind review and reviewContext matching your own
|
|
10586
|
-
host session; the lead may also record checks. Fresh-context review requires a
|
|
10587
|
-
session distinct from the task creator and every other evidence recorder. Record
|
|
10588
|
-
verification evidence last: later evidence or commits can reopen fixed findings
|
|
10589
|
-
for re-verification, so verify, resolve, then close with nothing in between.
|
|
10590
|
-
decision.record {binding with taskId/workspaceId from inspect plus presented and
|
|
10591
|
-
approvedContent} only through a native approval question asked receipt-shaped:
|
|
10592
|
-
header Workit decision: <purpose>, the same label repeated in the question
|
|
10593
|
-
text (some host UIs do not render headers), exactly two options approved and
|
|
10594
|
-
rejected; ask once, never re-ask to mint a receipt. task.close {outcome,
|
|
10595
|
-
summary, decisionIds}. A completed native subagent run stops its bound worker
|
|
10596
|
-
by itself; if a worker strands in cancelling with its run verifiably over,
|
|
10597
|
-
repeat worker.cancel {reason} to confirm the stop. Check and review evidence
|
|
10598
|
-
without a bound candidate goes stale; findings close only as fixed with passing
|
|
10599
|
-
verification, dismissed with supporting evidence, or deferred under an approved
|
|
10600
|
-
limitation. Use only the
|
|
10601
|
-
shared operations for task, policy, evidence, finding, decision, worker, writer,
|
|
10602
|
-
and state changes. Authority is bounded by the requested scope, current
|
|
10603
|
-
revision, caller/session provenance, and observed capabilities. Never claim host
|
|
10604
|
-
enforcement or evidence that the host cannot provide. Shell-executed writes are
|
|
10605
|
-
unattested agent-guided work even when a writer is held. Preserve unresolved
|
|
10606
|
-
requirements, gaps, and uncertain workers. Skill routing: slash aliases /wk-*
|
|
10607
|
-
always load on demand. Model-invoked: new instructions, interruptions, or
|
|
10608
|
-
forgotten items mid-task → load workit-steer before responding; about to open
|
|
10609
|
-
a PR or fresh from implementing → load workit-deslop; CI red → load
|
|
10610
|
-
workit-green-run; small-looking change before close or merge → load
|
|
10611
|
-
workit-blast-radius; ambiguous, consequential, or disputed proposal before
|
|
10612
|
-
recommending → load workit-challenge; spec or plan needs a flow diagram →
|
|
10613
|
-
load workit-diagram, a UI sketch → load workit-mockup. Load the skill; never
|
|
10614
|
-
act from memory of it.
|
|
10615
|
-
`.trim();
|
|
10616
|
-
var init_methods = () => {};
|
|
10670
|
+
// packages/workit-core/src/core/route-intent.ts
|
|
10671
|
+
var init_route_intent = () => {};
|
|
10617
10672
|
|
|
10618
10673
|
// packages/workit-core/src/core/external-action.ts
|
|
10619
10674
|
var externalActionSchema;
|
|
@@ -11140,7 +11195,7 @@ class WorkitCore {
|
|
|
11140
11195
|
activeWorkerBlocker(task, workspace) {
|
|
11141
11196
|
if (workspace.writer)
|
|
11142
11197
|
return failure("recovery_required", "writer ownership must be released first");
|
|
11143
|
-
if (task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
11198
|
+
if (task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
11144
11199
|
return failure("recovery_required", "worker state requires reconciliation");
|
|
11145
11200
|
return null;
|
|
11146
11201
|
}
|
|
@@ -11904,7 +11959,12 @@ class WorkitCore {
|
|
|
11904
11959
|
return failure("invalid_transition", "worker is not awaiting dispatch");
|
|
11905
11960
|
return success(mutation.revision, null, {
|
|
11906
11961
|
...current,
|
|
11907
|
-
workers: current.workers.map((item) => item.id === input.workerId ? {
|
|
11962
|
+
workers: current.workers.map((item) => item.id === input.workerId ? {
|
|
11963
|
+
...item,
|
|
11964
|
+
recordedAt: mutation.now,
|
|
11965
|
+
provenance: attested.data,
|
|
11966
|
+
data: { ...item.data, state: "dispatching" }
|
|
11967
|
+
} : item)
|
|
11908
11968
|
});
|
|
11909
11969
|
}
|
|
11910
11970
|
});
|
|
@@ -12373,7 +12433,7 @@ class WorkitCore {
|
|
|
12373
12433
|
const base = reconcileResume(effectiveView);
|
|
12374
12434
|
if (!base.ok)
|
|
12375
12435
|
return base;
|
|
12376
|
-
const needsReconciliation = view.task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
|
|
12436
|
+
const needsReconciliation = view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
|
|
12377
12437
|
const blockers = [...base.data.blockers];
|
|
12378
12438
|
if (needsReconciliation && !blockers.some((entry) => entry.reason === "worker state requires reconciliation"))
|
|
12379
12439
|
blockers.push({
|
|
@@ -12664,10 +12724,10 @@ var provenance = (context, kind = context.provenanceKind ?? "host_observed") =>
|
|
|
12664
12724
|
const notStarted = input.dispatch === true;
|
|
12665
12725
|
if (notStarted && (input.state !== "stopped" || input.session !== null))
|
|
12666
12726
|
return failure("invalid_input", "a never-dispatched worker stops with no session");
|
|
12667
|
-
if (notStarted &&
|
|
12668
|
-
return failure("invalid_transition", "worker is no longer awaiting dispatch");
|
|
12669
|
-
if (notStarted && (entry.data.session !== null || entry.data.state === "running"))
|
|
12727
|
+
if (notStarted && entry.data.session !== null)
|
|
12670
12728
|
return failure("invalid_transition", "worker already has an observed session");
|
|
12729
|
+
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
12730
|
+
return failure("invalid_transition", "worker launch was not claimed");
|
|
12671
12731
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
12672
12732
|
return failure("invalid_input", "running worker observations require a session");
|
|
12673
12733
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -13002,6 +13062,7 @@ var init_core3 = __esm(() => {
|
|
|
13002
13062
|
init_task_store();
|
|
13003
13063
|
init_task_context();
|
|
13004
13064
|
init_methods();
|
|
13065
|
+
init_route_intent();
|
|
13005
13066
|
init_authority();
|
|
13006
13067
|
init_external_action();
|
|
13007
13068
|
init_external_action();
|
package/dist/mcp-server.js
CHANGED
|
@@ -20310,7 +20310,7 @@ var workerReportSchema = object4({
|
|
|
20310
20310
|
}).strict();
|
|
20311
20311
|
var workerSchema = object4({
|
|
20312
20312
|
assignment: assignmentSchema,
|
|
20313
|
-
state: _enum2(["assigned", "running", "cancelling", "stopped", "unknown"]),
|
|
20313
|
+
state: _enum2(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
20314
20314
|
session: refSchema.nullable(),
|
|
20315
20315
|
report: workerReportSchema.nullable()
|
|
20316
20316
|
}).strict();
|
|
@@ -23144,7 +23144,7 @@ function reconcileResume(view, observations = []) {
|
|
|
23144
23144
|
return candidate;
|
|
23145
23145
|
const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
|
|
23146
23146
|
const blockers = [...view.task.progress.blockers];
|
|
23147
|
-
if (view.task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
23147
|
+
if (view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
23148
23148
|
blockers.push({
|
|
23149
23149
|
reason: "worker state requires reconciliation",
|
|
23150
23150
|
dependentAction: "resume",
|
|
@@ -23752,10 +23752,10 @@ var applyWorkerLifecycle = (input) => {
|
|
|
23752
23752
|
const notStarted = input.dispatch === true;
|
|
23753
23753
|
if (notStarted && (input.state !== "stopped" || input.session !== null))
|
|
23754
23754
|
return failure("invalid_input", "a never-dispatched worker stops with no session");
|
|
23755
|
-
if (notStarted &&
|
|
23756
|
-
return failure("invalid_transition", "worker is no longer awaiting dispatch");
|
|
23757
|
-
if (notStarted && (entry.data.session !== null || entry.data.state === "running"))
|
|
23755
|
+
if (notStarted && entry.data.session !== null)
|
|
23758
23756
|
return failure("invalid_transition", "worker already has an observed session");
|
|
23757
|
+
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
23758
|
+
return failure("invalid_transition", "worker launch was not claimed");
|
|
23759
23759
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
23760
23760
|
return failure("invalid_input", "running worker observations require a session");
|
|
23761
23761
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -24142,7 +24142,7 @@ class WorkitCore {
|
|
|
24142
24142
|
activeWorkerBlocker(task, workspace) {
|
|
24143
24143
|
if (workspace.writer)
|
|
24144
24144
|
return failure("recovery_required", "writer ownership must be released first");
|
|
24145
|
-
if (task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
24145
|
+
if (task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
24146
24146
|
return failure("recovery_required", "worker state requires reconciliation");
|
|
24147
24147
|
return null;
|
|
24148
24148
|
}
|
|
@@ -24906,7 +24906,12 @@ class WorkitCore {
|
|
|
24906
24906
|
return failure("invalid_transition", "worker is not awaiting dispatch");
|
|
24907
24907
|
return success(mutation.revision, null, {
|
|
24908
24908
|
...current,
|
|
24909
|
-
workers: current.workers.map((item) => item.id === input.workerId ? {
|
|
24909
|
+
workers: current.workers.map((item) => item.id === input.workerId ? {
|
|
24910
|
+
...item,
|
|
24911
|
+
recordedAt: mutation.now,
|
|
24912
|
+
provenance: attested.data,
|
|
24913
|
+
data: { ...item.data, state: "dispatching" }
|
|
24914
|
+
} : item)
|
|
24910
24915
|
});
|
|
24911
24916
|
}
|
|
24912
24917
|
});
|
|
@@ -25375,7 +25380,7 @@ class WorkitCore {
|
|
|
25375
25380
|
const base = reconcileResume(effectiveView);
|
|
25376
25381
|
if (!base.ok)
|
|
25377
25382
|
return base;
|
|
25378
|
-
const needsReconciliation = view.task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
|
|
25383
|
+
const needsReconciliation = view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
|
|
25379
25384
|
const blockers = [...base.data.blockers];
|
|
25380
25385
|
if (needsReconciliation && !blockers.some((entry) => entry.reason === "worker state requires reconciliation"))
|
|
25381
25386
|
blockers.push({
|
package/dist/workit-hook.js
CHANGED
|
@@ -7611,7 +7611,7 @@ var workerReportSchema = object({
|
|
|
7611
7611
|
}).strict();
|
|
7612
7612
|
var workerSchema = object({
|
|
7613
7613
|
assignment: assignmentSchema,
|
|
7614
|
-
state: _enum(["assigned", "running", "cancelling", "stopped", "unknown"]),
|
|
7614
|
+
state: _enum(["assigned", "dispatching", "running", "cancelling", "stopped", "unknown"]),
|
|
7615
7615
|
session: refSchema.nullable(),
|
|
7616
7616
|
report: workerReportSchema.nullable()
|
|
7617
7617
|
}).strict();
|
|
@@ -10380,6 +10380,95 @@ function evaluateClosure(requestedOutcome, view) {
|
|
|
10380
10380
|
});
|
|
10381
10381
|
}
|
|
10382
10382
|
|
|
10383
|
+
// packages/workit-core/src/core/methods.ts
|
|
10384
|
+
var METHODS = {
|
|
10385
|
+
"workit-challenge": { dimensions: ["challenge", "decisions"] },
|
|
10386
|
+
"workit-behavioral-tdd": { dimensions: ["testing"] },
|
|
10387
|
+
"workit-review": { dimensions: ["review"] },
|
|
10388
|
+
"workit-plan": { dimensions: ["artifacts", "continuity"] },
|
|
10389
|
+
"workit-implement": { dimensions: ["delegation"] },
|
|
10390
|
+
"workit-debug": { ruleIds: ["root-cause-investigation"] },
|
|
10391
|
+
"workit-handoff": { ruleIds: ["durable-handoff"] },
|
|
10392
|
+
"workit-deslop": { ruleIds: ["pre-pr-cleanup"] }
|
|
10393
|
+
};
|
|
10394
|
+
var methodMatches = (id, definition, requirement) => definition.dimensions?.includes(requirement.dimension) === true && !(id === "workit-review" && requirement.ruleId === "self-review") || definition.ruleIds?.includes(requirement.ruleId) === true;
|
|
10395
|
+
var assuranceFor = (id, definition, capabilities) => {
|
|
10396
|
+
const keys = new Set([
|
|
10397
|
+
id,
|
|
10398
|
+
...definition.dimensions ?? [],
|
|
10399
|
+
...definition.ruleIds ?? []
|
|
10400
|
+
]);
|
|
10401
|
+
const matches = capabilities.filter((capability) => keys.has(capability.name) || keys.has(capability.surface));
|
|
10402
|
+
if (id === "workit-review" && matches.length === 0)
|
|
10403
|
+
return { assurance: "unavailable", reasons: ["independent review capability is unavailable"] };
|
|
10404
|
+
if (matches.some((capability) => capability.assurance === "unavailable"))
|
|
10405
|
+
return {
|
|
10406
|
+
assurance: "unavailable",
|
|
10407
|
+
reasons: matches.filter((capability) => capability.assurance === "unavailable").map((capability) => capability.reason)
|
|
10408
|
+
};
|
|
10409
|
+
if (matches.some((capability) => capability.assurance === "enforced"))
|
|
10410
|
+
return { assurance: "enforced", reasons: matches.map((capability) => capability.reason) };
|
|
10411
|
+
return { assurance: "agent_guided", reasons: matches.map((capability) => capability.reason) };
|
|
10412
|
+
};
|
|
10413
|
+
function selectMethods(policy, capabilities) {
|
|
10414
|
+
return Object.entries(METHODS).flatMap(([id, definition]) => {
|
|
10415
|
+
const requirements = policy.requirements.filter((requirement) => methodMatches(id, definition, requirement));
|
|
10416
|
+
if (requirements.length === 0)
|
|
10417
|
+
return [];
|
|
10418
|
+
const capability = assuranceFor(id, definition, capabilities);
|
|
10419
|
+
const reasons = [
|
|
10420
|
+
...new Set(requirements.map((requirement) => requirement.reason)),
|
|
10421
|
+
...capability.reasons
|
|
10422
|
+
];
|
|
10423
|
+
if (capability.assurance === "unavailable")
|
|
10424
|
+
reasons.unshift("required capability is unavailable");
|
|
10425
|
+
return [{ id, reason: reasons.join("; "), assurance: capability.assurance }];
|
|
10426
|
+
});
|
|
10427
|
+
}
|
|
10428
|
+
var invariantBootstrap = () => `
|
|
10429
|
+
Workit keeps one accountable lead and one shared task state. Inspect current task
|
|
10430
|
+
state before acting. An empty task.list means no session yet, not permission to
|
|
10431
|
+
skip Workit; for user-requested product, debug, or behavior work with no active
|
|
10432
|
+
or paused task, run task.start then policy.assess before other product mutations.
|
|
10433
|
+
Omitted expectedRevision, expectedWorkspaceRevision, and writer workerId default
|
|
10434
|
+
to the current records; explicit values are still concurrency-checked, so never
|
|
10435
|
+
copy revisions between calls. Happy path: task.list, task.start {intent} with no
|
|
10436
|
+
revisions, policy.assess {assessment: {facts, signals, consequences,
|
|
10437
|
+
verification}} where facts are inferred or observed with file refs,
|
|
10438
|
+
writer.acquire {taskId} before product writes, evidence.record {evidence} where
|
|
10439
|
+
check and review kinds auto-bind the current tree and need no digests. Record
|
|
10440
|
+
self-review requirements with kind review and reviewContext matching your own
|
|
10441
|
+
host session; the lead may also record checks. Fresh-context review requires a
|
|
10442
|
+
session distinct from the task creator and every other evidence recorder. Record
|
|
10443
|
+
verification evidence last: later evidence or commits can reopen fixed findings
|
|
10444
|
+
for re-verification, so verify, resolve, then close with nothing in between.
|
|
10445
|
+
decision.record {binding with taskId/workspaceId from inspect plus presented and
|
|
10446
|
+
approvedContent} only through a native approval question asked receipt-shaped:
|
|
10447
|
+
header Workit decision: <purpose>, the same label repeated in the question
|
|
10448
|
+
text (some host UIs do not render headers), exactly two options approved and
|
|
10449
|
+
rejected; ask once, never re-ask to mint a receipt. task.close {outcome,
|
|
10450
|
+
summary, decisionIds}. A completed native subagent run stops its bound worker
|
|
10451
|
+
by itself; if a worker strands in cancelling with its run verifiably over,
|
|
10452
|
+
repeat worker.cancel {reason} to confirm the stop. Check and review evidence
|
|
10453
|
+
without a bound candidate goes stale; findings close only as fixed with passing
|
|
10454
|
+
verification, dismissed with supporting evidence, or deferred under an approved
|
|
10455
|
+
limitation. Use only the
|
|
10456
|
+
shared operations for task, policy, evidence, finding, decision, worker, writer,
|
|
10457
|
+
and state changes. Authority is bounded by the requested scope, current
|
|
10458
|
+
revision, caller/session provenance, and observed capabilities. Never claim host
|
|
10459
|
+
enforcement or evidence that the host cannot provide. Shell-executed writes are
|
|
10460
|
+
unattested agent-guided work even when a writer is held. Preserve unresolved
|
|
10461
|
+
requirements, gaps, and uncertain workers. Skill routing: slash aliases /wk-*
|
|
10462
|
+
always load on demand. Model-invoked: new instructions, interruptions, or
|
|
10463
|
+
forgotten items mid-task → load workit-steer before responding; about to open
|
|
10464
|
+
a PR or fresh from implementing → load workit-deslop; CI red → load
|
|
10465
|
+
workit-green-run; small-looking change before close or merge → load
|
|
10466
|
+
workit-blast-radius; ambiguous, consequential, or disputed proposal before
|
|
10467
|
+
recommending → load workit-challenge; spec or plan needs a flow diagram →
|
|
10468
|
+
load workit-diagram, a UI sketch → load workit-mockup. Load the skill; never
|
|
10469
|
+
act from memory of it.
|
|
10470
|
+
`.trim();
|
|
10471
|
+
|
|
10383
10472
|
// packages/workit-core/src/core/task-context.ts
|
|
10384
10473
|
function reconcileResume(view, observations = []) {
|
|
10385
10474
|
if (observations.length > 0)
|
|
@@ -10389,7 +10478,7 @@ function reconcileResume(view, observations = []) {
|
|
|
10389
10478
|
return candidate;
|
|
10390
10479
|
const staleEvidenceIds = evaluateEvidence(view.task, candidate.data).filter((entry) => entry.status === "stale").map((entry) => entry.evidenceId);
|
|
10391
10480
|
const blockers = [...view.task.progress.blockers];
|
|
10392
|
-
if (view.task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
10481
|
+
if (view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
10393
10482
|
blockers.push({
|
|
10394
10483
|
reason: "worker state requires reconciliation",
|
|
10395
10484
|
dependentAction: "resume",
|
|
@@ -10439,6 +10528,11 @@ function compactTaskContext(view) {
|
|
|
10439
10528
|
digest: entry.data.digest,
|
|
10440
10529
|
references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
|
|
10441
10530
|
}));
|
|
10531
|
+
const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
|
|
10532
|
+
id: method.id,
|
|
10533
|
+
assurance: method.assurance,
|
|
10534
|
+
reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
|
|
10535
|
+
})) : [];
|
|
10442
10536
|
const gaps = Array.from(new Set([
|
|
10443
10537
|
...view.evidence.filter((entry) => entry.status === "stale").map((entry) => `stale evidence: ${entry.evidenceId}`),
|
|
10444
10538
|
...view.requirements.filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
|
|
@@ -10448,15 +10542,18 @@ function compactTaskContext(view) {
|
|
|
10448
10542
|
objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
|
|
10449
10543
|
status: view.task.status,
|
|
10450
10544
|
decisions,
|
|
10545
|
+
methods,
|
|
10451
10546
|
gaps,
|
|
10452
10547
|
nextAction: compactText(view.task.progress.nextAction, COMPACT_TEXT_BYTES)
|
|
10453
10548
|
};
|
|
10454
10549
|
let encoded = JSON.stringify(context);
|
|
10455
|
-
while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.gaps.length)) {
|
|
10550
|
+
while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
|
|
10456
10551
|
if (context.gaps.length)
|
|
10457
10552
|
context.gaps.pop();
|
|
10458
|
-
else
|
|
10553
|
+
else if (context.decisions.length)
|
|
10459
10554
|
context.decisions.pop();
|
|
10555
|
+
else
|
|
10556
|
+
context.methods.pop();
|
|
10460
10557
|
encoded = JSON.stringify(context);
|
|
10461
10558
|
}
|
|
10462
10559
|
if (compactBytes(encoded) > COMPACT_MAX_BYTES) {
|
|
@@ -10472,50 +10569,6 @@ var exportDigest = (bundle) => sha256({
|
|
|
10472
10569
|
sourceWorkspaceId: bundle.sourceWorkspaceId,
|
|
10473
10570
|
task: bundle.task
|
|
10474
10571
|
});
|
|
10475
|
-
// packages/workit-core/src/core/methods.ts
|
|
10476
|
-
var invariantBootstrap = () => `
|
|
10477
|
-
Workit keeps one accountable lead and one shared task state. Inspect current task
|
|
10478
|
-
state before acting. An empty task.list means no session yet, not permission to
|
|
10479
|
-
skip Workit; for user-requested product, debug, or behavior work with no active
|
|
10480
|
-
or paused task, run task.start then policy.assess before other product mutations.
|
|
10481
|
-
Omitted expectedRevision, expectedWorkspaceRevision, and writer workerId default
|
|
10482
|
-
to the current records; explicit values are still concurrency-checked, so never
|
|
10483
|
-
copy revisions between calls. Happy path: task.list, task.start {intent} with no
|
|
10484
|
-
revisions, policy.assess {assessment: {facts, signals, consequences,
|
|
10485
|
-
verification}} where facts are inferred or observed with file refs,
|
|
10486
|
-
writer.acquire {taskId} before product writes, evidence.record {evidence} where
|
|
10487
|
-
check and review kinds auto-bind the current tree and need no digests. Record
|
|
10488
|
-
self-review requirements with kind review and reviewContext matching your own
|
|
10489
|
-
host session; the lead may also record checks. Fresh-context review requires a
|
|
10490
|
-
session distinct from the task creator and every other evidence recorder. Record
|
|
10491
|
-
verification evidence last: later evidence or commits can reopen fixed findings
|
|
10492
|
-
for re-verification, so verify, resolve, then close with nothing in between.
|
|
10493
|
-
decision.record {binding with taskId/workspaceId from inspect plus presented and
|
|
10494
|
-
approvedContent} only through a native approval question asked receipt-shaped:
|
|
10495
|
-
header Workit decision: <purpose>, the same label repeated in the question
|
|
10496
|
-
text (some host UIs do not render headers), exactly two options approved and
|
|
10497
|
-
rejected; ask once, never re-ask to mint a receipt. task.close {outcome,
|
|
10498
|
-
summary, decisionIds}. A completed native subagent run stops its bound worker
|
|
10499
|
-
by itself; if a worker strands in cancelling with its run verifiably over,
|
|
10500
|
-
repeat worker.cancel {reason} to confirm the stop. Check and review evidence
|
|
10501
|
-
without a bound candidate goes stale; findings close only as fixed with passing
|
|
10502
|
-
verification, dismissed with supporting evidence, or deferred under an approved
|
|
10503
|
-
limitation. Use only the
|
|
10504
|
-
shared operations for task, policy, evidence, finding, decision, worker, writer,
|
|
10505
|
-
and state changes. Authority is bounded by the requested scope, current
|
|
10506
|
-
revision, caller/session provenance, and observed capabilities. Never claim host
|
|
10507
|
-
enforcement or evidence that the host cannot provide. Shell-executed writes are
|
|
10508
|
-
unattested agent-guided work even when a writer is held. Preserve unresolved
|
|
10509
|
-
requirements, gaps, and uncertain workers. Skill routing: slash aliases /wk-*
|
|
10510
|
-
always load on demand. Model-invoked: new instructions, interruptions, or
|
|
10511
|
-
forgotten items mid-task → load workit-steer before responding; about to open
|
|
10512
|
-
a PR or fresh from implementing → load workit-deslop; CI red → load
|
|
10513
|
-
workit-green-run; small-looking change before close or merge → load
|
|
10514
|
-
workit-blast-radius; ambiguous, consequential, or disputed proposal before
|
|
10515
|
-
recommending → load workit-challenge; spec or plan needs a flow diagram →
|
|
10516
|
-
load workit-diagram, a UI sketch → load workit-mockup. Load the skill; never
|
|
10517
|
-
act from memory of it.
|
|
10518
|
-
`.trim();
|
|
10519
10572
|
// packages/workit-core/src/core/external-action.ts
|
|
10520
10573
|
var externalActionSchema = discriminatedUnion("operation", [
|
|
10521
10574
|
object({
|
|
@@ -11104,10 +11157,10 @@ var applyWorkerLifecycle = (input) => {
|
|
|
11104
11157
|
const notStarted = input.dispatch === true;
|
|
11105
11158
|
if (notStarted && (input.state !== "stopped" || input.session !== null))
|
|
11106
11159
|
return failure("invalid_input", "a never-dispatched worker stops with no session");
|
|
11107
|
-
if (notStarted &&
|
|
11108
|
-
return failure("invalid_transition", "worker is no longer awaiting dispatch");
|
|
11109
|
-
if (notStarted && (entry.data.session !== null || entry.data.state === "running"))
|
|
11160
|
+
if (notStarted && entry.data.session !== null)
|
|
11110
11161
|
return failure("invalid_transition", "worker already has an observed session");
|
|
11162
|
+
if (notStarted && !["dispatching", "cancelling"].includes(entry.data.state))
|
|
11163
|
+
return failure("invalid_transition", "worker launch was not claimed");
|
|
11111
11164
|
if ((input.state === "running" || input.state === "cancelling" || input.state === "stopped" && !notStarted) && !input.session)
|
|
11112
11165
|
return failure("invalid_input", "running worker observations require a session");
|
|
11113
11166
|
if (entry.data.session && !sameValue(entry.data.session, input.session))
|
|
@@ -11494,7 +11547,7 @@ class WorkitCore {
|
|
|
11494
11547
|
activeWorkerBlocker(task, workspace) {
|
|
11495
11548
|
if (workspace.writer)
|
|
11496
11549
|
return failure("recovery_required", "writer ownership must be released first");
|
|
11497
|
-
if (task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
11550
|
+
if (task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(entry.data.state)))
|
|
11498
11551
|
return failure("recovery_required", "worker state requires reconciliation");
|
|
11499
11552
|
return null;
|
|
11500
11553
|
}
|
|
@@ -12258,7 +12311,12 @@ class WorkitCore {
|
|
|
12258
12311
|
return failure("invalid_transition", "worker is not awaiting dispatch");
|
|
12259
12312
|
return success(mutation.revision, null, {
|
|
12260
12313
|
...current,
|
|
12261
|
-
workers: current.workers.map((item) => item.id === input.workerId ? {
|
|
12314
|
+
workers: current.workers.map((item) => item.id === input.workerId ? {
|
|
12315
|
+
...item,
|
|
12316
|
+
recordedAt: mutation.now,
|
|
12317
|
+
provenance: attested.data,
|
|
12318
|
+
data: { ...item.data, state: "dispatching" }
|
|
12319
|
+
} : item)
|
|
12262
12320
|
});
|
|
12263
12321
|
}
|
|
12264
12322
|
});
|
|
@@ -12727,7 +12785,7 @@ class WorkitCore {
|
|
|
12727
12785
|
const base = reconcileResume(effectiveView);
|
|
12728
12786
|
if (!base.ok)
|
|
12729
12787
|
return base;
|
|
12730
|
-
const needsReconciliation = view.task.workers.some((entry) => ["running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
|
|
12788
|
+
const needsReconciliation = view.task.workers.some((entry) => ["dispatching", "running", "cancelling", "unknown"].includes(observedStates.get(entry.id) ?? entry.data.state));
|
|
12731
12789
|
const blockers = [...base.data.blockers];
|
|
12732
12790
|
if (needsReconciliation && !blockers.some((entry) => entry.reason === "worker state requires reconciliation"))
|
|
12733
12791
|
blockers.push({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cursor",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.9",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit Cursor plugin — shared MCP transport, native hooks, and fourteen method skills",
|
|
6
6
|
"keywords": [
|
|
@@ -44,8 +44,8 @@
|
|
|
44
44
|
"build": "bun scripts/build.ts"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@brainervirus/workit-core": "^1.0.
|
|
48
|
-
"@brainervirus/workit-mcp": "^1.0.
|
|
47
|
+
"@brainervirus/workit-core": "^1.0.9",
|
|
48
|
+
"@brainervirus/workit-mcp": "^1.0.9"
|
|
49
49
|
},
|
|
50
50
|
"engines": {
|
|
51
51
|
"node": ">=24"
|
|
@@ -7,7 +7,9 @@ description: Use after creating a PR (auto-starts, drive default), when a PR nee
|
|
|
7
7
|
|
|
8
8
|
Babysit starts automatically on PR creation unless declined (`babysit:false`).
|
|
9
9
|
One babysitter per PR; never mutate PR topology (no rebase strategy changes,
|
|
10
|
-
no force-push).
|
|
10
|
+
no force-push). When a PR URL is observed from a route Workit did not enforce
|
|
11
|
+
(for example a raw `gh pr create` the host allowed), load this skill and drive
|
|
12
|
+
that PR anyway; do not claim the Workit route was enforced.
|
|
11
13
|
|
|
12
14
|
## Before method work
|
|
13
15
|
|