Index of utilities in scripts/. Run from the repo root unless noted.
Prints exactly one model name from the author roster using the tool's session record.
deno task running-modelCodex 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.
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 --helpThe 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.
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.
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.
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 --helpThe 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.
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.tsScaffolds 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_DIRnear the top of the file to match your machine before use.
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.shNote: contains hard-coded paths that assume the maintainer's home directory layout. Adapt before using on a different machine.
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 clonebranch— branch name to create for the new worktreebase— 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'scp/testexception (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.
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-settingsSee docs/circleci-project-settings.md for full documentation.
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:latestwhen Docker is available; falls back to an installedgitleaksonPATHwhen 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"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):
- Reads global git configuration and verifies
core.hooksPathpoints to this clone'sgit-hooks. - Sets up a throwaway repository under
/tmpwith a local bare remote. - 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).
- Cleans the commit and confirms clean pushes succeed.
- Verifies push refusal when Docker is stopped and no gitleaks program is on PATH.
bash scripts/prove-push-guard.shDownloads 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):
-
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/binon PATH. -
From inside the repo:
deno task update:reaper
-
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-updateSafety: the script checks that REAPER is not running before updating and fails if it detects a running process. Quit REAPER before updating.
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.shEnvironment 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.
Master update script that updates the local AI agent CLIs and DAW tooling in sequence:
claude update(Anthropic Claude Code CLI)agy update(Google Antigravity CLI)codex update(OpenAI Codex CLI)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):
-
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/binon PATH. -
From inside the repo:
deno task update:all
-
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).
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-currentContinue 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 devAfter 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.
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).
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).
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.jsonis 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 insettings.json. A hand edit tosettings.jsonis reported by--checkand overwritten by the next run. The previous file is kept as the usual.bak-backup. - The drift report names what differs. Both
--checkand 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 intoAUTO_MODE_JSONfirst. - Paths are filled in at install time.
__HOME__in the block becomes$HOME, and__REPOS_DIR__becomes the directory that holds theweb-jam-toolscheckout the installer runs from (~/WebJamAppson Josh's laptop). The installed object is therefore correct on any machine. - Its strings are secret-scanned with the same credential-literal check as
permissionsandhooks; a match refuses the write.
Claude Code ONLY, like statusLine: it is never merged into agy's hooks.json.
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.jsonis touched. - Change it in
src/session-load/limits.json, not insettings.json. A hand edit tosettings.jsonis reported by--checkand overwritten by the next run. - The drift report names what differs. Both
--checkand a replacing run list each key that differs or is missing. - Its strings are secret-scanned with the same credential-literal check as
permissionsandhooks; a match refuses the write.
Claude Code ONLY: it is never merged into agy's hooks.json.
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-virtprintingnonemeans laptop,wslmeans 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.timerruns the job Sundays at 03:00 (Persistent=true) at idle CPU and disk priority. The job runs the installed--checkand, 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.confwithPasswordAuthentication no(named00-because sshd keeps the first value it reads) and restartsssh.servicewhen that file changed; on a real systemsshd -tmust accept the configuration first. A restart that fails (or a run that stops before it) is not forgotten: a marker file,ssh-restart-pendingbeside the installed script, stays until the restart succeeds, the next apply retries it, and--checknames it meanwhile. The firewall step runsufw allow in on tailscale0,ufw default deny incomingandufw enable, each only when ufw does not already hold it, so a second run with everything set makes noufworsshchange; ufw asks you to confirmufw enableitself. 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 withufw status numberedandufw 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 nosudo. Reports each drifted step asDRIFT <step>: <what>(job-files,unit-files,timer,email-login, and on the laptopsshandfirewall), orno drift. Thesshstep names an effectivePasswordAuthenticationother thanno, read fromsshd_configand the files it includes, and aMatchblock (anywhere in them) that sets it to anything butno, 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) printsNOTE: ... not fully checked without sudo); it also names anssh.servicerestart still pending. Readable Include matches are still scanned when a sibling directory is inaccessible, so known password drift is reported alongside the NOTE. Thefirewallstep names "firewall off" (ENABLEDis notyesin/etc/ufw/ufw.conf), a missing default deny, a missingtailscale0rule (the blanketufw 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 withoutsudoit printsNOTE: ... not checked without sudofor them instead of guessing. Installed files are verified againstmanifest.sha256, which is how the weekly job (running the installed copy, with no repository beside it) still catches a changedweekly.shorgmail-send.sh; run from the repository, it also compares each file with its repository source.- Email.
gmail-send.shmakes the same two Gmail API requests assrc/shared/gmail_send.tswithcurl. It readsGMAIL_CLIENT_ID,GMAIL_CLIENT_SECRETandGMAIL_REFRESH_TOKENfrom 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.tspointsUBUNTU_HEALTH_ROOTat a temporary folder standing in for/and puts stub commands first onPATH; never run the script withsudowhile testing.
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 |
npm install # installs playwright + xlsx
npx playwright install chromiumAll 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.