@brainervirus/workit-cursor 0.10.0 → 0.11.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/.cursor-plugin/plugin.json +1 -1
- package/README.md +1 -1
- package/assets/templates/execution-contract.md +3 -3
- package/assets/templates/plan-template.md +1 -1
- package/assets/templates/superpowers-doc-contract.md +2 -2
- package/dist/mcp-server.js +317 -46
- package/package.json +2 -2
- package/rules/ask-question-only.mdc +1 -1
- package/skills/wk-implement/SKILL.md +19 -5
- package/vendor/superpowers/skills/executing-plans/SKILL.md +2 -2
- package/vendor/superpowers/skills/subagent-driven-development/SKILL.md +2 -0
package/README.md
CHANGED
|
@@ -46,7 +46,7 @@ npx @brainervirus/workit-cli init
|
|
|
46
46
|
|
|
47
47
|
## Host limitations
|
|
48
48
|
|
|
49
|
-
Cursor adapts workit through policy-only confirmation: approvals and lifecycle transitions are recorded as policy decisions (`attested: false`) rather than fabricated delegated identity
|
|
49
|
+
Cursor adapts workit through policy-only confirmation: approvals and lifecycle transitions are recorded as policy decisions (`attested: false`) rather than fabricated delegated identity; model selection remains a host-native action. Both execution modes are supported: **Subagent-driven** — `workit_plan_menu` returns a one-time `coordinator_lease`, `workit_delegate` mints a task-scoped `delegation_token` per task (fail-closed validation; only hashes persist), and the coordinator dispatches Cursor-native subagents that pass the token as `delegation_token` on mutation calls (the task token is revoked when the worker's progress line lands); **Inline** — `executing-plans` runs every task single-agent in the current session with no dispatch and no token minting. Approvals bind to the document's exact SHA-256 digest — editing an approved spec/plan invalidates the approval and forces a fresh reapproval. OpenCode records native `question` receipts and derives delegated status from session parentage (`parentID`), which Cursor does not have; see the root [README](../../README.md#host-capabilities) for the full host-capability matrix.
|
|
50
50
|
|
|
51
51
|
## Configuration
|
|
52
52
|
|
|
@@ -24,10 +24,10 @@ This session is a handoff destination for a continued plan. The originating sess
|
|
|
24
24
|
- Working state, briefs, ledgers, and review diffs live only under gitignored `<SDD_DIR>` in `docs/<slug>/sdd/` and use `workit_sdd_*` tools.
|
|
25
25
|
- Use native `todowrite` for visible task state as well as the gitignored ledger.
|
|
26
26
|
- Use native `question` for branch/stash choices and guarded external mutations; call mutation tools only after approval with `confirmed: true` (grounded in the recorded NativeChoiceEvidence).
|
|
27
|
-
- Flow-tool confirmations are never agent-typed booleans and never caller-supplied evidence objects: on OpenCode the plugin records the user's native-`question` answer as a host-observed one-use receipt (`attested: true`, `callID`, `selectedLabel`, `recordedAt`) consumed by `workit_spec_approve` / `workit_plan_approve` / `workit_plan_menu` — no evidence argument exists, and delegated worker status comes from host session parentage (`parentID`), never a caller `role` field. On Cursor, confirmations are policy-only (`attested: false`)
|
|
27
|
+
- Flow-tool confirmations are never agent-typed booleans and never caller-supplied evidence objects: on OpenCode the plugin records the user's native-`question` answer as a host-observed one-use receipt (`attested: true`, `callID`, `selectedLabel`, `recordedAt`) consumed by `workit_spec_approve` / `workit_plan_approve` / `workit_plan_menu` — no evidence argument exists, and delegated worker status comes from host session parentage (`parentID`), never a caller `role` field. On Cursor, confirmations are policy-only (`attested: false`), Subagent-driven execution is supported through the one-time `coordinator_lease` and per-task `delegation_token` minted by `workit_delegate` (fail-closed validation; no `parentID` identity exists on Cursor), and Inline runs single-agent in the current session.
|
|
28
28
|
- Delegated authority is direct-child-only: a worker is the session whose host `parentID` exactly equals the activating coordinator's recorded `coordinator_session_id`; missing, mismatched, or multi-owner lineage fails closed with `delegation_lineage_denied`, and nested `opencode` launches are denied during active delegated work. An authorized child receives only the compact worker contract (execute the supplied brief, follow TDD, land one contiguous non-empty commit range, report results) — never coordinator guidance, `wk-implement`, or ledger management; coordinator bookkeeping via `workit_sdd_*` stays with the coordinator session.
|
|
29
29
|
- On Cursor, for every repository-scoped `workit_*` call, pass the active Cursor workspace as `workspace_root`; never rely on the MCP process default.
|
|
30
|
-
- Use native `task` with only the built-in `explore` and `general` agents.
|
|
30
|
+
- Use native `task` with only the built-in `explore` and `general` agents (OpenCode); on Cursor, Subagent-driven dispatches Cursor-native subagents with a task `delegation_token`, Inline runs single-agent in the current session.
|
|
31
31
|
|
|
32
32
|
## Flow gates (HARD)
|
|
33
33
|
|
|
@@ -51,7 +51,7 @@ For each top-level task absent from `completed_task_ids`:
|
|
|
51
51
|
|
|
52
52
|
1. Mark it `in_progress` with `todowrite`.
|
|
53
53
|
2. Create a working-state brief with `workit_sdd_task_brief` and `confirmed: true`.
|
|
54
|
-
3.
|
|
54
|
+
3. Route by the approved execution mode: **Subagent-driven** delegates read-only discovery to an `explore` (OpenCode) agent, delegates implementation to a fresh `general` (OpenCode) agent (on Cursor, the coordinator mints a `delegation_token` with `workit_delegate` and the subagent prompt carries the raw token — the worker passes it as `delegation_token` on its mutation calls, and appending progress revokes it), and the coordinator never edits product code; **Inline** executes every task in the current agent with no dispatch and no token minting. Product changes follow TDD.
|
|
55
55
|
4. Create a working-state diff with `workit_sdd_review_package` and `confirmed: true`.
|
|
56
56
|
5. Delegate spec-compliance review and code-quality review to separate `general` agents.
|
|
57
57
|
6. **Blocking** findings (Critical, Important, or spec-compliance) may trigger at most **two** fix+re-review rounds per task. **Advisory** findings (Minor, style, YAGNI, taste) never pause the loop — append them with `workit_sdd_append_advisory` (`--task <id> --text <text>`, `confirmed: true`) instead of an unrestricted file edit.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# <Feature> Implementation Plan
|
|
2
2
|
|
|
3
|
-
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
3
|
+
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. On Cursor the two paths are: **Subagent-driven** — Cursor-native subagents dispatched by the coordinator, each carrying a task-scoped `delegation_token` minted with `workit_delegate` from the one-time `coordinator_lease`; **Inline** — `executing-plans` in the current session, single-agent, no dispatch, no token minting. Steps use checkbox (`- [ ]`) syntax for tracking.
|
|
4
4
|
|
|
5
5
|
**Spec:** `docs/<slug>/spec.md`
|
|
6
6
|
**Branch:** `feature/<slug>`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# Superpowers document contract
|
|
2
2
|
|
|
3
|
-
Use OpenCode's native `question` for every bounded user choice. Give concise choices and allow a custom answer; if the tool is unavailable, ask one concise plain-text question. Use `skill` to load workflow and Superpowers skills, `todowrite` for task state, and `task` for delegated work.
|
|
3
|
+
Use OpenCode's native `question` for every bounded user choice. Give concise choices and allow a custom answer; if the tool is unavailable, ask one concise plain-text question. Use `skill` to load workflow and Superpowers skills, `todowrite` for task state, and `task` for delegated work (OpenCode) or the host-native equivalent — on Cursor, Subagent-driven dispatches Cursor-native subagents with a task `delegation_token`, Inline runs single-agent in the current session.
|
|
4
4
|
|
|
5
5
|
## Tracked document layout
|
|
6
6
|
|
|
@@ -35,7 +35,7 @@ Before writing **Branch:** into a new spec or plan, call `workit_docs_branch` an
|
|
|
35
35
|
- Commits use `wk-commit` after its native `question` confirmation.
|
|
36
36
|
- Continuation uses `wk-handoff`, whose `workit_handoff_session` creates and seeds the OpenCode session automatically.
|
|
37
37
|
- Never use worktrees. Resolve the declared branch with `workit_resolve_branch`, preview dirty-tree stash choices with `question`, and apply an approved in-place checkout through `workit_branch_setup` with `confirmed: true` (grounded in the recorded NativeChoiceEvidence).
|
|
38
|
-
- Flow-tool confirmations are never agent-typed booleans and never caller-supplied evidence objects: on OpenCode the plugin records the user's native-`question` answer as a host-observed one-use receipt (`attested: true`, `callID`, `selectedLabel`, `recordedAt`) consumed by `workit_spec_approve` / `workit_plan_approve` / `workit_plan_menu` — no evidence argument exists, and delegated worker status comes from host session parentage (`parentID`), never a caller `role` field. On Cursor, confirmations are policy-only (`attested: false`)
|
|
38
|
+
- Flow-tool confirmations are never agent-typed booleans and never caller-supplied evidence objects: on OpenCode the plugin records the user's native-`question` answer as a host-observed one-use receipt (`attested: true`, `callID`, `selectedLabel`, `recordedAt`) consumed by `workit_spec_approve` / `workit_plan_approve` / `workit_plan_menu` — no evidence argument exists, and delegated worker status comes from host session parentage (`parentID`), never a caller `role` field. On Cursor, confirmations are policy-only (`attested: false`), Subagent-driven execution is supported through the one-time `coordinator_lease` and per-task `delegation_token` minted by `workit_delegate` (fail-closed validation; no `parentID` identity exists on Cursor), and Inline runs single-agent in the current session.
|
|
39
39
|
- Keep all SDD state under the gitignored `docs/<slug>/sdd/`; use `workit_sdd_context` and the registered `workit_sdd_*` tools.
|
|
40
40
|
- After implementation, use `question` before an approved stash reapply through `workit_branch_setup` with `confirmed: true`.
|
|
41
41
|
|
package/dist/mcp-server.js
CHANGED
|
@@ -24576,7 +24576,7 @@ import {
|
|
|
24576
24576
|
unlinkSync as unlinkSync3,
|
|
24577
24577
|
writeFileSync as writeFileSync6
|
|
24578
24578
|
} from "node:fs";
|
|
24579
|
-
import { createHash as createHash2 } from "node:crypto";
|
|
24579
|
+
import { createHash as createHash2, randomBytes } from "node:crypto";
|
|
24580
24580
|
import path20 from "node:path";
|
|
24581
24581
|
|
|
24582
24582
|
class HostReceiptStore {
|
|
@@ -24666,7 +24666,7 @@ class HostReceiptStore {
|
|
|
24666
24666
|
return { ok: true, receipt };
|
|
24667
24667
|
}
|
|
24668
24668
|
}
|
|
24669
|
-
var COORDINATOR_RECOVERY_TEXT,
|
|
24669
|
+
var COORDINATOR_RECOVERY_TEXT, MENU_CHOICES, err2 = (code, error2, details) => ({
|
|
24670
24670
|
ok: false,
|
|
24671
24671
|
code,
|
|
24672
24672
|
error: error2,
|
|
@@ -24714,18 +24714,39 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24714
24714
|
status: execution.status ?? "pending",
|
|
24715
24715
|
mode: execution.mode ?? null,
|
|
24716
24716
|
evidence: execution.evidence ?? null,
|
|
24717
|
-
coordinator_session_id: execution.coordinator_session_id ?? null
|
|
24717
|
+
coordinator_session_id: execution.coordinator_session_id ?? null,
|
|
24718
|
+
...execution.delegation !== undefined ? { delegation: normalizeDelegation(execution.delegation) } : {}
|
|
24718
24719
|
},
|
|
24719
24720
|
handoff_destination: p.handoff_destination ?? false,
|
|
24720
24721
|
updated_at: p.updated_at ?? Date.now()
|
|
24721
24722
|
};
|
|
24722
|
-
},
|
|
24723
|
+
}, normalizeDelegation = (v) => {
|
|
24724
|
+
if (v === null || v === undefined)
|
|
24725
|
+
return null;
|
|
24726
|
+
if (!isRecord(v))
|
|
24727
|
+
return null;
|
|
24728
|
+
const hash = (x) => typeof x === "string" && HEX64_RE.test(x) ? x : null;
|
|
24729
|
+
return {
|
|
24730
|
+
coordinator_lease_hash: hash(v.coordinator_lease_hash),
|
|
24731
|
+
active_task_id: typeof v.active_task_id === "number" && Number.isSafeInteger(v.active_task_id) ? v.active_task_id : null,
|
|
24732
|
+
token_hash: hash(v.token_hash),
|
|
24733
|
+
token_workspace: typeof v.token_workspace === "string" ? v.token_workspace : null,
|
|
24734
|
+
token_slug: typeof v.token_slug === "string" ? v.token_slug : null,
|
|
24735
|
+
status: v.status === "active" ? "active" : "revoked"
|
|
24736
|
+
};
|
|
24737
|
+
}, delegationHash = (raw) => createHash2("sha256").update(raw).digest("hex"), emptyState = (slug) => ({
|
|
24723
24738
|
slug,
|
|
24724
24739
|
activated: false,
|
|
24725
24740
|
spec: { path: "", status: "draft", evidence: null, approved_digest: null },
|
|
24726
24741
|
plan: { path: "", status: "draft", evidence: null, approved_digest: null },
|
|
24727
24742
|
menu: { presented: false, chosen: "", evidence: null },
|
|
24728
|
-
execution: {
|
|
24743
|
+
execution: {
|
|
24744
|
+
status: "pending",
|
|
24745
|
+
mode: null,
|
|
24746
|
+
evidence: null,
|
|
24747
|
+
coordinator_session_id: null,
|
|
24748
|
+
delegation: null
|
|
24749
|
+
},
|
|
24729
24750
|
handoff_destination: false,
|
|
24730
24751
|
updated_at: Date.now()
|
|
24731
24752
|
}), readFlowState = (root, slug) => {
|
|
@@ -24856,7 +24877,8 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24856
24877
|
status: execRaw?.status ?? "pending",
|
|
24857
24878
|
mode: execRaw?.mode ?? null,
|
|
24858
24879
|
evidence: execRaw?.evidence ?? null,
|
|
24859
|
-
coordinator_session_id: execRaw?.coordinator_session_id ?? null
|
|
24880
|
+
coordinator_session_id: execRaw?.coordinator_session_id ?? null,
|
|
24881
|
+
...execRaw?.delegation !== undefined ? { delegation: normalizeDelegation(execRaw.delegation) } : {}
|
|
24860
24882
|
},
|
|
24861
24883
|
handoff_destination: parsed.handoff_destination ?? false,
|
|
24862
24884
|
updated_at: parsed.updated_at ?? Date.now()
|
|
@@ -24983,7 +25005,13 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24983
25005
|
spec: { ...state.spec, status: "draft", evidence: null, approved_digest: null },
|
|
24984
25006
|
plan: { ...state.plan, status: "draft", evidence: null, approved_digest: null },
|
|
24985
25007
|
menu: { presented: false, chosen: "", evidence: null },
|
|
24986
|
-
execution: {
|
|
25008
|
+
execution: {
|
|
25009
|
+
status: "pending",
|
|
25010
|
+
mode: null,
|
|
25011
|
+
evidence: null,
|
|
25012
|
+
coordinator_session_id: null,
|
|
25013
|
+
...state.execution.delegation !== undefined ? { delegation: null } : {}
|
|
25014
|
+
},
|
|
24987
25015
|
handoff_destination: false,
|
|
24988
25016
|
updated_at: Date.now()
|
|
24989
25017
|
}), resetForPlanDrift = (state) => ({
|
|
@@ -25029,7 +25057,12 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
25029
25057
|
coordinator_session_id: null
|
|
25030
25058
|
};
|
|
25031
25059
|
}
|
|
25032
|
-
return {
|
|
25060
|
+
return {
|
|
25061
|
+
status: "pending",
|
|
25062
|
+
mode: null,
|
|
25063
|
+
evidence: null,
|
|
25064
|
+
coordinator_session_id: null
|
|
25065
|
+
};
|
|
25033
25066
|
}, normalizeCompatibility = (root, slug, parsed, state) => {
|
|
25034
25067
|
if (!isRecord(parsed) || !("execution" in parsed)) {
|
|
25035
25068
|
const derived = deriveLegacyExecution(root, slug, state);
|
|
@@ -25310,7 +25343,12 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
25310
25343
|
spec: { path: specPath, status: "draft", evidence: null, approved_digest: null },
|
|
25311
25344
|
plan: { path: planPath, status: "draft", evidence: null, approved_digest: null },
|
|
25312
25345
|
menu: { presented: false, chosen: "", evidence: null },
|
|
25313
|
-
execution: {
|
|
25346
|
+
execution: {
|
|
25347
|
+
status: "pending",
|
|
25348
|
+
mode: null,
|
|
25349
|
+
evidence: null,
|
|
25350
|
+
coordinator_session_id: null
|
|
25351
|
+
},
|
|
25314
25352
|
handoff_destination: false,
|
|
25315
25353
|
updated_at: Date.now()
|
|
25316
25354
|
});
|
|
@@ -25435,16 +25473,16 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
25435
25473
|
if (typeof choice !== "string" || !MENU_CHOICES.includes(choice)) {
|
|
25436
25474
|
return err2("menu_choice_invalid", `invalid menu choice: ${JSON.stringify(choice)}`);
|
|
25437
25475
|
}
|
|
25438
|
-
if (recorded.evidence.host === "cursor" && choice === "subagent-driven") {
|
|
25439
|
-
return err2("unsupported_mode", CURSOR_SUBAGENT_UNSUPPORTED_TEXT);
|
|
25440
|
-
}
|
|
25441
25476
|
if (recorded.evidence.host === "opencode" && !sameChoiceLabel(recorded.evidence.selectedLabel, choice)) {
|
|
25442
25477
|
return err2("evidence_mismatch", `evidence selectedLabel ${JSON.stringify(recorded.evidence.selectedLabel)} does not match choice ${JSON.stringify(choice)}`);
|
|
25443
25478
|
}
|
|
25444
25479
|
const doc2 = resolveDoc(root, slug, planPath, "plan");
|
|
25445
25480
|
if (!doc2.ok)
|
|
25446
25481
|
return err2("path_invalid", doc2.error);
|
|
25447
|
-
|
|
25482
|
+
const cursorSubagent = recorded.evidence.host === "cursor" && choice === "subagent-driven";
|
|
25483
|
+
const lease = cursorSubagent ? randomBytes(32).toString("hex") : null;
|
|
25484
|
+
const leaseHash = lease === null ? null : delegationHash(lease);
|
|
25485
|
+
const result = readModifyWrite(root, slug, (state) => {
|
|
25448
25486
|
if (state.spec.status !== "approved")
|
|
25449
25487
|
return err2("spec_not_approved", "spec must be approved before the execution menu");
|
|
25450
25488
|
if (state.plan.status !== "approved")
|
|
@@ -25454,6 +25492,14 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
25454
25492
|
}
|
|
25455
25493
|
const executing = choice === "subagent-driven" || choice === "inline";
|
|
25456
25494
|
const coordinatorSessionId = choice === "subagent-driven" && recorded.evidence.host === "opencode" ? ctx?.sessionId ?? null : null;
|
|
25495
|
+
const delegation = cursorSubagent ? {
|
|
25496
|
+
coordinator_lease_hash: leaseHash,
|
|
25497
|
+
active_task_id: null,
|
|
25498
|
+
token_hash: null,
|
|
25499
|
+
token_workspace: null,
|
|
25500
|
+
token_slug: null,
|
|
25501
|
+
status: "active"
|
|
25502
|
+
} : state.execution.delegation;
|
|
25457
25503
|
return {
|
|
25458
25504
|
ok: true,
|
|
25459
25505
|
next: {
|
|
@@ -25464,17 +25510,133 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
25464
25510
|
status: "active",
|
|
25465
25511
|
mode: choice,
|
|
25466
25512
|
evidence: recorded.evidence,
|
|
25467
|
-
coordinator_session_id: coordinatorSessionId
|
|
25513
|
+
coordinator_session_id: coordinatorSessionId,
|
|
25514
|
+
delegation
|
|
25468
25515
|
} : {
|
|
25469
25516
|
status: "pending",
|
|
25470
25517
|
mode: null,
|
|
25471
25518
|
evidence: recorded.evidence,
|
|
25472
|
-
coordinator_session_id: null
|
|
25519
|
+
coordinator_session_id: null,
|
|
25520
|
+
delegation
|
|
25521
|
+
},
|
|
25522
|
+
updated_at: Date.now()
|
|
25523
|
+
}
|
|
25524
|
+
};
|
|
25525
|
+
});
|
|
25526
|
+
if (!result.ok)
|
|
25527
|
+
return result;
|
|
25528
|
+
return cursorSubagent && lease !== null ? { ok: true, coordinator_lease: lease } : { ok: true };
|
|
25529
|
+
}, mintDelegateToken = (root, slug, planPath, taskId, coordinatorLease) => {
|
|
25530
|
+
if (typeof taskId !== "number" || !Number.isSafeInteger(taskId) || taskId <= 0) {
|
|
25531
|
+
return err2("task_invalid", `task id must be a positive safe integer: ${JSON.stringify(taskId)}`);
|
|
25532
|
+
}
|
|
25533
|
+
if (typeof coordinatorLease !== "string" || coordinatorLease === "") {
|
|
25534
|
+
return err2("coordinator_lease_invalid", "a coordinator lease is required to mint a delegation token");
|
|
25535
|
+
}
|
|
25536
|
+
const doc2 = resolveDoc(root, slug, planPath, "plan");
|
|
25537
|
+
if (!doc2.ok)
|
|
25538
|
+
return err2("path_invalid", doc2.error);
|
|
25539
|
+
const token = randomBytes(32).toString("hex");
|
|
25540
|
+
const tokenHash = delegationHash(token);
|
|
25541
|
+
const result = readModifyWrite(root, slug, (state) => {
|
|
25542
|
+
const exec = state.execution;
|
|
25543
|
+
if (exec.status !== "active" || exec.mode !== "subagent-driven") {
|
|
25544
|
+
return err2("flow_not_active", "a delegation token requires an active subagent-driven execution");
|
|
25545
|
+
}
|
|
25546
|
+
const delegation = exec.delegation;
|
|
25547
|
+
if (!delegation || delegation.status !== "active" || !delegation.coordinator_lease_hash) {
|
|
25548
|
+
return err2("coordinator_lease_invalid", "this flow has no active coordinator lease — record the execution menu with Cursor subagent-driven first");
|
|
25549
|
+
}
|
|
25550
|
+
if (delegation.coordinator_lease_hash !== delegationHash(coordinatorLease)) {
|
|
25551
|
+
return err2("coordinator_lease_invalid", "the supplied coordinator lease does not match the flow's recorded lease");
|
|
25552
|
+
}
|
|
25553
|
+
if (state.plan.status !== "approved") {
|
|
25554
|
+
return err2("plan_not_approved", "plan must be approved before delegating a task");
|
|
25555
|
+
}
|
|
25556
|
+
let planText;
|
|
25557
|
+
try {
|
|
25558
|
+
planText = readFileSync13(doc2.path, "utf8");
|
|
25559
|
+
} catch {
|
|
25560
|
+
return err2("plan_missing", `plan not found: ${planPath}`);
|
|
25561
|
+
}
|
|
25562
|
+
if (!parseTasksFromPlan(planText).some((t) => t.id === taskId)) {
|
|
25563
|
+
return err2("task_invalid", `task ${taskId} does not exist in the approved plan`);
|
|
25564
|
+
}
|
|
25565
|
+
if (ledgerCompletion(root, slug).completed.includes(taskId)) {
|
|
25566
|
+
return err2("task_completed", `task ${taskId} is already completed in the SDD ledger`);
|
|
25567
|
+
}
|
|
25568
|
+
return {
|
|
25569
|
+
ok: true,
|
|
25570
|
+
next: {
|
|
25571
|
+
...state,
|
|
25572
|
+
execution: {
|
|
25573
|
+
...exec,
|
|
25574
|
+
delegation: {
|
|
25575
|
+
...delegation,
|
|
25576
|
+
active_task_id: taskId,
|
|
25577
|
+
token_hash: tokenHash,
|
|
25578
|
+
token_workspace: root,
|
|
25579
|
+
token_slug: slug,
|
|
25580
|
+
status: "active"
|
|
25581
|
+
}
|
|
25473
25582
|
},
|
|
25474
25583
|
updated_at: Date.now()
|
|
25475
25584
|
}
|
|
25476
25585
|
};
|
|
25477
25586
|
});
|
|
25587
|
+
if (!result.ok)
|
|
25588
|
+
return result;
|
|
25589
|
+
return { ok: true, token };
|
|
25590
|
+
}, validateDelegateToken = (root, token) => {
|
|
25591
|
+
if (typeof token !== "string" || token === "") {
|
|
25592
|
+
return err2("delegation_token_invalid", "a delegation token is required");
|
|
25593
|
+
}
|
|
25594
|
+
const hash = delegationHash(token);
|
|
25595
|
+
let entries = [];
|
|
25596
|
+
try {
|
|
25597
|
+
entries = readdirSync7(path20.join(root, "docs"), { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
25598
|
+
} catch {
|
|
25599
|
+
return err2("delegation_token_invalid", `no flows exist under ${JSON.stringify(root)}`);
|
|
25600
|
+
}
|
|
25601
|
+
for (const slug of entries) {
|
|
25602
|
+
try {
|
|
25603
|
+
const state = readFlowState(root, slug);
|
|
25604
|
+
const delegation = state.execution.delegation;
|
|
25605
|
+
if (!delegation || !delegation.token_hash)
|
|
25606
|
+
continue;
|
|
25607
|
+
if (delegation.token_hash !== hash)
|
|
25608
|
+
continue;
|
|
25609
|
+
if (delegation.token_workspace !== root || delegation.token_slug !== slug)
|
|
25610
|
+
continue;
|
|
25611
|
+
if (delegation.status !== "active" || state.execution.status !== "active" || state.execution.mode !== "subagent-driven" || delegation.active_task_id === null) {
|
|
25612
|
+
return err2("delegation_token_revoked", "the delegation token is no longer active for this flow");
|
|
25613
|
+
}
|
|
25614
|
+
return {
|
|
25615
|
+
ok: true,
|
|
25616
|
+
context: { slug, taskId: delegation.active_task_id, hostWorkspace: root }
|
|
25617
|
+
};
|
|
25618
|
+
} catch {}
|
|
25619
|
+
}
|
|
25620
|
+
return err2("delegation_token_invalid", "the delegation token does not match any active flow token in this workspace");
|
|
25621
|
+
}, revokeDelegateToken = (root, slug, taskId) => {
|
|
25622
|
+
if (typeof taskId !== "number" || !Number.isSafeInteger(taskId) || taskId <= 0) {
|
|
25623
|
+
return err2("task_invalid", `task id must be a positive safe integer: ${JSON.stringify(taskId)}`);
|
|
25624
|
+
}
|
|
25625
|
+
return readModifyWrite(root, slug, (state) => {
|
|
25626
|
+
const exec = state.execution;
|
|
25627
|
+
const delegation = exec.delegation;
|
|
25628
|
+
if (!delegation || delegation.status !== "active" || delegation.active_task_id !== taskId || !delegation.token_hash) {
|
|
25629
|
+
return err2("delegation_token_not_active", `no active delegation token for task ${taskId} in ${slug}`);
|
|
25630
|
+
}
|
|
25631
|
+
return {
|
|
25632
|
+
ok: true,
|
|
25633
|
+
next: {
|
|
25634
|
+
...state,
|
|
25635
|
+
execution: { ...exec, delegation: { ...delegation, status: "revoked" } },
|
|
25636
|
+
updated_at: Date.now()
|
|
25637
|
+
}
|
|
25638
|
+
};
|
|
25639
|
+
});
|
|
25478
25640
|
}, markHandoffDestination = (root, slug, planPath) => {
|
|
25479
25641
|
const doc2 = resolveDoc(root, slug, planPath, "plan");
|
|
25480
25642
|
if (!doc2.ok)
|
|
@@ -25656,7 +25818,6 @@ var init_flow_state = __esm(() => {
|
|
|
25656
25818
|
init_verify_project();
|
|
25657
25819
|
init_menu();
|
|
25658
25820
|
COORDINATOR_RECOVERY_TEXT = "A subagent-driven plan is active: coordinator product edits are blocked. " + "Delegate product mutations to an authenticated delegated worker via `task` / `wk-implement` instead of " + "editing in the coordinator session.";
|
|
25659
|
-
CURSOR_SUBAGENT_UNSUPPORTED_TEXT = "Cursor cannot execute subagent-driven plans: the MCP has no child-session " + "support. Choose Inline, Handoff, or a review option in this session, or " + "run the plan in OpenCode with `wk-implement`.";
|
|
25660
25821
|
MENU_CHOICES = [
|
|
25661
25822
|
"subagent-driven",
|
|
25662
25823
|
"inline",
|
|
@@ -25810,12 +25971,30 @@ var init_flow_state = __esm(() => {
|
|
|
25810
25971
|
});
|
|
25811
25972
|
|
|
25812
25973
|
// packages/workit-cursor/mcp/flow-evidence.ts
|
|
25813
|
-
var cursorQuestionEvidence = () => createCursorConfirmation(), CURSOR_HOST = "cursor",
|
|
25974
|
+
var cursorQuestionEvidence = () => createCursorConfirmation(), CURSOR_HOST = "cursor", cursorCoordinatorContext = (workspaceRoot) => ({
|
|
25814
25975
|
hostWorkspace: workspaceRoot,
|
|
25815
25976
|
role: "coordinator",
|
|
25816
25977
|
sessionId: `${CURSOR_HOST}:${workspaceRoot}`,
|
|
25817
25978
|
taskIdentity: undefined
|
|
25818
|
-
})
|
|
25979
|
+
}), cursorMutationContext = (workspaceRoot, delegationToken) => {
|
|
25980
|
+
if (delegationToken === undefined) {
|
|
25981
|
+
return { ok: true, context: cursorCoordinatorContext(workspaceRoot) };
|
|
25982
|
+
}
|
|
25983
|
+
const validated = validateDelegateToken(workspaceRoot, delegationToken);
|
|
25984
|
+
if (!validated.ok) {
|
|
25985
|
+
return { ok: false, code: validated.code, error: validated.error };
|
|
25986
|
+
}
|
|
25987
|
+
return {
|
|
25988
|
+
ok: true,
|
|
25989
|
+
slug: validated.context.slug,
|
|
25990
|
+
context: {
|
|
25991
|
+
hostWorkspace: workspaceRoot,
|
|
25992
|
+
role: "delegated",
|
|
25993
|
+
sessionId: `${CURSOR_HOST}:${workspaceRoot}:task-${validated.context.taskId}`,
|
|
25994
|
+
taskIdentity: String(validated.context.taskId)
|
|
25995
|
+
}
|
|
25996
|
+
};
|
|
25997
|
+
};
|
|
25819
25998
|
var init_flow_evidence = __esm(() => {
|
|
25820
25999
|
init_flow_state();
|
|
25821
26000
|
});
|
|
@@ -27874,7 +28053,7 @@ var logger, readServerVersion = () => {
|
|
|
27874
28053
|
if (!result.ok)
|
|
27875
28054
|
throw new Error(result.error);
|
|
27876
28055
|
return result.evidence;
|
|
27877
|
-
}, workspaceRootSchema, server, registerTool = (name, config2, cb) => {
|
|
28056
|
+
}, workspaceRootSchema, delegationTokenSchema, server, registerTool = (name, config2, cb) => {
|
|
27878
28057
|
server.registerTool(name, config2, async (args) => {
|
|
27879
28058
|
try {
|
|
27880
28059
|
return await cb(args);
|
|
@@ -27901,7 +28080,7 @@ var logger, readServerVersion = () => {
|
|
|
27901
28080
|
return jsonResult(withWorkspace(workspace_root, { error: resolved.error }));
|
|
27902
28081
|
}
|
|
27903
28082
|
const { workspace, slug } = resolved.layout;
|
|
27904
|
-
const result = transitionExecution(workspace, slug, plan_path, action, cursorConfirmation(),
|
|
28083
|
+
const result = transitionExecution(workspace, slug, plan_path, action, cursorConfirmation(), cursorCoordinatorContext(workspace));
|
|
27905
28084
|
if (result.ok === false) {
|
|
27906
28085
|
return jsonResult(withWorkspace(workspace_root, {
|
|
27907
28086
|
error: result.error,
|
|
@@ -27965,6 +28144,7 @@ var init_server3 = __esm(async () => {
|
|
|
27965
28144
|
process.exit(1);
|
|
27966
28145
|
});
|
|
27967
28146
|
workspaceRootSchema = exports_external.string().optional().default(() => process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd()).describe("Git repository root. Defaults to the launcher workspace (WORKFLOW_WORKSPACE_ROOT), then the Cursor workspace folder (${workspaceFolder}), then the process cwd.");
|
|
28147
|
+
delegationTokenSchema = exports_external.string().optional().describe("Task-scoped delegation token from workit_delegate for delegated workers.");
|
|
27968
28148
|
server = new McpServer({
|
|
27969
28149
|
name: "workit",
|
|
27970
28150
|
version: VERSION
|
|
@@ -28067,9 +28247,13 @@ var init_server3 = __esm(async () => {
|
|
|
28067
28247
|
body: exports_external.string().optional(),
|
|
28068
28248
|
draft: exports_external.boolean().optional(),
|
|
28069
28249
|
target_branch: exports_external.string().optional(),
|
|
28250
|
+
delegation_token: delegationTokenSchema,
|
|
28070
28251
|
workspace_root: workspaceRootSchema
|
|
28071
28252
|
}
|
|
28072
|
-
}, async ({ confirmed, title, body, draft, target_branch, workspace_root }) => {
|
|
28253
|
+
}, async ({ confirmed, title, body, draft, target_branch, delegation_token, workspace_root }) => {
|
|
28254
|
+
const identity = cursorMutationContext(workspace_root, delegation_token);
|
|
28255
|
+
if (!identity.ok)
|
|
28256
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28073
28257
|
if (!confirmed)
|
|
28074
28258
|
return jsonResult({ error: "confirmed: true required" });
|
|
28075
28259
|
const data = prCreate({
|
|
@@ -28131,9 +28315,13 @@ var init_server3 = __esm(async () => {
|
|
|
28131
28315
|
]).optional(),
|
|
28132
28316
|
normalize_only: exports_external.boolean().optional().describe("If true, only collapse duplicate ### headings under [Unreleased] (no new bullets)."),
|
|
28133
28317
|
path: exports_external.string().optional().describe("Defaults to CHANGELOG.md"),
|
|
28318
|
+
delegation_token: delegationTokenSchema,
|
|
28134
28319
|
workspace_root: workspaceRootSchema
|
|
28135
28320
|
}
|
|
28136
|
-
}, async ({ entries, normalize_only, path: changelogPath, workspace_root }) => {
|
|
28321
|
+
}, async ({ entries, normalize_only, path: changelogPath, delegation_token, workspace_root }) => {
|
|
28322
|
+
const identity = cursorMutationContext(workspace_root, delegation_token);
|
|
28323
|
+
if (!identity.ok)
|
|
28324
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28137
28325
|
const data = changelogApply({
|
|
28138
28326
|
entries,
|
|
28139
28327
|
path: changelogPath,
|
|
@@ -28221,9 +28409,13 @@ var init_server3 = __esm(async () => {
|
|
|
28221
28409
|
sdd_dir: exports_external.string().optional(),
|
|
28222
28410
|
target_branch: exports_external.string().optional(),
|
|
28223
28411
|
stash: exports_external.enum(["yes", "no"]).optional(),
|
|
28412
|
+
delegation_token: delegationTokenSchema,
|
|
28224
28413
|
workspace_root: workspaceRootSchema
|
|
28225
28414
|
}
|
|
28226
|
-
}, async ({ action, sdd_dir, target_branch, stash, workspace_root }) => {
|
|
28415
|
+
}, async ({ action, sdd_dir, target_branch, stash, delegation_token, workspace_root }) => {
|
|
28416
|
+
const identity = cursorMutationContext(workspace_root, delegation_token);
|
|
28417
|
+
if (!identity.ok)
|
|
28418
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28227
28419
|
const data = branchSetup({
|
|
28228
28420
|
action,
|
|
28229
28421
|
sdd_dir,
|
|
@@ -28255,13 +28447,23 @@ var init_server3 = __esm(async () => {
|
|
|
28255
28447
|
sdd_dir: exports_external.string(),
|
|
28256
28448
|
task_id: exports_external.number(),
|
|
28257
28449
|
section_text: exports_external.string(),
|
|
28450
|
+
delegation_token: delegationTokenSchema,
|
|
28258
28451
|
workspace_root: workspaceRootSchema
|
|
28259
28452
|
}
|
|
28260
|
-
}, async ({ sdd_dir, task_id, section_text, workspace_root }) => {
|
|
28453
|
+
}, async ({ sdd_dir, task_id, section_text, delegation_token, workspace_root }) => {
|
|
28454
|
+
const identity = cursorMutationContext(workspace_root, delegation_token);
|
|
28455
|
+
if (!identity.ok)
|
|
28456
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28261
28457
|
const slug = slugFromSddPath(sdd_dir);
|
|
28262
28458
|
if (!slug)
|
|
28263
28459
|
return jsonResult({ error: "could not derive slug — expected docs/<slug>/sdd/..." });
|
|
28264
|
-
|
|
28460
|
+
if (identity.slug !== undefined && identity.slug !== slug) {
|
|
28461
|
+
return jsonResult({
|
|
28462
|
+
error: `delegation token is bound to flow ${JSON.stringify(identity.slug)}, not ${JSON.stringify(slug)}`,
|
|
28463
|
+
code: "slug_mismatch"
|
|
28464
|
+
});
|
|
28465
|
+
}
|
|
28466
|
+
const gate = assertSddControlGates(workspace_root, slug, { requireMenu: true, requireDocs: true }, identity.context);
|
|
28265
28467
|
if (!gate.ok)
|
|
28266
28468
|
return jsonResult({ error: gate.error, code: gate.code });
|
|
28267
28469
|
const data = sddTaskBrief({
|
|
@@ -28278,13 +28480,23 @@ var init_server3 = __esm(async () => {
|
|
|
28278
28480
|
sdd_dir: exports_external.string(),
|
|
28279
28481
|
base_sha: exports_external.string(),
|
|
28280
28482
|
head_sha: exports_external.string(),
|
|
28483
|
+
delegation_token: delegationTokenSchema,
|
|
28281
28484
|
workspace_root: workspaceRootSchema
|
|
28282
28485
|
}
|
|
28283
|
-
}, async ({ sdd_dir, base_sha, head_sha, workspace_root }) => {
|
|
28486
|
+
}, async ({ sdd_dir, base_sha, head_sha, delegation_token, workspace_root }) => {
|
|
28487
|
+
const identity = cursorMutationContext(workspace_root, delegation_token);
|
|
28488
|
+
if (!identity.ok)
|
|
28489
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28284
28490
|
const slug = slugFromSddPath(sdd_dir);
|
|
28285
28491
|
if (!slug)
|
|
28286
28492
|
return jsonResult({ error: "could not derive slug — expected docs/<slug>/sdd/..." });
|
|
28287
|
-
|
|
28493
|
+
if (identity.slug !== undefined && identity.slug !== slug) {
|
|
28494
|
+
return jsonResult({
|
|
28495
|
+
error: `delegation token is bound to flow ${JSON.stringify(identity.slug)}, not ${JSON.stringify(slug)}`,
|
|
28496
|
+
code: "slug_mismatch"
|
|
28497
|
+
});
|
|
28498
|
+
}
|
|
28499
|
+
const gate = assertSddControlGates(workspace_root, slug, { requireMenu: true, requireDocs: true }, identity.context);
|
|
28288
28500
|
if (!gate.ok)
|
|
28289
28501
|
return jsonResult({ error: gate.error, code: gate.code });
|
|
28290
28502
|
const data = sddReviewPackage({
|
|
@@ -28302,18 +28514,31 @@ var init_server3 = __esm(async () => {
|
|
|
28302
28514
|
inputSchema: {
|
|
28303
28515
|
progress_path: exports_external.string(),
|
|
28304
28516
|
line: exports_external.string(),
|
|
28517
|
+
delegation_token: delegationTokenSchema,
|
|
28305
28518
|
workspace_root: workspaceRootSchema
|
|
28306
28519
|
}
|
|
28307
|
-
}, async ({ progress_path, line, workspace_root }) => {
|
|
28520
|
+
}, async ({ progress_path, line, delegation_token, workspace_root }) => {
|
|
28521
|
+
const identity = cursorMutationContext(workspace_root, delegation_token);
|
|
28522
|
+
if (!identity.ok)
|
|
28523
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28308
28524
|
const slug = slugFromSddPath(progress_path);
|
|
28309
28525
|
if (!slug)
|
|
28310
28526
|
return jsonResult({ error: "could not derive slug — expected docs/<slug>/sdd/..." });
|
|
28311
|
-
|
|
28527
|
+
if (identity.slug !== undefined && identity.slug !== slug) {
|
|
28528
|
+
return jsonResult({
|
|
28529
|
+
error: `delegation token is bound to flow ${JSON.stringify(identity.slug)}, not ${JSON.stringify(slug)}`,
|
|
28530
|
+
code: "slug_mismatch"
|
|
28531
|
+
});
|
|
28532
|
+
}
|
|
28533
|
+
const gate = assertSddControlGates(workspace_root, slug, { requireMenu: true, requireDocs: true }, identity.context);
|
|
28312
28534
|
if (!gate.ok)
|
|
28313
28535
|
return jsonResult({ error: gate.error, code: gate.code });
|
|
28314
28536
|
const data = sddAppendProgress({ progress_path, line, workspace_root });
|
|
28315
28537
|
if (data.error)
|
|
28316
28538
|
return jsonResult({ error: data.error });
|
|
28539
|
+
if (identity.context.role === "delegated" && identity.context.taskIdentity) {
|
|
28540
|
+
revokeDelegateToken(workspace_root, slug, Number(identity.context.taskIdentity));
|
|
28541
|
+
}
|
|
28317
28542
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
28318
28543
|
});
|
|
28319
28544
|
registerTool("workit_sdd_append_advisory", {
|
|
@@ -28328,7 +28553,7 @@ var init_server3 = __esm(async () => {
|
|
|
28328
28553
|
const slug = slugFromSddPath(advisories_path);
|
|
28329
28554
|
if (!slug)
|
|
28330
28555
|
return jsonResult({ error: "could not derive slug — expected docs/<slug>/sdd/..." });
|
|
28331
|
-
const gate = assertSddControlGates(workspace_root, slug, { requireMenu: true, requireDocs: true },
|
|
28556
|
+
const gate = assertSddControlGates(workspace_root, slug, { requireMenu: true, requireDocs: true }, cursorCoordinatorContext(workspace_root));
|
|
28332
28557
|
if (!gate.ok)
|
|
28333
28558
|
return jsonResult({ error: gate.error, code: gate.code });
|
|
28334
28559
|
const data = sddAppendAdvisory({ advisories_path, task_id, text, workspace_root });
|
|
@@ -28357,9 +28582,13 @@ var init_server3 = __esm(async () => {
|
|
|
28357
28582
|
spec_path: exports_external.string().optional(),
|
|
28358
28583
|
plan_path: exports_external.string().optional(),
|
|
28359
28584
|
confirmed: exports_external.boolean().optional(),
|
|
28585
|
+
delegation_token: delegationTokenSchema,
|
|
28360
28586
|
workspace_root: workspaceRootSchema
|
|
28361
28587
|
}
|
|
28362
|
-
}, async ({ action, slug, spec_path, plan_path, confirmed, workspace_root }) => {
|
|
28588
|
+
}, async ({ action, slug, spec_path, plan_path, confirmed, delegation_token, workspace_root }) => {
|
|
28589
|
+
const identity = cursorMutationContext(workspace_root, delegation_token);
|
|
28590
|
+
if (!identity.ok)
|
|
28591
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28363
28592
|
if (action === "migrate") {
|
|
28364
28593
|
const detect = detectLegacyDocs(workspace_root);
|
|
28365
28594
|
if (confirmed === undefined) {
|
|
@@ -28690,9 +28919,13 @@ var init_server3 = __esm(async () => {
|
|
|
28690
28919
|
minutes: exports_external.number(),
|
|
28691
28920
|
text: exports_external.string().optional(),
|
|
28692
28921
|
dateMs: exports_external.number().optional().describe("Epoch ms for work item date; omit for today in config timezone"),
|
|
28922
|
+
delegation_token: delegationTokenSchema,
|
|
28693
28923
|
workspace_root: workspaceRootSchema
|
|
28694
28924
|
}
|
|
28695
|
-
}, async ({ issueId, minutes, text, dateMs, workspace_root }) => {
|
|
28925
|
+
}, async ({ issueId, minutes, text, dateMs, delegation_token, workspace_root }) => {
|
|
28926
|
+
const identity = cursorMutationContext(workspace_root, delegation_token);
|
|
28927
|
+
if (!identity.ok)
|
|
28928
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28696
28929
|
const data = await logTime({
|
|
28697
28930
|
issueId,
|
|
28698
28931
|
minutes,
|
|
@@ -28723,9 +28956,13 @@ var init_server3 = __esm(async () => {
|
|
|
28723
28956
|
issueId: exports_external.string(),
|
|
28724
28957
|
markdown: exports_external.string(),
|
|
28725
28958
|
minutes: exports_external.number().optional(),
|
|
28959
|
+
delegation_token: delegationTokenSchema,
|
|
28726
28960
|
workspace_root: workspaceRootSchema
|
|
28727
28961
|
}
|
|
28728
|
-
}, async ({ confirmed, issueId, markdown, minutes, workspace_root }) => {
|
|
28962
|
+
}, async ({ confirmed, issueId, markdown, minutes, delegation_token, workspace_root }) => {
|
|
28963
|
+
const identity = cursorMutationContext(workspace_root, delegation_token);
|
|
28964
|
+
if (!identity.ok)
|
|
28965
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28729
28966
|
const data = await postUpdate({
|
|
28730
28967
|
confirmed,
|
|
28731
28968
|
issueId,
|
|
@@ -28790,7 +29027,7 @@ var init_server3 = __esm(async () => {
|
|
|
28790
29027
|
const { workspace, slug } = resolved.layout;
|
|
28791
29028
|
let effective = readEffectiveFlowState(workspace, slug);
|
|
28792
29029
|
if (!effective.ok && effective.code === "flow_not_activated") {
|
|
28793
|
-
const prepared = prepareFlowState(workspace, slug, { spec_path, plan_path },
|
|
29030
|
+
const prepared = prepareFlowState(workspace, slug, { spec_path, plan_path }, cursorCoordinatorContext(workspace));
|
|
28794
29031
|
if (!prepared.ok)
|
|
28795
29032
|
return jsonResult({ error: prepared.error, code: prepared.code });
|
|
28796
29033
|
effective = readEffectiveFlowState(workspace, slug);
|
|
@@ -28819,7 +29056,7 @@ var init_server3 = __esm(async () => {
|
|
|
28819
29056
|
if (!resolved.ok)
|
|
28820
29057
|
return jsonResult(withWorkspace(workspace_root, { error: resolved.error }));
|
|
28821
29058
|
const { workspace, slug } = resolved.layout;
|
|
28822
|
-
const result = transitionSpec(workspace, slug, spec_path, cursorConfirmation(),
|
|
29059
|
+
const result = transitionSpec(workspace, slug, spec_path, cursorConfirmation(), cursorCoordinatorContext(workspace));
|
|
28823
29060
|
if (result.ok === false)
|
|
28824
29061
|
return jsonResult({ error: result.error, code: result.code });
|
|
28825
29062
|
return jsonResult({ spec: spec_path, status: readFlowState(workspace, slug).spec.status });
|
|
@@ -28835,13 +29072,13 @@ var init_server3 = __esm(async () => {
|
|
|
28835
29072
|
if (!resolved.ok)
|
|
28836
29073
|
return jsonResult(withWorkspace(workspace_root, { error: resolved.error }));
|
|
28837
29074
|
const { workspace, slug } = resolved.layout;
|
|
28838
|
-
const result = transitionPlan(workspace, slug, plan_path, cursorConfirmation(),
|
|
29075
|
+
const result = transitionPlan(workspace, slug, plan_path, cursorConfirmation(), cursorCoordinatorContext(workspace));
|
|
28839
29076
|
if (result.ok === false)
|
|
28840
29077
|
return jsonResult({ error: result.error, code: result.code });
|
|
28841
29078
|
return jsonResult({ plan: plan_path, status: readFlowState(workspace, slug).plan.status });
|
|
28842
29079
|
});
|
|
28843
29080
|
registerTool("workit_plan_menu", {
|
|
28844
|
-
description: "Record the answered post-plan choice menu with the Cursor policy-only confirmation. subagent-driven
|
|
29081
|
+
description: "Record the answered post-plan choice menu with the Cursor policy-only confirmation. An accepted Cursor subagent-driven choice activates execution and returns a coordinator_lease (mint delegation tokens with it); there is no evidence argument.",
|
|
28845
29082
|
inputSchema: {
|
|
28846
29083
|
plan_path: exports_external.string(),
|
|
28847
29084
|
choice: exports_external.enum(["subagent-driven", "inline", "handoff", "review-spec", "review-plan"]),
|
|
@@ -28852,10 +29089,28 @@ var init_server3 = __esm(async () => {
|
|
|
28852
29089
|
if (!resolved.ok)
|
|
28853
29090
|
return jsonResult(withWorkspace(workspace_root, { error: resolved.error }));
|
|
28854
29091
|
const { workspace, slug } = resolved.layout;
|
|
28855
|
-
const result = recordMenuChoice(workspace, slug, plan_path, choice, cursorConfirmation(),
|
|
29092
|
+
const result = recordMenuChoice(workspace, slug, plan_path, choice, cursorConfirmation(), cursorCoordinatorContext(workspace));
|
|
28856
29093
|
if (result.ok === false)
|
|
28857
29094
|
return jsonResult({ error: result.error, code: result.code });
|
|
28858
|
-
return jsonResult({
|
|
29095
|
+
return jsonResult({
|
|
29096
|
+
menu: { presented: true, chosen: choice },
|
|
29097
|
+
...result.coordinator_lease !== undefined ? { coordinator_lease: result.coordinator_lease } : {}
|
|
29098
|
+
});
|
|
29099
|
+
});
|
|
29100
|
+
registerTool("workit_delegate", {
|
|
29101
|
+
description: "Mint a task-scoped delegation token for a Cursor-native subagent. Requires the coordinator_lease returned once by workit_plan_menu with choice subagent-driven. Pass the returned delegation_token to the subagent; mutation tools accept it as delegation_token.",
|
|
29102
|
+
inputSchema: {
|
|
29103
|
+
slug: exports_external.string(),
|
|
29104
|
+
plan_path: exports_external.string(),
|
|
29105
|
+
task_id: exports_external.number(),
|
|
29106
|
+
coordinator_lease: exports_external.string(),
|
|
29107
|
+
workspace_root: workspaceRootSchema
|
|
29108
|
+
}
|
|
29109
|
+
}, async ({ slug, plan_path, task_id, coordinator_lease, workspace_root }) => {
|
|
29110
|
+
const minted = mintDelegateToken(workspace_root, slug, plan_path, task_id, coordinator_lease);
|
|
29111
|
+
if (!minted.ok)
|
|
29112
|
+
return jsonResult({ error: minted.error, code: minted.code });
|
|
29113
|
+
return jsonResult({ delegation_token: minted.token, slug, task_id });
|
|
28859
29114
|
});
|
|
28860
29115
|
lifecycleTool("pause", "Pause a running plan with the Cursor policy-only confirmation: active -> paused. The MCP cannot observe AskQuestion results, so it records attested: false; there is no evidence argument (CA-42). Requires plan_path and workspace_root.");
|
|
28861
29116
|
lifecycleTool("resume", "Resume a paused plan with the Cursor policy-only confirmation: paused -> active. The MCP cannot observe AskQuestion results, so it records attested: false; there is no evidence argument (CA-42). Requires plan_path and workspace_root.");
|
|
@@ -28864,9 +29119,13 @@ var init_server3 = __esm(async () => {
|
|
|
28864
29119
|
description: "Link the component docs repo in the toolkit config",
|
|
28865
29120
|
inputSchema: {
|
|
28866
29121
|
path: exports_external.string(),
|
|
28867
|
-
confirmed: exports_external.boolean()
|
|
29122
|
+
confirmed: exports_external.boolean(),
|
|
29123
|
+
delegation_token: delegationTokenSchema
|
|
28868
29124
|
}
|
|
28869
|
-
}, async ({ path: docsPath, confirmed }) => {
|
|
29125
|
+
}, async ({ path: docsPath, confirmed, delegation_token }) => {
|
|
29126
|
+
const identity = cursorMutationContext(process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd(), delegation_token);
|
|
29127
|
+
if (!identity.ok)
|
|
29128
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28870
29129
|
const result = linkDocsRepo(docsPath, confirmed);
|
|
28871
29130
|
if (!result.ok)
|
|
28872
29131
|
return jsonResult({ error: result.error });
|
|
@@ -28882,9 +29141,13 @@ var init_server3 = __esm(async () => {
|
|
|
28882
29141
|
slug: exports_external.string(),
|
|
28883
29142
|
confirmed: exports_external.boolean(),
|
|
28884
29143
|
force: exports_external.boolean().optional(),
|
|
29144
|
+
delegation_token: delegationTokenSchema,
|
|
28885
29145
|
workspace_root: workspaceRootSchema
|
|
28886
29146
|
}
|
|
28887
|
-
}, async ({ slug, confirmed, force, workspace_root }) => {
|
|
29147
|
+
}, async ({ slug, confirmed, force, delegation_token, workspace_root }) => {
|
|
29148
|
+
const identity = cursorMutationContext(workspace_root, delegation_token);
|
|
29149
|
+
if (!identity.ok)
|
|
29150
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28888
29151
|
const result = promoteSpec(workspace_root, slug, { confirmed, force });
|
|
28889
29152
|
if (!result.ok)
|
|
28890
29153
|
return jsonResult({ error: result.error, findings: result.findings ?? [] });
|
|
@@ -28903,9 +29166,13 @@ var init_server3 = __esm(async () => {
|
|
|
28903
29166
|
inputSchema: {
|
|
28904
29167
|
name: exports_external.enum(["issue-update", "greeting", "headers"]),
|
|
28905
29168
|
content: exports_external.string(),
|
|
28906
|
-
confirmed: exports_external.boolean()
|
|
29169
|
+
confirmed: exports_external.boolean(),
|
|
29170
|
+
delegation_token: delegationTokenSchema
|
|
28907
29171
|
}
|
|
28908
|
-
}, async ({ name, content, confirmed }) => {
|
|
29172
|
+
}, async ({ name, content, confirmed, delegation_token }) => {
|
|
29173
|
+
const identity = cursorMutationContext(process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd(), delegation_token);
|
|
29174
|
+
if (!identity.ok)
|
|
29175
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28909
29176
|
const result = writeTemplate(name, content, confirmed);
|
|
28910
29177
|
if (!result.ok)
|
|
28911
29178
|
return jsonResult({ error: result.error });
|
|
@@ -28922,9 +29189,13 @@ var init_server3 = __esm(async () => {
|
|
|
28922
29189
|
description: exports_external.string(),
|
|
28923
29190
|
platforms: exports_external.array(exports_external.enum(["cursor", "opencode"])),
|
|
28924
29191
|
body: exports_external.string(),
|
|
28925
|
-
confirmed: exports_external.boolean()
|
|
29192
|
+
confirmed: exports_external.boolean(),
|
|
29193
|
+
delegation_token: delegationTokenSchema
|
|
28926
29194
|
}
|
|
28927
|
-
}, async ({ name, description, platforms, body, confirmed }) => {
|
|
29195
|
+
}, async ({ name, description, platforms, body, confirmed, delegation_token }) => {
|
|
29196
|
+
const identity = cursorMutationContext(process.env.WORKFLOW_WORKSPACE_ROOT ?? process.cwd(), delegation_token);
|
|
29197
|
+
if (!identity.ok)
|
|
29198
|
+
return jsonResult({ error: identity.error, code: identity.code });
|
|
28928
29199
|
const result = writeRule({ name, description, platforms, body }, confirmed);
|
|
28929
29200
|
if (!result.ok)
|
|
28930
29201
|
return jsonResult({ error: result.error });
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cursor",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.11.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit Cursor plugin: MCP server, hooks, rules, marketplace manifest (thin over @brainervirus/workit-core)",
|
|
6
6
|
"keywords": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"build": "bun scripts/build.ts"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@brainervirus/workit-core": "^0.
|
|
42
|
+
"@brainervirus/workit-core": "^0.11.0",
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
44
44
|
"zod": "^3.24.0"
|
|
45
45
|
},
|
|
@@ -39,7 +39,7 @@ After writing a plan, **do not emit** Superpowers' inline text beginning “Two
|
|
|
39
39
|
|
|
40
40
|
After `workit_docs_validate` succeeds on the saved spec/plan pair, call Cursor `AskQuestion` with exactly:
|
|
41
41
|
|
|
42
|
-
- Subagent-driven →
|
|
42
|
+
- Subagent-driven → dispatches Cursor-native subagents: `workit_plan_menu` returns a one-time `coordinator_lease`, `workit_delegate` mints a task-scoped `delegation_token` per task, and the worker passes it as `delegation_token` on mutation calls
|
|
43
43
|
- Inline → execute in this session
|
|
44
44
|
- Handoff → `/wk-handoff` (new session only)
|
|
45
45
|
- Review spec first
|
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: wk-implement
|
|
3
|
-
description: Execute an approved Superpowers plan on Cursor as the coordinator session
|
|
3
|
+
description: Execute an approved Superpowers plan on Cursor as the coordinator session in either approved execution mode. Subagent-driven dispatches Cursor-native subagents through a lease/token capability; Inline executes every task in this session. Use for /wk-implement or "implement from plan".
|
|
4
4
|
disable-model-invocation: true
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# Implement
|
|
8
8
|
|
|
9
|
-
Execute the plan
|
|
9
|
+
Execute the plan in the **approved execution mode** recorded by `workit_plan_menu` (call `workit_flow_status` to read it). In Subagent-driven mode this session never edits product code — dispatch Cursor-native subagents; in Inline mode it executes every task itself. Plan-level edits stay coordinator-gated.
|
|
10
10
|
|
|
11
11
|
## Step 1 — Gather facts (required)
|
|
12
12
|
|
|
@@ -20,11 +20,11 @@ Use the returned `tasks[]` as ground truth. Cache each `section_text` for subage
|
|
|
20
20
|
|
|
21
21
|
Resolve plugin root: `WORKFLOW_TOOLKIT_ROOT` env or `~/.cursor/plugins/local/workit/`.
|
|
22
22
|
|
|
23
|
-
Load `templates/execution-contract.md`. Substitute `<SPEC_PATH>`, `<PLAN_PATH>`, `<BRANCH>`, `<SDD_DIR>`, `<TASK_LIST>` from MCP. OMIT the `## Handoff destination` section and its `<workflow-handoff-destination>` marker line — that section is only for sessions started from a `workit_handoff_prompt` destination prompt. This
|
|
23
|
+
Load `templates/execution-contract.md`. Substitute `<SPEC_PATH>`, `<PLAN_PATH>`, `<BRANCH>`, `<SDD_DIR>`, `<TASK_LIST>` from MCP. OMIT the `## Handoff destination` section and its `<workflow-handoff-destination>` marker line — that section is only for sessions started from a `workit_handoff_prompt` destination prompt. This executor is NOT a destination and must never present itself as one (five-choice source menu, Handoff still available). If template missing, stop with error.
|
|
24
24
|
|
|
25
|
-
## Step 3 —
|
|
25
|
+
## Step 3 — Route by execution mode
|
|
26
26
|
|
|
27
|
-
Announce: "Using implement +
|
|
27
|
+
Announce: "Using implement + <mode>."
|
|
28
28
|
|
|
29
29
|
**Before Task 1 — validate + SDD + TodoWrite UI + branch (no worktrees):**
|
|
30
30
|
|
|
@@ -41,4 +41,18 @@ Each task lands exactly one contiguous non-empty commit range (`base..head`): fi
|
|
|
41
41
|
|
|
42
42
|
Do not emit a handoff fence — this is in-session execution.
|
|
43
43
|
|
|
44
|
+
### Subagent-driven
|
|
45
|
+
|
|
46
|
+
Requires the flow's execution mode `subagent-driven` (set by `workit_plan_menu`).
|
|
47
|
+
|
|
48
|
+
1. `workit_plan_menu` returned the raw **`coordinator_lease`** exactly once — use that stored value; the flow state persists only its hash.
|
|
49
|
+
2. Per task: call MCP `workit_delegate` with `{slug, plan_path, task_id, coordinator_lease, workspace_root}` to mint a task-scoped **`delegation_token`**.
|
|
50
|
+
3. Dispatch a Cursor-native subagent whose prompt includes the task brief and the raw `delegation_token`; the worker passes it as `delegation_token` on its mutation calls (`workit_sdd_task_brief`, `workit_sdd_review_package`, `workit_sdd_append_progress`, `workit_branch_setup`, `workit_pr_create`, …). The MCP validates the token against the flow state and fails closed on invalid, wrong-task, wrong-workspace, or revoked tokens.
|
|
51
|
+
4. Coordinator product edits stay blocked — implementation, reviews, and fix rounds happen in the dispatched subagents.
|
|
52
|
+
5. Appending the task's progress line with the worker's token via `workit_sdd_append_progress` revokes that task's token; mint a fresh token per task.
|
|
53
|
+
|
|
54
|
+
### Inline
|
|
55
|
+
|
|
56
|
+
Load and follow `executing-plans` in the current session. Every task runs in this agent — single-agent, no dispatch, no token minting.
|
|
57
|
+
|
|
44
58
|
**Mandatory:** end the run by calling MCP `workit_plan_complete` after the final task once the SDD ledger is complete (all task IDs appended) and `workit_verify` passes — a complete ledger and green verification are the tool's gates. Never finish the run while the plan is still `active`.
|
|
@@ -7,11 +7,11 @@ description: Use when you have a written implementation plan to execute in a sep
|
|
|
7
7
|
|
|
8
8
|
## Overview
|
|
9
9
|
|
|
10
|
-
Load plan, review critically, execute all tasks, report when complete.
|
|
10
|
+
Load plan, review critically, execute all tasks in the current session, report when complete. This is the single-agent Inline execution path.
|
|
11
11
|
|
|
12
12
|
**Announce at start:** "I'm using the executing-plans skill to implement this plan."
|
|
13
13
|
|
|
14
|
-
**Note:** Tell your human partner that Superpowers works much better with access to subagents. The quality of its work will be significantly higher if run on a platform with subagent support (Claude Code, Codex CLI, Codex App, and Copilot CLI all qualify; see the per-platform tool refs in `../using-superpowers/references/`). If subagents are available, use superpowers:subagent-driven-development instead of this skill.
|
|
14
|
+
**Note:** This skill is the single-agent Inline execution path: every task runs in the current session — no token minting, no subagent dispatching. Tell your human partner that Superpowers works much better with access to subagents. The quality of its work will be significantly higher if run on a platform with subagent support (Claude Code, Codex CLI, Codex App, and Copilot CLI all qualify; see the per-platform tool refs in `../using-superpowers/references/`). If subagents are available and the plan's approved execution mode is Subagent-driven, use superpowers:subagent-driven-development instead of this skill.
|
|
15
15
|
|
|
16
16
|
## The Process
|
|
17
17
|
|
|
@@ -11,6 +11,8 @@ Execute plan by dispatching a fresh implementer subagent per task, a task review
|
|
|
11
11
|
|
|
12
12
|
**Core principle:** Fresh subagent per task + task review (spec + quality) + broad final review = high quality, fast iteration
|
|
13
13
|
|
|
14
|
+
On Cursor, the coordinator mints a task-scoped delegation token via `workit_delegate` (from the `workit_plan_menu` coordinator lease) and the subagent prompt must carry it as `delegation_token` for mutation calls.
|
|
15
|
+
|
|
14
16
|
**Narration:** between tool calls, narrate at most one short line — the
|
|
15
17
|
ledger and the tool results carry the record.
|
|
16
18
|
|