Skip to content

Latest commit

 

History

History
558 lines (435 loc) · 27.5 KB

scripts.md

File metadata and controls

558 lines (435 loc) · 27.5 KB

Scripts

Index of utilities in scripts/. Run from the repo root unless noted.

Workspace utilities

running-model

Prints exactly one model name from the author roster using the tool's session record.

deno task running-model

Codex reads the newest recorded turn of the session named by CODEX_THREAD_ID. Claude Code locates the transcript named by CLAUDE_CODE_SESSION_ID across ~/.claude/projects/ and reads its newest genuine assistant entry. If both IDs are set, the command exits 1 because two tools claim the session. An identified session that cannot be read never falls back to another tool.

Only when neither ID is set does it read the newest agy hook record from /tmp/agy-hook-invocations.jsonl (or AGY_HOOK_RECORD_PATH). The record must name a conversation and its model. That hook log is shared across agy sessions: concurrent invocations can leave another session's record last, so the command cannot distinguish those conversations. It never uses model-written prose or the configured default model.

A missing, unreadable, ambiguous, or unrostered result prints Cannot tell the running model with the reason on stderr and exits 1 without stdout. The command reads local files and the roster's shell probe only; it has no network or write permissions.

backlog-groom:rank

Ranks one active repository's open issues and prints a batch of proposed native Priority moves. It only reads GitHub through gh api; it never applies the proposals.

deno task backlog-groom:rank --repo web-jam-tools --judgments /tmp/backlog-groom/judgments.json --focus gig-outreach
deno task backlog-groom:rank --repo web-jam-tools --judgments /tmp/backlog-groom/judgments.json --json
deno task backlog-groom:rank --help

The caller supplies the two judgments the design assigns to the skill's model, plus a one-line reason per issue. Keys are issue numbers in that repository:

{
  "1351": {
    "liveBroken": false,
    "costlyDefect": false,
    "reason": "Planned ranking capability",
    "levelFromTests": "Medium"
  }
}

levelFromTests is required below 20 open non-Epics: the caller selects it using skills/file-issue/SKILL.md, so this module does not duplicate those level tests. Missing or invalid judgments, failed GitHub reads and malformed responses refuse the run rather than making up facts. Parked issues and pull requests are excluded. Epics inherit the highest existing native Priority of their open, unparked children and are excluded from shares. An Epic with no child Priority supplies no proposal.

Shares are rounded independently; the last band includes any rounding remainder. Overflow is measured from the current snapshot: more than three above a share proposes moving up to the excess count of eligible lowest-ranked issues down one level, without cascading those proposals into another level's count. Live broken issues stay Urgent and cannot be demoted. The JSON result includes the full ranking, current counts, shares, focus notices and proposals with reasons. A missing focus milestone skips that step and reports why.

Create the input file under /tmp/backlog-groom/, outside every repository. That is the task's only permitted input directory; no user home, network or write permissions are granted to Deno. The GitHub subprocess authenticates through the existing gh setup.

edit-issue.ts

Guarded edits to an existing issue. Ordinary gh issue edit flags pass through; native Priority uses the same GraphQL mutation as issue creation and is read back through REST before success.

deno task edit-issue --repo WebJamApps/web-jam-tools --issue 1329 --priority High --dry-run
deno task edit-issue --repo WebJamApps/web-jam-tools --issue 1329 --priority High --add-label Sol

--priority accepts exactly Urgent, High, Medium or Low; invalid or missing values refuse before any command runs. A Priority-only edit needs no --author. Body replacements retain their author-footer, empty-body and credential guards. --dry-run previews the edit without GitHub calls. Combined edits run the pass-through edit first, then set and verify Priority, reporting both. If Priority fails after the other edit succeeded, the command exits nonzero; the earlier edit remains applied. Failed or unreadable verification also exits nonzero and does not claim a verified level.

skill-start-check.ts

