Bootstrap Claude Code web environments
Project description
ccweb
Bootstrap Claude Code web environments with one command.
uvx ccweb init
Generates setup.sh, session-start.sh, diagnose.sh, and wires .claude/settings.json for your project. When you start a Claude Code web session, the VM is automatically provisioned with your selected toolchains.
Quick start
# In your project root
uvx ccweb init
# Commit and push
git add scripts/ .claude/settings.json
git commit -m "Add Claude Code web environment setup"
git push
# Start a Claude Code web session — it auto-provisions
# Then verify:
uvx ccweb doctor
Local Docker validation
Before pushing, validate the generated scripts against a clean Ubuntu 24.04 container:
uvx ccweb test # runs setup.sh + diagnose.sh in ubuntu:24.04
uvx ccweb test --image ubuntu:22.04
uvx ccweb test --shell # interactive shell for ad-hoc debugging
Requires a local Docker daemon. The repo is mounted read-only at /workspace, so the test can never modify your working copy.
Options
uvx ccweb init --toolchains auto --extras auto # Detect from repo files
uvx ccweb init --toolchains node,python # Just Node + Python
uvx ccweb init --toolchains go --extras postgres # Go + psql
uvx ccweb init --versions go=1.23.0,zig=0.14.0 # Pin tool versions
uvx ccweb init --env-file .env.example # Declare required env vars
uvx ccweb init --env-file "" # Disable env-file auto-detect
uvx ccweb init --force # Overwrite existing files
uvx ccweb init --scripts-dir ci/scripts # Custom scripts directory
uvx ccweb init --skills ai/skills # Custom skills directory
uvx ccweb init --skills "" # Disable skills wiring
Toolchains
node, python, go, rust, ruby, java, deno, elixir, zig, dotnet, php — default: all
Extras
uv, pnpm, yarn, bun, browser, postgres, redis, docker — default: all
gh, duckdb, yq, sqlite3, jq, pandoc, shellcheck, and friends are always installed — no flag needed.
Auto-detection
Pass auto to either flag to install only what the repo actually needs. Detection inspects the project root for marker files:
| Toolchain | Markers |
|---|---|
node |
package.json, package-lock.json, pnpm-lock.yaml, yarn.lock, bun.lock |
python |
pyproject.toml, requirements.txt, setup.py, Pipfile, uv.lock |
go |
go.mod |
rust |
Cargo.toml |
ruby |
Gemfile, *.gemspec |
java |
pom.xml, build.gradle, build.gradle.kts |
deno |
deno.json, deno.jsonc |
elixir |
mix.exs |
zig |
build.zig, build.zig.zon |
dotnet |
*.csproj, *.fsproj, *.sln |
php |
composer.json |
Extras are detected from lockfiles, Dockerfile / docker-compose.yml, playwright/puppeteer in package.json, [tool.uv] in pyproject.toml, and postgres/redis images referenced in compose files.
The cloud extra (aws, gcloud, terraform, kubectl, helm) is detected from any of: *.tf / *.tfvars at the root, a terraform/ / infra/ / iac/ / k8s/ / kubernetes/ / manifests/ directory, or a Chart.yaml, helmfile.yaml, kubeconfig, or kustomization.yaml file at the root.
Version pinning
Override any of the baked-in versions with --versions KEY=VALUE (comma-separated for multiple). Unspecified tools use the defaults in DEFAULT_VERSIONS.
Valid keys: go, zig, gh, duckdb, yq, dotnet_channel, terraform, kubectl.
uvx ccweb init --versions go=1.23.0
uvx ccweb init --versions go=1.23.0,gh=2.74.1,dotnet_channel=LTS
Pins are also auto-detected from common version files at the project root — no flag required:
| File | Source of pin |
|---|---|
.tool-versions |
asdf/mise entries for golang/go, zig, terraform, kubectl |
.go-version |
Go (takes precedence over .tool-versions) |
.terraform-version |
Terraform (takes precedence over .tool-versions) |
.nvmrc, .python-version |
Read but ignored — node and python are pre-installed and not pinnable |
Explicit --versions entries always win over auto-detected pins, per key.
Environment variables
If the repo contains .env.example or .env.template (or you point --env-file somewhere else), session-start.sh reads the variable names from it and warns when any are unset in the session. diagnose.sh gets a matching section that shows each declared variable as set or missing.
The file is treated as a schema — ccweb never reads, copies, or transmits the values. Store actual secrets in the claude.ai/code Environment Variables UI at the project level.
# .env.example (checked into the repo; values are placeholders)
DATABASE_URL=
OPENAI_API_KEY=
STRIPE_SECRET=
Pass --env-file "" to disable auto-detection.
Skills
By default, ccweb looks for Claude Code skills in .claude/skills/ in your
repo (each subdirectory holds a SKILL.md). On session start, every skill
found there is symlinked into ~/.claude/skills/<name> so Claude Code
discovers them at the user level across every session on the VM. Pass
--skills ai/skills for a custom path, or --skills "" to disable.
<repo>/<DIR>/my-skill/SKILL.md
<repo>/<DIR>/my-skill/reference.md
Auto-formatting on Edit/Write
ccweb init also generates scripts/post-tool-use.sh and wires it as a
PostToolUse hook in .claude/settings.json (matcher: Edit|Write|MultiEdit).
After every file edit, the hook reads the file path from Claude's hook payload
and runs the matching formatter:
| Formatter | Extensions |
|---|---|
ruff format + ruff check --fix |
.py |
gofmt |
.go |
rustfmt |
.rs |
zig fmt |
.zig |
mix format |
.ex, .exs |
shfmt |
.sh, .bash |
clang-format |
.c, .h, .cc, .cpp, .cxx, .hpp, .hh, .hxx, .m, .mm |
rubocop -A |
.rb |
google-java-format |
.java |
php-cs-fixer |
.php |
terraform fmt |
.tf, .tfvars |
prettier (falls back to deno fmt) |
.js, .jsx, .ts, .tsx, .mjs, .cjs, .json, .jsonc, .md, .mdx, .css, .scss, .html, .yaml, .yml |
Each formatter is guarded by command -v, and the hook always exits 0 so a
missing or failing formatter never blocks the agent.
How it works
-
setup.shruns once when a new VM is created. Installs system packages, toolchains, and persists environment variables to/etc/environment. -
session-start.shruns on every session start (new + resumed). Sources env vars, detects project lockfiles, and installs dependencies. Auto-runssetup.shif the VM hasn't been provisioned yet. -
diagnose.shchecks what's installed, what's missing, and what's misconfigured. -
post-tool-use.shruns after every Edit/Write/MultiEdit and formats the changed file. -
.claude/settings.jsonwires both hooks so they run automatically.
License
MIT
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file ccweb-0.5.0.tar.gz.
File metadata
- Download URL: ccweb-0.5.0.tar.gz
- Upload date:
- Size: 45.8 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
e5e16a99137aa800678f6eaadfeb6b168873c65bd8dafaa5c153e628f964237e
|
|
| MD5 |
9859aeaf61a17cdeff69af0f2a369982
|
|
| BLAKE2b-256 |
6caffcaec89c2d6017e59a9e452553400b87520835c0c4b33601bd39bcbd4527
|
Provenance
The following attestation bundles were made for ccweb-0.5.0.tar.gz:
Publisher:
publish.yml on nclandrei/ccweb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ccweb-0.5.0.tar.gz -
Subject digest:
e5e16a99137aa800678f6eaadfeb6b168873c65bd8dafaa5c153e628f964237e - Sigstore transparency entry: 1379758963
- Sigstore integration time:
-
Permalink:
nclandrei/ccweb@9d98fad36e051b99b8a24286f9a2ef16da5e9d38 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/nclandrei
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9d98fad36e051b99b8a24286f9a2ef16da5e9d38 -
Trigger Event:
release
-
Statement type:
File details
Details for the file ccweb-0.5.0-py3-none-any.whl.
File metadata
- Download URL: ccweb-0.5.0-py3-none-any.whl
- Upload date:
- Size: 29.1 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
311c3135faede6c9ec095b3f65f2f6df8ee0698899e0d71da0b3a7f375992300
|
|
| MD5 |
20a216c9d933d9ef3922f4f0ffaf65b2
|
|
| BLAKE2b-256 |
a86e7ff96c9d64937d31dc2216ad5282008a210929f19c1ec0e21de99d677973
|
Provenance
The following attestation bundles were made for ccweb-0.5.0-py3-none-any.whl:
Publisher:
publish.yml on nclandrei/ccweb
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
ccweb-0.5.0-py3-none-any.whl -
Subject digest:
311c3135faede6c9ec095b3f65f2f6df8ee0698899e0d71da0b3a7f375992300 - Sigstore transparency entry: 1379759096
- Sigstore integration time:
-
Permalink:
nclandrei/ccweb@9d98fad36e051b99b8a24286f9a2ef16da5e9d38 -
Branch / Tag:
refs/tags/v0.5.0 - Owner: https://github.com/nclandrei
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@9d98fad36e051b99b8a24286f9a2ef16da5e9d38 -
Trigger Event:
release
-
Statement type: