@staff0rd/assist 0.205.1 → 0.206.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.
Files changed (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +111 -52
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -117,7 +117,7 @@ After installation, the `assist` command will be available globally. You can als
117
117
  - `assist config list` - List all config values
118
118
  - `assist verify` - Run all verify:* commands in parallel (from run configs in assist.yml and scripts in package.json)
119
119
  - `assist verify all` - Run all checks, ignoring diff-based filters
120
- - `assist verify init` - Add verify scripts to a project
120
+ - `assist verify init` - Add verify scripts to a project (writes to `assist.yml` by default; pass `--package-json` to write to `package.json` scripts instead)
121
121
  - `assist verify hardcoded-colors` - Check for hardcoded hex colors in src/ (supports `hardcodedColors.ignore` globs in config)
122
122
  - `assist lint [-f, --fix]` - Run lint checks for conventions not enforced by biomejs (use `-f` to auto-fix)
123
123
  - `assist lint init` - Initialize Biome with standard linter config
package/dist/index.js CHANGED
@@ -6,7 +6,7 @@ import { Command } from "commander";
6
6
  // package.json
7
7
  var package_default = {
8
8
  name: "@staff0rd/assist",
9
- version: "0.205.1",
9
+ version: "0.206.1",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -3192,10 +3192,7 @@ function detectExistingSetup(pkg) {
3192
3192
  ...buildToolStatuses(pkg, configScriptNames),
3193
3193
  hasVite: hasDep(pkg, "vite"),
3194
3194
  hasTypescript: !!pkg.devDependencies?.typescript,
3195
- hasOpenColor: hasDep(pkg, "open-color"),
3196
- hasConfigScripts: [...configScriptNames].some(
3197
- (n) => n.startsWith("verify:")
3198
- )
3195
+ hasOpenColor: hasDep(pkg, "open-color")
3199
3196
  };
3200
3197
  }
3201
3198
 
@@ -3326,12 +3323,12 @@ async function promptForScripts(availableOptions) {
3326
3323
  }
3327
3324
  return selected;
3328
3325
  }
