@brainervirus/workit-core 0.8.0 → 0.8.1

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-core",
3
- "version": "0.8.0",
3
+ "version": "0.8.1",
4
4
  "private": false,
5
5
  "description": "Workit — workflow rails for agentic coding: specs, plans, YouTrack, CI-gated commits (shared core)",
6
6
  "keywords": [
@@ -3,4 +3,4 @@
3
3
  # Launch the published package's MCP bin through npx, never a repo-relative
4
4
  # dist or share clone; a startup/network failure surfaces via npx's nonzero exit.
5
5
  set -euo pipefail
6
- exec npx -y --package=@brainervirus/workit-cursor@latest workit-cursor-mcp "$@"
6
+ exec npx -y --package=@brainervirus/workit-cursor@0.8.0 workit-cursor-mcp "$@"
@@ -11,7 +11,7 @@ import path from "node:path";
11
11
  import { SUPPORT_MATRIX } from "./support-matrix";
12
12
  import { EVENT } from "./boundary";
13
13
  import { getDiagnosticLogger, isConfigObject } from "./config";
14
- import { isWorkitPlugin } from "./registration";
14
+ import { CURSOR_RUNTIME_PACKAGE, cursorHooksEntry, isWorkitPlugin } from "./registration";
15
15
  import { resolveWorkspaceFrom } from "./workspaces";
16
16
  import { validateCursorSkills } from "./skill-manifests";
17
17
 
@@ -422,7 +422,7 @@ const registeredCursorLauncher = (res: Resolved): CursorLauncher | null | "inval
422
422
  // CA-17: exact positional tokens — a substring match would accept
423
423
  // `@latest-alpha` or `workit-cursor-mcp-foo`.
424
424
  if (args[0] !== "-y") return "invalid";
425
- if (args[1] !== "--package=@brainervirus/workit-cursor@latest") return "invalid";
425
+ if (args[1] !== `--package=${CURSOR_RUNTIME_PACKAGE}`) return "invalid";
426
426
  if (args[2] !== "workit-cursor-mcp") return "invalid";
427
427
  return { kind: "npx" };
428
428
  }
@@ -434,6 +434,26 @@ const registeredCursorLauncher = (res: Resolved): CursorLauncher | null | "inval
434
434
  };
435
435
  };
436
436
 
437
+ // CA-17: the canonical session-start hook runs the published package through
438
+ // npx as a single command string (Cursor's documented hook format). The doctor
439
+ // validates its shape exactly like the MCP launcher — no substring matching,
440
+ // no version abstraction.
441
+ const canonicalCursorHook = cursorHooksEntry("").command;
442
+
443
+ const registeredCursorHook = (res: Resolved): string | null | "invalid" => {
444
+ const hooksFile = path.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
445
+ if (!existsSync(hooksFile)) return "invalid";
446
+ const config = readJson(hooksFile);
447
+ // Unlike mcp.json, the hook file is not covered by checkMalformedConfig, so
448
+ // an unparseable hook must fail the launcher check rather than slip through.
449
+ if (!config) return "invalid";
450
+ const sessionStart = config.hooks?.sessionStart;
451
+ if (!Array.isArray(sessionStart) || sessionStart.length !== 1) return "invalid";
452
+ const entry = sessionStart[0];
453
+ if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return "invalid";
454
+ return typeof entry.command === "string" ? entry.command : "invalid";
455
+ };
456
+
437
457
  const checkLauncher = (res: Resolved): DoctorCheck => {
438
458
  const dev = res.dev;
439
459
  const hosts = hostsFor(res.host);
@@ -458,6 +478,16 @@ const checkLauncher = (res: Resolved): DoctorCheck => {
458
478
  ) {
459
479
  missing.push(`cursor: registered ${registered.entry}`);
460
480
  }
481
+ const hook = registeredCursorHook(res);
482
+ if (hook === "invalid") {
483
+ missing.push(
484
+ `cursor: canonical session-start hook in ${path.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")}`,
485
+ );
486
+ } else if (hook !== null && hook !== canonicalCursorHook) {
487
+ missing.push(
488
+ `cursor: canonical session-start hook in ${path.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")} (registered ${hook})`,
489
+ );
490
+ }
461
491
  }
462
492
  if (dev) {
463
493
  missing.push(
@@ -179,6 +179,13 @@ export function mergeCursorHooks(
179
179
  return { config: base, changed: ["hooks.sessionStart"] };
180
180
  }
181
181
 
182
+ /**
183
+ * Canonical reviewed pin for the Cursor npm runtime (README "Update review").
184
+ * The single source for every source-derived Cursor runtime selector; the
185
+ * committed manifests keep the literal (static data cannot import TS).
186
+ */
187
+ export const CURSOR_RUNTIME_PACKAGE = "@brainervirus/workit-cursor@0.8.0";
188
+
182
189
  /**
183
190
  * Portable Cursor MCP server entry (CA-16/CA-17): launch the published package
184
191
  * through npx against its npm bin, so the Marketplace plugin never depends on a
@@ -190,12 +197,7 @@ export function cursorMcpServerEntry(_packageDir: string): {
190
197
  } {
191
198
  return {
192
199
  command: "npx",
193
- args: [
194
- "-y",
195
- "--package=@brainervirus/workit-cursor@latest",
196
- "workit-cursor-mcp",
197
- "${workspaceFolder}",
198
- ],
200
+ args: ["-y", `--package=${CURSOR_RUNTIME_PACKAGE}`, "workit-cursor-mcp", "${workspaceFolder}"],
199
201
  };
200
202
  }
201
203
 
@@ -208,7 +210,7 @@ export function cursorHooksEntry(_packageDir: string): {
208
210
  args: string[];
209
211
  } {
210
212
  return {
211
- command: "npx -y --package=@brainervirus/workit-cursor@latest workit-cursor-session-start",
213
+ command: `npx -y --package=${CURSOR_RUNTIME_PACKAGE} workit-cursor-session-start`,
212
214
  args: [],
213
215
  };
214
216
  }