Skip to content

Commit cb957e2

Browse files
stranmaclaude
andcommitted
refactor: clean up template repo, add ToB devcontainer + firewall setup
- Remove stale files (PDFs, IMPLEMENTATION_PLAN stub, .dockerignore) - Add --devcontainer trailofbits and --egress-firewall to setup_project.py - Rewrite README with composition diagram showing 3 external components - Update CLAUDE.md with correct repo URLs - Update GETTING_STARTED.md for split architecture - Update CHANGELOG with split summary Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1 parent 3911060 commit cb957e2

10 files changed

Lines changed: 215 additions & 344 deletions

.dockerignore

Lines changed: 0 additions & 22 deletions
This file was deleted.

.github/workflows/template-sync.yml

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@ jobs:
5959
run: |
6060
# Paths managed by the template (synced from upstream)
6161
# Defined once here; reused in the apply step via GITHUB_OUTPUT
62-
TEMPLATE_PATHS=".claude/agents/ .claude/commands/ .claude/hooks/ .claude/rules/ .claude/skills/ .devcontainer/ .github/workflows/ docs/DEVELOPMENT_PROCESS.md"
62+
TEMPLATE_PATHS=".github/workflows/ scripts/"
6363
echo "template_paths=${TEMPLATE_PATHS}" >> "$GITHUB_OUTPUT"
6464
6565
# Get changed files between local and upstream
@@ -156,7 +156,8 @@ jobs:
156156
157157
### What to review
158158
- Check if any synced files conflict with project-specific customizations
159-
- Template-managed paths: `.claude/`, `.devcontainer/`, `.github/workflows/`, `docs/DEVELOPMENT_PROCESS.md`
159+
- Template-managed paths: `.github/workflows/`, `scripts/`
160+
- `.claude/` is managed by `pyclaude-forge`, `.devcontainer/` by `claude-code-devcontainer`
160161
- Project-specific files (`apps/`, `libs/`, `tests/`, `pyproject.toml`, `README.md`) are NOT touched
161162
162163
### How to resolve conflicts

CLAUDE.md

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,5 +32,6 @@ All packages maintain synchronized MAJOR.MINOR versions. Patch versions can diff
3232
## Optional Integrations
3333

3434
This template can be composed with:
35-
- **[claude-code-harness](../claude-code-harness)** -- Claude Code workflow (skills, agents, rules, hooks)
36-
- **[claude-code-devcontainer](../claude-code-devcontainer)** -- secure devcontainer with egress firewall
35+
- **[pyclaude-forge](https://github.com/stranma/pyclaude-forge)** -- Claude Code workflow (skills, agents, rules, hooks)
36+
- **[trailofbits/claude-code-devcontainer](https://github.com/trailofbits/claude-code-devcontainer)** -- secure devcontainer with Claude Code
37+
- **[Egress firewall](https://gist.github.com/stranma/f43d932bedc8335e24404c9784fcf190)** -- iptables whitelist preventing code exfiltration

README.md

Lines changed: 73 additions & 258 deletions
Large diffs are not rendered by default.

docs/CHANGELOG.md

Lines changed: 10 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -8,26 +8,18 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
88
## [Unreleased]
99

1010
### Changed
11-
- Security model simplified to 2-layer exfiltration defense: iptables firewall (primary) blocks non-approved network domains; `dangerous-actions-blocker.sh` (narrowed) blocks exfiltration via trusted channels (gh gist, gh issue --body, package publishing, secrets in args) -- local destruction (rm -rf, sudo, etc.) is no longer blocked since devcontainer is disposable
12-
- CLAUDE.md Security section rewritten to describe the 2-layer defense model instead of listing individual hooks
13-
- Devcontainer simplified: permission tiers removed, single settings.json baseline for all environments
11+
- Repository split into 3 independent repos: template (this), [pyclaude-forge](https://github.com/stranma/pyclaude-forge) (workflow), [claude-code-devcontainer](https://github.com/stranma/claude-code-devcontainer) (deprecated, use Trail of Bits)
12+
- `setup_project.py` gains `--devcontainer trailofbits` and `--egress-firewall` options to compose external components
13+
- README rewritten with composition diagram showing how the 3 external pieces plug in
14+
- GETTING_STARTED.md updated for the split architecture
15+
- `template-sync.yml` paths reduced to `.github/workflows/` and `scripts/` only
1416

1517
### Removed
16-
- Permission tier system (`.devcontainer/permissions/tier1-assisted.json`, `tier2-autonomous.json`, `tier3-full-trust.json`) and `PERMISSION_TIER` env var -- single settings.json baseline replaces graduated tiers
17-
- `devcontainer-policy-blocker.sh` hook -- tier-dependent policy enforcement no longer needed
18-
- `output-secrets-scanner.sh` hook -- conversation leaks to Anthropic are accepted risk
19-
- `unicode-injection-scanner.sh` hook -- exotic threat with low practical risk
20-
- `test-on-change.sh` hook -- informational-only hook that added latency without preventing issues
21-
- All slash commands (`/cove`, `/cove-isolated`, `/security-audit`) -- niche utilities that added complexity without proportional value
22-
- 6 agents: `agent-auditor`, `security-auditor`, `output-evaluator`, `acceptance-criteria-validator`, `implementation-tracker`, `refactoring-specialist` -- pruned to the 6 agents directly used by the QSP workflow
23-
- `/edit-permissions` skill -- permission tier system removed
24-
- `docs/ARCHITECTURE_GUIDE.md`, `docs/DEVCONTAINER_PERMISSIONS.md`, `docs/community/` -- supporting docs for removed features
25-
- Local destruction patterns from `dangerous-actions-blocker.sh` (`rm -rf`, `sudo`, `DROP DATABASE`, `git push --force`, etc.) -- devcontainer is disposable, these blocks added friction without security value
26-
27-
### Added
28-
- Architecture Deep Dive guide (`docs/ARCHITECTURE_GUIDE.md`) explains why each component exists, what it does under the hood, and what happens if you remove or modify it -- covers all hooks, agents, skills, rules, configuration files, devcontainer layers, and CI/CD workflows with a defense-in-depth diagram and customization guide
29-
- `/landed` skill for post-merge lifecycle -- verifies merge CI, optionally checks deployments (via `.claude/deploy.json`), cleans up feature branches, and identifies the next phase for P-scope work
30-
- `.claude/deploy.json.example` template for configuring deployment verification in `/landed`
18+
- All `.claude/` content (moved to [pyclaude-forge](https://github.com/stranma/pyclaude-forge))
19+
- All `.devcontainer/` content (use [trailofbits/claude-code-devcontainer](https://github.com/trailofbits/claude-code-devcontainer) instead)
20+
- Permission tiers, security hooks, policy enforcement -- dropped from scope
21+
- Stale docs: PDF artifacts, IMPLEMENTATION_PLAN.md stub, .dockerignore
22+
- Broken doc references: ARCHITECTURE_GUIDE.md, DEVCONTAINER_PERMISSIONS.md
3123
- Chain-of-Verification (CoVe) commands (`/cove`, `/cove-isolated`) for high-stakes accuracy -- 4-step self-verification process based on Meta's CoVe paper, with an isolated variant that runs verification in a separate agent to prevent confirmation bias
3224
- Template sync workflow (`.github/workflows/template-sync.yml`) for downstream projects to auto-sync upstream template improvements -- runs weekly or on manual trigger, creates PRs with changed template-managed files while preserving project-specific code
3325
- Python-specific SOLID checklist in `refactoring-specialist` agent -- checks for mutable default arguments, ABC/Protocol misuse, missing dependency injection, god classes, `@property` overuse, and circular imports

docs/GETTING_STARTED.md

Lines changed: 20 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -10,19 +10,13 @@ Before using this template, you should be comfortable with:
1010
- Opening a terminal (VS Code integrated terminal, Windows Terminal, macOS Terminal)
1111
- Navigating directories (`cd`, `ls`/`dir`)
1212
- Running commands and reading their output
13-
- Understanding file paths
1413

1514
### Git basics
16-
- What a repository is
1715
- `git clone`, `git add`, `git commit`, `git push`
1816
- What a branch is and how to create one (`git checkout -b`)
19-
- What a pull request (PR) is -- you don't need to be an expert, but you should understand the concept
17+
- What a pull request (PR) is
2018

21-
If git is new to you, work through [Git - the simple guide](https://rogerdudler.github.io/git-guide/) first. It takes about 15 minutes.
22-
23-
### GitHub account
24-
- You need a [GitHub](https://github.com) account to use the template and its CI/CD workflows
25-
- Install the [GitHub CLI](https://cli.github.com/) (`gh`) -- the template's `/done` command uses it to create PRs and check CI status
19+
If git is new to you, work through [Git - the simple guide](https://rogerdudler.github.io/git-guide/) first.
2620

2721
### Python basics
2822
- You can write and run a Python script
@@ -42,8 +36,6 @@ If you need to install or upgrade: [python.org/downloads](https://www.python.org
4236

4337
### 2. uv (package manager)
4438

45-
uv replaces pip, virtualenv, and poetry. It's fast and handles everything.
46-
4739
```bash
4840
# macOS/Linux:
4941
curl -LsSf https://astral.sh/uv/install.sh | sh
@@ -62,31 +54,31 @@ npm install -g @anthropic-ai/claude-code
6254

6355
You'll need an Anthropic API key or a Claude subscription. See [Claude Code docs](https://docs.anthropic.com/en/docs/claude-code/overview).
6456

65-
### 4. VS Code + Dev Containers (recommended)
57+
### 4. Devcontainer (optional)
58+
59+
For a secure sandbox where Claude Code runs in isolation:
6660

67-
The template includes a devcontainer that sandboxes Claude Code for safety.
61+
```bash
62+
python setup_project.py --name my-project --devcontainer trailofbits --egress-firewall
63+
```
6864

69-
1. Install [VS Code](https://code.visualstudio.com/)
70-
2. Install [Docker Desktop](https://www.docker.com/products/docker-desktop/)
71-
3. Install the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
72-
4. Clone your project, open in VS Code, and click "Reopen in Container" when prompted
65+
This clones [trailofbits/claude-code-devcontainer](https://github.com/trailofbits/claude-code-devcontainer) and adds an egress firewall. You'll need:
7366

74-
The devcontainer comes with Python, uv, ruff, git, and Claude Code pre-installed.
67+
1. [Docker Desktop](https://www.docker.com/products/docker-desktop/)
68+
2. VS Code [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers)
7569

7670
## Your First Project
7771

7872
### 1. Create from template
7973

80-
Go to [stranma/claude-code-python-template](https://github.com/stranma/claude-code-python-template) and click **"Use this template"** > **"Create a new repository"**. Clone it locally.
74+
Go to [stranma/claude-code-python-template](https://github.com/stranma/claude-code-python-template) and click **"Use this template"**. Clone it locally.
8175

8276
### 2. Run setup
8377

8478
```bash
8579
python setup_project.py --name my-first-project --namespace my_first_project --type single
8680
```
8781

88-
This renames all placeholder files to match your project name.
89-
9082
### 3. Install dependencies
9183

9284
```bash
@@ -101,50 +93,39 @@ uv run ruff check .
10193
uv run pyright
10294
```
10395

104-
All three should pass with no errors.
105-
10696
### 5. Start Claude Code
10797

10898
```bash
10999
claude
110100
```
111101

112-
Try these to get a feel for the workflow:
113-
102+
Try:
114103
```
115104
> /sync
116105
> "Add a function that calculates fibonacci numbers with tests"
117106
> /done
118107
```
119108

120-
Claude Code will write the code, write tests, and `/done` will validate, commit, and create a PR.
121-
122109
## Key Concepts
123110

124111
### What is CLAUDE.md?
125112

126-
A file in your project root that tells Claude Code how to behave. It contains project rules, development commands, and workflow references. Think of it as a briefing document for your AI assistant. The template provides one pre-configured for the `/sync` -> `/design` -> `/done` workflow.
113+
A file in your project root that tells Claude Code how to behave -- project rules, development commands, workflow references. The template provides one pre-configured.
127114

128115
### What are agents?
129116

130-
Agents are specialized Claude Code sub-processes that handle specific tasks (linting, testing, code review). They run automatically as part of the workflow. You don't invoke them directly -- `/done` orchestrates them.
117+
Specialized Claude Code sub-processes for specific tasks (linting, testing, code review). They run automatically as part of `/done`.
131118

132119
### What are hooks?
133120

134-
Shell scripts that run before or after Claude Code actions. For example, the `dangerous-actions-blocker` hook prevents Claude from running `rm -rf` or leaking secrets. They run silently in the background.
135-
136-
### What is TDD?
137-
138-
Test-Driven Development: write the test first, then write the code to make it pass. The template enforces this order. It feels backwards at first, but it produces more reliable code because you're always building toward a defined expectation.
121+
Shell scripts that run before or after Claude Code actions. For example, `auto-format` runs ruff after edits.
139122

140123
### What is a devcontainer?
141124

142-
A Docker container configured for development. VS Code opens your project inside the container, so all tools are pre-installed and Claude Code runs in a sandbox. If something goes wrong, your host machine is unaffected.
125+
A Docker container configured for development. VS Code opens your project inside it, so all tools are pre-installed and Claude Code runs in a sandbox.
143126

144127
## Next Steps
145128

146-
- Read the [Development Process](DEVELOPMENT_PROCESS.md) to understand the full workflow
147-
- Read the [Architecture Deep Dive](ARCHITECTURE_GUIDE.md) to understand why each component exists and what happens if you remove it
148-
- Try the `/design` command to plan a small feature before implementing it
149-
- Run `/security-audit` to see how the security scanning works
150-
- Check [Devcontainer Permissions](DEVCONTAINER_PERMISSIONS.md) if you want to adjust Claude Code's autonomy level
129+
- Install [pyclaude-forge](https://github.com/stranma/pyclaude-forge) for the full Claude Code workflow (`/sync`, `/design`, `/done`, `/landed`)
130+
- Try the `/design` command to plan a feature before implementing it
131+
- Read the [CHANGELOG](CHANGELOG.md) for the latest updates

docs/IMPLEMENTATION_PLAN.md

Lines changed: 0 additions & 3 deletions
This file was deleted.
-976 KB
Binary file not shown.

docs/evaluating_agents_md.pdf

-2.05 MB
Binary file not shown.

setup_project.py

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
import shutil
1919
import subprocess
2020
import sys
21+
import urllib.request
2122
from datetime import datetime
2223
from pathlib import Path
2324

@@ -473,6 +474,74 @@ def get_input(prompt: str, default: str = "") -> str:
473474
return input(f"{prompt}: ").strip()
474475

475476

477+
FIREWALL_GIST_URL = (
478+
"https://gist.githubusercontent.com/stranma/"
479+
"f43d932bedc8335e24404c9784fcf190/raw/init-firewall.sh"
480+
)
481+
482+
TOB_REPO = "https://github.com/trailofbits/claude-code-devcontainer.git"
483+
484+
485+
def setup_devcontainer(root: Path, *, devcontainer: str, egress_firewall: bool) -> list[str]:
486+
"""Set up devcontainer from Trail of Bits + optional egress firewall.
487+
488+
:param root: Project root directory.
489+
:param devcontainer: Devcontainer type ("trailofbits" or "none").
490+
:param egress_firewall: Whether to fetch the egress firewall script.
491+
:returns: List of actions taken.
492+
"""
493+
if devcontainer == "none":
494+
return []
495+
496+
actions: list[str] = []
497+
dc_dir = root / ".devcontainer"
498+
499+
if devcontainer == "trailofbits":
500+
# Clone Trail of Bits devcontainer and extract template files
501+
try:
502+
subprocess.run(
503+
["git", "clone", "--depth", "1", TOB_REPO, str(dc_dir)],
504+
check=True,
505+
capture_output=True,
506+
timeout=60,
507+
)
508+
# Remove .git from the clone (we don't want a submodule)
509+
git_dir = dc_dir / ".git"
510+
if git_dir.exists():
511+
shutil.rmtree(git_dir)
512+
actions.append(" Cloned trailofbits/claude-code-devcontainer into .devcontainer/")
513+
except subprocess.CalledProcessError as e:
514+
actions.append(f" WARNING: Failed to clone Trail of Bits devcontainer: {e}")
515+
return actions
516+
except subprocess.TimeoutExpired:
517+
actions.append(" WARNING: Clone timed out after 60s")
518+
return actions
519+
520+
if egress_firewall:
521+
dc_dir.mkdir(exist_ok=True)
522+
firewall_path = dc_dir / "init-firewall.sh"
523+
try:
524+
urllib.request.urlretrieve(FIREWALL_GIST_URL, firewall_path)
525+
firewall_path.chmod(firewall_path.stat().st_mode | 0o755)
526+
actions.append(" Fetched egress firewall (init-firewall.sh) from gist")
527+
528+
# Add postStartCommand to devcontainer.json if it exists
529+
dc_json = dc_dir / "devcontainer.json"
530+
if dc_json.exists():
531+
raw = dc_json.read_text(encoding="utf-8")
532+
try:
533+
config = json.loads(raw)
534+
config["postStartCommand"] = "sudo /usr/local/bin/init-firewall.sh"
535+
dc_json.write_text(json.dumps(config, indent=2) + "\n", encoding="utf-8")
536+
actions.append(" Added postStartCommand for firewall to devcontainer.json")
537+
except json.JSONDecodeError:
538+
actions.append(" WARNING: Could not parse devcontainer.json to add firewall command")
539+
except Exception as e:
540+
actions.append(f" WARNING: Failed to fetch firewall script: {e}")
541+
542+
return actions
543+
544+
476545
def interactive_setup() -> dict[str, str]:
477546
"""Collect configuration interactively."""
478547
print("\n=== Claude Code Python Template Setup ===\n")
@@ -511,6 +580,18 @@ def interactive_setup() -> dict[str, str]:
511580
svc_map = {"1": "none", "2": "postgres", "3": "postgres-redis", "4": "custom"}
512581
config["services"] = svc_map.get(svc_choice, "none")
513582

583+
print("\nDevcontainer setup:")
584+
print(" 1. None")
585+
print(" 2. Trail of Bits (recommended -- secure sandbox for Claude Code)")
586+
dc_choice = get_input("Choose [1/2]", "1")
587+
config["devcontainer"] = "trailofbits" if dc_choice == "2" else "none"
588+
589+
if config["devcontainer"] != "none":
590+
fw_choice = get_input("Include egress firewall? (blocks code exfiltration) [y/n]", "y")
591+
config["egress_firewall"] = fw_choice.lower() in ("y", "yes")
592+
else:
593+
config["egress_firewall"] = False
594+
514595
return config
515596

516597

@@ -532,6 +613,17 @@ def main() -> None:
532613
default="none",
533614
help="Docker Compose services profile for devcontainer (default: none)",
534615
)
616+
parser.add_argument(
617+
"--devcontainer",
618+
choices=["none", "trailofbits"],
619+
default="none",
620+
help="Devcontainer setup (default: none)",
621+
)
622+
parser.add_argument(
623+
"--egress-firewall",
624+
action="store_true",
625+
help="Add egress firewall to devcontainer (blocks code exfiltration)",
626+
)
535627
parser.add_argument("--git-init", action="store_true", help="Initialize git and make initial commit")
536628
parser.add_argument("--keep-setup", action="store_true", help="Don't delete this setup script after running")
537629

@@ -552,6 +644,8 @@ def main() -> None:
552644
"type": args.type,
553645
"packages": args.packages,
554646
"services": args.services,
647+
"devcontainer": args.devcontainer,
648+
"egress_firewall": args.egress_firewall,
555649
}
556650

557651
# Validate required fields
@@ -577,6 +671,9 @@ def main() -> None:
577671
print(f" Type: {config.get('type', 'mono')}")
578672
print(f" Base branch: {config.get('base_branch', 'master')}")
579673
print(f" Devcontainer services: {config.get('services', 'none')}")
674+
print(f" Devcontainer: {config.get('devcontainer', 'none')}")
675+
if config.get("egress_firewall"):
676+
print(" Egress firewall: yes")
580677

581678
# Step 1: Rename {{namespace}} directories
582679
print("\nRenaming namespace directories...")
@@ -648,6 +745,15 @@ def main() -> None:
648745
for a in actions:
649746
print(a)
650747

748+
# Step 5b: Set up devcontainer
749+
dc_type = config.get("devcontainer", "none")
750+
if dc_type != "none":
751+
print(f"\nSetting up devcontainer ({dc_type})...")
752+
fw = config.get("egress_firewall", False)
753+
actions = setup_devcontainer(TEMPLATE_DIR, devcontainer=dc_type, egress_firewall=fw)
754+
for a in actions:
755+
print(a)
756+
651757
# Step 6: Git init if requested
652758
if getattr(args, "git_init", False):
653759
print("\nInitializing git repository...")

0 commit comments

Comments
 (0)