genshin-mods 4.2.0 → 4.3.2

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.
@@ -4,7 +4,7 @@
4
4
  "displayName": "Genshin Mods",
5
5
  "description": "Six Claude Code mods: five under one band above the prompt, waypoints (the next steps, one press each), resin (cache, context, limits and cost, with warm, compact and a one-click handoff), veil (recording mode), commission (a goal's task list and clock) and ward (a question before editing a file another session just changed), and a delegation guard that nudges a run of lookups toward a haiku agent.",
6
6
  "author": { "name": "Esposter", "url": "https://github.com/Esposter" },
7
- "homepage": "https://esposter.com/docs/infra/claude-interface/genshin-mods",
7
+ "homepage": "https://github.com/Esposter/Esposter/tree/main/openspec/specs/infra/claude-interface/genshin-mods",
8
8
  "repository": "https://github.com/Esposter/Esposter",
9
9
  "license": "Apache-2.0",
10
10
  "keywords": ["cache", "collision", "genshin", "handoff", "mods", "recording", "tasks"],
package/README.md CHANGED
@@ -45,7 +45,7 @@ claude --plugin-dir packages/genshin-mods
45
45
 
46
46
  ## <a name="documentation">📖 Documentation</a>
47
47
 
48
- We highly recommend you take a look at the [documentation](https://esposter.com/docs/infra/claude-interface/genshin-mods) to level up.
48
+ We highly recommend you take a look at the [documentation](https://github.com/Esposter/Esposter/tree/main/openspec/specs/infra/claude-interface/genshin-mods) to level up.
49
49
 
50
50
  ### Command reference
51
51
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "genshin-mods",
3
- "version": "4.2.0",
3
+ "version": "4.3.2",
4
4
  "description": "A Claude Code plugin of mods: five under one band above the prompt (the next steps, the cache and usage with a one-click handoff, recording mode, a goal's task list and a question before editing a file another session just changed) a delegation guard that nudges a run of lookups toward a haiku agent, a disk-scan guard that refuses a Bash scan from a root or a home folder, and a whole-index guard that refuses a git command over the whole index while another session shares the checkout.",
5
5
  "keywords": [
6
6
  "claude-code",
@@ -8,7 +8,7 @@
8
8
  "genshin",
9
9
  "mods"
10
10
  ],
11
- "homepage": "https://esposter.com/docs/infra/claude-interface/genshin-mods",
11
+ "homepage": "https://github.com/Esposter/Esposter/tree/main/openspec/specs/infra/claude-interface/genshin-mods",
12
12
  "bugs": {
13
13
  "url": "https://github.com/Esposter/Esposter/issues"
14
14
  },
@@ -31,12 +31,10 @@
31
31
  "access": "public"
32
32
  },
33
33
  "scripts": {
34
- "format": "oxfmt",
35
- "format:check": "oxfmt --check",
36
34
  "lint": "eslint .",
37
35
  "lint:fix": "eslint --fix .",
38
36
  "test": "vitest",
39
37
  "validate": "claude plugin validate ."
40
38
  },
41
- "gitHead": "fef4971cf6a621a2c1850f32fcec19ee4ec0de99"
39
+ "gitHead": "58d7f270fcb942d6ddf63c32deb29b67ba62f348"
42
40
  }