3329
- async function init2() {
3326
+ async function init2(options2 = {}) {
3330
3327
  const { packageJsonPath, pkg } = requirePackageJson();
3331
3328
  const setup2 = detectExistingSetup(pkg);
3332
3329
  const selected = await promptForScripts(getAvailableOptions(setup2));
3333
3330
  if (!selected) return;
3334
- const writer = setup2.hasConfigScripts ? setupVerifyRunEntry : (name, cmd) => setupVerifyScript(packageJsonPath, name, cmd);
3331
+ const writer = options2.packageJson ? (name, cmd) => setupVerifyScript(packageJsonPath, name, cmd) : setupVerifyRunEntry;
3335
3332
  const handlers2 = getSetupHandlers(
3336
3333
  setup2.hasVite,
3337
3334
  setup2.hasTypescript,
@@ -5466,6 +5463,86 @@ import { basename as basename4 } from "path";
5466
5463
  // src/shared/splitCompound.ts
5467
5464
  import { parse } from "shell-quote";
5468
5465
 
5466
+ // src/shared/consumeRedirect.ts
5467
+ import { tmpdir as tmpdir2 } from "os";
5468
+
5469
+ // src/shared/isRedirectTargetAllowed.ts
5470
+ function isRedirectTargetAllowed(target, tmp) {
5471
+ if (/^[A-Za-z]:[\\/]/.test(tmp)) return isUnderWindowsTmp(target, tmp);
5472
+ return isUnderPosixTmp(target, tmp);
5473
+ }
5474
+ function isUnderWindowsTmp(target, tmp) {
5475
+ if (!/^[A-Za-z]:[\\/]/.test(target)) return false;
5476
+ const nTmp = normalizeWindowsPath(tmp);
5477
+ const nTarget = normalizeWindowsPath(target);
5478
+ return nTarget === nTmp || nTarget.startsWith(`${nTmp}\\`);
5479
+ }
5480
+ function normalizeWindowsPath(p) {
5481
+ return p.replace(/\//g, "\\").toLowerCase().replace(/\\+$/, "");
5482
+ }
5483
+ function isUnderPosixTmp(target, tmp) {
5484
+ if (!target.startsWith("/")) return false;
5485
+ const nTmp = tmp.replace(/\/+$/, "");
5486
+ return target === nTmp || target.startsWith(`${nTmp}/`);
5487
+ }
5488
+
5489
+ // src/shared/consumeRedirect.ts
5490
+ function consumeRedirect(tokens, opIndex, current) {
5491
+ const target = tokens[opIndex + 1];
5492
+ if (typeof target !== "string") {
5493
+ return { ok: false, error: "unable to parse" };
5494
+ }
5495
+ if (!isRedirectTargetAllowed(target, tmpdir2())) {
5496
+ return {
5497
+ ok: false,
5498
+ error: `redirect target '${target}' is outside the OS temp directory`
5499
+ };
5500
+ }
5501
+ if (current.length > 0 && /^\d$/.test(current[current.length - 1])) {
5502
+ current.pop();
5503
+ }
5504
+ return { ok: true, nextIndex: opIndex + 1 };
5505
+ }
5506
+
5507
+ // src/shared/groupByOperator.ts
5508
+ var SEPARATOR_OPS = /* @__PURE__ */ new Set(["|", "&&", "||", ";"]);
5509
+ var OUTPUT_REDIRECT_OPS = /* @__PURE__ */ new Set([">", ">>"]);
5510
+ var UNPARSEABLE = { ok: false, error: "unable to parse" };
5511
+ function groupByOperator(tokens) {
5512
+ const groups = [[]];
5513
+ for (let i = 0; i < tokens.length; i++) {
5514
+ const step = handleToken(tokens, i, groups);
5515
+ if (!step.ok) return step;
5516
+ i = step.nextIndex;
5517
+ }
5518
+ return { ok: true, groups };
5519
+ }
5520
+ function handleToken(tokens, i, groups) {
5521
+ const token = tokens[i];
5522
+ const op = getOp(token);
5523
+ if (op === void 0) return appendWord(token, groups, i);
5524
+ if (op === "glob") {
5525
+ groups[groups.length - 1].push(token.pattern);
5526
+ return { ok: true, nextIndex: i };
5527
+ }
5528
+ if (SEPARATOR_OPS.has(op)) {
5529
+ groups.push([]);
5530
+ return { ok: true, nextIndex: i };
5531
+ }
5532
+ if (OUTPUT_REDIRECT_OPS.has(op)) {
5533
+ return consumeRedirect(tokens, i, groups[groups.length - 1]);
5534
+ }
5535
+ return UNPARSEABLE;
5536
+ }
5537
+ function appendWord(token, groups, i) {
5538
+ if (typeof token !== "string") return UNPARSEABLE;
5539
+ groups[groups.length - 1].push(token);
5540
+ return { ok: true, nextIndex: i };
5541
+ }
5542
+ function getOp(token) {
5543
+ return typeof token === "object" && token !== null && "op" in token ? token.op : void 0;
5544
+ }
5545
+
5469
5546
  // src/shared/hasUnquotedBackticks.ts
5470
5547
  var QUOTED_OR_BACKTICK_RE = /\\.|'[^']*'|"[^"]*"|(`)/g;
5471
5548
  function hasUnquotedBackticks(command) {
@@ -5477,17 +5554,17 @@ function hasUnquotedBackticks(command) {
5477
5554
  }
5478
5555
 
5479
5556
  // src/shared/splitCompound.ts
5480
- var SEPARATOR_OPS = /* @__PURE__ */ new Set(["|", "&&", "||", ";"]);
5481
- var UNSAFE_OPS = /* @__PURE__ */ new Set(["(", ")", ">", ">>", "<", "<&", "|&", ">&"]);
5482
5557
  var FD_REDIRECT_RE = /\d+>&\d+/g;
5483
5558
  var FD_DEVNULL_RE = /\d*>(?:\/dev\/null|\$null)/g;
5559
+ var UNPARSEABLE2 = { ok: false, error: "unable to parse" };
5484
5560
  function splitCompound(command) {
5485
5561
  const tokens = tokenizeCommand(command);
5486
- if (!tokens) return void 0;
5487
- const groups = groupByOperator(tokens);
5488
- if (!groups) return void 0;
5489
- const result = groups.map((parts) => stripEnvPrefix(parts).join(" ")).filter((cmd) => cmd !== "");
5490
- return result.length > 0 ? result : void 0;
5562
+ if (!tokens) return UNPARSEABLE2;
5563
+ const grouped = groupByOperator(tokens);
5564
+ if (!grouped.ok) return grouped;
5565
+ const parts = grouped.groups.map((g) => stripEnvPrefix(g).join(" ")).filter((cmd) => cmd !== "");
5566
+ if (parts.length === 0) return UNPARSEABLE2;
5567
+ return { ok: true, parts };
5491
5568
  }
5492
5569
  function tokenizeCommand(command) {
5493
5570
  const trimmed = command.trim().replace(FD_DEVNULL_RE, "").replace(FD_REDIRECT_RE, "");
@@ -5499,28 +5576,6 @@ function tokenizeCommand(command) {
5499
5576
  return void 0;
5500
5577
  }
5501
5578
  }
5502
- function getOp(token) {
5503
- return typeof token === "object" && token !== null && "op" in token ? token.op : void 0;
5504
- }
5505
- function groupByOperator(tokens) {
5506
- const groups = [[]];
5507
- for (const token of tokens) {
5508
- const op = getOp(token);
5509
- if (op === void 0) {
5510
- if (typeof token !== "string") return void 0;
5511
- groups[groups.length - 1].push(token);
5512
- } else if (op === "glob") {
5513
- groups[groups.length - 1].push(token.pattern);
5514
- } else if (SEPARATOR_OPS.has(op)) {
5515
- groups.push([]);
5516
- } else if (UNSAFE_OPS.has(op)) {
5517
- return void 0;
5518
- } else {
5519
- return void 0;
5520
- }
5521
- }
5522
- return groups;
5523
- }
5524
5579
  function stripEnvPrefix(parts) {
5525
5580
  let i = 0;
5526
5581
  while (i < parts.length && /^[A-Za-z_]\w*=/.test(parts[i])) i++;
@@ -5898,8 +5953,8 @@ function tryParseInput(raw) {
5898
5953
  }
5899
5954
  }
5900
5955
  function decide(toolName, rawCommand) {
5901
- const parts = splitCompound(rawCommand);
5902
- if (parts) return resolvePermission(toolName, parts);
5956
+ const result = splitCompound(rawCommand);
5957
+ if (result.ok) return resolvePermission(toolName, result.parts);
5903
5958
  return findDeny(toolName, [rawCommand]);
5904
5959
  }
5905
5960
  async function cliHook() {
@@ -5929,12 +5984,13 @@ async function cliHook() {
5929
5984
  // src/commands/cliHook/cliHookCheck.ts
5930
5985
  function cliHookCheck(command, toolName = "Bash") {
5931
5986
  const trimmed = command.trim();
5932
- const parts = splitCompound(trimmed);
5933
- if (!parts) {
5934
- console.log("not approved (unable to parse)");
5987
+ const result = splitCompound(trimmed);
5988
+ if (!result.ok) {
5989
+ console.log(`not approved (${result.error})`);
5935
5990
  process.exitCode = 1;
5936
5991
  return;
5937
5992
  }
5993
+ const parts = result.parts;
5938
5994
  for (const part of parts) {
5939
5995
  const configDeny = matchesConfigDeny(part);
5940
5996
  if (configDeny) {
@@ -8063,7 +8119,7 @@ function parseInspectReport(json) {
8063
8119
  // src/commands/dotnet/runInspectCode.ts
8064
8120
  import { execSync as execSync22 } from "child_process";
8065
8121
  import { existsSync as existsSync28, readFileSync as readFileSync25, unlinkSync as unlinkSync5 } from "fs";
8066
- import { tmpdir as tmpdir2 } from "os";
8122
+ import { tmpdir as tmpdir3 } from "os";
8067
8123
  import path29 from "path";
8068
8124
  import chalk91 from "chalk";
8069
8125
  function assertJbInstalled() {
@@ -8078,7 +8134,7 @@ function assertJbInstalled() {
8078
8134
  }
8079
8135
  }
8080
8136
  function runInspectCode(slnPath, include, swea) {
8081
- const reportPath = path29.join(tmpdir2(), `inspect-${Date.now()}.xml`);
8137
+ const reportPath = path29.join(tmpdir3(), `inspect-${Date.now()}.xml`);
8082
8138
  const includeFlag = include ? ` --include="${include}"` : "";
8083
8139
  const sweaFlag = swea ? " --swea" : "";
8084
8140
  try {
@@ -8718,7 +8774,7 @@ function registerPrompts(program2) {
8718
8774
  // src/commands/prs/comment.ts
8719
8775
  import { spawnSync as spawnSync2 } from "child_process";
8720
8776
  import { unlinkSync as unlinkSync6, writeFileSync as writeFileSync21 } from "fs";
8721
- import { tmpdir as tmpdir3 } from "os";
8777
+ import { tmpdir as tmpdir4 } from "os";
8722
8778
  import { join as join29 } from "path";
8723
8779
 
8724
8780
  // src/commands/prs/shared.ts
@@ -8791,7 +8847,7 @@ function comment2(path53, line, body) {
8791
8847
  validateLine(line);
8792
8848
  try {
8793
8849
  const prId = getCurrentPrNodeId();
8794
- const queryFile = join29(tmpdir3(), `gh-query-${Date.now()}.graphql`);
8850
+ const queryFile = join29(tmpdir4(), `gh-query-${Date.now()}.graphql`);
8795
8851
  writeFileSync21(queryFile, MUTATION);
8796
8852
  try {
8797
8853
  const result = spawnSync2(
@@ -8835,7 +8891,7 @@ import { execSync as execSync28 } from "child_process";
8835
8891
  // src/commands/prs/resolveCommentWithReply.ts
8836
8892
  import { execSync as execSync27 } from "child_process";
8837
8893
  import { unlinkSync as unlinkSync8, writeFileSync as writeFileSync22 } from "fs";
8838
- import { tmpdir as tmpdir4 } from "os";
8894
+ import { tmpdir as tmpdir5 } from "os";
8839
8895
  import { join as join31 } from "path";
8840
8896
 
8841
8897
  // src/commands/prs/loadCommentsCache.ts
@@ -8870,7 +8926,7 @@ function replyToComment(org, repo, prNumber, commentId, message) {
8870
8926
  }
8871
8927
  function resolveThread(threadId) {
8872
8928
  const mutation = `mutation($threadId: ID!) { resolveReviewThread(input: {threadId: $threadId}) { thread { isResolved } } }`;
8873
- const queryFile = join31(tmpdir4(), `gh-mutation-${Date.now()}.graphql`);
8929
+ const queryFile = join31(tmpdir5(), `gh-mutation-${Date.now()}.graphql`);
8874
8930
  writeFileSync22(queryFile, mutation);
8875
8931
  try {
8876
8932
  execSync27(
@@ -8959,11 +9015,11 @@ import { stringify } from "yaml";
8959
9015
  // src/commands/prs/fetchThreadIds.ts
8960
9016
  import { execSync as execSync29 } from "child_process";
8961
9017
  import { unlinkSync as unlinkSync9, writeFileSync as writeFileSync23 } from "fs";
8962
- import { tmpdir as tmpdir5 } from "os";
9018
+ import { tmpdir as tmpdir6 } from "os";
8963
9019
  import { join as join32 } from "path";
8964
9020
  var THREAD_QUERY = `query($owner: String!, $repo: String!, $prNumber: Int!) { repository(owner: $owner, name: $repo) { pullRequest(number: $prNumber) { reviewThreads(first: 100) { nodes { id isResolved comments(first: 100) { nodes { databaseId } } } } } } }`;
8965
9021
  function fetchThreadIds(org, repo, prNumber) {
8966
- const queryFile = join32(tmpdir5(), `gh-query-${Date.now()}.graphql`);
9022
+ const queryFile = join32(tmpdir6(), `gh-query-${Date.now()}.graphql`);
8967
9023
  writeFileSync23(queryFile, THREAD_QUERY);
8968
9024
  try {
8969
9025
  const result = execSync29(
@@ -12210,7 +12266,10 @@ function registerVerify(program2) {
12210
12266
  run2({ ...options2, all: scope === "all" });
12211
12267
  });
12212
12268
  verifyCommand.command("list").description("List configured verify commands").action(list);
12213
- verifyCommand.command("init").description("Add verify scripts to a project").action(init2);
12269
+ verifyCommand.command("init").description("Add verify scripts to a project").option(
12270
+ "--package-json",
12271
+ "Write scripts to package.json instead of assist.yml"
12272
+ ).action(init2);
12214
12273
  verifyCommand.command("hardcoded-colors").description("Check for hardcoded hex colors in src/").action(hardcodedColors);
12215
12274
  verifyCommand.command("no-venv").description("Check that no venv folders exist in the repo").action(noVenv);
12216
12275
  }
@@ -13067,7 +13126,7 @@ function registerRun(program2) {
13067
13126
  // src/commands/screenshot/index.ts
13068
13127
  import { execSync as execSync39 } from "child_process";
13069
13128
  import { existsSync as existsSync43, mkdirSync as mkdirSync15, unlinkSync as unlinkSync12, writeFileSync as writeFileSync29 } from "fs";
13070
- import { tmpdir as tmpdir6 } from "os";
13129
+ import { tmpdir as tmpdir7 } from "os";
13071
13130
  import { join as join49, resolve as resolve11 } from "path";
13072
13131
  import chalk133 from "chalk";
13073
13132
 
@@ -13205,7 +13264,7 @@ function buildOutputPath(outputDir, processName) {
13205
13264
  return resolve11(outputDir, `${processName}-${timestamp}.png`);
13206
13265
  }
13207
13266
  function runPowerShellScript(processName, outputPath) {
13208
- const scriptPath = join49(tmpdir6(), `assist-screenshot-${Date.now()}.ps1`);
13267
+ const scriptPath = join49(tmpdir7(), `assist-screenshot-${Date.now()}.ps1`);
13209
13268
  writeFileSync29(scriptPath, captureWindowPs1, "utf-8");
13210
13269
  try {
13211
13270
  execSync39(
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.205.1",
3
+ "version": "0.206.1",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {