Skip to content
WebViewGold Vibe Coding to App Store
English
Get WebViewGold

Claude Code

Custom slash commands and hooks for releasing WebViewGold apps with Claude Code

Two small files remove most of the risk from a release. A /release-check command makes Claude Code walk the same checklist every time, starting with the question whether this release needs a store build at all, and a hook refuses any commit that would ship Config.swift or Config.java pointing at localhost. Both live in the repository, so your whole team and every cloud session get them.

From $109 one-time per platform, or let our team do it all from $698.

  • Approval guarantee – or money back within 14 days
  • No coding needed
  • One-time payment, free updates
  • 24/7 human support
  • Made in Germany since 2014 · ISO 27001

Commands guide, hooks enforce

A custom command is a saved prompt. A file at .claude/commands/release-check.md becomes /release-check in every session in that repository. Claude Code has merged commands into skills: a skill at .claude/skills/release-check/SKILL.md creates the same command and can bundle extra files, and existing command files keep working. For a single checklist, the commands folder is the simplest home.

A hook is a different kind of thing. It is a shell command that Claude Code itself runs at a fixed moment, for example right before a tool call. The model does not get a vote: when a PreToolUse hook exits with code 2, the tool call is blocked and Claude receives your error message instead. That is exactly what you want for the one mistake that must never reach users.

A practical rule: guidance goes into CLAUDE.md, repeatable procedures become commands, and anything that must never happen becomes a hook.

The /release-check command

Save as .claude/commands/release-check.md. Lines with a leading ! run before Claude sees the prompt and paste their output in, so every check starts from the real repository state. allowed-tools pre-approves exactly the commands the check needs, and disable-model-invocation keeps Claude from starting a release check on its own.

markdown
---
description: Pre-release check for the web app and the WebViewGold iOS and Android projects. Starts by deciding whether this release needs a store build.
argument-hint: "[version, for example 1.4.0]"
allowed-tools: Bash(git status *), Bash(git log *), Bash(git describe *), Bash(git diff *), Bash(git ls-files *), Bash(npm run lint), Bash(npm run build), Read, Grep, Glob
disable-model-invocation: true
---

# Release check for version $ARGUMENTS

## Repository state
- Branch and changes: !`git status --short --branch`
- Last release tag (a commit hash if there is no tag yet): !`git describe --tags --abbrev=0 --always`
- Recent commits: !`git log --oneline -20`

## 1. Does this release need a store build?
Run git diff --stat from the last release tag to HEAD. Native changes are files under
native/ios or native/android: Config.swift, Config.java, Info.plist, AndroidManifest.xml,
build.gradle, entitlements, icons and splash images.
- No native changes: report "Web-only release: deploy, no store build needed" and go to step 3.
- Native changes: a new App Store and Google Play build is needed. Continue with step 2.

## 2. Native checks
- Config.swift: host and webviewurl use the production domain over HTTPS.
- Config.java: HOST and HOME_URL match the iOS values.
- Neither file contains localhost, 127.0.0.1, 10.0.2.2, a 192.168.x.x address,
  an ngrok or trycloudflare host, or a preview deployment URL.
- iOS: the marketing version is $ARGUMENTS, the build number is higher than the last
  uploaded build, and every extension target (for example OneSignalNotificationServiceExtension)
  uses the main bundle ID as prefix.
- Android: versionName is $ARGUMENTS and versionCode is higher than the last upload.
  Report targetSdk; I will compare it with Google's current requirement.
- Every iOS usage description and every Android permission belongs to a feature we use.
- git ls-files shows no signing material: *.jks, *.keystore, *.p12, *.mobileprovision,
  keystore.properties.

## 3. Web app checks
- npm run lint and npm run build pass.
- /privacy, /terms and /delete-account still exist and are linked from settings.
- No new checkout, price or external payment link is visible when isNativeApp() is true.
- No new logging of tokens, email addresses or other personal data.

## Output
One table: check, result (pass, fail, needs me), evidence (file:line or command output).
Then a short list of what I must do by hand: deploy, store uploads, TestFlight,
release notes. Do not fix anything inside this command; I will ask for fixes separately.

The hook configuration

Merge this into .claude/settings.json, the project settings file that is committed and shared (personal tweaks belong in .claude/settings.local.json). The Bash matcher runs the script before every shell command Claude wants to run; the script itself ignores everything that is not a commit.

json
{
  "hooks": {
    "PreToolUse": [
      {
        "matcher": "Bash",
        "hooks": [
          {
            "type": "command",
            "command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/guard-native-config.sh",
            "timeout": 30
          }
        ]
      }
    ]
  }
}

With a good optimized version of your web app you can have a stunning mobile app within a couple of minutes. Additionally to that the customer service is very nice and very helpful! I can just recommend WebViewGold :-)

Julian Koch · Co-Founder of DuelDonkey · CodeCanyon

The hook script

Save as .claude/hooks/guard-native-config.sh and make it executable. Claude Code sends the planned tool call as JSON on stdin; the script reads the command with jq and, for commits, runs the shared checker. Exit code 2 blocks the commit and hands the stderr text to Claude, which then usually corrects the URL by itself.

