@staff0rd/assist 0.184.0 → 0.185.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 (3) hide show
  1. package/README.md +1 -0
  2. package/dist/index.js +188 -140
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -108,6 +108,7 @@ After installation, the `assist` command will be available globally. You can als
108
108
  - `assist roam show-claude-code-icon` - Forward Claude Code hook activity to Roam local API
109
109
  - `assist run <name> [params...]` - Run a configured command from assist.yml (positional params are matched to `params` config; supports `pre` array of commands to run first)
110
110
  - `assist run add` - Add a new run configuration to assist.yml and create a Claude command file
111
+ - `assist run remove <name>` - Remove a run configuration from assist.yml and delete its Claude command file
111
112
  - `assist config set <key> <value>` - Set a config value (e.g. commit.push true)
112
113
  - `assist config get <key>` - Get a config value
113
114
  - `assist config list` - List all config values
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.184.0",
9
+ version: "0.185.0",
10
10
  type: "module",
11
11
  main: "dist/index.js",
12
12
  bin: {
@@ -4591,6 +4591,70 @@ function registerBacklog(program2) {
4591
4591
  registerUpdateCommands(cmd);
4592
4592
  }
4593
4593
 
4594
+ // src/shared/splitCompound.ts
4595
+ import { parse } from "shell-quote";
4596
+
4597
+ // src/shared/hasUnquotedBackticks.ts
4598
+ var QUOTED_OR_BACKTICK_RE = /\\.|'[^']*'|"[^"]*"|(`)/g;
4599
+ function hasUnquotedBackticks(command) {
4600
+ QUOTED_OR_BACKTICK_RE.lastIndex = 0;
4601
+ for (let m = QUOTED_OR_BACKTICK_RE.exec(command); m !== null; m = QUOTED_OR_BACKTICK_RE.exec(command)) {
4602
+ if (m[1] !== void 0) return true;
4603
+ }
4604
+ return false;
4605
+ }
4606
+
4607
+ // src/shared/splitCompound.ts
4608
+ var SEPARATOR_OPS = /* @__PURE__ */ new Set(["|", "&&", "||", ";"]);
4609
+ var UNSAFE_OPS = /* @__PURE__ */ new Set(["(", ")", ">", ">>", "<", "<&", "|&", ">&"]);
4610
+ var FD_REDIRECT_RE = /\d+>&\d+/g;
4611
+ var FD_DEVNULL_RE = /\d*>\/dev\/null/g;
4612
+ function splitCompound(command) {
4613
+ const tokens = tokenizeCommand(command);
4614
+ if (!tokens) return void 0;
4615
+ const groups = groupByOperator(tokens);
4616
+ if (!groups) return void 0;
4617
+ const result = groups.map((parts) => stripEnvPrefix(parts).join(" ")).filter((cmd) => cmd !== "");
4618
+ return result.length > 0 ? result : void 0;
4619
+ }
4620
+ function tokenizeCommand(command) {
4621
+ const trimmed = command.trim().replace(FD_DEVNULL_RE, "").replace(FD_REDIRECT_RE, "");
4622
+ if (!trimmed) return void 0;
4623
+ if (hasUnquotedBackticks(trimmed)) return void 0;
4624
+ try {
4625
+ return parse(trimmed);
4626
+ } catch {
4627
+ return void 0;
4628
+ }
4629
+ }
4630
+ function getOp(token) {
4631
+ return typeof token === "object" && token !== null && "op" in token ? token.op : void 0;
4632
+ }
4633
+ function groupByOperator(tokens) {
4634
+ const groups = [[]];
4635
+ for (const token of tokens) {
4636
+ const op = getOp(token);
4637
+ if (op === void 0) {
4638
+ if (typeof token !== "string") return void 0;
4639
+ groups[groups.length - 1].push(token);
4640
+ } else if (op === "glob") {
4641
+ groups[groups.length - 1].push(token.pattern);
4642
+ } else if (SEPARATOR_OPS.has(op)) {
4643
+ groups.push([]);
4644
+ } else if (UNSAFE_OPS.has(op)) {
4645
+ return void 0;
4646
+ } else {
4647
+ return void 0;
4648
+ }
4649
+ }
4650
+ return groups;
4651
+ }
4652
+ function stripEnvPrefix(parts) {
4653
+ let i = 0;
4654
+ while (i < parts.length && /^[A-Za-z_]\w*=/.test(parts[i])) i++;
4655
+ return i > 0 ? parts.slice(i) : parts;
4656
+ }
4657
+
4594
4658
  // src/shared/isApprovedRead.ts
