@brainervirus/workit-cli 0.8.4 → 0.8.6

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/README.md CHANGED
@@ -24,6 +24,7 @@ workit flow status --plan docs/<slug>/plan.md # read the effective flow s
24
24
  workit flow pause --plan docs/<slug>/plan.md [--confirm] # active -> paused
25
25
  workit flow resume --plan docs/<slug>/plan.md [--confirm] # paused -> active
26
26
  workit flow complete --plan docs/<slug>/plan.md [--confirm] # complete (ledger + verification gated)
27
+ workit flow review-package --plan docs/<slug>/plan.md --base <sha> --head <sha> [--confirm] # write a review diff
27
28
  workit handoff --message "docs/<slug>/plan.md" # print the destination handoff prompt
28
29
  workit # help
29
30
  ```
@@ -32,7 +33,7 @@ workit # help
32
33
 
33
34
  `workit doctor` checks the offline installation health and exits nonzero when problems are found; `--json` prints the full report as JSON instead of the human-readable table.
34
35
 
35
- `workit flow` maps the shared-core execution lifecycle (`pending`/`active`/`paused`/`completed`) to the CLI: `status` reads the effective flow state (approval digests, drift, execution, handoff-destination flag); `pause`/`resume`/`complete` mutate it with `--confirm` (or a TTY prompt when stdin is a TTY, exit 2 when neither applies). `workit handoff` prints the core destination handoff prompt (four-choice menu, never the originating Handoff option) and marks the flow as a handoff destination; a second handoff on an already-marked destination is rejected. Domain/verification failures exit 1 with structured JSON on stderr; usage errors exit 2.
36
+ `workit flow` maps the shared-core execution lifecycle (`pending`/`active`/`paused`/`completed`) to the CLI: `status` reads the effective flow state (approval digests, drift, execution, handoff-destination flag); `pause`/`resume`/`complete` mutate it with `--confirm` (or a TTY prompt when stdin is a TTY, exit 2 when neither applies); `review-package` writes the review diff for a `--base..--head` range through the shared core guard, which rejects empty ranges. `workit handoff` prints the core destination handoff prompt (four-choice menu, never the originating Handoff option) and marks the flow as a handoff destination; a second handoff on an already-marked destination is rejected. Domain/verification failures exit 1 with structured JSON on stderr; usage errors exit 2.
36
37
 
37
38
  ## Behavior
38
39
 
@@ -59,6 +59,8 @@ For each top-level task absent from `completed_task_ids`:
59
59
 
60
60
  Run a separate full-branch code review, then `workflow_verify`. Present the full `<SDD_DIR>/advisories.md` roll-up once, then use native `question` so the user can choose which advisory items to fix, discuss, or discard. Report exact check results and never infer success. Use `workflow_git_context` for a commit preview and load `wk-commit` through `skill` for an approved commit. If working state contains a stash reference, preview reapplication through `question`, then call `workflow_branch_setup` with `confirmed: true` after approval.
61
61
 
62
+ **Mandatory:** end the run by calling `workflow_plan_complete` (OpenCode/Cursor) or the CLI `workit flow complete` (CLI host) after the final task once the SDD ledger is complete (all task IDs appended) and `workflow_verify` passes — a complete ledger and green verification are the tool's gates. Never finish the run while the plan is still `active`.
63
+
62
64
  ## Task order
63
65
 
64
66
  <TASK_LIST>
@@ -9,6 +9,8 @@
9
9
 
10
10
  ## Global Constraints
11
11
 
12
+ - Each task lands exactly one contiguous non-empty commit range (`base..head`): fix rounds append commits to that range and never rewrite/amend an active review range; each progress line records the task's real base..head shas.
13
+ - The final task ends execution with `workflow_plan_complete` (or the CLI `workit flow complete`) once the SDD ledger is complete and repository verification passes — a run never finishes while the plan is still `active`.
12
14
  - <project-wide requirements, one line each>
13
15
 
14
16
  ---
package/dist/index.js CHANGED
@@ -43026,7 +43026,7 @@ function mergeCursorMcp(mcp, serverName, server) {
43026
43026
  base2.mcpServers = servers;
43027
43027
  return { config: base2, changed };
43028
43028
  }
43029
- var CURSOR_RUNTIME_PACKAGE = "@brainervirus/workit-cursor@0.8.0";
43029
+ var CURSOR_RUNTIME_PACKAGE = "@brainervirus/workit-cursor@0.8.5";
43030
43030
  function cursorMcpServerEntry(_packageDir) {
43031
43031
  return {
43032
43032
  command: "npx",
@@ -43438,6 +43438,18 @@ var registeredCursorHook = (res) => {
43438
43438
  return "invalid";
43439
43439
  return typeof entry.command === "string" ? entry.command : "invalid";
43440
43440
  };
43441
+ var validLocalDistHook = (command, res) => {
43442
+ const m = /^node\s+(.+)$/.exec(command.trim());
43443
+ if (!m)
43444
+ return false;
43445
+ const entry = m[1].trim();
43446
+ if (!path9.isAbsolute(entry))
43447
+ return false;
43448
+ if (path9.resolve(entry) !== path9.resolve(path9.join(res.cursorPluginDir, "dist", "cursor-session-start.js"))) {
43449
+ return false;
43450
+ }
43451
+ return validNodeEntry(entry, "node", res.env);
43452
+ };
43441
43453
  var checkLauncher = (res) => {
43442
43454
  const dev = res.dev;
43443
43455
  const hosts = hostsFor(res.host);
@@ -43453,7 +43465,7 @@ var checkLauncher = (res) => {
43453
43465
  const hook = registeredCursorHook(res);
43454
43466
  if (hook === "invalid") {
43455
43467
  missing.push(`cursor: canonical session-start hook in ${path9.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")}`);
43456
- } else if (hook !== null && hook !== canonicalCursorHook) {
43468
+ } else if (hook !== null && hook !== canonicalCursorHook && !validLocalDistHook(hook, res)) {
43457
43469
  missing.push(`cursor: canonical session-start hook in ${path9.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")} (registered ${hook})`);
43458
43470
  }
