@brainervirus/workit-cursor 0.8.8 → 0.8.10
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 +3 -3
- package/assets/templates/execution-contract.md +20 -18
- package/assets/templates/plan-template.md +1 -1
- package/assets/templates/spec-template.md +2 -2
- package/assets/templates/superpowers-doc-contract.md +13 -10
- package/dist/cursor-session-start.js +10 -8
- package/dist/mcp-server.js +247 -91
- package/package.json +2 -2
- package/rules/ask-question-only.mdc +1 -1
- package/rules/cursor-todowrite.mdc +2 -2
- package/rules/no-worktrees.mdc +3 -3
- package/rules/sdd-docs-path.mdc +3 -3
- package/skills/wk-changelog/SKILL.md +5 -5
- package/skills/wk-commit/SKILL.md +3 -3
- package/skills/wk-docs-refresh/SKILL.md +3 -3
- package/skills/wk-handoff/SKILL.md +6 -6
- package/skills/wk-implement/SKILL.md +9 -9
- package/skills/wk-issue-update/SKILL.md +9 -9
- package/skills/wk-issue-update/references/youtrack-update-style.md +1 -1
- package/skills/wk-meetings/SKILL.md +4 -4
- package/skills/wk-pr/SKILL.md +7 -7
- package/skills/wk-release-notes/SKILL.md +3 -3
- package/skills/wk-status/SKILL.md +1 -1
- package/skills/wk-verify/SKILL.md +3 -3
- package/vendor/superpowers/skills/subagent-driven-development/SKILL.md +4 -4
package/dist/mcp-server.js
CHANGED
|
@@ -20868,7 +20868,7 @@ var resolveBranchPolicyFor = (workspaceRoot) => resolveBranchPolicy(readConfig()
|
|
|
20868
20868
|
if (dirty) {
|
|
20869
20869
|
if (stash !== "yes") {
|
|
20870
20870
|
return {
|
|
20871
|
-
error: "dirty working tree — ask with native question, then call
|
|
20871
|
+
error: "dirty working tree — ask with native question, then call workit_branch_setup with stash=yes"
|
|
20872
20872
|
};
|
|
20873
20873
|
}
|
|
20874
20874
|
try {
|
|
@@ -21621,7 +21621,7 @@ var runGit = (cwd, args) => {
|
|
|
21621
21621
|
- Entries should be human-readable and user-facing.
|
|
21622
21622
|
- Do not use raw commit messages as changelog bullets.
|
|
21623
21623
|
- MERGE into existing ### Category under [Unreleased] — never append a second ### Added / ### Fixed block.
|
|
21624
|
-
- Apply with the native
|
|
21624
|
+
- Apply with the native workit_changelog_apply tool only (not hand-edits under Unreleased).
|
|
21625
21625
|
- If Unreleased already has duplicate category headings, normalize_only first.`;
|
|
21626
21626
|
var init_repo_context = __esm(() => {
|
|
21627
21627
|
init_vcs_config();
|
|
@@ -24182,7 +24182,7 @@ function sddContext({
|
|
|
24182
24182
|
task_count,
|
|
24183
24183
|
flow: { spec: flow.spec, plan: flow.plan, menu: flow.menu },
|
|
24184
24184
|
todowrite_required: true,
|
|
24185
|
-
todowrite_hint: "REQUIRED: Call OpenCode todowrite with todos from this result so the native task list shows progress. Before each task set status in_progress; after
|
|
24185
|
+
todowrite_hint: "REQUIRED: Call OpenCode todowrite with todos from this result so the native task list shows progress. Before each task set status in_progress; after workit_sdd_append_progress set it completed."
|
|
24186
24186
|
};
|
|
24187
24187
|
}
|
|
24188
24188
|
function sddTaskBrief({
|
|
@@ -24266,6 +24266,58 @@ function sddAppendProgress({
|
|
|
24266
24266
|
const rel = posix2(path19.relative(contained.base, path_));
|
|
24267
24267
|
return { ok: true, line: trimmed, progress_path: rel };
|
|
24268
24268
|
}
|
|
24269
|
+
function sddAppendAdvisory({
|
|
24270
|
+
advisories_path,
|
|
24271
|
+
task_id,
|
|
24272
|
+
text,
|
|
24273
|
+
workspace_root
|
|
24274
|
+
}) {
|
|
24275
|
+
if (typeof task_id !== "number" || !Number.isSafeInteger(task_id) || task_id <= 0) {
|
|
24276
|
+
return { error: "task_id must be a positive safe integer", code: "advisory_task_invalid" };
|
|
24277
|
+
}
|
|
24278
|
+
if (typeof text !== "string") {
|
|
24279
|
+
return {
|
|
24280
|
+
error: "advisory text must be a string of 1-1000 characters after normalization",
|
|
24281
|
+
code: "advisory_text_invalid"
|
|
24282
|
+
};
|
|
24283
|
+
}
|
|
24284
|
+
if (text.includes("\r") || text.includes(`
|
|
24285
|
+
`)) {
|
|
24286
|
+
return {
|
|
24287
|
+
error: "advisory text must be a single line (no CR/LF)",
|
|
24288
|
+
code: "advisory_text_invalid"
|
|
24289
|
+
};
|
|
24290
|
+
}
|
|
24291
|
+
const collapsed = text.trim().replace(/[ \t]+/g, " ");
|
|
24292
|
+
if (collapsed.length === 0 || collapsed.length > 1000) {
|
|
24293
|
+
return {
|
|
24294
|
+
error: "advisory text must be 1-1000 characters after trim and horizontal-space collapse",
|
|
24295
|
+
code: "advisory_text_invalid"
|
|
24296
|
+
};
|
|
24297
|
+
}
|
|
24298
|
+
if (!/^docs\/[^/]+\/sdd\/advisories\.md$/.test(advisories_path)) {
|
|
24299
|
+
return {
|
|
24300
|
+
error: `advisories_path must be docs/<slug>/sdd/advisories.md: ${advisories_path}`,
|
|
24301
|
+
code: "advisory_path_invalid"
|
|
24302
|
+
};
|
|
24303
|
+
}
|
|
24304
|
+
const contained = resolveDocsPath({ workspace_root, path: advisories_path });
|
|
24305
|
+
if (!contained.ok)
|
|
24306
|
+
return { error: contained.error, code: "advisory_path_invalid" };
|
|
24307
|
+
const abs = contained.path;
|
|
24308
|
+
if (existsSync11(abs) && statSync6(abs).isDirectory()) {
|
|
24309
|
+
return {
|
|
24310
|
+
error: `advisory target is a directory: ${advisories_path}`,
|
|
24311
|
+
code: "advisory_target_invalid"
|
|
24312
|
+
};
|
|
24313
|
+
}
|
|
24314
|
+
mkdirSync6(path19.dirname(abs), { recursive: true });
|
|
24315
|
+
const line = `- Task ${task_id}: ${collapsed}
|
|
24316
|
+
`;
|
|
24317
|
+
appendFileSync2(abs, line, "utf8");
|
|
24318
|
+
const rel = posix2(path19.relative(contained.base, abs));
|
|
24319
|
+
return { ok: true, advisory: collapsed, advisories_path: rel };
|
|
24320
|
+
}
|
|
24269
24321
|
var posix2 = (p) => p.split(path19.sep).join("/"), PROGRESS_RE;
|
|
24270
24322
|
var init_sdd = __esm(() => {
|
|
24271
24323
|
init_docs_validate();
|
|
@@ -24286,6 +24338,7 @@ import {
|
|
|
24286
24338
|
fsyncSync,
|
|
24287
24339
|
mkdirSync as mkdirSync7,
|
|
24288
24340
|
openSync,
|
|
24341
|
+
readdirSync as readdirSync6,
|
|
24289
24342
|
readFileSync as readFileSync13,
|
|
24290
24343
|
renameSync,
|
|
24291
24344
|
rmSync,
|
|
@@ -24298,16 +24351,20 @@ import path20 from "node:path";
|
|
|
24298
24351
|
|
|
24299
24352
|
class HostReceiptStore {
|
|
24300
24353
|
#bySession = new Map;
|
|
24301
|
-
record(sessionId, callID, selectedLabel, recordedAt = Date.now(), question) {
|
|
24302
|
-
const
|
|
24303
|
-
if (!
|
|
24354
|
+
record(sessionId, callID, selectedLabel, recordedAt = Date.now(), question = "", purpose) {
|
|
24355
|
+
const trimmed = selectedLabel.trim();
|
|
24356
|
+
if (!trimmed)
|
|
24304
24357
|
return;
|
|
24305
24358
|
if (recordedAt > Date.now() + MAX_CLOCK_SKEW_MS)
|
|
24306
24359
|
return;
|
|
24360
|
+
const derived = purpose ?? receiptPurposeForLabel(selectedLabel);
|
|
24361
|
+
if (derived === undefined)
|
|
24362
|
+
return;
|
|
24363
|
+
const q = question ?? "";
|
|
24307
24364
|
const queue = this.#bySession.get(sessionId) ?? [];
|
|
24308
24365
|
if (queue.length >= MAX_RECEIPTS_PER_SESSION)
|
|
24309
24366
|
queue.shift();
|
|
24310
|
-
queue.push({ sessionId, callID, selectedLabel
|
|
24367
|
+
queue.push({ sessionId, callID, selectedLabel, recordedAt, question: q, purpose: derived });
|
|
24311
24368
|
this.#bySession.set(sessionId, queue);
|
|
24312
24369
|
}
|
|
24313
24370
|
count(sessionId) {
|
|
@@ -24324,10 +24381,40 @@ class HostReceiptStore {
|
|
|
24324
24381
|
if (!queue || queue.length === 0) {
|
|
24325
24382
|
return err2("receipt_missing", "no host-observed native-question receipt for this session — ask the native " + "`question` tool and have the user answer before calling this tool");
|
|
24326
24383
|
}
|
|
24327
|
-
|
|
24384
|
+
let index = -1;
|
|
24385
|
+
if (opts.purpose !== undefined) {
|
|
24386
|
+
const top = queue[queue.length - 1];
|
|
24387
|
+
if (top && top.purpose === opts.purpose && isNegativeLabel(top.selectedLabel)) {
|
|
24388
|
+
const filtered = queue.filter((r) => r.purpose !== opts.purpose);
|
|
24389
|
+
if (filtered.length === 0)
|
|
24390
|
+
this.#bySession.delete(sessionId);
|
|
24391
|
+
else
|
|
24392
|
+
this.#bySession.set(sessionId, filtered);
|
|
24393
|
+
return err2("receipt_rejected", `the user's most recent answer (${JSON.stringify(top.selectedLabel)}) is a ` + "negative answer — it cannot authorize an approval; ask the native question again");
|
|
24394
|
+
}
|
|
24395
|
+
for (let i = queue.length - 1;i >= 0; i--) {
|
|
24396
|
+
if (queue[i].purpose === opts.purpose) {
|
|
24397
|
+
index = i;
|
|
24398
|
+
break;
|
|
24399
|
+
}
|
|
24400
|
+
}
|
|
24401
|
+
if (index === -1) {
|
|
24402
|
+
return err2("receipt_missing", `no host-observed receipt for purpose ${JSON.stringify(opts.purpose)} — ask the native question for that purpose`);
|
|
24403
|
+
}
|
|
24404
|
+
} else {
|
|
24405
|
+
index = queue.length - 1;
|
|
24406
|
+
}
|
|
24328
24407
|
const receipt = queue[index];
|
|
24329
24408
|
if (isNegativeLabel(receipt.selectedLabel)) {
|
|
24330
|
-
|
|
24409
|
+
if (opts.purpose !== undefined) {
|
|
24410
|
+
const filtered = queue.filter((r) => r.purpose !== opts.purpose);
|
|
24411
|
+
if (filtered.length === 0)
|
|
24412
|
+
this.#bySession.delete(sessionId);
|
|
24413
|
+
else
|
|
24414
|
+
this.#bySession.set(sessionId, filtered);
|
|
24415
|
+
} else {
|
|
24416
|
+
this.#bySession.delete(sessionId);
|
|
24417
|
+
}
|
|
24331
24418
|
return err2("receipt_rejected", `the user's most recent answer (${JSON.stringify(receipt.selectedLabel)}) is a ` + "negative answer — it cannot authorize an approval; ask the native question again");
|
|
24332
24419
|
}
|
|
24333
24420
|
if (opts.label !== undefined && !sameChoiceLabel(receipt.selectedLabel, opts.label)) {
|
|
@@ -24396,7 +24483,8 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24396
24483
|
execution: {
|
|
24397
24484
|
status: execution.status ?? "pending",
|
|
24398
24485
|
mode: execution.mode ?? null,
|
|
24399
|
-
evidence: execution.evidence ?? null
|
|
24486
|
+
evidence: execution.evidence ?? null,
|
|
24487
|
+
coordinator_session_id: execution.coordinator_session_id ?? null
|
|
24400
24488
|
},
|
|
24401
24489
|
handoff_destination: p.handoff_destination ?? false,
|
|
24402
24490
|
updated_at: p.updated_at ?? Date.now()
|
|
@@ -24407,7 +24495,7 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24407
24495
|
spec: { path: "", status: "draft", evidence: null, approved_digest: null },
|
|
24408
24496
|
plan: { path: "", status: "draft", evidence: null, approved_digest: null },
|
|
24409
24497
|
menu: { presented: false, chosen: "", evidence: null },
|
|
24410
|
-
execution: { status: "pending", mode: null, evidence: null },
|
|
24498
|
+
execution: { status: "pending", mode: null, evidence: null, coordinator_session_id: null },
|
|
24411
24499
|
handoff_destination: false,
|
|
24412
24500
|
updated_at: Date.now()
|
|
24413
24501
|
}), readFlowState = (root, slug) => {
|
|
@@ -24516,6 +24604,12 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24516
24604
|
if (execRaw?.evidence !== undefined && !validateEvidenceValue(execRaw.evidence, true)) {
|
|
24517
24605
|
return { ok: false, error: "flow state execution.evidence has an unsupported shape" };
|
|
24518
24606
|
}
|
|
24607
|
+
if (execRaw?.coordinator_session_id !== undefined && execRaw.coordinator_session_id !== null && typeof execRaw.coordinator_session_id !== "string") {
|
|
24608
|
+
return {
|
|
24609
|
+
ok: false,
|
|
24610
|
+
error: "flow state execution.coordinator_session_id must be a string or null"
|
|
24611
|
+
};
|
|
24612
|
+
}
|
|
24519
24613
|
return {
|
|
24520
24614
|
ok: true,
|
|
24521
24615
|
state: {
|
|
@@ -24531,7 +24625,8 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24531
24625
|
execution: {
|
|
24532
24626
|
status: execRaw?.status ?? "pending",
|
|
24533
24627
|
mode: execRaw?.mode ?? null,
|
|
24534
|
-
evidence: execRaw?.evidence ?? null
|
|
24628
|
+
evidence: execRaw?.evidence ?? null,
|
|
24629
|
+
coordinator_session_id: execRaw?.coordinator_session_id ?? null
|
|
24535
24630
|
},
|
|
24536
24631
|
handoff_destination: parsed.handoff_destination ?? false,
|
|
24537
24632
|
updated_at: parsed.updated_at ?? Date.now()
|
|
@@ -24541,7 +24636,7 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24541
24636
|
const file = flowPath(root, slug);
|
|
24542
24637
|
const rel = path20.posix.join("docs", slug, "sdd", "flow.json");
|
|
24543
24638
|
if (!existsSync12(file)) {
|
|
24544
|
-
return err2("flow_not_activated", `flow not activated for ${slug} — run
|
|
24639
|
+
return err2("flow_not_activated", `flow not activated for ${slug} — run workit_flow_status first`);
|
|
24545
24640
|
}
|
|
24546
24641
|
let text;
|
|
24547
24642
|
try {
|
|
@@ -24658,7 +24753,7 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24658
24753
|
spec: { ...state.spec, status: "draft", evidence: null, approved_digest: null },
|
|
24659
24754
|
plan: { ...state.plan, status: "draft", evidence: null, approved_digest: null },
|
|
24660
24755
|
menu: { presented: false, chosen: "", evidence: null },
|
|
24661
|
-
execution: { status: "pending", mode: null, evidence: null },
|
|
24756
|
+
execution: { status: "pending", mode: null, evidence: null, coordinator_session_id: null },
|
|
24662
24757
|
handoff_destination: false,
|
|
24663
24758
|
updated_at: Date.now()
|
|
24664
24759
|
}), resetForPlanDrift = (state) => ({
|
|
@@ -24697,9 +24792,14 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24697
24792
|
}, deriveLegacyExecution = (root, slug, state) => {
|
|
24698
24793
|
const ledger = ledgerCompletion(root, slug);
|
|
24699
24794
|
if (state.plan.status === "approved" && state.menu.chosen === "subagent-driven" && ledger.started && !ledger.complete) {
|
|
24700
|
-
return {
|
|
24795
|
+
return {
|
|
24796
|
+
status: "active",
|
|
24797
|
+
mode: "subagent-driven",
|
|
24798
|
+
evidence: null,
|
|
24799
|
+
coordinator_session_id: null
|
|
24800
|
+
};
|
|
24701
24801
|
}
|
|
24702
|
-
return { status: "pending", mode: null, evidence: null };
|
|
24802
|
+
return { status: "pending", mode: null, evidence: null, coordinator_session_id: null };
|
|
24703
24803
|
}, normalizeCompatibility = (root, slug, parsed, state) => {
|
|
24704
24804
|
if (!isRecord(parsed) || !("execution" in parsed)) {
|
|
24705
24805
|
const derived = deriveLegacyExecution(root, slug, state);
|
|
@@ -24707,6 +24807,11 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24707
24807
|
if (derived.status !== current.status || derived.mode !== current.mode) {
|
|
24708
24808
|
return { state: { ...state, execution: derived, updated_at: Date.now() }, changed: true };
|
|
24709
24809
|
}
|
|
24810
|
+
return { state, changed: false };
|
|
24811
|
+
}
|
|
24812
|
+
const execRaw = isRecord(parsed.execution) ? parsed.execution : undefined;
|
|
24813
|
+
if (execRaw && !("coordinator_session_id" in execRaw)) {
|
|
24814
|
+
return { state: { ...state, updated_at: Date.now() }, changed: true };
|
|
24710
24815
|
}
|
|
24711
24816
|
return { state, changed: false };
|
|
24712
24817
|
}, withFlowLock = (file, fn) => {
|
|
@@ -24836,14 +24941,6 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24836
24941
|
return err2("workspace_mismatch", `mutation context workspace ${JSON.stringify(ctx.hostWorkspace)} does not match flow workspace ${JSON.stringify(root)}`);
|
|
24837
24942
|
}
|
|
24838
24943
|
return { ok: true };
|
|
24839
|
-
}, assertCoordinatorBoundary = (ctx, state) => {
|
|
24840
|
-
if (ctx?.role === "coordinator" && state.execution.status === "active" && state.execution.mode === "subagent-driven") {
|
|
24841
|
-
return err2("coordinator_blocked", COORDINATOR_RECOVERY_TEXT);
|
|
24842
|
-
}
|
|
24843
|
-
if (ctx?.role === "delegated" && !ctx.taskIdentity) {
|
|
24844
|
-
return err2("delegated_unauthenticated", "delegated mutations require an authenticated task identity (taskIdentity) — re-run inside the delegated worker session");
|
|
24845
|
-
}
|
|
24846
|
-
return { ok: true };
|
|
24847
24944
|
}, MAX_CLOCK_SKEW_MS = 60000, MAX_RECEIPTS_PER_SESSION = 10, RECEIPT_FRESHNESS_MS, EVIDENCE_WINDOW_MS, NEGATIVE_ANSWER_LABELS, isNegativeLabel = (label) => {
|
|
24848
24945
|
const normalized = label.trim().toLowerCase();
|
|
24849
24946
|
return NEGATIVE_ANSWER_LABELS.some((entry) => {
|
|
@@ -24854,6 +24951,31 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24854
24951
|
}
|
|
24855
24952
|
return firstWord.replace(/[^a-z]/g, "") === entry;
|
|
24856
24953
|
});
|
|
24954
|
+
}, receiptPurposeForLabel = (label) => {
|
|
24955
|
+
const n = normalizeLabel(label);
|
|
24956
|
+
if (n === "approve spec" || n === "approve spec recommended")
|
|
24957
|
+
return "spec-approval";
|
|
24958
|
+
if (n === "approve plan" || n === "approve plan recommended")
|
|
24959
|
+
return "plan-approval";
|
|
24960
|
+
if (n === "approve")
|
|
24961
|
+
return;
|
|
24962
|
+
if (n === "pause plan")
|
|
24963
|
+
return "plan-pause";
|
|
24964
|
+
if (n === "resume plan")
|
|
24965
|
+
return "plan-resume";
|
|
24966
|
+
if (n === "complete plan")
|
|
24967
|
+
return "plan-complete";
|
|
24968
|
+
const exec = new Set([
|
|
24969
|
+
"subagent driven",
|
|
24970
|
+
"inline",
|
|
24971
|
+
"handoff",
|
|
24972
|
+
"review spec",
|
|
24973
|
+
"review plan",
|
|
24974
|
+
"change model"
|
|
24975
|
+
]);
|
|
24976
|
+
if (exec.has(n))
|
|
24977
|
+
return "execution-menu";
|
|
24978
|
+
return;
|
|
24857
24979
|
}, sameChoiceLabel = (a, b) => normalizeLabel(a) === normalizeLabel(b), normalizeLabel = (s) => s.replace(/\s*\([^)]*\)/g, " ").replace(/\s*\bfirst\b\s*$/i, " ").replace(/[^a-z0-9]+/gi, " ").trim().toLowerCase(), createCursorConfirmation = () => ({
|
|
24858
24980
|
ok: true,
|
|
24859
24981
|
evidence: { host: "cursor", attested: false, confirmation: "contract" }
|
|
@@ -24958,7 +25080,7 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
24958
25080
|
spec: { path: specPath, status: "draft", evidence: null, approved_digest: null },
|
|
24959
25081
|
plan: { path: planPath, status: "draft", evidence: null, approved_digest: null },
|
|
24960
25082
|
menu: { presented: false, chosen: "", evidence: null },
|
|
24961
|
-
execution: { status: "pending", mode: null, evidence: null },
|
|
25083
|
+
execution: { status: "pending", mode: null, evidence: null, coordinator_session_id: null },
|
|
24962
25084
|
handoff_destination: false,
|
|
24963
25085
|
updated_at: Date.now()
|
|
24964
25086
|
});
|
|
@@ -25101,13 +25223,24 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
25101
25223
|
return err2("recursive_handoff", "this flow is already a handoff destination — a second handoff is rejected");
|
|
25102
25224
|
}
|
|
25103
25225
|
const executing = choice === "subagent-driven" || choice === "inline";
|
|
25226
|
+
const coordinatorSessionId = choice === "subagent-driven" && recorded.evidence.host === "opencode" ? ctx?.sessionId ?? null : null;
|
|
25104
25227
|
return {
|
|
25105
25228
|
ok: true,
|
|
25106
25229
|
next: {
|
|
25107
25230
|
...state,
|
|
25108
25231
|
plan: { ...state.plan, path: state.plan.path || `docs/${slug}/plan.md` },
|
|
25109
25232
|
menu: { presented: true, chosen: choice, evidence: recorded.evidence },
|
|
25110
|
-
execution: executing ? {
|
|
25233
|
+
execution: executing ? {
|
|
25234
|
+
status: "active",
|
|
25235
|
+
mode: choice,
|
|
25236
|
+
evidence: recorded.evidence,
|
|
25237
|
+
coordinator_session_id: coordinatorSessionId
|
|
25238
|
+
} : {
|
|
25239
|
+
status: "pending",
|
|
25240
|
+
mode: null,
|
|
25241
|
+
evidence: recorded.evidence,
|
|
25242
|
+
coordinator_session_id: null
|
|
25243
|
+
},
|
|
25111
25244
|
updated_at: Date.now()
|
|
25112
25245
|
}
|
|
25113
25246
|
};
|
|
@@ -25195,7 +25328,7 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
25195
25328
|
}
|
|
25196
25329
|
const next = {
|
|
25197
25330
|
...reconciled.state,
|
|
25198
|
-
execution: { ...exec, status: "completed" },
|
|
25331
|
+
execution: { ...exec, status: "completed", coordinator_session_id: null },
|
|
25199
25332
|
handoff_destination: false,
|
|
25200
25333
|
updated_at: Date.now()
|
|
25201
25334
|
};
|
|
@@ -25255,7 +25388,7 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
25255
25388
|
}, slugFromSddPath = (p) => {
|
|
25256
25389
|
const match = p.split(path20.sep).join("/").match(/^docs\/([^/]+)\/sdd(\/|$|['"])/);
|
|
25257
25390
|
return match?.[1] ?? "";
|
|
25258
|
-
},
|
|
25391
|
+
}, assertSddControlGates = (root, slug, opts = {}, ctx) => {
|
|
25259
25392
|
const bound = assertMutationWorkspace(root, ctx);
|
|
25260
25393
|
if (!bound.ok)
|
|
25261
25394
|
return bound;
|
|
@@ -25264,13 +25397,13 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
25264
25397
|
return effective;
|
|
25265
25398
|
const state = effective.state;
|
|
25266
25399
|
if (state.spec.status !== "approved") {
|
|
25267
|
-
return err2("spec_not_approved", `spec not approved (status: ${state.spec.status}). Run
|
|
25400
|
+
return err2("spec_not_approved", `spec not approved (status: ${state.spec.status}). Run workit_spec_approve after the user's approval.`);
|
|
25268
25401
|
}
|
|
25269
25402
|
if (state.plan.status !== "approved") {
|
|
25270
|
-
return err2("plan_not_approved", `plan not approved (status: ${state.plan.status}). Run
|
|
25403
|
+
return err2("plan_not_approved", `plan not approved (status: ${state.plan.status}). Run workit_plan_approve after the user's approval.`);
|
|
25271
25404
|
}
|
|
25272
25405
|
if (opts.requireMenu && !state.menu.presented) {
|
|
25273
|
-
return err2("menu_not_presented", "post-plan menu not presented. Record the native question answer with
|
|
25406
|
+
return err2("menu_not_presented", "post-plan menu not presented. Record the native question answer with workit_plan_menu.");
|
|
25274
25407
|
}
|
|
25275
25408
|
if (opts.requireDocs) {
|
|
25276
25409
|
const validated = docsValidate({
|
|
@@ -25281,15 +25414,18 @@ var COORDINATOR_RECOVERY_TEXT, CURSOR_SUBAGENT_UNSUPPORTED_TEXT, MENU_CHOICES, e
|
|
|
25281
25414
|
if (validated.ok === false)
|
|
25282
25415
|
return err2("docs_invalid", validated.error);
|
|
25283
25416
|
}
|
|
25284
|
-
|
|
25285
|
-
|
|
25417
|
+
if (state.execution.status === "active" && state.execution.mode === "subagent-driven" && roleFromParentage(ctx?.parentSessionId, state.execution.coordinator_session_id) === "delegated") {
|
|
25418
|
+
return err2("sdd_control_denied", "SDD control metadata is coordinator-owned while a subagent-driven plan is active — delegated workers cannot mutate task briefs, review packages, progress, or advisories");
|
|
25419
|
+
}
|
|
25420
|
+
return { ok: true };
|
|
25421
|
+
}, roleFromParentage = (parentID, coordinatorSessionId) => typeof parentID === "string" && parentID !== "" && parentID === coordinatorSessionId ? "delegated" : "coordinator", BASH_READ_TOKENS, BASH_GIT_READ_SUBCOMMANDS, BASH_GIT_MUTABLE_SUBCOMMANDS, BASH_GIT_READ_FLAGS, BASH_FIND_DENIED_FLAGS, BASH_TEST_VERBS, BASH_DENIED_HEADS, BASH_OUTPUT_FLAG_VERBS, BASH_PAREN_EXEMPT_HEADS, BASH_GIT_VALUE_FLAGS, COORDINATOR_SHELL_DENIED_TEXT;
|
|
25286
25422
|
var init_flow_state = __esm(() => {
|
|
25287
25423
|
init_docs_validate();
|
|
25288
25424
|
init_docs_layout();
|
|
25289
25425
|
init_sdd();
|
|
25290
25426
|
init_verify_project();
|
|
25291
25427
|
init_menu();
|
|
25292
|
-
COORDINATOR_RECOVERY_TEXT = "A subagent-driven plan is active: coordinator product edits are blocked. " + "Delegate product mutations
|
|
25428
|
+
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.";
|
|
25293
25429
|
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`.";
|
|
25294
25430
|
MENU_CHOICES = [
|
|
25295
25431
|
"subagent-driven",
|
|
@@ -25460,7 +25596,7 @@ import {
|
|
|
25460
25596
|
existsSync as existsSync13,
|
|
25461
25597
|
lstatSync as lstatSync2,
|
|
25462
25598
|
mkdirSync as mkdirSync8,
|
|
25463
|
-
readdirSync as
|
|
25599
|
+
readdirSync as readdirSync7,
|
|
25464
25600
|
readFileSync as readFileSync14,
|
|
25465
25601
|
realpathSync as realpathSync3,
|
|
25466
25602
|
renameSync as renameSync2,
|
|
@@ -25505,7 +25641,7 @@ var LEGACY_DIR = "docs/superpowers", MIGRATION_CHOICES, SLUG_RE3, RESERVED_SLUG
|
|
|
25505
25641
|
const root = legacyRoot(workspace_root);
|
|
25506
25642
|
const raw = [];
|
|
25507
25643
|
if (existsSync13(root)) {
|
|
25508
|
-
for (const entry of
|
|
25644
|
+
for (const entry of readdirSync7(root, { withFileTypes: true })) {
|
|
25509
25645
|
const abs = path21.join(root, entry.name);
|
|
25510
25646
|
const rel = posix3(path21.relative(workspace_root, abs));
|
|
25511
25647
|
if (entry.isDirectory()) {
|
|
@@ -25639,7 +25775,7 @@ var LEGACY_DIR = "docs/superpowers", MIGRATION_CHOICES, SLUG_RE3, RESERVED_SLUG
|
|
|
25639
25775
|
if (visits.has(real))
|
|
25640
25776
|
return { ok: true };
|
|
25641
25777
|
visits.add(real);
|
|
25642
|
-
for (const entry of
|
|
25778
|
+
for (const entry of readdirSync7(real, { withFileTypes: true })) {
|
|
25643
25779
|
const fromAbs = path21.join(real, entry.name);
|
|
25644
25780
|
const fromRel = posix3(path21.join(legacyRel, entry.name));
|
|
25645
25781
|
const toRel = posix3(path21.join(destRelRoot, entry.name));
|
|
@@ -25828,7 +25964,7 @@ var init_docs_migration = __esm(() => {
|
|
|
25828
25964
|
});
|
|
25829
25965
|
|
|
25830
25966
|
// packages/workit-core/src/core/docs-repo.ts
|
|
25831
|
-
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync8, readdirSync as
|
|
25967
|
+
import { existsSync as existsSync14, mkdirSync as mkdirSync9, readFileSync as readFileSync15, writeFileSync as writeFileSync8, readdirSync as readdirSync8 } from "node:fs";
|
|
25832
25968
|
import { execFileSync as execFileSync6 } from "node:child_process";
|
|
25833
25969
|
import path22 from "node:path";
|
|
25834
25970
|
var configPath = () => process.env.WORKFLOW_DOCS_REPO_CONFIG ?? path22.join(configDir(), "docs-repo.json"), readDocsRepoConfig = () => {
|
|
@@ -25868,7 +26004,7 @@ var configPath = () => process.env.WORKFLOW_DOCS_REPO_CONFIG ?? path22.join(conf
|
|
|
25868
26004
|
const specs = [];
|
|
25869
26005
|
const docsDir = path22.join(workspaceRoot, "docs");
|
|
25870
26006
|
if (existsSync14(docsDir)) {
|
|
25871
|
-
for (const slug of
|
|
26007
|
+
for (const slug of readdirSync8(docsDir)) {
|
|
25872
26008
|
if (slug.startsWith("."))
|
|
25873
26009
|
continue;
|
|
25874
26010
|
const spec = path22.posix.join("docs", slug, "spec.md");
|
|
@@ -25879,7 +26015,7 @@ var configPath = () => process.env.WORKFLOW_DOCS_REPO_CONFIG ?? path22.join(conf
|
|
|
25879
26015
|
if (repoPath) {
|
|
25880
26016
|
const featuresDir = path22.join(repoPath, "features");
|
|
25881
26017
|
if (existsSync14(featuresDir)) {
|
|
25882
|
-
const match =
|
|
26018
|
+
const match = readdirSync8(featuresDir).find((d) => new RegExp(`^20\\d{2}-\\d{2}-${slug}$`).test(d));
|
|
25883
26019
|
if (match) {
|
|
25884
26020
|
promoted = true;
|
|
25885
26021
|
target = path22.join(repoPath, "features", match);
|
|
@@ -25918,7 +26054,7 @@ var configPath = () => process.env.WORKFLOW_DOCS_REPO_CONFIG ?? path22.join(conf
|
|
|
25918
26054
|
const workspaceRootCanonical = resolved.layout.workspace;
|
|
25919
26055
|
const repoPath = docsRepoPath();
|
|
25920
26056
|
if (!repoPath)
|
|
25921
|
-
return { ok: false, error: "docs repo not linked — run
|
|
26057
|
+
return { ok: false, error: "docs repo not linked — run workit_docs_repo_link" };
|
|
25922
26058
|
const repoValid = validateDocsRepo(repoPath);
|
|
25923
26059
|
if (!repoValid.ok)
|
|
25924
26060
|
return { ok: false, error: repoValid.error };
|
|
@@ -25966,7 +26102,7 @@ var configPath = () => process.env.WORKFLOW_DOCS_REPO_CONFIG ?? path22.join(conf
|
|
|
25966
26102
|
}
|
|
25967
26103
|
const prefix = monthPrefix();
|
|
25968
26104
|
const featuresDir = path22.join(repoPath, "features");
|
|
25969
|
-
const existing = existsSync14(featuresDir) ?
|
|
26105
|
+
const existing = existsSync14(featuresDir) ? readdirSync8(featuresDir).find((d) => new RegExp(`^20\\d{2}-\\d{2}-${slug}$`).test(d)) : undefined;
|
|
25970
26106
|
const targetDir = path22.join(featuresDir, existing ?? `${prefix}-${slug}`);
|
|
25971
26107
|
mkdirSync9(targetDir, { recursive: true });
|
|
25972
26108
|
const files = ["spec.md"];
|
|
@@ -26111,7 +26247,7 @@ var init_templates = __esm(() => {
|
|
|
26111
26247
|
});
|
|
26112
26248
|
|
|
26113
26249
|
// packages/workit-core/src/core/rules.ts
|
|
26114
|
-
import { existsSync as existsSync17, mkdirSync as mkdirSync11, readFileSync as readFileSync18, readdirSync as
|
|
26250
|
+
import { existsSync as existsSync17, mkdirSync as mkdirSync11, readFileSync as readFileSync18, readdirSync as readdirSync9, writeFileSync as writeFileSync11 } from "node:fs";
|
|
26115
26251
|
import path25 from "node:path";
|
|
26116
26252
|
var rulesDir = () => path25.join(configDir(), "rules"), parseRule = (markdown) => {
|
|
26117
26253
|
const fm = markdown.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
|
|
@@ -26141,7 +26277,7 @@ var rulesDir = () => path25.join(configDir(), "rules"), parseRule = (markdown) =
|
|
|
26141
26277
|
const result = [];
|
|
26142
26278
|
const dir = rulesDir();
|
|
26143
26279
|
if (existsSync17(dir)) {
|
|
26144
|
-
for (const entry of
|
|
26280
|
+
for (const entry of readdirSync9(dir)) {
|
|
26145
26281
|
const file = path25.join(dir, entry, "rule.md");
|
|
26146
26282
|
if (!existsSync17(file))
|
|
26147
26283
|
continue;
|
|
@@ -26264,7 +26400,7 @@ import {
|
|
|
26264
26400
|
mkdirSync as mkdirSync12,
|
|
26265
26401
|
mkdtempSync,
|
|
26266
26402
|
readFileSync as readFileSync19,
|
|
26267
|
-
readdirSync as
|
|
26403
|
+
readdirSync as readdirSync10,
|
|
26268
26404
|
renameSync as renameSync3,
|
|
26269
26405
|
rmSync as rmSync3,
|
|
26270
26406
|
statSync as statSync9,
|
|
@@ -26849,7 +26985,7 @@ async function postUpdate({
|
|
|
26849
26985
|
postedComment: true,
|
|
26850
26986
|
loggedMinutes: 0,
|
|
26851
26987
|
error: time3.error,
|
|
26852
|
-
retry: "
|
|
26988
|
+
retry: "workit_youtrack_log_time"
|
|
26853
26989
|
};
|
|
26854
26990
|
}
|
|
26855
26991
|
return { ok: true, issueId, postedComment: true, loggedMinutes: minutes };
|
|
@@ -27523,7 +27659,7 @@ var logger, readServerVersion = () => {
|
|
|
27523
27659
|
}
|
|
27524
27660
|
});
|
|
27525
27661
|
}, changelogCategorySchema, lifecycleTool = (action, description) => {
|
|
27526
|
-
registerTool(`
|
|
27662
|
+
registerTool(`workit_plan_${action}`, {
|
|
27527
27663
|
description,
|
|
27528
27664
|
inputSchema: {
|
|
27529
27665
|
plan_path: exports_external.string(),
|
|
@@ -27603,7 +27739,7 @@ var init_server3 = __esm(async () => {
|
|
|
27603
27739
|
name: "workit",
|
|
27604
27740
|
version: VERSION
|
|
27605
27741
|
});
|
|
27606
|
-
registerTool("
|
|
27742
|
+
registerTool("workit_verify", {
|
|
27607
27743
|
description: "Run project validation scripts (verify-project.sh). Defaults to Cursor workspace; pass workspace_root when the target repo differs.",
|
|
27608
27744
|
inputSchema: {
|
|
27609
27745
|
dry_run: exports_external.boolean().optional(),
|
|
@@ -27620,7 +27756,7 @@ var init_server3 = __esm(async () => {
|
|
|
27620
27756
|
workspace_root: cwd
|
|
27621
27757
|
}));
|
|
27622
27758
|
});
|
|
27623
|
-
registerTool("
|
|
27759
|
+
registerTool("workit_doctor", {
|
|
27624
27760
|
description: "Run the offline workit doctor and report installation health. Defaults to Cursor workspace; pass workspace_root when the target repo differs.",
|
|
27625
27761
|
inputSchema: {
|
|
27626
27762
|
workspace_root: workspaceRootSchema
|
|
@@ -27629,7 +27765,7 @@ var init_server3 = __esm(async () => {
|
|
|
27629
27765
|
const report = runDoctor({ host: "cursor", cwd: workspace_root });
|
|
27630
27766
|
return jsonResult(report);
|
|
27631
27767
|
});
|
|
27632
|
-
registerTool("
|
|
27768
|
+
registerTool("workit_pr_context", {
|
|
27633
27769
|
description: "Gather PR-ready repository context. On feature/* or bugfix/*, fetches and fast-forwards develop + current branch before diffing against develop.",
|
|
27634
27770
|
inputSchema: {
|
|
27635
27771
|
range: exports_external.string().optional(),
|
|
@@ -27693,7 +27829,7 @@ var init_server3 = __esm(async () => {
|
|
|
27693
27829
|
stderr: stderr || undefined
|
|
27694
27830
|
}));
|
|
27695
27831
|
});
|
|
27696
|
-
registerTool("
|
|
27832
|
+
registerTool("workit_pr_create", {
|
|
27697
27833
|
description: "Create GitLab MR or GitHub PR via glab/gh using vcs.json — requires confirmed: true",
|
|
27698
27834
|
inputSchema: {
|
|
27699
27835
|
confirmed: exports_external.boolean(),
|
|
@@ -27721,7 +27857,7 @@ var init_server3 = __esm(async () => {
|
|
|
27721
27857
|
}
|
|
27722
27858
|
return jsonResult(withWorkspace(workspace_root, { ...data }));
|
|
27723
27859
|
});
|
|
27724
|
-
registerTool("
|
|
27860
|
+
registerTool("workit_changelog_context", {
|
|
27725
27861
|
description: "Gather changelog update context. Defaults to Cursor workspace; pass workspace_root when the target repo differs.",
|
|
27726
27862
|
inputSchema: {
|
|
27727
27863
|
range: exports_external.string().optional(),
|
|
@@ -27738,7 +27874,7 @@ var init_server3 = __esm(async () => {
|
|
|
27738
27874
|
diff_stat: sections["Diff Stat"] ?? "",
|
|
27739
27875
|
files: sections["Changed Files"] ?? "",
|
|
27740
27876
|
unreleased,
|
|
27741
|
-
apply_hint: "ALWAYS merge via
|
|
27877
|
+
apply_hint: "ALWAYS merge via workit_changelog_apply — never hand-edit ### Added/Changed/… under [Unreleased].",
|
|
27742
27878
|
workspace_root: cwd,
|
|
27743
27879
|
stdout,
|
|
27744
27880
|
exitCode,
|
|
@@ -27753,7 +27889,7 @@ var init_server3 = __esm(async () => {
|
|
|
27753
27889
|
"Fixed",
|
|
27754
27890
|
"Security"
|
|
27755
27891
|
]);
|
|
27756
|
-
registerTool("
|
|
27892
|
+
registerTool("workit_changelog_apply", {
|
|
27757
27893
|
description: "Merge Keep a Changelog bullets into ## [Unreleased] under the correct ### category. Collapses duplicate category headings. NEVER append a second ### Added block.",
|
|
27758
27894
|
inputSchema: {
|
|
27759
27895
|
entries: exports_external.union([
|
|
@@ -27781,7 +27917,7 @@ var init_server3 = __esm(async () => {
|
|
|
27781
27917
|
unreleased: changelogUnreleasedStats(workspace_root, changelogPath)
|
|
27782
27918
|
}));
|
|
27783
27919
|
});
|
|
27784
|
-
registerTool("
|
|
27920
|
+
registerTool("workit_release_notes_context", {
|
|
27785
27921
|
description: "Gather release notes context. Defaults to Cursor workspace; pass workspace_root when the target repo differs.",
|
|
27786
27922
|
inputSchema: {
|
|
27787
27923
|
range_or_tag: exports_external.string().min(1),
|
|
@@ -27805,7 +27941,7 @@ var init_server3 = __esm(async () => {
|
|
|
27805
27941
|
stderr: stderr || undefined
|
|
27806
27942
|
}));
|
|
27807
27943
|
});
|
|
27808
|
-
registerTool("
|
|
27944
|
+
registerTool("workit_docs_context", {
|
|
27809
27945
|
description: "Gather docs refresh context. Defaults to Cursor workspace; pass workspace_root when the target repo differs.",
|
|
27810
27946
|
inputSchema: {
|
|
27811
27947
|
range: exports_external.string().optional(),
|
|
@@ -27825,7 +27961,7 @@ var init_server3 = __esm(async () => {
|
|
|
27825
27961
|
stderr: stderr || undefined
|
|
27826
27962
|
}));
|
|
27827
27963
|
});
|
|
27828
|
-
registerTool("
|
|
27964
|
+
registerTool("workit_git_context", {
|
|
27829
27965
|
description: "Gather git status for commit skill. Defaults to Cursor workspace; pass workspace_root when the target repo differs.",
|
|
27830
27966
|
inputSchema: {
|
|
27831
27967
|
paths: exports_external.array(exports_external.string()).optional(),
|
|
@@ -27835,7 +27971,7 @@ var init_server3 = __esm(async () => {
|
|
|
27835
27971
|
const data = gitContext(workspace_root, paths ?? []);
|
|
27836
27972
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
27837
27973
|
});
|
|
27838
|
-
registerTool("
|
|
27974
|
+
registerTool("workit_resolve_branch", {
|
|
27839
27975
|
description: "Resolve feature/* or bugfix/* branch from spec/plan. Returns current_branch, dirty, needs_checkout. No worktrees.",
|
|
27840
27976
|
inputSchema: {
|
|
27841
27977
|
spec_path: exports_external.string(),
|
|
@@ -27848,7 +27984,7 @@ var init_server3 = __esm(async () => {
|
|
|
27848
27984
|
return jsonResult({ error: data.error });
|
|
27849
27985
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
27850
27986
|
});
|
|
27851
|
-
registerTool("
|
|
27987
|
+
registerTool("workit_branch_setup", {
|
|
27852
27988
|
description: "In-place checkout of feature/* or bugfix/* branch. Stash required when dirty. NEVER uses worktrees.",
|
|
27853
27989
|
inputSchema: {
|
|
27854
27990
|
action: exports_external.enum(["setup", "reapply_stash"]).optional(),
|
|
@@ -27869,7 +28005,7 @@ var init_server3 = __esm(async () => {
|
|
|
27869
28005
|
return jsonResult({ error: data.error });
|
|
27870
28006
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
27871
28007
|
});
|
|
27872
|
-
registerTool("
|
|
28008
|
+
registerTool("workit_sdd_context", {
|
|
27873
28009
|
description: "Resolve canonical docs/<slug>/sdd/ paths; creates nothing (progress.md appears only on the first confirmed append). NEVER use .superpowers/sdd.",
|
|
27874
28010
|
inputSchema: {
|
|
27875
28011
|
slug: exports_external.string().optional(),
|
|
@@ -27882,7 +28018,7 @@ var init_server3 = __esm(async () => {
|
|
|
27882
28018
|
return jsonResult({ error: data.error });
|
|
27883
28019
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
27884
28020
|
});
|
|
27885
|
-
registerTool("
|
|
28021
|
+
registerTool("workit_sdd_task_brief", {
|
|
27886
28022
|
description: "Write task-N-brief.md under docs/<slug>/sdd/",
|
|
27887
28023
|
inputSchema: {
|
|
27888
28024
|
sdd_dir: exports_external.string(),
|
|
@@ -27894,7 +28030,7 @@ var init_server3 = __esm(async () => {
|
|
|
27894
28030
|
const slug = slugFromSddPath(sdd_dir);
|
|
27895
28031
|
if (!slug)
|
|
27896
28032
|
return jsonResult({ error: "could not derive slug — expected docs/<slug>/sdd/..." });
|
|
27897
|
-
const gate =
|
|
28033
|
+
const gate = assertSddControlGates(workspace_root, slug, { requireMenu: true, requireDocs: true }, cursorMutationContext(workspace_root));
|
|
27898
28034
|
if (!gate.ok)
|
|
27899
28035
|
return jsonResult({ error: gate.error, code: gate.code });
|
|
27900
28036
|
const data = sddTaskBrief({
|
|
@@ -27905,7 +28041,7 @@ var init_server3 = __esm(async () => {
|
|
|
27905
28041
|
});
|
|
27906
28042
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
27907
28043
|
});
|
|
27908
|
-
registerTool("
|
|
28044
|
+
registerTool("workit_sdd_review_package", {
|
|
27909
28045
|
description: "Write review diff under SDD dir between base and head SHAs",
|
|
27910
28046
|
inputSchema: {
|
|
27911
28047
|
sdd_dir: exports_external.string(),
|
|
@@ -27917,7 +28053,7 @@ var init_server3 = __esm(async () => {
|
|
|
27917
28053
|
const slug = slugFromSddPath(sdd_dir);
|
|
27918
28054
|
if (!slug)
|
|
27919
28055
|
return jsonResult({ error: "could not derive slug — expected docs/<slug>/sdd/..." });
|
|
27920
|
-
const gate =
|
|
28056
|
+
const gate = assertSddControlGates(workspace_root, slug, { requireMenu: true, requireDocs: true }, cursorMutationContext(workspace_root));
|
|
27921
28057
|
if (!gate.ok)
|
|
27922
28058
|
return jsonResult({ error: gate.error, code: gate.code });
|
|
27923
28059
|
const data = sddReviewPackage({
|
|
@@ -27930,7 +28066,7 @@ var init_server3 = __esm(async () => {
|
|
|
27930
28066
|
return jsonResult({ error: data.error });
|
|
27931
28067
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
27932
28068
|
});
|
|
27933
|
-
registerTool("
|
|
28069
|
+
registerTool("workit_sdd_append_progress", {
|
|
27934
28070
|
description: "Append one validated line to docs/<slug>/sdd/progress.md",
|
|
27935
28071
|
inputSchema: {
|
|
27936
28072
|
progress_path: exports_external.string(),
|
|
@@ -27941,7 +28077,7 @@ var init_server3 = __esm(async () => {
|
|
|
27941
28077
|
const slug = slugFromSddPath(progress_path);
|
|
27942
28078
|
if (!slug)
|
|
27943
28079
|
return jsonResult({ error: "could not derive slug — expected docs/<slug>/sdd/..." });
|
|
27944
|
-
const gate =
|
|
28080
|
+
const gate = assertSddControlGates(workspace_root, slug, { requireMenu: true, requireDocs: true }, cursorMutationContext(workspace_root));
|
|
27945
28081
|
if (!gate.ok)
|
|
27946
28082
|
return jsonResult({ error: gate.error, code: gate.code });
|
|
27947
28083
|
const data = sddAppendProgress({ progress_path, line, workspace_root });
|
|
@@ -27949,7 +28085,27 @@ var init_server3 = __esm(async () => {
|
|
|
27949
28085
|
return jsonResult({ error: data.error });
|
|
27950
28086
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
27951
28087
|
});
|
|
27952
|
-
registerTool("
|
|
28088
|
+
registerTool("workit_sdd_append_advisory", {
|
|
28089
|
+
description: "Append a validated advisory line to docs/<slug>/sdd/advisories.md (coordinator-owned)",
|
|
28090
|
+
inputSchema: {
|
|
28091
|
+
advisories_path: exports_external.string(),
|
|
28092
|
+
task_id: exports_external.number(),
|
|
28093
|
+
text: exports_external.string(),
|
|
28094
|
+
workspace_root: workspaceRootSchema
|
|
28095
|
+
}
|
|
28096
|
+
}, async ({ advisories_path, task_id, text, workspace_root }) => {
|
|
28097
|
+
const slug = slugFromSddPath(advisories_path);
|
|
28098
|
+
if (!slug)
|
|
28099
|
+
return jsonResult({ error: "could not derive slug — expected docs/<slug>/sdd/..." });
|
|
28100
|
+
const gate = assertSddControlGates(workspace_root, slug, { requireMenu: true, requireDocs: true }, cursorMutationContext(workspace_root));
|
|
28101
|
+
if (!gate.ok)
|
|
28102
|
+
return jsonResult({ error: gate.error, code: gate.code });
|
|
28103
|
+
const data = sddAppendAdvisory({ advisories_path, task_id, text, workspace_root });
|
|
28104
|
+
if ("error" in data)
|
|
28105
|
+
return jsonResult({ error: data.error, code: data.code });
|
|
28106
|
+
return jsonResult(withWorkspace(workspace_root, data));
|
|
28107
|
+
});
|
|
28108
|
+
registerTool("workit_docs_branch", {
|
|
27953
28109
|
description: "Resolve branch for spec/plan authors: keep current feature|bugfix or create from the configured base.",
|
|
27954
28110
|
inputSchema: {
|
|
27955
28111
|
plan_path: exports_external.string().optional(),
|
|
@@ -27962,7 +28118,7 @@ var init_server3 = __esm(async () => {
|
|
|
27962
28118
|
return jsonResult(withWorkspace(workspace_root, { error: data.error }));
|
|
27963
28119
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
27964
28120
|
});
|
|
27965
|
-
registerTool("
|
|
28121
|
+
registerTool("workit_docs_layout", {
|
|
27966
28122
|
description: "Canonical docs layout: prepare creates missing docs/ and docs/<slug>/; migrate detects legacy docs/superpowers/ and copies safe pairs after a native Migrate safely / Not now question",
|
|
27967
28123
|
inputSchema: {
|
|
27968
28124
|
action: exports_external.enum(["prepare", "migrate"]).default("prepare"),
|
|
@@ -28006,7 +28162,7 @@ var init_server3 = __esm(async () => {
|
|
|
28006
28162
|
return jsonResult(withWorkspace(workspace_root, { error: result.error }));
|
|
28007
28163
|
return jsonResult(withWorkspace(workspace_root, result));
|
|
28008
28164
|
});
|
|
28009
|
-
registerTool("
|
|
28165
|
+
registerTool("workit_docs_validate", {
|
|
28010
28166
|
description: "Hard-fail validate spec/plan headers, link, branch, task order; returns quality findings (hard/warning). Defaults to Cursor workspace; pass workspace_root when paths are relative to another repo.",
|
|
28011
28167
|
inputSchema: {
|
|
28012
28168
|
spec_path: exports_external.string(),
|
|
@@ -28020,7 +28176,7 @@ var init_server3 = __esm(async () => {
|
|
|
28020
28176
|
}
|
|
28021
28177
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
28022
28178
|
});
|
|
28023
|
-
registerTool("
|
|
28179
|
+
registerTool("workit_plan_tasks", {
|
|
28024
28180
|
description: "Parse plan ### Task N sections into structured tasks with section_text. Defaults to Cursor workspace; pass workspace_root when paths are relative to another repo.",
|
|
28025
28181
|
inputSchema: {
|
|
28026
28182
|
plan_path: exports_external.string(),
|
|
@@ -28044,7 +28200,7 @@ var init_server3 = __esm(async () => {
|
|
|
28044
28200
|
}
|
|
28045
28201
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
28046
28202
|
});
|
|
28047
|
-
registerTool("
|
|
28203
|
+
registerTool("workit_handoff_prompt", {
|
|
28048
28204
|
description: "Build copy-paste handoff prompt for next session. Defaults to Cursor workspace; pass workspace_root when spec/plan paths are relative to another repo.",
|
|
28049
28205
|
inputSchema: {
|
|
28050
28206
|
message: exports_external.string(),
|
|
@@ -28232,7 +28388,7 @@ var init_server3 = __esm(async () => {
|
|
|
28232
28388
|
return jsonResult({ error: data.error });
|
|
28233
28389
|
return jsonResult(data.data);
|
|
28234
28390
|
});
|
|
28235
|
-
registerTool("
|
|
28391
|
+
registerTool("workit_youtrack_verify_token", {
|
|
28236
28392
|
description: "Read-only YouTrack token test (GET /api/users/me). No work items created.",
|
|
28237
28393
|
inputSchema: {}
|
|
28238
28394
|
}, async () => {
|
|
@@ -28244,7 +28400,7 @@ var init_server3 = __esm(async () => {
|
|
|
28244
28400
|
return jsonResult({ error: data.error ?? "token invalid", ...data });
|
|
28245
28401
|
return jsonResult(data);
|
|
28246
28402
|
});
|
|
28247
|
-
registerTool("
|
|
28403
|
+
registerTool("workit_youtrack_parse_issue", {
|
|
28248
28404
|
description: "Parse YouTrack issue URL or bare id (e.g. NSR-40) into issueId",
|
|
28249
28405
|
inputSchema: {
|
|
28250
28406
|
issue_ref: exports_external.string().describe("YouTrack URL or issue id, e.g. https://…/issue/NSR-40 or NSR-40")
|
|
@@ -28255,7 +28411,7 @@ var init_server3 = __esm(async () => {
|
|
|
28255
28411
|
return jsonResult({ error: data.error });
|
|
28256
28412
|
return jsonResult(data);
|
|
28257
28413
|
});
|
|
28258
|
-
registerTool("
|
|
28414
|
+
registerTool("workit_youtrack_context", {
|
|
28259
28415
|
description: "YouTrack config, greeting, issue resolution (from issue_url/id or meetings)",
|
|
28260
28416
|
inputSchema: {
|
|
28261
28417
|
mode: exports_external.enum(["meetings", "task"]).optional(),
|
|
@@ -28284,7 +28440,7 @@ var init_server3 = __esm(async () => {
|
|
|
28284
28440
|
}
|
|
28285
28441
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
28286
28442
|
});
|
|
28287
|
-
registerTool("
|
|
28443
|
+
registerTool("workit_youtrack_parse_duration", {
|
|
28288
28444
|
description: "Parse duration text (e.g. 1h 30m) to integer minutes",
|
|
28289
28445
|
inputSchema: {
|
|
28290
28446
|
text: exports_external.string(),
|
|
@@ -28296,7 +28452,7 @@ var init_server3 = __esm(async () => {
|
|
|
28296
28452
|
return jsonResult({ error: data.error });
|
|
28297
28453
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
28298
28454
|
});
|
|
28299
|
-
registerTool("
|
|
28455
|
+
registerTool("workit_youtrack_log_time", {
|
|
28300
28456
|
description: "POST YouTrack work item (time only, no comment)",
|
|
28301
28457
|
inputSchema: {
|
|
28302
28458
|
issueId: exports_external.string(),
|
|
@@ -28317,7 +28473,7 @@ var init_server3 = __esm(async () => {
|
|
|
28317
28473
|
return jsonResult({ error: data.error });
|
|
28318
28474
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
28319
28475
|
});
|
|
28320
|
-
registerTool("
|
|
28476
|
+
registerTool("workit_youtrack_draft", {
|
|
28321
28477
|
description: "Build ES-CL comment markdown without posting (envelope only by default)",
|
|
28322
28478
|
inputSchema: {
|
|
28323
28479
|
issueId: exports_external.string(),
|
|
@@ -28329,7 +28485,7 @@ var init_server3 = __esm(async () => {
|
|
|
28329
28485
|
facts: exports_external.record(exports_external.any()).optional()
|
|
28330
28486
|
}
|
|
28331
28487
|
}, async (input) => jsonResult(buildDraft(input)));
|
|
28332
|
-
registerTool("
|
|
28488
|
+
registerTool("workit_youtrack_post", {
|
|
28333
28489
|
description: "Post YouTrack comment and optional time — requires confirmed: true",
|
|
28334
28490
|
inputSchema: {
|
|
28335
28491
|
confirmed: exports_external.boolean(),
|
|
@@ -28350,7 +28506,7 @@ var init_server3 = __esm(async () => {
|
|
|
28350
28506
|
return jsonResult({ error: data.error });
|
|
28351
28507
|
return jsonResult(withWorkspace(workspace_root, data));
|
|
28352
28508
|
});
|
|
28353
|
-
registerTool("
|
|
28509
|
+
registerTool("workit_present_ascii", {
|
|
28354
28510
|
description: "Render deterministic ASCII UI wireframe from JSON spec",
|
|
28355
28511
|
inputSchema: {
|
|
28356
28512
|
title: exports_external.string().optional(),
|
|
@@ -28363,7 +28519,7 @@ var init_server3 = __esm(async () => {
|
|
|
28363
28519
|
return jsonResult({ error: data.error });
|
|
28364
28520
|
return jsonResult(data.data);
|
|
28365
28521
|
});
|
|
28366
|
-
registerTool("
|
|
28522
|
+
registerTool("workit_present_flow", {
|
|
28367
28523
|
description: "Render mermaid flowchart from JSON nodes/edges",
|
|
28368
28524
|
inputSchema: {
|
|
28369
28525
|
title: exports_external.string().optional(),
|
|
@@ -28385,7 +28541,7 @@ var init_server3 = __esm(async () => {
|
|
|
28385
28541
|
return jsonResult({ error: data.error });
|
|
28386
28542
|
return jsonResult(data.data);
|
|
28387
28543
|
});
|
|
28388
|
-
registerTool("
|
|
28544
|
+
registerTool("workit_flow_status", {
|
|
28389
28545
|
description: "Read the spec/plan approval flow state for a workflow; on first read it records flow activation and canonical document paths (FG-01)",
|
|
28390
28546
|
inputSchema: {
|
|
28391
28547
|
plan_path: exports_external.string().optional(),
|
|
@@ -28421,7 +28577,7 @@ var init_server3 = __esm(async () => {
|
|
|
28421
28577
|
flow_path: `docs/${slug}/sdd/flow.json`
|
|
28422
28578
|
});
|
|
28423
28579
|
});
|
|
28424
|
-
registerTool("
|
|
28580
|
+
registerTool("workit_spec_approve", {
|
|
28425
28581
|
description: "Advance spec status with the Cursor policy-only confirmation: draft -> approved in a single call. The self-review validation runs automatically inside the transition; only the final approval asks for your confirmation. Cursor records attested: false (the MCP cannot observe AskQuestion results); there is no evidence argument (CA-42).",
|
|
28426
28582
|
inputSchema: {
|
|
28427
28583
|
spec_path: exports_external.string(),
|
|
@@ -28437,7 +28593,7 @@ var init_server3 = __esm(async () => {
|
|
|
28437
28593
|
return jsonResult({ error: result.error, code: result.code });
|
|
28438
28594
|
return jsonResult({ spec: spec_path, status: readFlowState(workspace, slug).spec.status });
|
|
28439
28595
|
});
|
|
28440
|
-
registerTool("
|
|
28596
|
+
registerTool("workit_plan_approve", {
|
|
28441
28597
|
description: "Advance plan status with the Cursor policy-only confirmation: draft -> approved in a single call. The self-review validation runs automatically inside the transition; only the final approval asks for your confirmation. Requires approved spec. Cursor records attested: false; there is no evidence argument (CA-42).",
|
|
28442
28598
|
inputSchema: {
|
|
28443
28599
|
plan_path: exports_external.string(),
|
|
@@ -28453,7 +28609,7 @@ var init_server3 = __esm(async () => {
|
|
|
28453
28609
|
return jsonResult({ error: result.error, code: result.code });
|
|
28454
28610
|
return jsonResult({ plan: plan_path, status: readFlowState(workspace, slug).plan.status });
|
|
28455
28611
|
});
|
|
28456
|
-
registerTool("
|
|
28612
|
+
registerTool("workit_plan_menu", {
|
|
28457
28613
|
description: "Record the answered post-plan choice menu with the Cursor policy-only confirmation. subagent-driven is rejected as unsupported on Cursor (CA-42); there is no evidence argument.",
|
|
28458
28614
|
inputSchema: {
|
|
28459
28615
|
plan_path: exports_external.string(),
|
|
@@ -28473,7 +28629,7 @@ var init_server3 = __esm(async () => {
|
|
|
28473
28629
|
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.");
|
|
28474
28630
|
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.");
|
|
28475
28631
|
lifecycleTool("complete", "Complete a running plan with the Cursor policy-only confirmation: active/paused -> completed, after the SDD ledger is complete and repository verification passes. The MCP cannot observe AskQuestion results, so it records attested: false; there is no evidence argument (CA-42). Requires plan_path and workspace_root.");
|
|
28476
|
-
registerTool("
|
|
28632
|
+
registerTool("workit_docs_repo_link", {
|
|
28477
28633
|
description: "Link the component docs repo in the toolkit config",
|
|
28478
28634
|
inputSchema: {
|
|
28479
28635
|
path: exports_external.string(),
|
|
@@ -28485,11 +28641,11 @@ var init_server3 = __esm(async () => {
|
|
|
28485
28641
|
return jsonResult({ error: result.error });
|
|
28486
28642
|
return jsonResult({ path: result.path });
|
|
28487
28643
|
});
|
|
28488
|
-
registerTool("
|
|
28644
|
+
registerTool("workit_docs_list", {
|
|
28489
28645
|
description: "List local specs with docs-repo promotion status",
|
|
28490
28646
|
inputSchema: { workspace_root: workspaceRootSchema }
|
|
28491
28647
|
}, async ({ workspace_root }) => jsonResult(listSpecs(workspace_root)));
|
|
28492
|
-
registerTool("
|
|
28648
|
+
registerTool("workit_docs_promote", {
|
|
28493
28649
|
description: "Promote a spec (+plan) to the linked docs repo with quality gate",
|
|
28494
28650
|
inputSchema: {
|
|
28495
28651
|
slug: exports_external.string(),
|
|
@@ -28507,11 +28663,11 @@ var init_server3 = __esm(async () => {
|
|
|
28507
28663
|
index_updated: result.index_updated
|
|
28508
28664
|
});
|
|
28509
28665
|
});
|
|
28510
|
-
registerTool("
|
|
28666
|
+
registerTool("workit_template_list", {
|
|
28511
28667
|
description: "List editable templates with their source",
|
|
28512
28668
|
inputSchema: { workspace_root: workspaceRootSchema }
|
|
28513
28669
|
}, async () => jsonResult({ templates: listTemplates() }));
|
|
28514
|
-
registerTool("
|
|
28670
|
+
registerTool("workit_template_edit", {
|
|
28515
28671
|
description: "Write an edited template to the toolkit config dir",
|
|
28516
28672
|
inputSchema: {
|
|
28517
28673
|
name: exports_external.enum(["issue-update", "greeting", "headers"]),
|
|
@@ -28524,11 +28680,11 @@ var init_server3 = __esm(async () => {
|
|
|
28524
28680
|
return jsonResult({ error: result.error });
|
|
28525
28681
|
return jsonResult({ path: result.path });
|
|
28526
28682
|
});
|
|
28527
|
-
registerTool("
|
|
28683
|
+
registerTool("workit_rule_list", {
|
|
28528
28684
|
description: "List canonical rules (config) with platforms",
|
|
28529
28685
|
inputSchema: { workspace_root: workspaceRootSchema }
|
|
28530
28686
|
}, async () => jsonResult({ rules: listRules() }));
|
|
28531
|
-
registerTool("
|
|
28687
|
+
registerTool("workit_rule_edit", {
|
|
28532
28688
|
description: "Write a canonical rule to the toolkit config dir",
|
|
28533
28689
|
inputSchema: {
|
|
28534
28690
|
name: exports_external.string(),
|