@@ -60,6 +60,11 @@ export const LEADING_SEGMENT_REGEX: RegExp = /^\s*(?:export\s[^&;]*&&|cd\s[^&;]*
60
60
  // oxlint-disable-next-line typescript/no-inferrable-types -- `isolatedDeclarations` demands the annotation this pattern would otherwise infer
61
61
  export const LOOKUP_COMMAND_REGEX: RegExp =
62
62
  /^\s*(?:cat|sed|grep|rg|find|ls|head|tail|awk|wc|get-content|get-childitem|select-string|git\s+(?:show|log|diff)|gh\s+run\s+view)(?:\s|$)/iu;
63
+ // The reads that write as well: sed's in-place edit, find's actions that write, and a redirection into a file, the
64
+ // Stderr and stdout merges and the null device aside
65
+ // oxlint-disable-next-line typescript/no-inferrable-types -- `isolatedDeclarations` demands the annotation this pattern would otherwise infer
66
+ export const WRITING_LOOKUP_REGEX: RegExp =
67
+ /\bsed\b.*\s(?:-[a-zA-Z]*i|--in-place)|\bfind\b.*\s-(?:delete|exec|execdir|ok|okdir|fprint0?|fprintf|fls)\b|>(?!&|\s*\/dev\/null\b)/iu;
63
68
  // Every third lookup in a row, the chain a haiku agent should answer as one bounded question
64
69
  export const DELEGATION_NUDGE_EVERY = 3;
65
70
  export const DELEGATION_NUDGE =
@@ -1,4 +1,4 @@
1
- import { LEADING_SEGMENT_REGEX, LOOKUP_COMMAND_REGEX } from "../constants";
1
+ import { LEADING_SEGMENT_REGEX, LOOKUP_COMMAND_REGEX, WRITING_LOOKUP_REGEX } from "../constants";
2
2
 
3
3
  // A session chains `export` and `cd` ahead of its real command, so each leading segment is stripped before the
4
4
  // Command's first word is read
@@ -7,5 +7,8 @@ const stripLeadingSegments = (command: string): string => {
7
7
  return rest === command ? command : stripLeadingSegments(rest);
8
8
  };
9
9
 
10
- export const checkIsLookupCommand = (command: string): boolean =>
11
- LOOKUP_COMMAND_REGEX.test(stripLeadingSegments(command));
10
+ export const checkIsLookupCommand = (command: string): boolean => {
11
+ const rest = stripLeadingSegments(command);
12
+
13
+ return LOOKUP_COMMAND_REGEX.test(rest) && !WRITING_LOOKUP_REGEX.test(rest);
14
+ };
@@ -19,11 +19,15 @@ const normalizePath = (token: string) => {
19
19
 
20
20
  const isRoot = (token: string) => ROOT_REGEX.test(normalizePath(token));
21
21
 
22
- // A flag group holding r or R: `-R`, `-rn`, `-lR`, `--recursive`
23
- const isRecursiveFlag = (token: string) => /^-[a-zA-Z]*[rR]/u.test(token) || token === "--recursive";
22
+ // The long form `--recursive`, which getopt also takes as any prefix of it from `--rec` on, since `--re` names two options
23
+ const isRecursiveLongFlag = (token: string) => token.startsWith("--rec") && "recursive".startsWith(token.slice(2));
24
24
 
25
- // The ls flag recurses on the capital R alone, since its lowercase r sorts in reverse
26
- const isUpperRecursiveFlag = (token: string) => /^-[a-zA-Z]*R/u.test(token);
25
+ // A flag group holding the scan's recursive letter, or the long form: grep takes r or R (`-R`, `-rn`, `-lR`), and ls takes
26
+ // The capital R alone, since its lowercase r sorts in reverse
27
+ const GREP_RECURSIVE_FLAG_REGEX = /^-[a-zA-Z]*[rR]/u;
28
+ const LS_RECURSIVE_FLAG_REGEX = /^-[a-zA-Z]*R/u;
29
+ const isRecursiveFlag = (token: string, shortFlagRegex: RegExp) =>
30
+ shortFlagRegex.test(token) || isRecursiveLongFlag(token);
27
31
 
28
32
  // A flag that gives grep its pattern as the next argument, or as the rest of the same one
29
33
  const SEPARATE_PATTERN_FLAG_REGEX = /^-[a-zA-Z]*[ef]$|^--(?:regexp|file)$/u;
@@ -55,9 +59,13 @@ const readGrepArguments = (args: string[]): { flags: string[]; paths: string[] }
55
59
  const getScanRefusal = (name: string, args: string[]): string | undefined => {
56
60
  if (name === "find" || name === "du") return args.some((arg) => isRoot(arg)) ? name : undefined;
57
61
  if (name === "ls")
58
- return args.some((arg) => isUpperRecursiveFlag(arg)) && args.some((arg) => isRoot(arg)) ? name : undefined;
62
+ return args.some((arg) => isRecursiveFlag(arg, LS_RECURSIVE_FLAG_REGEX)) && args.some((arg) => isRoot(arg))
63
+ ? name
64
+ : undefined;
59
65
  const { flags, paths } = readGrepArguments(args);
60
- return flags.some((flag) => isRecursiveFlag(flag)) && paths.some((path) => isRoot(path)) ? name : undefined;
66
+ return flags.some((flag) => isRecursiveFlag(flag, GREP_RECURSIVE_FLAG_REGEX)) && paths.some((path) => isRoot(path))
67
+ ? name
68
+ : undefined;
61
69
  };
62
70
 
63
71
  const getScanCommandRefusal: CommandRefusal = (name, args) =>