Proves a skill still starts after its description or listing changes, on Claude Code and agy (web-jam-tools#1237).

deno task skill-start-check <skill-name> [--tool claude|agy|all]

The check reads the installed skill without changing its links. If its instructions or resources differ from the candidate, it refuses rather than reporting a result for the installed copy. The harness-only start-check.json is excluded from that comparison. agy checks require Linux setsid and kill to terminate the check's process group and its descendants on timeout.

install-codex.ts

Installs the Codex configuration described by web-jam-tools#1143 "scripts: Codex installer for hooks, rules file, skills and settings". Run after scripts/install-hooks.sh has installed the shared hook scripts under ~/.claude/hooks/.

deno task install-codex
deno task install-codex --check
deno task install-codex --home /tmp/codex-install-check
deno task install-codex --help

The installer reads the four shared hook arrays from scripts/install-hooks.sh, registers them in nested TOML tables with WJT_SURFACE=codex, copies the rules file as a regular file, links shared skills into ~/.codex/skills/ except handle-gmails, and sets the top-level sandbox_mode to danger-full-access. When run from a worktree, skill links point into the canonical clone so they survive worktree removal.

config.toml is edited in place: only the top-level sandbox_mode line and the installer's own [[hooks.<Event>]] tables change. Every other line, comments included, is kept byte for byte, so existing settings, unrelated hooks and hook trust values cannot change. The edit is parsed and checked against the values read before anything is written. A run with nothing to change leaves the file byte-identical. hooks.json, rules/default.rules, built-in .system skills and ~/.agents/ are untouched. Hook trust remains a manual Codex prompt.

Invalid TOML, unreadable hook arrays, missing hook scripts, missing rules, or a config.toml whose installer hook tables cannot be edited in place (for example written as an inline array) refuse installation before any writes. The rules file is written before config.toml. A conflicting skill path is left in place and reported as skipped, while the safety set and other skills are installed; the exit code is nonzero. --check writes nothing, returns zero when current, and reports each difference or refusal with a nonzero exit code. On an empty home it reports missing hook prerequisites alongside the installation drift.

Manual steps are in ~/Dropbox/web-jam-llms/Token_Savings/codex-install-manual-steps-2026-09-24.md.

session-load-report.ts

Emits the session load report at Claude Code startup in tab 1 of agents (web-jam-tools#1234). Reads the size of every session-load part from disk across Claude Code, agy, and Codex against the canonical limits in src/session-load/limits.json, prints a systemMessage JSON line, and exits 0.

deno run --allow-read --allow-env scripts/session-load-report.ts

bootstrap-project.sh

Scaffolds a new sibling project directory in the WebJamApps workspace with basic README and structure.

./scripts/bootstrap-project.sh <project-name>

Note: the script currently hard-codes a workspace root path. Edit ROOT_DIR near the top of the file to match your machine before use.

check-env.sh

Quick health check for the local development environment. Reports Node version, rclone Google Drive mount status, GitHub CLI auth, and basic Drive visibility.

./scripts/check-env.sh

Note: contains hard-coded paths that assume the maintainer's home directory layout. Adapt before using on a different machine.

new-agent-worktree.sh

Creates an isolated git worktree for a WebJamApps sibling repo/branch — the setup an agent needs to work on that repo without touching the shared main clone. Seeds the gitignored .env / .env.test from the repo's main clone into the new worktree when present (a fresh worktree never inherits gitignored files, which otherwise breaks local DB-backed test runs there — web-jam-tools#257). Prints the new worktree's absolute path as the last line of stdout.

scripts/new-agent-worktree.sh <Repo> <branch> [base]
  • Repo — sibling directory name under the workspace root (e.g. WebJamSocketCluster), must already be a git clone
  • branch — branch name to create for the new worktree
  • base — base ref to branch from (default: dev)

The worktree is created at <Repo>/.claude/worktrees/<branch> (/ in the branch name is flattened to -). Set WEBJAMAPPS_ROOT to override the default workspace root (/home/joshua/WebJamApps).

Depends on hooks/block-secret-dumps.sh's cp/test exception (web-jam-tools#257) — without it, an agent working inside the new worktree can't re-seed these files by hand if it ever needs to.

circleci-settings.ts

Manages the CircleCI project settings standard (autocancel_builds: true) across all 8 active WebJamApps projects (web-jam-tools#697). Supports drift checking via --check and idempotent application.

# Check for configuration drift across all 8 projects
deno task circleci-settings -- --check

# Enforce the standard across all 8 projects
deno task circleci-settings

See docs/circleci-project-settings.md for full documentation.

gitleaks.sh

Runs gitleaks detect with repository configuration and shared .gitleaks.toml rules (web-jam-tools#658, #1403).

  • Supports scanning a named repository via --repo <path> (defaults to current repository root).
  • Runs on the latest gitleaks release: uses docker run --pull=always zricethezav/gitleaks:latest when Docker is available; falls back to an installed gitleaks on PATH when Docker is absent; fails closed when neither is available.
  • Passes --exit-code=2 (0 = clean, 2 = finding, non-zero and != 2 = failure), --ignore-gitleaks-allow, and runs with --source . relative to the repository.
# Scan current repository
bash scripts/gitleaks.sh

# Scan a target repository
bash scripts/gitleaks.sh --repo /path/to/repo

# Scan git history range on target repository
bash scripts/gitleaks.sh --repo /path/to/repo --log-opts="origin/dev..HEAD"

prove-push-guard.sh

Runs the design's "Proof before any new key" on the laptop, proving that git's global pre-push hook (git-hooks/pre-push, installed via scripts/install-hooks.sh setting core.hooksPath) prevents credential leaks before they can leave the machine (web-jam-tools#1404):

  1. Reads global git configuration and verifies core.hooksPath points to this clone's git-hooks.
  2. Sets up a throwaway repository under /tmp with a local bare remote.
  3. Commits a fake Google API key built at runtime and confirms the push is refused, naming file, line, and rule ID (never the secret value).
  4. Cleans the commit and confirms clean pushes succeed.
  5. Verifies push refusal when Docker is stopped and no gitleaks program is on PATH.
bash scripts/prove-push-guard.sh

reaper-update.sh

Downloads and installs the latest REAPER version to a specified prefix. Detects the currently installed version, compares it to the latest available, and updates in place if needed. Preserves user configuration in ~/.config/REAPER.

Invocation options (in preference order):

  1. From anywhere (after one-time setup):

    reaper-update

    One-time setup (run from repo root):

    ln -s "$PWD/scripts/reaper-update.sh" ~/.local/bin/reaper-update

    Requires ~/.local/bin on PATH.

  2. From inside the repo:

    deno task update:reaper
  3. Direct script invocation:

    bash scripts/reaper-update.sh

Environment variables:

  • REAPER_PREFIX (default: /home/joshua/opt) — the parent directory where REAPER is installed

Example with custom prefix:

REAPER_PREFIX=/opt reaper-update

Safety: the script checks that REAPER is not running before updating and fails if it detects a running process. Quit REAPER before updating.

statusline.sh

Model-aware Claude Code status line (web-jam-tools#688). Reads the status-line JSON payload Claude Code writes to stdin, extracts .model.display_name, and prints a color-coded [Opus] / [Sonnet] / [Haiku] badge in front of the existing status line — so a terminal running the expensive tier is visually distinguishable from a cheaper one at a glance. The match is on the family word in display_name, case-insensitive, so a version bump (Opus 5 to Opus 6) doesn't break it; an unrecognized display_name prints uncolored rather than erroring, and a missing .model key or malformed JSON on stdin both still produce a usable status line. The original stdin payload is passed through unmodified to the downstream status-line command (npx -y ccusage statusline by default) — the badge is a prefix, never a replacement.

Installed automatically by scripts/install-hooks.sh, which symlinks scripts/statusline.sh into the same destination the *.sh hooks are linked into (honoring --hooks-dir / CLAUDE_HOOKS_DIR), then merges a statusLine entry pointing at that stable installed path — never $REPO_DIR/scripts/statusline.sh, which would break if the repo moved or a branch lacking the file were checked out — into ~/.claude/settings.json (Claude Code only — agy has no status-line surface). The script is not a hook: it stays out of HOOKS_SRC and out of every hook-registration loop, so it never gains a PreToolUse/PostToolUse/SessionStart/Stop entry. hooks/lib/check_hook_install_drift.ts covers it anyway — a dead or unregistered statusLine is reported at SessionStart the same way a dead or unregistered hook already is. Not meant to be run standalone in normal use, but it can be for manual testing by piping a payload to it:

echo '{"model":{"id":"claude-opus-5","display_name":"Opus 5"}}' | scripts/statusline.sh

Environment variables:

  • STATUSLINE_DOWNSTREAM_CMD (default: npx -y ccusage statusline) — the downstream command the captured payload is piped to after the badge. Overriding this is a test-only seam (the real default hits the network, which an automated test must not depend on); leave it unset for normal use.

update-all.sh

Master update script that updates the local AI agent CLIs and DAW tooling in sequence:

  1. claude update (Anthropic Claude Code CLI)
  2. agy update (Google Antigravity CLI)
  3. codex update (OpenAI Codex CLI)
  4. reaper-update (Cockos REAPER digital audio workstation)

See docs/local-dev-setup.md for full developer environment setup and installation steps on Linux.

Invocation options (in preference order):

  1. From anywhere (after one-time setup):

    update-all

    One-time setup (run from repo root):

    ln -s "$PWD/scripts/update-all.sh" ~/.local/bin/update-all

    Requires ~/.local/bin on PATH.

  2. From inside the repo:

    deno task update:all
  3. Direct script invocation:

    bash scripts/update-all.sh

Options & Flags:

  • --dry-run (-n): Preview the update commands without executing them.
  • --fail-on-missing / --strict (-s): Treat missing tools as failures (exit 1).
  • --help (-h): Show usage and installation commands.

Environment variables:

  • CLAUDE_BIN: Path or binary name for Claude CLI (default: claude).
  • AGY_BIN: Path or binary name for Antigravity CLI (default: agy).
  • CODEX_BIN: Path or binary name for Codex CLI (default: codex).
  • REAPER_UPDATE_BIN: Path or command for REAPER updater (default: auto-detected from PATH or $SCRIPT_DIR/reaper-update.sh).

update-scripts.sh (the update-scripts command)

scripts/update-scripts.sh fast-forward pulls dev in the web-jam-tools clone ($WJT_REPO_DIR, default ~/WebJamApps/web-jam-tools) and then runs install-hooks.sh, passing its arguments through (for example --check). It runs from any directory and refuses, exiting 1, unless the clone is on dev.

install-hooks.sh symlinks it into ~/.local/bin/update-scripts, so after the first install you update by typing just update-scripts in any terminal. For the first install after this change merges, confirm the clone is on dev:

git -C "$HOME/WebJamApps/web-jam-tools" branch --show-current

Continue only if that prints dev. Fetch the merged source before invoking the new helper, because an older checkout does not contain scripts/update-scripts.sh:

git -C "$HOME/WebJamApps/web-jam-tools" pull --ff-only origin dev

After the pull succeeds, run the helper by its full path to install the command:

"$HOME/WebJamApps/web-jam-tools/scripts/update-scripts.sh"

The agents command also runs it, right after update-all.sh, whenever it creates or restarts the session (not when it only attaches). A failure prints a warning and the agents still start. Override the command with AGENTS_UPDATE_SCRIPTS_CMD.

install-hooks.sh — what actually gets symlinked

scripts/install-hooks.sh symlinks hooks/*.sh only — hooks/lib/ is never installed, so there is no ~/.claude/hooks/lib/ path on disk. A hook script reaches its shared hooks/lib/*.ts modules by resolving its own symlink back to the canonical clone (HOOK_DIR=$(cd "$(dirname "$(readlink -f "${BASH_SOURCE[0]}")")" && pwd)), then reading $HOOK_DIR/lib/<module>.ts from there — agy reaches the same modules through those same Claude Code symlinks via hooks/agy-hook-shim.sh. That means a content-only change to an already-installed hook script, skill body, or hooks/lib/*.ts module is live on both surfaces the moment dev is pulled; no install step is needed unless the change is structural (a new/renamed/deleted skill or hook, or a changed event/matcher registration).

permissions.defaultMode

The installer does not manage Claude Code's permission mode (permissions.defaultMode in ~/.claude/settings.json). Whatever mode is configured by the user remains untouched. A Sonnet or Haiku subagent may edit in auto mode because hooks/opus-delegation-gate.sh judges a subagent by its model rather than blocking edits based on auto mode (web-jam-tools#965, web-jam-tools#1232).

autoMode (managed by install-hooks.sh)

The autoMode section of ~/.claude/settings.json holds what Claude Code's auto-mode classifier reads: the environment prose, the allow list and the soft_deny list. scripts/install-hooks.sh owns the whole object. Its content is the AUTO_MODE_JSON block in that script.

  • It is replaced as a whole. A run installs the object when it is absent and replaces it when it differs; no other key in settings.json is touched. Object key order is ignored; the order of entries in a list counts. This differs from the permissions lists, which are additive.
  • Change it in AUTO_MODE_JSON, not in settings.json. A hand edit to settings.json is reported by --check and overwritten by the next run. The previous file is kept as the usual .bak- backup.
  • The drift report names what differs. Both --check and a replacing run list each top-level key that differs and, for a list, each entry only one side has, so a hand edit can be carried into AUTO_MODE_JSON first.
  • Paths are filled in at install time. __HOME__ in the block becomes $HOME, and __REPOS_DIR__ becomes the directory that holds the web-jam-tools checkout the installer runs from (~/WebJamApps on Josh's laptop). The installed object is therefore correct on any machine.
  • Its strings are secret-scanned with the same credential-literal check as permissions and hooks; a match refuses the write.

Claude Code ONLY, like statusLine: it is never merged into agy's hooks.json.

skillOverrides (managed by install-hooks.sh)

The skillOverrides section of ~/.claude/settings.json holds overrides for Claude Code bundled skills. scripts/install-hooks.sh sets the 30 skills bundled with Claude Code (13 document skills named anthropic-skills:<name> and 17 built-in skills) to name-only, so their descriptions stop loading at every session open while every skill stays listed and callable by name (web-jam-tools#1240). Its content is generated by scripts/install-hooks.sh from the canonical list in src/session-load/limits.json (web-jam-tools#1416).

  • It is replaced as a whole. A run installs the object when it is absent and replaces it when it differs; no other key in settings.json is touched.
  • Change it in src/session-load/limits.json, not in settings.json. A hand edit to settings.json is reported by --check and overwritten by the next run.
  • The drift report names what differs. Both --check and a replacing run list each key that differs or is missing.
  • Its strings are secret-scanned with the same credential-literal check as permissions and hooks; a match refuses the write.

Claude Code ONLY: it is never merged into agy's hooks.json.

ubuntu-health.sh

The one script that applies the Ubuntu security/efficiency design on a system: the laptop, and the Ubuntu in WSL on the desktop. Josh runs it with sudo, once per system; no agent runs it as root. Supporting files live in scripts/ubuntu-health/.

sudo scripts/ubuntu-health.sh          # apply
scripts/ubuntu-health.sh --check       # change nothing; name every drift; exit 1 if any
  • Mode. systemd-detect-virt printing none means laptop, wsl means WSL. Anything else, or the command failing, refuses the run and names the command.
  • Root-owned copies. A run installs the weekly job, its sender and the script itself under /usr/local/lib/ubuntu-health/, and the unit files under /etc/systemd/system/, all owned by root. The weekly job runs only those copies, never a file from the repository or a home folder. Running it again changes nothing that is already right. A run also writes /usr/local/lib/ubuntu-health/manifest.sha256, a root-owned checksum of every installed file, and refuses to run from the installed copy, which would record its own files as correct.
  • Weekly timer. ubuntu-health-weekly.timer runs the job Sundays at 03:00 (Persistent=true) at idle CPU and disk priority. The job runs the installed --check and, when it finds anything, sends one email naming the system and what to run next. A week with nothing to report sends nothing. The job's log is /var/log/ubuntu-health.log.
  • SSH and the firewall (laptop only). SSH into the laptop accepts a key only and the firewall admits only Tailscale. A run installs /etc/ssh/sshd_config.d/00-ubuntu-health.conf with PasswordAuthentication no (named 00- because sshd keeps the first value it reads) and restarts ssh.service when that file changed; on a real system sshd -t must accept the configuration first. A restart that fails (or a run that stops before it) is not forgotten: a marker file, ssh-restart-pending beside the installed script, stays until the restart succeeds, the next apply retries it, and --check names it meanwhile. The firewall step runs ufw allow in on tailscale0, ufw default deny incoming and ufw enable, each only when ufw does not already hold it, so a second run with everything set makes no ufw or ssh change; ufw asks you to confirm ufw enable itself. If ufw already holds a rule that admits port 22 off Tailscale, apply refuses, names the rule and leaves the firewall as it was, because that rule would survive every change; remove it with ufw status numbered and ufw delete <number>, then run apply again. It never deletes a rule itself. In WSL both steps are skipped and the output says so, because WSL runs no SSH server (Windows' own SSH server reaches it).
  • --check. Needs no sudo. Reports each drifted step as DRIFT <step>: <what> (job-files, unit-files, timer, email-login, and on the laptop ssh and firewall), or no drift. The ssh step names an effective PasswordAuthentication other than no, read from sshd_config and the files it includes, and a Match block (anywhere in them) that sets it to anything but no, because that can switch passwords back on for the connections it matches (an included file or a directory holding included files only root can read, search or list (including directories beneath wildcard path components) prints NOTE: ... not fully checked without sudo); it also names an ssh.service restart still pending. Readable Include matches are still scanned when a sibling directory is inaccessible, so known password drift is reported alongside the NOTE. The firewall step names "firewall off" (ENABLED is not yes in /etc/ufw/ufw.conf), a missing default deny, a missing tailscale0 rule (the blanket ufw allow in on tailscale0; one limited to a source, destination, port or protocol does not count), or a rule, logged or not, that admits port 22 on any other interface; ufw's rule files are readable only by root, so without sudo it prints NOTE: ... not checked without sudo for them instead of guessing. Installed files are verified against manifest.sha256, which is how the weekly job (running the installed copy, with no repository beside it) still catches a changed weekly.sh or gmail-send.sh; run from the repository, it also compares each file with its repository source.
  • Email. gmail-send.sh makes the same two Gmail API requests as src/shared/gmail_send.ts with curl. It reads GMAIL_CLIENT_ID, GMAIL_CLIENT_SECRET and GMAIL_REFRESH_TOKEN from the send-only login file /etc/ubuntu-health/gmail-send.env, which must be owned by root with mode 0600; a missing file, a wider mode or a failed token refresh sends nothing and is written to the job's log.
  • Tests. test/ubuntu_health.test.ts points UBUNTU_HEALTH_ROOT at a temporary folder standing in for / and puts stub commands first on PATH; never run the script with sudo while testing.

Example scraping / data utilities

These scripts target a specific Wix-hosted site and were built as one-offs for the maintainer's use case. They're committed as examples of Playwright scraping patterns against a Wix site backed by MUI DataGrid, not as general-purpose tools.

Script What it does
debug-wix.js Dumps the rendered DOM structure of the target site for inspection
find-pagination.js Detects pagination controls on the target site
scrape-gigs-v2.js First-pass scraper that walks pages of gig listings
scrape-gigs-v3.js Newer scraper that handles MUI DataGrid virtualization
scrape-and-sync.js Scrapes listings and writes them out as XLSX
get-unique-venues.js Reads a text list of past gigs and emits unique venue names

Prerequisites

npm install   # installs playwright + xlsx
npx playwright install chromium

All of these scripts read from / write to local paths that are hard-coded near the top of each file (Dropbox, Google Drive mount, etc.). Edit the paths before running, or use them as reference implementations only.