@brainervirus/workit-cursor 0.8.0 → 0.8.2
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 +8 -8
- package/assets/templates/execution-contract.md +11 -0
- package/assets/templates/superpowers-doc-contract.md +2 -0
- package/dist/cursor-session-start.js +121 -19
- package/dist/mcp-server.js +1218 -538
- package/hooks/hooks-cursor.json +1 -1
- package/mcp.json +1 -1
- package/package.json +2 -2
- package/skills/wk-implement/SKILL.md +1 -1
package/README.md
CHANGED
|
@@ -23,7 +23,7 @@ npx @brainervirus/workit-cli init
|
|
|
23
23
|
"command": "npx",
|
|
24
24
|
"args": [
|
|
25
25
|
"-y",
|
|
26
|
-
"--package=@brainervirus/workit-cursor@
|
|
26
|
+
"--package=@brainervirus/workit-cursor@0.8.0",
|
|
27
27
|
"workit-cursor-mcp",
|
|
28
28
|
"${workspaceFolder}"
|
|
29
29
|
]
|
|
@@ -35,17 +35,17 @@ npx @brainervirus/workit-cli init
|
|
|
35
35
|
### Requirements
|
|
36
36
|
|
|
37
37
|
- **Node.js ≥ 22** — the MCP server and session-start hook are self-contained Node bundles invoked through `npx`.
|
|
38
|
-
- **Network** — `npx -y …@
|
|
38
|
+
- **Network** — `npx -y …@0.8.0` resolves and downloads the package on first run in each environment; a machine that cannot reach the npm registry cannot start the MCP server or hook (see [Runtime](#runtime)).
|
|
39
39
|
|
|
40
40
|
## What it provides
|
|
41
41
|
|
|
42
|
-
- MCP server exposing the `workflow_*` tools (branch setup, PR create/context, docs validate/promote, YouTrack post/log/time, templates, rules, presentation, doctor, handoff).
|
|
42
|
+
- MCP server exposing the `workflow_*` tools (branch setup, PR create/context, docs validate/promote, YouTrack post/log/time, templates, rules, presentation, doctor, handoff, and plan lifecycle `workflow_plan_pause`/`resume`/`complete`).
|
|
43
43
|
- Session-start contract hook.
|
|
44
44
|
- 4 rules and 12 `wk-*` skills (plus 14 sanitized Superpowers skills).
|
|
45
45
|
|
|
46
46
|
## Host limitations
|
|
47
47
|
|
|
48
|
-
Cursor adapts workit through policy-only confirmation: approvals and
|
|
48
|
+
Cursor adapts workit through policy-only confirmation: approvals and lifecycle transitions are recorded as policy decisions (`attested: false`) rather than fabricated delegated identity, and subagent-driven plan execution is not supported on this host. 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 runs delegated tasks; see the root [README](../../README.md#host-capabilities) for the full host-capability matrix.
|
|
49
49
|
|
|
50
50
|
## Configuration
|
|
51
51
|
|
|
@@ -59,11 +59,11 @@ Cursor adapts workit through policy-only confirmation: approvals and commits are
|
|
|
59
59
|
|
|
60
60
|
Cursor launches the MCP server and session-start hook through `npx`, so the shipped manifests contain no repository-relative `dist` paths:
|
|
61
61
|
|
|
62
|
-
- **MCP server** — `npx -y --package=@brainervirus/workit-cursor@
|
|
63
|
-
- **Session-start hook** — `npx -y --package=@brainervirus/workit-cursor@
|
|
62
|
+
- **MCP server** — `npx -y --package=@brainervirus/workit-cursor@0.8.0 workit-cursor-mcp ${workspaceFolder}`. It speaks the MCP stdio protocol; `stdout` is reserved for protocol messages and diagnostics go to `stderr`.
|
|
63
|
+
- **Session-start hook** — `npx -y --package=@brainervirus/workit-cursor@0.8.0 workit-cursor-session-start`. It emits valid hook output and a diagnostic on runtime failure, and remains fail-open where Cursor's hook contract requires startup continuity.
|
|
64
64
|
- `npx` startup or network failure is surfaced by Cursor as an MCP/hook startup failure; Workit never silently substitutes stale local runtime code.
|
|
65
65
|
|
|
66
|
-
The
|
|
66
|
+
The runtime runs from the exact reviewed pin `@0.8.0`: Cursor reviews plugin metadata from Git, while npm serves the pinned runtime. Bumping the pin is a deliberate reviewed update, made only after the target npm version is public — never a mutable `latest` dist-tag. See [Update review](#update-review).
|
|
67
67
|
|
|
68
68
|
## Security and data handling
|
|
69
69
|
|
|
@@ -96,7 +96,7 @@ The repository root carries `.cursor-plugin/marketplace.json`, indexing `package
|
|
|
96
96
|
|
|
97
97
|
- **Installing from Marketplace** — a Marketplace admin adds the repository URL through Cursor's authenticated publisher flow; end users then install the plugin from the Cursor Marketplace UI, which reads `.cursor-plugin/plugin.json` and the tracked components directly from Git.
|
|
98
98
|
- **Submission** — Marketplace submission is a separate, later authenticated action at `https://cursor.com/marketplace/publish`. It is **not** performed here and no publication or acceptance is claimed; the repository is kept validated and submission-ready.
|
|
99
|
-
- **Update review** — Git plugin metadata (manifest, rules, skills, assets) is reviewed by Cursor on Marketplace updates, while the npm runtime
|
|
99
|
+
- **Update review** — Git plugin metadata (manifest, rules, skills, assets) is reviewed by Cursor on Marketplace updates, while the npm runtime is pinned to the exact reviewed `@0.8.0`. Bumping that pin in `mcp.json` / `hooks-cursor.json` is a deliberate reviewed change: only after the target npm version is public, never a mutable `latest` dist-tag.
|
|
100
100
|
- **Troubleshooting** — `workit doctor` (or the `workflow_doctor` tool) reports installation health including runtime, token, VCS/YouTrack, and log-writability checks; it exits nonzero on failure. An MCP/hook startup failure with no network is an `npx`/registry reachability issue, not a Workit defect.
|
|
101
101
|
|
|
102
102
|
## Docs
|
|
@@ -5,6 +5,17 @@ Load `using-superpowers`, `subagent-driven-development`, `test-driven-developmen
|
|
|
5
5
|
**Branch:** <BRANCH>
|
|
6
6
|
**SDD:** `<SDD_DIR>`
|
|
7
7
|
|
|
8
|
+
## Handoff destination
|
|
9
|
+
|
|
10
|
+
This session is a handoff destination for a continued plan. The originating session already recorded the post-plan menu choice; present exactly these four choices and never re-offer the originating handoff option:
|
|
11
|
+
|
|
12
|
+
- Subagent-driven
|
|
13
|
+
- Inline
|
|
14
|
+
- Review spec first
|
|
15
|
+
- Review plan first
|
|
16
|
+
|
|
17
|
+
<workflow-handoff-destination>true</workflow-handoff-destination>
|
|
18
|
+
|
|
8
19
|
## Hard gates
|
|
9
20
|
|
|
10
21
|
- The parent is coordinator-only: it does not edit product code or perform delegated exploration.
|
|
@@ -63,6 +63,8 @@ On success, use native `question` / Cursor `AskQuestion` with exactly these opti
|
|
|
63
63
|
|
|
64
64
|
Never emit Superpowers text beginning “Two execution options”.
|
|
65
65
|
|
|
66
|
+
A handoff destination session (the seeded contract carries `<workflow-handoff-destination>true</workflow-handoff-destination>`) presents exactly four choices — Subagent-driven, Inline, Review spec first, Review plan first — and never re-offers the originating handoff option.
|
|
67
|
+
|
|
66
68
|
- Specs/plans must follow `templates/spec-template.md` / `templates/plan-template.md` (mandated diagrams, tables, CA-XX).
|
|
67
69
|
|
|
68
70
|
## Doc delivery
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
3
|
// packages/workit-cursor/hooks/session-start.ts
|
|
4
|
-
import { existsSync, readFileSync } from "node:fs";
|
|
5
|
-
import
|
|
4
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2 } from "node:fs";
|
|
5
|
+
import path3 from "node:path";
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
|
|
8
8
|
// packages/workit-core/src/core/logger.ts
|
|
@@ -277,35 +277,91 @@ var DEFAULTS = {
|
|
|
277
277
|
}
|
|
278
278
|
};
|
|
279
279
|
|
|
280
|
+
// packages/workit-core/src/core/menu.ts
|
|
281
|
+
import { existsSync, readFileSync, readdirSync as readdirSync2 } from "node:fs";
|
|
282
|
+
import path2 from "node:path";
|
|
283
|
+
var SOURCE_MENU_LABELS = [
|
|
284
|
+
"Subagent-driven",
|
|
285
|
+
"Inline",
|
|
286
|
+
"Handoff",
|
|
287
|
+
"Review spec first",
|
|
288
|
+
"Review plan first"
|
|
289
|
+
];
|
|
290
|
+
var DESTINATION_MENU_LABELS = [
|
|
291
|
+
"Subagent-driven",
|
|
292
|
+
"Inline",
|
|
293
|
+
"Review spec first",
|
|
294
|
+
"Review plan first"
|
|
295
|
+
];
|
|
296
|
+
var HANDOFF_DESTINATION_MARKER = "<workflow-handoff-destination>true</workflow-handoff-destination>";
|
|
297
|
+
var findMarkedDestinations = (root) => {
|
|
298
|
+
const docsDir = path2.join(root, "docs");
|
|
299
|
+
if (!existsSync(docsDir))
|
|
300
|
+
return [];
|
|
301
|
+
const slugs = [];
|
|
302
|
+
for (const slug of readdirSync2(docsDir)) {
|
|
303
|
+
const file = path2.join(docsDir, slug, "sdd", "flow.json");
|
|
304
|
+
if (!existsSync(file))
|
|
305
|
+
continue;
|
|
306
|
+
try {
|
|
307
|
+
const parsed = JSON.parse(readFileSync(file, "utf8"));
|
|
308
|
+
if (parsed.handoff_destination === true)
|
|
309
|
+
slugs.push(slug);
|
|
310
|
+
} catch {
|
|
311
|
+
continue;
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
return slugs;
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
// packages/workit-core/src/core/reminder.ts
|
|
318
|
+
var SOURCE_MENU_LABELS_DISPLAY = SOURCE_MENU_LABELS.map((label) => label === "Handoff" ? "Handoff (new session only)" : label);
|
|
319
|
+
var REMINDER_TEXT = `<workflow-contract-reminder>
|
|
320
|
+
- Bounded user choices → call the native \`question\` tool (never A/B/C or 1/2/3 lists in prose).
|
|
321
|
+
- After a plan is approved → native \`question\` menu with exactly: ${SOURCE_MENU_LABELS_DISPLAY.join(", ")}.
|
|
322
|
+
- Tools with \`confirmed\` → call them; never fabricate their result.
|
|
323
|
+
- Before the first \`workflow_spec_approve\`/\`workflow_plan_approve\` (self-review) run the superpowers writing-plans Self-Review checklist: spec coverage (every spec requirement maps to a task), placeholder scan, type consistency; fix findings inline.
|
|
324
|
+
- Delivering docs → clickable markdown link \`[spec.md](docs/<slug>/spec.md)\` + 3-5 bullet summary.
|
|
325
|
+
</workflow-contract-reminder>`;
|
|
326
|
+
var DESTINATION_REMINDER_TEXT = `<workflow-contract-reminder>
|
|
327
|
+
- Bounded user choices → call the native \`question\` tool (never A/B/C or 1/2/3 lists in prose).
|
|
328
|
+
- This session is a handoff destination: present the post-plan menu with exactly: ${DESTINATION_MENU_LABELS.join(", ")}.
|
|
329
|
+
- Tools with \`confirmed\` → call them; never fabricate their result.
|
|
330
|
+
- Before the first \`workflow_spec_approve\`/\`workflow_plan_approve\` (self-review) run the superpowers writing-plans Self-Review checklist: spec coverage (every spec requirement maps to a task), placeholder scan, type consistency; fix findings inline.
|
|
331
|
+
- Delivering docs → clickable markdown link \`[spec.md](docs/<slug>/spec.md)\` + 3-5 bullet summary.
|
|
332
|
+
${HANDOFF_DESTINATION_MARKER}
|
|
333
|
+
</workflow-contract-reminder>`;
|
|
334
|
+
var reminderTextFor = (destination) => destination ? DESTINATION_REMINDER_TEXT : REMINDER_TEXT;
|
|
335
|
+
|
|
280
336
|
// packages/workit-cursor/hooks/session-start.ts
|
|
281
337
|
var logger = createLogger({
|
|
282
338
|
stderr: (event) => process.stderr.write(`${JSON.stringify(event)}
|
|
283
339
|
`)
|
|
284
340
|
});
|
|
285
|
-
var hookDir =
|
|
286
|
-
var pluginDir =
|
|
287
|
-
var marker =
|
|
341
|
+
var hookDir = path3.dirname(fileURLToPath(import.meta.url));
|
|
342
|
+
var pluginDir = path3.resolve(hookDir, "..");
|
|
343
|
+
var marker = path3.join(pluginDir, ".workflow-toolkit-root");
|
|
288
344
|
setDiagnosticLogger(logger);
|
|
289
345
|
logger.info(EVENT.initialization, { host: "cursor-hook", hook_dir: hookDir });
|
|
290
346
|
var resolveRepoRoot = () => {
|
|
291
|
-
if (process.env.WORKFLOW_TOOLKIT_ROOT &&
|
|
347
|
+
if (process.env.WORKFLOW_TOOLKIT_ROOT && existsSync2(path3.join(process.env.WORKFLOW_TOOLKIT_ROOT, "templates"))) {
|
|
292
348
|
return process.env.WORKFLOW_TOOLKIT_ROOT;
|
|
293
349
|
}
|
|
294
|
-
if (
|
|
295
|
-
return
|
|
350
|
+
if (existsSync2(marker)) {
|
|
351
|
+
return readFileSync2(marker, "utf8").replace(/\n+$/, "");
|
|
296
352
|
}
|
|
297
|
-
const ownAssets =
|
|
298
|
-
if (
|
|
353
|
+
const ownAssets = path3.join(pluginDir, "assets");
|
|
354
|
+
if (existsSync2(path3.join(ownAssets, "templates"))) {
|
|
299
355
|
return ownAssets;
|
|
300
356
|
}
|
|
301
|
-
return
|
|
357
|
+
return path3.resolve(hookDir, "../../workit-core");
|
|
302
358
|
};
|
|
303
359
|
var body = null;
|
|
304
360
|
var repoRoot = resolveRepoRoot();
|
|
305
361
|
try {
|
|
306
|
-
const contract =
|
|
307
|
-
if (
|
|
308
|
-
body =
|
|
362
|
+
const contract = path3.join(repoRoot, "templates", "superpowers-doc-contract.md");
|
|
363
|
+
if (existsSync2(contract))
|
|
364
|
+
body = readFileSync2(contract, "utf8");
|
|
309
365
|
} catch (err) {
|
|
310
366
|
logger.error(EVENT.hooks, { boundary: "session-start", root: repoRoot, ...errorDetail(err) });
|
|
311
367
|
body = null;
|
|
@@ -315,7 +371,45 @@ if (body === null) {
|
|
|
315
371
|
`);
|
|
316
372
|
process.exit(0);
|
|
317
373
|
}
|
|
318
|
-
var
|
|
374
|
+
var HOOK_READ_TIMEOUT_MS = Number(process.env.WORKFLOW_HOOK_READ_TIMEOUT_MS ?? "2000");
|
|
375
|
+
var readHookInput = (timeoutMs) => {
|
|
376
|
+
if (process.stdin.isTTY)
|
|
377
|
+
return Promise.resolve({});
|
|
378
|
+
return new Promise((resolve) => {
|
|
379
|
+
let buffer = "";
|
|
380
|
+
let settled = false;
|
|
381
|
+
const timer = setTimeout(() => settle({}), timeoutMs);
|
|
382
|
+
const onData = (chunk) => {
|
|
383
|
+
buffer += String(chunk);
|
|
384
|
+
const text = buffer.trim();
|
|
385
|
+
if (!text)
|
|
386
|
+
return;
|
|
387
|
+
try {
|
|
388
|
+
const parsed = JSON.parse(text);
|
|
389
|
+
settle(typeof parsed === "object" && parsed !== null ? parsed : {});
|
|
390
|
+
} catch {}
|
|
391
|
+
};
|
|
392
|
+
const onEnd = () => settle({});
|
|
393
|
+
function settle(value) {
|
|
394
|
+
if (settled)
|
|
395
|
+
return;
|
|
396
|
+
settled = true;
|
|
397
|
+
clearTimeout(timer);
|
|
398
|
+
process.stdin.off("data", onData);
|
|
399
|
+
process.stdin.off("end", onEnd);
|
|
400
|
+
resolve(value);
|
|
401
|
+
}
|
|
402
|
+
process.stdin.on("data", onData);
|
|
403
|
+
process.stdin.on("end", onEnd);
|
|
404
|
+
});
|
|
405
|
+
};
|
|
406
|
+
var isDestination = (input) => {
|
|
407
|
+
const roots = Array.isArray(input.workspace_roots) ? input.workspace_roots : [];
|
|
408
|
+
return roots.some((root) => typeof root === "string" && findMarkedDestinations(root).length > 0);
|
|
409
|
+
};
|
|
410
|
+
var main = async () => {
|
|
411
|
+
const reminder = reminderTextFor(isDestination(await readHookInput(HOOK_READ_TIMEOUT_MS)));
|
|
412
|
+
const context = `<workflow-toolkit-askquestion-hard-gate>
|
|
319
413
|
HARD-GATE: Any user choice with options → call Cursor AskQuestion directly with workflow-specific copy. NEVER A/B/C in chat. Overrides Superpowers brainstorming conversational options.
|
|
320
414
|
</workflow-toolkit-askquestion-hard-gate>
|
|
321
415
|
|
|
@@ -340,12 +434,20 @@ ${body}
|
|
|
340
434
|
</workflow-toolkit-superpowers-doc-contract>
|
|
341
435
|
|
|
342
436
|
<workflow-toolkit-reminder>
|
|
343
|
-
HARD-GATE: Bounded user choices → call Cursor AskQuestion directly (never A/B/C or 1/2/3 lists in prose).
|
|
344
|
-
|
|
437
|
+
HARD-GATE: Bounded user choices → call Cursor AskQuestion directly (never A/B/C or 1/2/3 lists in prose).
|
|
438
|
+
|
|
439
|
+
${reminder}
|
|
345
440
|
</workflow-toolkit-reminder>`;
|
|
346
|
-
process.stdout.write(JSON.stringify({ additional_context: context }, null, 2) + `
|
|
441
|
+
process.stdout.write(JSON.stringify({ additional_context: context }, null, 2) + `
|
|
347
442
|
`);
|
|
348
|
-
process.exit(0);
|
|
443
|
+
process.exit(0);
|
|
444
|
+
};
|
|
445
|
+
main().catch((err) => {
|
|
446
|
+
logger.error(EVENT.hooks, { boundary: "session-start", ...errorDetail(err) });
|
|
447
|
+
process.stdout.write(`{}
|
|
448
|
+
`);
|
|
449
|
+
process.exit(0);
|
|
450
|
+
});
|
|
349
451
|
export {
|
|
350
452
|
logger
|
|
351
453
|
};
|