Make a Scratch Advanced Input extension and use this as a reference:
class SelectorEX {
  getInfo() {
    return {
      id: 'selectorExtension',
      name: 'Selector',
      color1: '#7fde2c',
      color2: '#73bf30',
      blocks: [
        {
          opcode: 'selectorOn',
          blockType: Scratch.BlockType.BOOLEAN,
          text: 'is [SELECTOR] on',
          arguments: {
            SELECTOR: {
              type: Scratch.ArgumentType.NULL,
            }
          }
        },
        {
          opcode: 'reportSelector',
          blockType: Scratch.BlockType.REPORTER,
          text: '[SELECTOR]',
          arguments: {
            SELECTOR: {
              type: Scratch.ArgumentType.STRING,
              menu: 'SELECTOR'
            }
          }
        },
        {
            opcode: 'toSelectorS',
            blockType: Scratch.BlockType.REPORTER,
            text: "convert [BOOL] to selector",
            arguments: {
              BOOL: {
                type: Scratch.ArgumentType.BOOLEAN
              }
            }
        },
        {
            opcode: 'toBooleanS',
            blockType: Scratch.BlockType.BOOLEAN,
            text: "convert [SELECTOR] to boolean",
            arguments: {
              SELECTOR: {
                type: Scratch.ArgumentType.NULL
              }
            }
        },
        {
            opcode: 'whileOn',
            blockType: Scratch.BlockType.LOOP,
            text: "while [SELECTOR] is [SELECTORMENU]",
            arguments: {
              SELECTOR: {
                type: Scratch.ArgumentType.NULL
              },
              SELECTORMENU: {
                type: Scratch.ArgumentType.STRING,
                menu: 'SELECTOR'
              }
            }
        },
        {
            opcode: 'repeatUntilOn',
            blockType: Scratch.BlockType.LOOP,
            text: "repeat until [SELECTOR] is [SELECTORMENU]",
            arguments: {
              SELECTOR: {
                type: Scratch.ArgumentType.NULL
              },
              SELECTORMENU: {
                type: Scratch.ArgumentType.STRING,
                menu: 'SELECTOR'
              }
            }
        },
        {
            opcode: 'ifSelectorIsOn',
            blockType: Scratch.BlockType.CONDITIONAL,
            text: "if [SELECTOR] is [SELECTORMENU] then",
            arguments: {
              SELECTOR: {
                type: Scratch.ArgumentType.NULL
              },
              SELECTORMENU: {
                type: Scratch.ArgumentType.STRING,
                menu: 'SELECTOR'
              }
            }
        },
        {
          opcode: 'selectorOneAndSelectorTwoAreEqual',
          blockType: Scratch.BlockType.BOOLEAN,
          text: "[SELECTORONE] and [SELECTORTWO] are equal",
          arguments: {
            SELECTORONE: {
              type: Scratch.ArgumentType.NULL
            },
            SELECTORTWO: {
              type: Scratch.ArgumentType.NULL
            }
          }
        },
        {
          opcode: 'ifSelectorOneAndSelectorTwoAreEqual',
          blockType: Scratch.BlockType.CONDITIONAL,
          text: "if [SELECTORONE] and [SELECTORTWO] are equal then",
          arguments: {
            SELECTORONE: {
              type: Scratch.ArgumentType.NULL
            },
            SELECTORTWO: {
              type: Scratch.ArgumentType.NULL
            }
          }
        },
        {
          opcode: 'convertSelectorToNumber',
          blockType: Scratch.BlockType.REPORTER,
          text: "convert [SELECTOR] to number",
          arguments: {
            SELECTOR: {
              type: Scratch.ArgumentType.NULL
            }
          }
        },
        {
          opcode: 'convertNumberToSelector',
          blockType: Scratch.BlockType.REPORTER,
          text: "convert [NUMBER] to selector",
          arguments: {
            NUMBER: {
              type: Scratch.ArgumentType.NUMBER
            }
          }
        },
        {
          opcode: 'selectorIsTheSameAsNumber',
          blockType: Scratch.BlockType.BOOLEAN,
          text: "[SELECTOR] is the same as [NUMBER]",
          arguments: {
            SELECTOR: {
              type: Scratch.ArgumentType.NULL
            },
            NUMBER: {
              type: Scratch.ArgumentType.NUMBER
            }
          }
        },
        {
          opcode: 'selectorIsTheSameAsBoolean',
          blockType: Scratch.BlockType.BOOLEAN,
          text: "[SELECTOR] is the same as [BOOL]",
          arguments: {
            SELECTOR: {
              type: Scratch.ArgumentType.NULL
            },
            BOOL: {
              type: Scratch.ArgumentType.BOOLEAN
            }
          }
        },
        {
          opcode: 'ifSelectorIsTheSameAsNumber',
          blockType: Scratch.BlockType.CONDITIONAL,
          text: "if [SELECTOR] is the same as [NUMBER] then",
          arguments: {
            SELECTOR: {
              type: Scratch.ArgumentType.NULL
            },
            NUMBER: {
              type: Scratch.ArgumentType.NUMBER
            }
          }
        },
        {
          opcode: 'ifSelectorIsTheSameAsBoolean',
          blockType: Scratch.BlockType.CONDITIONAL,
          text: "if [SELECTOR] is the same as [BOOL] then",
          arguments: {
            SELECTOR: {
              type: Scratch.ArgumentType.NULL
            },
            BOOL: {
              type: Scratch.ArgumentType.BOOLEAN
            }
          }
        },
        {
          opcode: 'textIsSelector',
          blockType: Scratch.BlockType.BOOLEAN,
          text: "text [TEXT] is selector",
          arguments: {
            TEXT: {
              type: Scratch.ArgumentType.STRING
            }
          }
        },
        {
          opcode: 'textIsNotSelector',
          blockType: Scratch.BlockType.BOOLEAN,
          text: "text [TEXT] is not selector",
          arguments: {
            TEXT: {
              type: Scratch.ArgumentType.STRING
            }
          }
        },
        {
          opcode: 'ifTextIsSelector',
          blockType: Scratch.BlockType.CONDITIONAL,
          text: "if text [TEXT] is selector then",
          arguments: {
            TEXT: {
              type: Scratch.ArgumentType.STRING
            }
          }
        },
        {
          opcode: 'ifTextIsNotSelector',
          blockType: Scratch.BlockType.CONDITIONAL,
          text: "if text [TEXT] is not selector then",
          arguments: {
            TEXT: {
              type: Scratch.ArgumentType.STRING
            }
          }
        }
      ],
      menus: {
        SELECTOR: {
        acceptReporters: true,
        items: ["on", "off"]
        }
      }
    };
  }
  selectorOn(args) {
    return args.SELECTOR === 'on';
  }
  reportSelector(args) {
    return args.SELECTOR;
  }
  toSelectorS(args) {
    if (args.BOOL) {
      return 'on';
    } else {
      return 'off';
    }
  }
  toBooleanS(args) {
    if (args.SELECTOR === 'on') {
      return true;
    } else if (args.SELECTOR === 'off') {
      return false;
    }
  }
  whileOn(args, util) {
    if (args.SELECTOR === args.SELECTORMENU) {
      util.startBranch(1, true);
    }
  }
  repeatUntilOn(args, util) {
    if (args.SELECTOR !== args.SELECTORMENU) {
      util.startBranch(1, true);
    }
  }
  ifSelectorIsOn(args, util) {
    return (args.SELECTOR === args.SELECTORMENU);
  }
  selectorOneAndSelectorTwoAreEqual(args) {
    return (args.SELECTORONE === args.SELECTORTWO);
  }
  ifSelectorOneAndSelectorTwoAreEqual(args, util) {
    return (args.SELECTORONE === args.SELECTORTWO);
  }
  convertSelectorToNumber(args) {
    if (args.SELECTOR === 'on') {
      return 1;
    } else if (args.SELECTOR === 'off') {
      return 0;
    }
  }
  convertNumberToSelector(args) {
    if (args.NUMBER === 1) {
      return 'on';
    } else if (args.NUMBER === 0) {
      return 'off';
    }
  }
  selectorIsTheSameAsNumber(args) {
    return (args.SELECTOR === args.NUMBER);
  }
  selectorIsTheSameAsBoolean(args) {
    return (args.SELECTOR === args.BOOL);
  }
  ifSelectorIsTheSameAsNumber(args, util) {
    return (args.SELECTOR === args.NUMBER);
  }
  ifSelectorIsTheSameAsBoolean(args, util) {
    return (args.SELECTOR === args.BOOL);
  }
  textIsSelector(args) {
    if (args.TEXT === 'on' || args.TEXT === 'off') {
      return true;
    } else {
      return false;
    }
  }
  textIsNotSelector(args) {
    if (args.TEXT !== 'on' || args.TEXT !== 'off') {
      return true;
    } else {
      return false;
    }
  }
  ifTextIsSelector(args, util) {
    return (args.TEXT === 'on' || args.TEXT === 'off');
  }
  ifTextIsNotSelector(args, util) {
    return (args.TEXT !== 'on' || args.TEXT !== 'off');
  }
}

Scratch.extensions.register(new SelectorEX());