@brainervirus/workit-cli 8.39.4 → 8.40.0

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.
Files changed (2) hide show
  1. package/dist/index.js +122 -10
  2. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -9317,7 +9317,7 @@ var package_default;
9317
9317
  var init_package = __esm(() => {
9318
9318
  package_default = {
9319
9319
  name: "@brainervirus/workit-cli",
9320
- version: "8.39.4",
9320
+ version: "8.40.0",
9321
9321
  private: false,
9322
9322
  description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
9323
9323
  keywords: [
@@ -9355,13 +9355,13 @@ var init_package = __esm(() => {
9355
9355
  },
9356
9356
  dependencies: {
9357
9357
  "@babel/parser": "7.29.9",
9358
- "@brainervirus/workit-cursor": "^8.39.4",
9358
+ "@brainervirus/workit-cursor": "^8.40.0",
9359
9359
  "@inkjs/ui": "2.0.0",
9360
9360
  ink: "7.1.1",
9361
9361
  react: "19.2.8"
9362
9362
  },
9363
9363
  devDependencies: {
9364
- "@brainervirus/workit-core": "^8.39.4"
9364
+ "@brainervirus/workit-core": "^8.40.0"
9365
9365
  },
9366
9366
  engines: {
9367
9367
  node: ">=24"
@@ -77034,6 +77034,7 @@ var init_uninstall2 = __esm(() => {
77034
77034
  // packages/workit-cli/src/verbs/grant.ts
77035
77035
  var exports_grant = {};
77036
77036
  __export(exports_grant, {
77037
+ agentMarker: () => agentMarker,
77037
77038
  run: () => run10,
77038
77039
  runGrant: () => runGrant
77039
77040
  });
@@ -77372,7 +77373,90 @@ function set2(argv, io) {
77372
77373
  `${name}: ${changes.map((change) => change.path.join(".")).join(", ")} updated in ${written(data.path, data.backup)}`
77373
77374
  ]);
77374
77375
  }
77375
- async function runWorkspace(argv, io) {
77376
+ async function rm(argv, io, deps) {
77377
+ const [name, extra] = argv;
77378
+ if (!name)
77379
+ return usage3(io, "rm needs <name>");
77380
+ if (extra)
77381
+ return usage3(io, `unexpected argument ${extra}`);
77382
+ const current = load(io);
77383
+ if (typeof current === "number")
77384
+ return current;
77385
+ const entries = entriesOf(current);
77386
+ const removed = entries.find((entry) => entry.name === name);
77387
+ if (!removed)
77388
+ return emit(io, fail2("not_found", `no workspace named "${name}" in ${current.path}`));
77389
+ const remaining = entries.filter((entry) => entry !== removed);
77390
+ if (remaining.length === 0)
77391
+ return emit(io, fail2("blocked", `"${name}" is the last workspace in ${current.path}: it holds your vcs, policies and grants, and without it every folder falls back to the defaults; workspace rm does not remove it`, {
77392
+ unblock: `add the workspace that should replace it first (workit workspace add <name> --glob <g> --from ${name}), or edit ${current.path} in your own editor`
77393
+ }));
77394
+ const before = deliveredCeilingOf(removed);
77395
+ const defaults = ceilingOf({});
77396
+ const nothing = {
77397
+ grants: Object.fromEntries(GRANT_KINDS.map((kind) => [kind, false])),
77398
+ endpoint: DEFAULT_ENDPOINT,
77399
+ verification: STRICTNESS[0]
77400
+ };
77401
+ const specificity = globSpecificity(removed.glob);
77402
+ const raised = [
77403
+ { taker: "defaults", kinds: raisedKinds(before, defaults), reason: "no entry matches" },
77404
+ ...remaining.map((entry) => ({
77405
+ taker: entry.name,
77406
+ kinds: raisedKinds(before, ceilingOf(entry)),
77407
+ reason: "may match a folder it covered"
77408
+ })),
77409
+ ...remaining.filter((entry) => globSpecificity(entry.glob) === specificity).map((entry) => ({
77410
+ taker: entry.name,
77411
+ kinds: raisedKinds(nothing, ceilingOf(entry)),
77412
+ reason: "equally specific: a folder both matched was ambiguous, with nothing granted"
77413
+ }))
77414
+ ].filter((raise) => raise.kinds.length > 0);
77415
+ if (raised.length > 0) {
77416
+ const kinds = [...new Set(raised.flatMap((raise) => raise.kinds))];
77417
+ const detail = raised.map((raise) => `${raise.taker} (${raise.reason}): ${raise.kinds.join(", ")}`).join("; ");
77418
+ const agent = agentMarker(io.env);
77419
+ if (!deps.interactive() || agent)
77420
+ return emit(io, fail2("blocked", `grant_raise_refused: removing "${name}" may raise ${kinds.join(", ")} for the folders it covered (${detail}); that needs the user at an interactive terminal${agent ? ` (this shell belongs to an agent: ${agent} is set)` : ""}; grants are never raised headless`, {
77421
+ unblock: `ask the user to run, in a terminal outside the agent host (not Claude Code's \`!\` prefix or an agent shell): workit workspace rm ${name}`
77422
+ }));
77423
+ const answer = await askOrCancel(io, () => deps.ask(`Removing workspace "${name}" may raise ${kinds.join(", ")} for the folders it covered (${detail}). An agent there may then do more without asking. Type the workspace name to confirm: `));
77424
+ if (answer === null)
77425
+ return cancelled(io);
77426
+ if (answer.trim() !== name)
77427
+ return emit(io, fail2("blocked", "workspace removal not confirmed; nothing was written"));
77428
+ }
77429
+ const result = save(current, remaining);
77430
+ if (!result.ok)
77431
+ return emit(io, fail2("invalid_input", result.error));
77432
+ const at = globRoot(removed.glob);
77433
+ let covers;
77434
+ try {
77435
+ covers = { at, workspace: resolveWorkspaceFromEntries(at, remaining)?.name ?? null };
77436
+ } catch (error) {
77437
+ covers = { at, workspace: null, error: error instanceof Error ? error.message : String(error) };
77438
+ }
77439
+ const coveringCeiling = ceilingOf(remaining.find((entry) => entry.name === covers.workspace));
77440
+ return emit(io, ok2({
77441
+ path: result.path,
77442
+ backup: result.backup,
77443
+ removed,
77444
+ covers: {
77445
+ ...covers,
77446
+ grants: coveringCeiling.grants,
77447
+ defaultEndpoint: coveringCeiling.endpoint,
77448
+ verification: coveringCeiling.verification
77449
+ },
77450
+ takers: overlappingEntries(removed.glob, remaining).map((entry) => entry.name),
77451
+ raises: raised
77452
+ }), (data) => [
77453
+ `${name} (${removed.glob}) removed from ${written(data.path, data.backup)}`,
77454
+ data.covers.error ? ` ${data.covers.at}: ${data.covers.error}` : ` ${data.covers.at} is now ${data.covers.workspace ? `workspace ${data.covers.workspace}` : "under the defaults (no workspace)"}: ${GRANT_KINDS.map((kind) => `${kind}=${String(data.covers.grants[kind])}`).join(" ")}; default endpoint ${data.covers.defaultEndpoint}; verification ${data.covers.verification}`,
77455
+ ...data.takers.length ? [` entries whose globs may cover its other folders: ${data.takers.join(", ")}`] : [],
77456
+ " workit workspace list --cwd <folder> shows which entry applies to a folder"
77457
+ ]);
77458
+ }
77459
+ async function runWorkspace(argv, io, deps = defaultDeps2) {
77376
77460
  const [sub, ...rest] = argv.filter((arg) => arg !== "--json" && arg !== "--");
77377
77461
  if (sub === undefined || sub === "list")
77378
77462
  return list2(rest, io);
@@ -77380,13 +77464,25 @@ async function runWorkspace(argv, io) {
77380
77464
  return add2(rest, io);
77381
77465
  if (sub === "set")
77382
77466
  return set2(rest, io);
77467
+ if (sub === "rm")
77468
+ return rm(rest, io, deps);
77383
77469
  return usage3(io, `unknown subcommand "${sub}"`);
77384
77470
  }
77385
- var USAGE5 = "workit workspace list | workspace add <name> --glob <g> [--from <parent>] | workspace set <name> <key>=<value>…", INHERITED_KEYS, GRANT_KEYS, SCOPE_KEYS, SETTABLE_KEYS, SEGMENT, UNSAFE_SEGMENTS, ceilingOf = (entry) => ({
77471
+ var USAGE5 = "workit workspace list | workspace add <name> --glob <g> [--from <parent>] | workspace set <name> <key>=<value>… | workspace rm <name>", INHERITED_KEYS, GRANT_KEYS, SCOPE_KEYS, SETTABLE_KEYS, SEGMENT, UNSAFE_SEGMENTS, ceilingOf = (entry) => ({
77386
77472
  grants: { ...DEFAULT_GRANTS, ...configuredGrants(entry).grants },
77387
77473
  endpoint: isDefaultEndpoint(entry?.defaultEndpoint) ? entry.defaultEndpoint : DEFAULT_ENDPOINT,
77388
77474
  verification: verificationOf(entry)
77389
- }), STRICTNESS, looser = (current, next) => STRICTNESS.indexOf(next) > STRICTNESS.indexOf(current), shadowedCeiling = (shadowed) => {
77475
+ }), deliveredCeilingOf = (entry) => {
77476
+ const ceiling = ceilingOf(entry);
77477
+ const account = entry.vcs?.account;
77478
+ if (Object.keys(configuredGrants(entry).grants).length === 0 || account)
77479
+ return ceiling;
77480
+ return {
77481
+ grants: Object.fromEntries(GRANT_KINDS.map((kind) => [kind, false])),
77482
+ endpoint: DEFAULT_ENDPOINT,
77483
+ verification: ceiling.verification
77484
+ };
77485
+ }, STRICTNESS, looser = (current, next) => STRICTNESS.indexOf(next) > STRICTNESS.indexOf(current), shadowedCeiling = (shadowed) => {
77390
77486
  if (shadowed.length === 0)
77391
77487
  return null;
77392
77488
  const ceilings = shadowed.map(ceilingOf);
@@ -77406,7 +77502,11 @@ var USAGE5 = "workit workspace list | workspace add <name> --glob <g> [--from <p
77406
77502
  ...Object.keys(autonomy).length ? { autonomy } : {},
77407
77503
  ...looser(shadowed.verification, DEFAULT_VERIFICATION) ? { verification: shadowed.verification } : {}
77408
77504
  };
77409
- }, usage3 = (io, message) => emit(io, fail2("invalid_input", `${message}
77505
+ }, defaultDeps2, raisedKinds = (before, after) => [
77506
+ ...GRANT_KINDS.filter((kind) => raises(kind, before.grants[kind], after.grants[kind])),
77507
+ ...raisesEndpoint(before.endpoint, after.endpoint) ? ["defaultEndpoint"] : [],
77508
+ ...looser(before.verification, after.verification) ? ["verification"] : []
77509
+ ], usage3 = (io, message) => emit(io, fail2("invalid_input", `${message}
77410
77510
  usage: ${USAGE5}`)), load = (io) => {
77411
77511
  const current = readWorkspacesResult();
77412
77512
  if (current.status === "malformed" || current.status === "invalid")
@@ -77455,6 +77555,8 @@ var init_workspace = __esm(() => {
77455
77555
  init_safe_write();
77456
77556
  init_workspaces();
77457
77557
  init_output();
77558
+ init_prompt();
77559
+ init_grant();
77458
77560
  INHERITED_KEYS = [
77459
77561
  "vcs",
77460
77562
  "issues",
@@ -77486,6 +77588,10 @@ var init_workspace = __esm(() => {
77486
77588
  SEGMENT = /^[A-Za-z0-9_-]+$/u;
77487
77589
  UNSAFE_SEGMENTS = new Set(["__proto__", "constructor", "prototype"]);
77488
77590
  STRICTNESS = ["independent", "self", "proportional"];
77591
+ defaultDeps2 = {
77592
+ interactive: () => process.stdin.isTTY === true && process.stdout.isTTY === true,
77593
+ ask: (question) => askLine(question)
77594
+ };
77489
77595
  });
77490
77596
 
77491
77597
  // packages/workit-core/src/core/heavy-rules.ts
@@ -107882,7 +107988,7 @@ var VERBS = [
107882
107988
  withSubcommands({
107883
107989
  name: "workspace",
107884
107990
  group: "setup",
107885
- summary: "Edit workspace entries without the wizard; never raises a grant",
107991
+ summary: "Edit workspace entries without the wizard; a raise (rm included) needs the user at a terminal",
107886
107992
  subcommands: [
107887
107993
  {
107888
107994
  name: "list",
@@ -107898,15 +108004,21 @@ var VERBS = [
107898
108004
  name: "set",
107899
108005
  usage: "workit workspace set <name> <key>=<value>…",
107900
108006
  summary: "Change non-grant keys (dotted paths, JSON values, null removes); grants stay with workit grant set"
108007
+ },
108008
+ {
108009
+ name: "rm",
108010
+ usage: "workit workspace rm <name>",
108011
+ summary: "Remove an entry; if another entry or the defaults could grant its folders more, the user confirms at their own terminal"
107901
108012
  }
107902
108013
  ],
107903
108014
  examples: [
107904
108015
  "workit workspace list",
107905
108016
  "workit workspace add kit --glob '/home/me/work/libs/kit/**' --from work",
107906
- "workit workspace set kit branchPolicy.integration=merge"
108017
+ "workit workspace set kit branchPolicy.integration=merge",
108018
+ "workit workspace rm kit"
107907
108019
  ],
107908
108020
  notes: [
107909
- "A new entry starts with no grants of its own. It starts at the strictest level of every entry and the defaults, so its folders never get more than they had. Remove an entry in your own editor. Raising a grant, defaultEndpoint or verification needs the user: workit grant set <name> <kind>=<value> in their own terminal.",
108021
+ "A new entry starts with no grants of its own. It starts at the strictest level of every entry and the defaults, so its folders never get more than they had. rm counts every other entry and the defaults as a possible taker of the removed folders: when any could grant more, it is refused in an agent or headless shell and the user types the name to confirm at their own terminal; it never removes the last entry. Raising a grant, defaultEndpoint or verification needs the user: workit grant set <name> <kind>=<value> in their own terminal.",
107910
108022
  "Every write keeps the previous file as workspaces.json.bak. `workit doctor` lists overlapping globs and which entry wins."
107911
108023
  ],
107912
108024
  load: () => Promise.resolve().then(() => (init_workspace(), exports_workspace))
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@brainervirus/workit-cli",
3
- "version": "8.39.4",
3
+ "version": "8.40.0",
4
4
  "private": false,
5
5
  "description": "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
6
6
  "keywords": [
@@ -38,13 +38,13 @@
38
38
  },
39
39
  "dependencies": {
40
40
  "@babel/parser": "7.29.9",
41
- "@brainervirus/workit-cursor": "^8.39.4",
41
+ "@brainervirus/workit-cursor": "^8.40.0",
42
42
  "@inkjs/ui": "2.0.0",
43
43
  "ink": "7.1.1",
44
44
  "react": "19.2.8"
45
45
  },
46
46
  "devDependencies": {
47
- "@brainervirus/workit-core": "^8.39.4"
47
+ "@brainervirus/workit-core": "^8.40.0"
48
48
  },
49
49
  "engines": {
50
50
  "node": ">=24"