@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.
@@ -2,7 +2,7 @@
2
2
  "name": "workit",
3
3
  "displayName": "Workit",
4
4
  "description": "OpenCode-style verify, PR, changelog, commit, and session handoff for Cursor",
5
- "version": "0.8.0",
5
+ "version": "0.8.2",
6
6
  "author": {
7
7
  "name": "Cristhofer Pincetti"
8
8
  },
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@latest",
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 …@latest` 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)).
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 commits are recorded as policy decisions (`attested: false`) rather than fabricated delegated identity, and subagent-driven plan execution is not supported on this host. OpenCode records native `question` receipts and runs delegated tasks; see the root [README](../../README.md#host-capabilities) for the full host-capability matrix.
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@latest 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@latest 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.
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 `@latest` tag means the runtime updates independently of the Marketplace metadata: Cursor reviews plugin metadata from Git, while npm serves the runtime. See [Update review](#update-review).
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 tagged `@latest` updates independently. Reviewing an npm runtime change is a manual step: pin `--package=@brainervirus/workit-cursor@<version>` in `mcp.json` / `hooks-cursor.json` if you need immutable reviewed code instead of `@latest`.
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 path2 from "node:path";
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 = path2.dirname(fileURLToPath(import.meta.url));
286
- var pluginDir = path2.resolve(hookDir, "..");
287
- var marker = path2.join(pluginDir, ".workflow-toolkit-root");
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 && existsSync(path2.join(process.env.WORKFLOW_TOOLKIT_ROOT, "templates"))) {
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 (existsSync(marker)) {
295
- return readFileSync(marker, "utf8").replace(/\n+$/, "");
350
+ if (existsSync2(marker)) {
351
+ return readFileSync2(marker, "utf8").replace(/\n+$/, "");
296
352
  }
297
- const ownAssets = path2.join(pluginDir, "assets");
298
- if (existsSync(path2.join(ownAssets, "templates"))) {
353
+ const ownAssets = path3.join(pluginDir, "assets");
354
+ if (existsSync2(path3.join(ownAssets, "templates"))) {
299
355
  return ownAssets;
300
356
  }
301
- return path2.resolve(hookDir, "../../workit-core");
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 = path2.join(repoRoot, "templates", "superpowers-doc-contract.md");
307
- if (existsSync(contract))
308
- body = readFileSync(contract, "utf8");
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 context = `<workflow-toolkit-askquestion-hard-gate>
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). After a plan is approved → AskQuestion menu with: Subagent-driven, Inline, Handoff (new session only), Review spec first, Review plan first. Tools with confirmed → call them; never fabricate results.
344
- Delivering docs → clickable markdown link (docs/<slug>/spec.md) + 3-5 bullet summary.
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
  };