4595
4659
  import { resolve as resolve3 } from "path";
4596
4660
 
@@ -4761,8 +4825,8 @@ var denyCache;
4761
4825
  var TOOL_RE = /^(Bash|PowerShell)\((.+?)(:.*)?\)$/;
4762
4826
  var SHELL_TOOLS = ["Bash", "PowerShell"];
4763
4827
  var DOTSLASH_RE = /^\.[\\/]/;
4764
- var FD_REDIRECT_RE = /\d+>&\d+/g;
4765
- var FD_DEVNULL_RE = /\d*>\/dev\/null/g;
4828
+ var FD_REDIRECT_RE2 = /\d+>&\d+/g;
4829
+ var FD_DEVNULL_RE2 = /\d*>\/dev\/null/g;
4766
4830
  function loadPerms(key) {
4767
4831
  return parsePerms(readSettingsPerms(key));
4768
4832
  }
@@ -4773,7 +4837,7 @@ function shellEntries(map, toolName) {
4773
4837
  return map.get(toolName) ?? [];
4774
4838
  }
4775
4839
  function normCmd(cmd) {
4776
- return cmd.replace(FD_DEVNULL_RE, "").replace(FD_REDIRECT_RE, "").trim().replace(DOTSLASH_RE, "");
4840
+ return cmd.replace(FD_DEVNULL_RE2, "").replace(FD_REDIRECT_RE2, "").trim().replace(DOTSLASH_RE, "");
4777
4841
  }
4778
4842
  function findMatch2(entries, command) {
4779
4843
  const norm = normCmd(command);
@@ -4838,72 +4902,7 @@ function matchesConfigDeny(command) {
4838
4902
  );
4839
4903
  }
4840
4904
 