bash
#!/usr/bin/env bash
# .claude/hooks/guard-native-config.sh
# PreToolUse hook (matcher: Bash). Blocks "git commit" while a staged Config.swift
# or Config.java contains a development URL. Exit 0 = allow, exit 2 = block.

input=$(cat)
cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty')

# Not a commit? Allow immediately.
printf '%s' "$cmd" | grep -Eq 'git( -C [^ ]+)? commit' || exit 0

cd "${CLAUDE_PROJECT_DIR:-.}" || exit 0

# "commit -a", "--all" or a "git add" in the same command also commit unstaged
# changes, so check the working tree too.
flag=""
if printf '%s' "$cmd" | grep -Eq 'commit.* (-[a-zA-Z]*a|--all)|git( -C [^ ]+)? add '; then
  flag="--include-worktree"
fi

if ! report=$(./scripts/check-native-config.sh $flag 2>&1); then
  {
    echo "Commit blocked: a native config file points to a development URL."
    echo "$report"
    echo "Set host and webviewurl (Config.swift) and HOST and HOME_URL (Config.java)"
    echo "to the production HTTPS domain, stage the files again and retry the commit."
  } >&2
  exit 2
fi
exit 0

The shared checker for Claude and Git

Keep the actual rule in one script so the Claude Code hook and a normal Git pre-commit hook enforce the same thing. Save as scripts/check-native-config.sh, executable. 10.0.2.2 is how the Android emulator reaches your laptop, a classic leftover from testing.

bash
#!/usr/bin/env bash
# scripts/check-native-config.sh [--include-worktree]
# Exit 1 if a staged Config.swift or Config.java contains a development URL.
# Used by the Claude Code hook and by the Git pre-commit hook.

pattern='localhost|127\.0\.0\.1|0\.0\.0\.0|10\.0\.2\.2|192\.168\.[0-9]+\.[0-9]+|\.ngrok(-free)?\.(app|io|dev)|\.trycloudflare\.com'
# Add the preview hosts you use, for example: |-git-[a-z0-9-]+\.vercel\.app

scan() { # $1 = file, $2 = label; file content on stdin
  local hits
  hits=$(grep -nE "$pattern" | grep -vE '^[0-9]+:[[:space:]]*(//|\*)')
  if [ -n "$hits" ]; then
    printf '%s (%s):\n%s\n' "$1" "$2" "$hits"
    return 1
  fi
}

status=0
for f in $(git diff --cached --name-only --diff-filter=ACMR | grep -E '(^|/)Config\.(swift|java)$'); do
  git show ":$f" | scan "$f" "staged" || status=1
done

if [ "$1" = "--include-worktree" ]; then
  for f in $(git diff --name-only | grep -E '(^|/)Config\.(swift|java)$'); do
    scan "$f" "working tree" < "$f" || status=1
  done
fi

exit $status

Set it up and prove it works

Fifteen minutes, including one deliberately blocked commit.

  1. Create the files

    Create .claude/commands/release-check.md, .claude/hooks/guard-native-config.sh and scripts/check-native-config.sh, then run chmod +x .claude/hooks/guard-native-config.sh scripts/check-native-config.sh. Or paste the prompt further down and let Claude Code create and adapt them.

  2. Make sure jq is installed

    The hook parses JSON with jq (brew install jq on macOS, sudo apt install jq on Debian or Ubuntu; cloud sessions already have it). Without jq the script cannot read the command and lets everything through, which is why the next steps include a test.

  3. Register the hook

    Merge the JSON into .claude/settings.json, keeping the settings that are already there, and commit it. Run /hooks in Claude Code to confirm that the PreToolUse entry is loaded.

  4. Trigger it on purpose

    Set HOME_URL in Config.java to http://10.0.2.2:3000, stage the file and ask Claude Code to commit. The commit must be refused with your message. Restore the production URL afterwards.

  5. Cover commits made by hand

    Create .githooks/pre-commit with the two lines #!/usr/bin/env bash and exec ./scripts/check-native-config.sh, make it executable and run git config core.hooksPath .githooks once per clone. Commits from Xcode, Android Studio or the terminal now hit the same rule, and so do Claude's own commits, at the exact moment Git creates them.

  6. Run the release check

    Before each release, type /release-check 1.4.0. The first answer tells you whether you are shipping a deploy or a store build; web vs native changes explains why that distinction matters so much for WebViewGold apps.

Let Claude Code install all of it

Useful when your folders differ from the examples. Claude adapts the paths and shows you every file before saving it.

Paste into Claude Code in your repository root
Set up release tooling for this repository. Our web app is also published as iOS and Android apps built with WebViewGold; the native projects are in native/ios and native/android (check the real layout first and adjust). Find the actual paths of Config.swift, Config.java, Info.plist, AndroidManifest.xml and app/build.gradle before you write anything.