43459
43471
  }
@@ -44213,7 +44225,7 @@ function adapterRoot(platform2, res) {
44213
44225
  dir = parent;
44214
44226
  }
44215
44227
  }
44216
- candidates.push(path10.join(res.home, ".local", "share", "workflow-toolkit", "packages", `workit-${platform2}`));
44228
+ candidates.push(path10.join(res.home, ".local", "share", "workit", "packages", `workit-${platform2}`));
44217
44229
  for (const candidate of candidates) {
44218
44230
  if (isAdapter(candidate, platform2))
44219
44231
  return candidate;
@@ -44289,7 +44301,7 @@ var samePluginContent = (src, dest, relative = "") => {
44289
44301
  if (existsSync8(path10.join(src, entry.name)))
44290
44302
  continue;
44291
44303
  const rel = path10.join(relative, entry.name);
44292
- if (rel === ".workflow-toolkit-root")
44304
+ if (rel === ".workit-root")
44293
44305
  continue;
44294
44306
  if (relative === "rules" && entry.isFile() && entry.name.endsWith(".mdc"))
44295
44307
  continue;
@@ -44316,7 +44328,7 @@ var preservedCursorRules = (src, dest) => {
44316
44328
  return preserved;
44317
44329
  };
44318
44330
  function copyPluginDir(src, dest) {
44319
- const marker = path10.join(dest, ".workflow-toolkit-root");
44331
+ const marker = path10.join(dest, ".workit-root");
44320
44332
  const synced = readFileSafe(marker)?.trim() === src && samePluginContent(src, dest);
44321
44333
  if (synced)
44322
44334
  return "Skipped";
@@ -44336,7 +44348,7 @@ function copyPluginDir(src, dest) {
44336
44348
  mkdirSync3(path10.join(stage, "rules"), { recursive: true });
44337
44349
  writeFileSync5(path10.join(stage, "rules", name), content);
44338
44350
  }
44339
- writeFileSync5(path10.join(stage, ".workflow-toolkit-root"), src + `
44351
+ writeFileSync5(path10.join(stage, ".workit-root"), src + `
44340
44352
  `, "utf8");
44341
44353
  if (!samePluginContent(src, stage))
44342
44354
  throw new Error("staged adapter content is incomplete");
@@ -46735,7 +46747,9 @@ import {
46735
46747
  statSync as statSync5,
46736
46748
  writeFileSync as writeFileSync6
46737
46749
  } from "node:fs";
46750
+ import { execFileSync as execFileSync3 } from "node:child_process";
46738
46751
  import path15 from "node:path";
46752
+ var posix2 = (p) => p.split(path15.sep).join("/");
46739
46753
  function ledgerCompletion(root, slug) {
46740
46754
  let started = false;
46741
46755
  const completed = [];
@@ -46771,6 +46785,45 @@ function ledgerCompletion(root, slug) {
46771
46785
  missing
46772
46786
  };
46773
46787
  }
46788
+ function sddReviewPackage({
46789
+ sdd_dir,
46790
+ base_sha,
46791
+ head_sha,
46792
+ workspace_root
46793
+ }) {
46794
+ const contained = resolveDocsPath({ workspace_root, path: sdd_dir });
46795
+ if (!contained.ok)
46796
+ return { error: contained.error };
46797
+ if (base_sha === head_sha) {
46798
+ return {
46799
+ error: `empty commit range (base_sha ${base_sha} === head_sha ${head_sha}): nothing to review`,
46800
+ code: "empty_commit_range"
46801
+ };
46802
+ }
46803
+ const dir = contained.path;
46804
+ const base7 = base_sha.slice(0, 7);
46805
+ const head7 = head_sha.slice(0, 7);
46806
+ const diffPath = path15.join(dir, `review-${base7}..${head7}.diff`);
46807
+ try {
46808
+ const diff2 = execFileSync3("git", ["diff", base_sha, head_sha], {
46809
+ cwd: contained.base,
46810
+ encoding: "utf8",
46811
+ stdio: ["pipe", "pipe", "pipe"]
46812
+ });
46813
+ if (diff2.trim() === "") {
46814
+ return {
46815
+ error: `empty commit range (${base_sha}..${head_sha}): diff is empty, nothing to review`,
46816
+ code: "empty_commit_range"
46817
+ };
46818
+ }
46819
+ mkdirSync6(dir, { recursive: true });
46820
+ writeFileSync6(diffPath, diff2, "utf8");
46821
+ const rel = posix2(path15.relative(contained.base, diffPath));
46822
+ return { diff_path: rel, base_sha, head_sha, base7, head7 };
46823
+ } catch (error) {
46824
+ return { error: error instanceof Error ? error.message : "git diff failed" };
46825
+ }
46826
+ }
46774
46827
 
46775
46828
  // packages/workit-core/src/core/verify-project.ts
46776
46829
  import { spawnSync as spawnSync4 } from "node:child_process";
@@ -46785,10 +46838,10 @@ import { readFileSync as readFileSync11, mkdirSync as mkdirSync7, writeFileSync
46785
46838
  import path16 from "node:path";
46786
46839
 
46787
46840
  // packages/workit-core/src/core/git.ts
46788
- import { execFileSync as execFileSync3 } from "node:child_process";
46841
+ import { execFileSync as execFileSync4 } from "node:child_process";
46789
46842
  var run = (cwd2, args) => {
46790
46843
  try {
46791
- const stdout = execFileSync3("git", args, {
46844
+ const stdout = execFileSync4("git", args, {
46792
46845
  cwd: cwd2,
46793
46846
  encoding: "utf8",
46794
46847
  stdio: ["pipe", "pipe", "pipe"]
@@ -48197,19 +48250,21 @@ var buildHandoffPrompt = (root, message) => {
48197
48250
  };
48198
48251
 
48199
48252
  // packages/workit-cli/src/flow.ts
48200
- var FLOW_ACTIONS = ["status", "pause", "resume", "complete"];
48253
+ var FLOW_ACTIONS = ["status", "pause", "resume", "complete", "review-package"];
48201
48254
  var COMMANDS = {
48202
48255
  status: "workit flow status --plan <path>",
48203
48256
  pause: "workit flow pause --plan <path> [--confirm]",
48204
48257
  resume: "workit flow resume --plan <path> [--confirm]",
48205
48258
  complete: "workit flow complete --plan <path> [--confirm]",
48259
+ "review-package": "workit flow review-package --plan <path> --base <sha> --head <sha> [--confirm]",
48206
48260
  handoff: "workit handoff --message <text>"
48207
48261
  };
48208
48262
  var FLOW_COMMANDS = {
48209
48263
  status: COMMANDS.status,
48210
48264
  pause: COMMANDS.pause,
48211
48265
  resume: COMMANDS.resume,
48212
- complete: COMMANDS.complete
48266
+ complete: COMMANDS.complete,
48267
+ "review-package": COMMANDS["review-package"]
48213
48268
  };
48214
48269
  var CLI_FLAG_EVIDENCE = { host: "cli", attested: false, confirmation: "flag" };
48215
48270
  var CLI_TTY_EVIDENCE = { host: "cli", attested: false, confirmation: "tty" };
@@ -48242,26 +48297,38 @@ var usage = (err3, text) => {
48242
48297
  write(err3, text);
48243
48298
  return 2;
48244
48299
  };
48300
+ var VALUE_FLAGS = ["--plan", "--base", "--head"];
48245
48301
  function parseFlowFlags(action, argv, err3) {
48246
- let plan;
48302
+ const parsed = {};
48247
48303
  let confirm = false;
48248
48304
  for (let i = 0;i < argv.length; i++) {
48249
48305
  const token = argv[i];
48250
- if (token === "--plan") {
48251
- if (plan !== undefined) {
48252
- usage(err3, `usage: ${FLOW_COMMANDS[action]} — duplicate --plan flag`);
48306
+ if (VALUE_FLAGS.includes(token)) {
48307
+ if (token !== "--plan" && action !== "review-package") {
48308
+ usage(err3, `usage: ${FLOW_COMMANDS[action]} — unknown flag: ${token}`);
48253
48309
  return { ok: false };
48254
48310
  }
48255
48311
  const value = argv[i + 1];
48256
48312
  if (value === undefined || value.trim() === "") {
48257
- usage(err3, `usage: ${FLOW_COMMANDS[action]} --plan requires a non-empty path`);
48313
+ const label = token === "--plan" ? "path" : "value";
48314
+ usage(err3, `usage: ${FLOW_COMMANDS[action]} — ${token} requires a non-empty ${label}`);
48258
48315
  return { ok: false };
48259
48316
  }
48260
48317
  if (value.startsWith("--")) {
48261
48318
  usage(err3, `usage: ${FLOW_COMMANDS[action]} — unknown flag: ${value}`);
48262
48319
  return { ok: false };
48263
48320
  }
48264
- plan = value;
48321
+ const existing = token === "--plan" ? parsed.plan : token === "--base" ? parsed.base : parsed.head;
48322
+ if (existing !== undefined) {
48323
+ usage(err3, `usage: ${FLOW_COMMANDS[action]} — duplicate ${token} flag`);
48324
+ return { ok: false };
48325
+ }
48326
+ if (token === "--plan")
48327
+ parsed.plan = value;
48328
+ else if (token === "--base")
48329
+ parsed.base = value;
48330
+ else
48331
+ parsed.head = value;
48265
48332
  i += 1;
48266
48333
  } else if (token === "--confirm") {
48267
48334
  if (action === "status") {
@@ -48278,11 +48345,21 @@ function parseFlowFlags(action, argv, err3) {
48278
48345
  return { ok: false };
48279
48346
  }
48280
48347
  }
48281
- if (plan === undefined) {
48348
+ if (parsed.plan === undefined) {
48282
48349
  usage(err3, `usage: ${FLOW_COMMANDS[action]} — --plan <path> required`);
48283
48350
  return { ok: false };
48284
48351
  }
48285
- return { ok: true, parsed: { plan, confirm } };
48352
+ if (action === "review-package") {
48353
+ if (parsed.base === undefined) {
48354
+ usage(err3, `usage: ${FLOW_COMMANDS[action]} — --base <sha> required`);
48355
+ return { ok: false };
48356
+ }
48357
+ if (parsed.head === undefined) {
48358
+ usage(err3, `usage: ${FLOW_COMMANDS[action]} — --head <sha> required`);
48359
+ return { ok: false };
48360
+ }
48361
+ }
48362
+ return { ok: true, parsed: { plan: parsed.plan, confirm, base: parsed.base, head: parsed.head } };
48286
48363
  }
48287
48364
  async function resolveConfirmation(deps, confirmFlag, out, err3) {
48288
48365
  if (confirmFlag)
@@ -48337,12 +48414,33 @@ function mutateCommand(root, plan, action, evidence, deps, out, err3) {
48337
48414
  });
48338
48415
  return 0;
48339
48416
  }
48417
+ function reviewPackageCommand(root, parsed, out, err3) {
48418
+ const resolved = resolveCanonicalLayout({ workspace_root: root, plan_path: parsed.plan });
48419
+ if (!resolved.ok)
48420
+ return domainFail(err3, "path_invalid", resolved.error);
48421
+ const result2 = sddReviewPackage({
48422
+ sdd_dir: path21.posix.join("docs", resolved.layout.slug, "sdd"),
48423
+ base_sha: parsed.base,
48424
+ head_sha: parsed.head,
48425
+ workspace_root: root
48426
+ });
48427
+ if ("diff_path" in result2) {
48428
+ writeJSON(out, {
48429
+ ok: true,
48430
+ diff_path: result2.diff_path,
48431
+ base_sha: result2.base_sha,
48432
+ head_sha: result2.head_sha
48433
+ });
48434
+ return 0;
48435
+ }
48436
+ return domainFail(err3, result2.code ?? "review_package_failed", result2.error);
48437
+ }
48340
48438
  async function runFlowCommand(argv, deps = {}) {
48341
48439
  const out = outStream(deps);
48342
48440
  const err3 = errStream(deps);
48343
48441
  const [action, ...rest2] = argv;
48344
48442
  if (!action || !FLOW_ACTIONS.includes(action)) {
48345
- return usage(err3, "usage: workit flow <status|pause|resume|complete> --plan <path> [--confirm]");
48443
+ return usage(err3, "usage: workit flow <status|pause|resume|complete|review-package> --plan <path> [--confirm]");
48346
48444
  }
48347
48445
  const flowAction = action;
48348
48446
  const parsed = parseFlowFlags(flowAction, rest2, err3);
@@ -48355,6 +48453,9 @@ async function runFlowCommand(argv, deps = {}) {
48355
48453
  const evidence = await resolveConfirmation(deps, parsed.parsed.confirm, out, err3);
48356
48454
  if (evidence === null)
48357
48455
  return 2;
48456
+ if (flowAction === "review-package") {
48457
+ return reviewPackageCommand(root, parsed.parsed, out, err3);
48458
+ }
48358
48459
  return mutateCommand(root, parsed.parsed.plan, flowAction, evidence, deps, out, err3);
48359
48460
  }
48360
48461
  async function runHandoffCommand(argv, deps = {}) {
@@ -48392,14 +48493,16 @@ var COMMAND_DESCRIPTIONS = [
48392
48493
  [COMMANDS.pause, "Pause an active plan"],
48393
48494
  [COMMANDS.resume, "Resume a paused plan"],
48394
48495
  [COMMANDS.complete, "Complete a plan (ledger and verification gated)"],
48496
+ [COMMANDS["review-package"], "Write a review diff for a base..head range"],
48395
48497
  [COMMANDS.handoff, "Emit the destination handoff prompt for a plan"]
48396
48498
  ];
48499
+ var helpColumn = Math.max(...COMMAND_DESCRIPTIONS.map(([cmd]) => cmd.length)) + 2;
48397
48500
  var HELP = `workit — workflow rails for agentic coding
48398
48501
 
48399
48502
  Usage:
48400
48503
  workit init Run the interactive setup wizard
48401
48504
  workit doctor Verify the offline installation health (add --json for a machine-readable report)
48402
- ${COMMAND_DESCRIPTIONS.map(([cmd, desc]) => ` ${cmd.padEnd(49)}${desc}`).join(`
48505
+ ${COMMAND_DESCRIPTIONS.map(([cmd, desc]) => ` ${cmd.padEnd(helpColumn)}${desc}`).join(`
48403
48506
  `)}
48404
48507
  workit Show this help
48405
48508
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-cli",
3
- "version": "0.8.4",
3
+ "version": "0.8.6",
4
4
  "private": false,
5
5
  "description": "Workit interactive setup wizard (Ink TUI)",
6
6
  "keywords": [
@@ -34,9 +34,9 @@
34
34
  "typecheck": "tsc --noEmit"
35
35
  },
36
36
  "dependencies": {
37
- "@brainervirus/workit-core": "^0.8.4",
38
- "@brainervirus/workit-cursor": "^0.8.4",
39
- "@brainervirus/workit-opencode": "^0.8.4",
37
+ "@brainervirus/workit-core": "^0.8.6",
38
+ "@brainervirus/workit-cursor": "^0.8.6",
39
+ "@brainervirus/workit-opencode": "^0.8.6",
40
40
  "@inkjs/ui": "2.0.0",
41
41
  "ink": "7.1.1",
42
42
  "react": "19.2.8"