4841
- // src/shared/splitCompound.ts
4842
- import { parse } from "shell-quote";
4843
-
4844
- // src/shared/hasUnquotedBackticks.ts
4845
- var QUOTED_OR_BACKTICK_RE = /\\.|'[^']*'|"[^"]*"|(`)/g;
4846
- function hasUnquotedBackticks(command) {
4847
- QUOTED_OR_BACKTICK_RE.lastIndex = 0;
4848
- for (let m = QUOTED_OR_BACKTICK_RE.exec(command); m !== null; m = QUOTED_OR_BACKTICK_RE.exec(command)) {
4849
- if (m[1] !== void 0) return true;
4850
- }
4851
- return false;
4852
- }
4853
-
4854
- // src/shared/splitCompound.ts
4855
- var SEPARATOR_OPS = /* @__PURE__ */ new Set(["|", "&&", "||", ";"]);
4856
- var UNSAFE_OPS = /* @__PURE__ */ new Set(["(", ")", ">", ">>", "<", "<&", "|&", ">&"]);
4857
- var FD_REDIRECT_RE2 = /\d+>&\d+/g;
4858
- var FD_DEVNULL_RE2 = /\d*>\/dev\/null/g;
4859
- function splitCompound(command) {
4860
- const tokens = tokenizeCommand(command);
4861
- if (!tokens) return void 0;
4862
- const groups = groupByOperator(tokens);
4863
- if (!groups) return void 0;
4864
- const result = groups.map((parts) => stripEnvPrefix(parts).join(" ")).filter((cmd) => cmd !== "");
4865
- return result.length > 0 ? result : void 0;
4866
- }
4867
- function tokenizeCommand(command) {
4868
- const trimmed = command.trim().replace(FD_DEVNULL_RE2, "").replace(FD_REDIRECT_RE2, "");
4869
- if (!trimmed) return void 0;
4870
- if (hasUnquotedBackticks(trimmed)) return void 0;
4871
- try {
4872
- return parse(trimmed);
4873
- } catch {
4874
- return void 0;
4875
- }
4876
- }
4877
- function getOp(token) {
4878
- return typeof token === "object" && token !== null && "op" in token ? token.op : void 0;
4879
- }
4880
- function groupByOperator(tokens) {
4881
- const groups = [[]];
4882
- for (const token of tokens) {
4883
- const op = getOp(token);
4884
- if (op === void 0) {
4885
- if (typeof token !== "string") return void 0;
4886
- groups[groups.length - 1].push(token);
4887
- } else if (op === "glob") {
4888
- groups[groups.length - 1].push(token.pattern);
4889
- } else if (SEPARATOR_OPS.has(op)) {
4890
- groups.push([]);
4891
- } else if (UNSAFE_OPS.has(op)) {
4892
- return void 0;
4893
- } else {
4894
- return void 0;
4895
- }
4896
- }
4897
- return groups;
4898
- }
4899
- function stripEnvPrefix(parts) {
4900
- let i = 0;
4901
- while (i < parts.length && /^[A-Za-z_]\w*=/.test(parts[i])) i++;
4902
- return i > 0 ? parts.slice(i) : parts;
4903
- }
4904
-
4905
- // src/commands/cliHook/index.ts
4906
- var SUPPORTED_TOOLS = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
4905
+ // src/commands/cliHook/resolvePermission.ts
4907
4906
  function findDeny(toolName, parts) {
4908
4907
  for (const part of parts) {
4909
4908
  const configDeny = matchesConfigDeny(part);
@@ -4939,25 +4938,31 @@ function resolvePermission(toolName, parts) {
4939
4938
  permissionDecisionReason: reasons.join("; ")
4940
4939
  };
4941
4940
  }
4942
- function tryParseJson(raw) {
4941
+
4942
+ // src/commands/cliHook/index.ts
4943
+ var SUPPORTED_TOOLS = /* @__PURE__ */ new Set(["Bash", "PowerShell"]);
4944
+ function tryParseInput(raw) {
4943
4945
  try {
4944
- return JSON.parse(raw);
4946
+ const data = JSON.parse(raw);
4947
+ if (!SUPPORTED_TOOLS.has(data.tool_name) || !data.tool_input?.command)
4948
+ return void 0;
4949
+ return {
4950
+ toolName: data.tool_name,
4951
+ command: data.tool_input.command.trim()
4952
+ };
4945
4953
  } catch {
4946
4954
  return void 0;
4947
4955
  }
4948
4956
  }
4949
- function extractCommand(data) {
4950
- if (!SUPPORTED_TOOLS.has(data.tool_name) || !data.tool_input?.command)
4951
- return void 0;
4952
- const parts = splitCompound(data.tool_input.command.trim());
4953
- return parts ? { toolName: data.tool_name, parts } : void 0;
4957
+ function decide(toolName, rawCommand) {
4958
+ const parts = splitCompound(rawCommand);
4959
+ if (parts) return resolvePermission(toolName, parts);
4960
+ return findDeny(toolName, [rawCommand]);
4954
4961
  }
4955
4962
  async function cliHook() {
4956
- const data = tryParseJson(await readStdin());
4957
- if (!data) return;
4958
- const cmd = extractCommand(data);
4959
- if (!cmd) return;
4960
- const decision = resolvePermission(cmd.toolName, cmd.parts);
4963
+ const input = tryParseInput(await readStdin());
4964
+ if (!input) return;
4965
+ const decision = decide(input.toolName, input.command);
4961
4966
  if (!decision) return;
4962
4967
  console.log(
4963
4968
  JSON.stringify({
@@ -11728,6 +11733,59 @@ Done in ${elapsed}`);
11728
11733
  });
11729
11734
  }
11730
11735
 
11736
+ // src/commands/run/index.ts
11737
+ function buildCommand(command, configArgs, extraArgs) {
11738
+ const parts = [command, ...configArgs];
11739
+ return [...parts.map(shellQuote), ...extraArgs.map(shellQuote)].join(" ");
11740
+ }
11741
+ function printAvailableConfigs(configs) {
11742
+ console.error("Available configurations:");
11743
+ for (const r of configs) {
11744
+ console.error(` - ${r.name}`);
11745
+ }
11746
+ }
11747
+ function exitNoRunConfigs() {
11748
+ console.error("No run configurations found in assist.yml");
11749
+ process.exit(1);
11750
+ }
11751
+ function requireRunConfigs() {
11752
+ const { run: run4 } = loadConfig();
11753
+ if (!run4 || run4.length === 0) return exitNoRunConfigs();
11754
+ return run4;
11755
+ }
11756
+ function exitWithConfigNotFound(name, configs) {
11757
+ console.error(`No run configuration found with name: ${name}`);
11758
+ printAvailableConfigs(configs);
11759
+ process.exit(1);
11760
+ }
11761
+ function findRunConfig(name) {
11762
+ const configs = requireRunConfigs();
11763
+ return configs.find((r) => r.name === name) ?? exitWithConfigNotFound(name, configs);
11764
+ }
11765
+ function listRunConfigs() {
11766
+ const configs = requireRunConfigs();
11767
+ for (const config of configs) {
11768
+ const args = config.args?.length ? ` ${config.args.join(" ")}` : "";
11769
+ console.log(`${config.name}: ${config.command}${args}`);
11770
+ }
11771
+ }
11772
+ function run3(name, args) {
11773
+ if (!name) {
11774
+ console.error("error: missing required argument 'name'");
11775
+ console.error(formatConfiguredCommands());
11776
+ process.exit(1);
11777
+ }
11778
+ const runConfig = findRunConfig(name);
11779
+ const resolvedCwd = runConfig.cwd ? resolve5(getConfigDir(), runConfig.cwd) : void 0;
11780
+ if (runConfig.pre) runPreCommands(runConfig.pre, resolvedCwd);
11781
+ const resolved = resolveParams(runConfig.params, args);
11782
+ spawnRunCommand(
11783
+ buildCommand(runConfig.command, runConfig.args ?? [], resolved),
11784
+ runConfig.env,
11785
+ resolvedCwd
11786
+ );
11787
+ }
11788
+
11731
11789
  // src/commands/run/add.ts
11732
11790
  import { mkdirSync as mkdirSync13, writeFileSync as writeFileSync28 } from "fs";
11733
11791
  import { join as join43 } from "path";
@@ -11808,64 +11866,62 @@ function add3() {
11808
11866
  );
11809
11867
  }
11810
11868
 
11811
- // src/commands/run/index.ts
11812
- function buildCommand(command, configArgs, extraArgs) {
11813
- const parts = [command, ...configArgs];
11814
- return [...parts.map(shellQuote), ...extraArgs.map(shellQuote)].join(" ");
11869
+ // src/commands/run/remove.ts
11870
+ import { existsSync as existsSync40, unlinkSync as unlinkSync11 } from "fs";
11871
+ import { join as join44 } from "path";
11872
+ function findRemoveIndex() {
11873
+ const idx = process.argv.indexOf("remove");
11874
+ if (idx === -1 || idx + 1 >= process.argv.length) return -1;
11875
+ return idx;
11815
11876
  }
11816
- function printAvailableConfigs(configs) {
11817
- console.error("Available configurations:");
11818
- for (const r of configs) {
11819
- console.error(` - ${r.name}`);
11877
+ function parseRemoveName() {
11878
+ const idx = findRemoveIndex();
11879
+ if (idx === -1) {
11880
+ console.error("Usage: assist run remove <name>");
11881
+ process.exit(1);
11820
11882
  }
11883
+ return process.argv[idx + 1];
11821
11884
  }
11822
- function exitNoRunConfigs() {
11823
- console.error("No run configurations found in assist.yml");
11824
- process.exit(1);
11825
- }
11826
- function requireRunConfigs() {
11827
- const { run: run4 } = loadConfig();
11828
- if (!run4 || run4.length === 0) return exitNoRunConfigs();
11829
- return run4;
11830
- }
11831
- function exitWithConfigNotFound(name, configs) {
11832
- console.error(`No run configuration found with name: ${name}`);
11833
- printAvailableConfigs(configs);
11834
- process.exit(1);
11835
- }
11836
- function findRunConfig(name) {
11837
- const configs = requireRunConfigs();
11838
- return configs.find((r) => r.name === name) ?? exitWithConfigNotFound(name, configs);
11839
- }
11840
- function listRunConfigs() {
11841
- const configs = requireRunConfigs();
11842
- for (const config of configs) {
11843
- const args = config.args?.length ? ` ${config.args.join(" ")}` : "";
11844
- console.log(`${config.name}: ${config.command}${args}`);
11885
+ function deleteCommandFile(name) {
11886
+ const filePath = join44(".claude", "commands", `${name}.md`);
11887
+ if (existsSync40(filePath)) {
11888
+ unlinkSync11(filePath);
11889
+ console.log(`Deleted command file: ${filePath}`);
11845
11890
  }
11846
11891
  }
11847
- function run3(name, args) {
11848
- if (!name) {
11849
- console.error("error: missing required argument 'name'");
11850
- console.error(formatConfiguredCommands());
11892
+ function remove() {
11893
+ const name = parseRemoveName();
11894
+ const config = loadProjectConfig();
11895
+ const runList = config.run;
11896
+ if (!runList || !runList.find((r) => r.name === name)) {
11897
+ console.error(`Run configuration "${name}" not found`);
11851
11898
  process.exit(1);
11852
11899
  }
11853
- const runConfig = findRunConfig(name);
11854
- const resolvedCwd = runConfig.cwd ? resolve5(getConfigDir(), runConfig.cwd) : void 0;
11855
- if (runConfig.pre) runPreCommands(runConfig.pre, resolvedCwd);
11856
- const resolved = resolveParams(runConfig.params, args);
11857
- spawnRunCommand(
11858
- buildCommand(runConfig.command, runConfig.args ?? [], resolved),
11859
- runConfig.env,
11860
- resolvedCwd
11861
- );
11900
+ config.run = runList.filter((r) => r.name !== name);
11901
+ saveConfig(config);
11902
+ deleteCommandFile(name);
11903
+ console.log(`Removed run configuration: ${name}`);
11904
+ }
11905
+
11906
+ // src/commands/run/registerRun.ts
11907
+ function registerRun(program2) {
11908
+ const runCommand = program2.command("run").description("Run a configured command from assist.yml").argument("[name]", "Name of the configured command").argument("[args...]", "Arguments to pass to the command").allowUnknownOption().addHelpText("after", () => formatConfiguredCommands()).action((name, args) => run3(name, args));
11909
+ runCommand.command("list").description("List configured run commands").action(listRunConfigs);
11910
+ runCommand.command("add").description("Add a new run configuration to assist.yml").argument("<name>", "Name for the run configuration").argument("<command>", "Command to execute").argument("[args...]", "Static args to pass to the command").option(
11911
+ "--cwd <dir>",
11912
+ "Working directory (resolved relative to the config file)"
11913
+ ).addHelpText(
11914
+ "after",
11915
+ '\nPositional params can be added to the config manually:\n params:\n - name: env # assist run deploy prod \u2192 appends "prod"\n required: true\n - name: tag\n default: latest'
11916
+ ).allowUnknownOption().allowExcessArguments().action(() => add3());
11917
+ runCommand.command("remove").description("Remove a run configuration from assist.yml").argument("<name>", "Name of the run configuration to remove").action(() => remove());
11862
11918
  }
11863
11919
 
11864
11920
  // src/commands/screenshot/index.ts
11865
11921
  import { execSync as execSync41 } from "child_process";
11866
- import { existsSync as existsSync40, mkdirSync as mkdirSync14, unlinkSync as unlinkSync11, writeFileSync as writeFileSync29 } from "fs";
11922
+ import { existsSync as existsSync41, mkdirSync as mkdirSync14, unlinkSync as unlinkSync12, writeFileSync as writeFileSync29 } from "fs";
11867
11923
  import { tmpdir as tmpdir6 } from "os";
11868
- import { join as join44, resolve as resolve6 } from "path";
11924
+ import { join as join45, resolve as resolve6 } from "path";
11869
11925
  import chalk131 from "chalk";
11870
11926
 
11871
11927
  // src/commands/screenshot/captureWindowPs1.ts
@@ -11995,14 +12051,14 @@ Write-Output $OutputPath
11995
12051
 
11996
12052
  // src/commands/screenshot/index.ts
11997
12053
  function buildOutputPath(outputDir, processName) {
11998
- if (!existsSync40(outputDir)) {
12054
+ if (!existsSync41(outputDir)) {
11999
12055
  mkdirSync14(outputDir, { recursive: true });
12000
12056
  }
12001
12057
  const timestamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[:.]/g, "-");
12002
12058
  return resolve6(outputDir, `${processName}-${timestamp}.png`);
12003
12059
  }
12004
12060
  function runPowerShellScript(processName, outputPath) {
12005
- const scriptPath = join44(tmpdir6(), `assist-screenshot-${Date.now()}.ps1`);
12061
+ const scriptPath = join45(tmpdir6(), `assist-screenshot-${Date.now()}.ps1`);
12006
12062
  writeFileSync29(scriptPath, captureWindowPs1, "utf-8");
12007
12063
  try {
12008
12064
  execSync41(
@@ -12010,7 +12066,7 @@ function runPowerShellScript(processName, outputPath) {
12010
12066
  { stdio: ["ignore", "pipe", "pipe"], encoding: "utf-8" }
12011
12067
  );
12012
12068
  } finally {
12013
- unlinkSync11(scriptPath);
12069
+ unlinkSync12(scriptPath);
12014
12070
  }
12015
12071
  }
12016
12072
  function screenshot(processName) {
@@ -12252,15 +12308,7 @@ program.command("sync").description("Copy command files to ~/.claude/commands").
12252
12308
  program.command("init").description("Initialize VS Code and verify configurations").action(init4);
12253
12309
  program.command("commit").description("Create a git commit with validation").argument("<args...>", "status | <message> [files...]").action(commit);
12254
12310
  registerConfig(program);
12255
- var runCommand = program.command("run").description("Run a configured command from assist.yml").argument("[name]", "Name of the configured command").argument("[args...]", "Arguments to pass to the command").allowUnknownOption().addHelpText("after", () => formatConfiguredCommands()).action((name, args) => run3(name, args));
12256
- runCommand.command("list").description("List configured run commands").action(listRunConfigs);
12257
- runCommand.command("add").description("Add a new run configuration to assist.yml").argument("<name>", "Name for the run configuration").argument("<command>", "Command to execute").argument("[args...]", "Static args to pass to the command").option(
12258
- "--cwd <dir>",
12259
- "Working directory (resolved relative to the config file)"
12260
- ).addHelpText(
12261
- "after",
12262
- '\nPositional params can be added to the config manually:\n params:\n - name: env # assist run deploy prod \u2192 appends "prod"\n required: true\n - name: tag\n default: latest'
12263
- ).allowUnknownOption().allowExcessArguments().action(() => add3());
12311
+ registerRun(program);
12264
12312
  registerNew(program);
12265
12313
  var lintCommand = program.command("lint").description("Run lint checks for conventions not enforced by biomejs").option("-f, --fix", "Auto-fix violations where possible").action(lint);
12266
12314
  lintCommand.command("init").description("Initialize Biome with standard linter config").action(init);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@staff0rd/assist",
3
- "version": "0.184.0",
3
+ "version": "0.185.0",
4
4
  "type": "module",
5
5
  "main": "dist/index.js",
6
6
  "bin": {