1. Create .claude/commands/release-check.md: a release checklist command with frontmatter (description, an argument-hint for the version, allowed-tools limited to read-only git commands, npm run lint, npm run build, Read, Grep and Glob, and disable-model-invocation: true). It must first decide whether the release changes native files and therefore needs a store build, then check production URLs, version and build numbers, permissions, tracked signing files, and the /privacy, /terms and /delete-account routes.
2. Create scripts/check-native-config.sh: exit 1 when a staged Config.swift or Config.java contains localhost, 127.0.0.1, 0.0.0.0, 10.0.2.2, a 192.168.x.x address, or an ngrok or trycloudflare host. Ignore comment lines. With --include-worktree, also check unstaged changes.
3. Create .claude/hooks/guard-native-config.sh: a PreToolUse hook for the Bash tool that reads the JSON input with jq, ignores everything except git commit, runs the checker (with --include-worktree for commit -a, --all, or a git add in the same command) and exits 2 with a clear message on stderr when the check fails.
4. Register the hook in .claude/settings.json without removing any existing settings.
5. Create .githooks/pre-commit that runs the same checker.

Make the scripts executable, show me every file before saving, then tell me how to test the hook with a deliberate localhost URL.

Which release rule belongs where

RuleBest placeWhy
Config files point to production, never to localhostHook plus Git pre-commit hookMust never ship. A blocked commit is cheaper than a broken app in users' hands or a rejected build.
Version and build numbers raised/release-checkNeeds judgment: only native releases need new numbers, and the last uploaded build number is not in the repository.
Store rules such as 4.2, 4.8, 5.1.1 and 3.1.1CLAUDE.md plus the review subagentGuidance while coding, a strict review before the release.
Lint and build pass/release-check or CIToo slow for a hook on every commit, ideal as a checklist step or a required status check.
Signing files never committed.gitignore plus the release checkPrevent with .gitignore, verify with git ls-files before every release.

Pitfalls

  • Exit 1 does not block. Only exit code 2 (or a JSON reply with permissionDecision set to deny) stops the tool call. Exit 1 is reported as a hook error and the commit goes ahead.
  • The hook only sees Claude. It runs for tool calls that Claude Code makes. Commits from your IDE or terminal bypass it, which is why the same checker also runs as a Git pre-commit hook.
  • Hooks run with your permissions. Review changes to .claude/settings.json and .claude/hooks/ in pull requests like any other script; a hook runs on the machine of everyone who opens the project.
  • Overloaded ! lines. Inline commands in a command file need matching allowed-tools entries. Keep them to simple read-only commands and put pipes and logic into the instructions, where Claude runs them as normal tool calls.
  • A missing argument. $ARGUMENTS is whatever you type after the command name. Run /release-check without a version and the check has nothing to compare against; the argument-hint is there to remind you.
  • Secrets in prompts. Command files are committed. Never put keystore passwords, API keys or demo account passwords into them.

Frequently asked questions

Are Claude Code custom slash commands deprecated?

No, they were merged into skills. Files in .claude/commands/ keep working and create the same /name commands. A skill such as .claude/skills/release-check/SKILL.md adds supporting files and automatic loading; switch to that format when a command grows beyond one file.

Where do personal commands go?

In ~/.claude/commands/ or ~/.claude/skills/ in your home folder. They are available in every project on your machine but are not shared with your team.

Could the hook fix the URL instead of blocking the commit?

It could rewrite the file, but silent rewrites hide problems. Blocking with a clear message is safer: Claude reads the message, sets the production URL and commits again, and you can see what happened.

Do hooks and commands work in Claude Code on the web?

Yes. Cloud sessions use the repository's .claude/settings.json, commands and skills, and jq is pre-installed in the cloud environment. Hooks in your personal ~/.claude/settings.json stay on your own machine.

Does a hook on every Bash call slow Claude down?

Barely. The script exits immediately for anything that is not a commit. Recent Claude Code versions also accept an if field on a hook, for example "Bash(git commit *)", so the script only starts for matching commands.

Three ways to get your app into the stores

Same result, different amount of work on your side. All three come with the approval guarantee and 24/7 human support.

Do it yourself

$109 per platform, one-time

The WebViewGold license: a finished Xcode or Android Studio project. Paste your URL, set name and icon, switch on features, build and submit. Free updates forever.

See plans on the main page

Cloud Builder

$349 per license, one-time add-on

No Mac, no Xcode, no Android Studio: configure in the browser, we build both apps in the cloud and hand them to App Store Connect and the Play Console.

How the Cloud Builder works Try the free demo

Full Service

$698 per platform $798

Send us your URL and icon. Our team configures, builds and delivers your app within 72 hours, white-label, with source projects. Both platforms for $1,158.

Full Service details Powered by WeSetupYourWebViewApp

Winter Sale prices valid until December 31, 2026.

Ready to ship your app?

App Store & Google Play approval guarantee – or your money back within 14 days

Rather hand it over? Full Service: we configure, build and deliver within 72 hours, from $698 per platform. Questions? Chat with us 24/7.

Refunds are granted under the conditions listed in our FAQ: a non-content rejection by Apple and/or Google, claimed within 14 days after purchase.