@jgamaraalv/ts-dev-kit 3.0.1 → 3.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude-plugin/marketplace.json +3 -3
- package/.claude-plugin/plugin.json +2 -2
- package/CHANGELOG.md +7 -0
- package/README.md +4 -2
- package/package.json +2 -2
- package/skills/debug/SKILL.md +4 -4
- package/skills/yolo/SKILL.md +319 -0
- package/skills/yolo/references/devcontainer-json.md +74 -0
- package/skills/yolo/references/dockerfile.md +108 -0
- package/skills/yolo/references/init-firewall.sh.md +167 -0
|
@@ -5,14 +5,14 @@
|
|
|
5
5
|
"url": "https://github.com/jgamaraalv"
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
|
-
"description": "15 agents and
|
|
8
|
+
"description": "15 agents and 22 skills for TypeScript fullstack development with Claude Code"
|
|
9
9
|
},
|
|
10
10
|
"plugins": [
|
|
11
11
|
{
|
|
12
12
|
"name": "ts-dev-kit",
|
|
13
13
|
"source": "./",
|
|
14
|
-
"description": "15 specialized agents and
|
|
15
|
-
"version": "3.0
|
|
14
|
+
"description": "15 specialized agents and 22 skills for TypeScript fullstack development",
|
|
15
|
+
"version": "3.1.0",
|
|
16
16
|
"author": {
|
|
17
17
|
"name": "jgamaraalv"
|
|
18
18
|
},
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-dev-kit",
|
|
3
|
-
"version": "3.0
|
|
4
|
-
"description": "15 specialized agents and
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "15 specialized agents and 22 skills for TypeScript fullstack development with Fastify, Next.js, PostgreSQL, Redis, and more.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "jgamaraalv",
|
|
7
7
|
"url": "https://github.com/jgamaraalv"
|
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,13 @@ All notable changes to this project will be documented in this file.
|
|
|
5
5
|
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
6
6
|
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
7
|
|
|
8
|
+
## [3.1.0] - 2026-02-27
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- `/yolo` skill: devcontainer setup and launch workflow for running Claude Code with `--dangerously-skip-permissions` in a secure, sandboxed environment — detects existing devcontainer, checks Docker status, scaffolds the Claude Code reference `.devcontainer/` (Dockerfile, firewall, config), starts Docker if needed, and opens VS Code to launch the container
|
|
13
|
+
- Dynamic context injection to `/yolo`: project root, devcontainer presence, Docker daemon status, Docker and VS Code installation are pre-injected before Claude receives the prompt
|
|
14
|
+
|
|
8
15
|
## [3.0.1] - 2026-02-27
|
|
9
16
|
|
|
10
17
|
### Fixed
|
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# ts-dev-kit
|
|
2
2
|
|
|
3
|
-
> 15 specialized agents +
|
|
3
|
+
> 15 specialized agents + 22 curated skills for TypeScript fullstack development
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/@jgamaraalv/ts-dev-kit)
|
|
6
6
|
[](LICENSE)
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
| typescript-pro | Generics, type inference, conditional types |
|
|
31
31
|
| ux-optimizer | User flows, form UX, friction reduction |
|
|
32
32
|
|
|
33
|
-
### Skills (
|
|
33
|
+
### Skills (22)
|
|
34
34
|
|
|
35
35
|
| Skill | Slug | Domain |
|
|
36
36
|
| ---------------------- | ------------------------- | ------------------------------------------------- |
|
|
@@ -55,6 +55,7 @@
|
|
|
55
55
|
| TanStack Query | `/tanstack-query` | React Query v5, caching, SSR/hydration |
|
|
56
56
|
| TypeScript Conventions | `/typescript-conventions` | Strict config, patterns, best practices |
|
|
57
57
|
| UI/UX Guidelines | `/ui-ux-guidelines` | Accessibility, layout, forms |
|
|
58
|
+
| Yolo | `/yolo` | Devcontainer setup for autonomous Claude Code |
|
|
58
59
|
|
|
59
60
|
---
|
|
60
61
|
|
|
@@ -206,6 +207,7 @@ Several skills use the `!`command`` syntax to pre-inject live data before Claude
|
|
|
206
207
|
| `/conventional-commits` | Staged diff summary, full staged diff, last 8 commit messages |
|
|
207
208
|
| `/debug` | Last 10 git commits, working tree status |
|
|
208
209
|
| `/codebase-adapter` | Working directory, lockfile, installed agents, MCP servers, `package.json` |
|
|
210
|
+
| `/yolo` | Project root, devcontainer presence, Docker status, VS Code installation |
|
|
209
211
|
|
|
210
212
|
---
|
|
211
213
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jgamaraalv/ts-dev-kit",
|
|
3
|
-
"version": "3.0
|
|
4
|
-
"description": "Claude Code plugin: 15 agents +
|
|
3
|
+
"version": "3.1.0",
|
|
4
|
+
"description": "Claude Code plugin: 15 agents + 22 skills for TypeScript fullstack development",
|
|
5
5
|
"author": "jgamaraalv",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"repository": {
|
package/skills/debug/SKILL.md
CHANGED
|
@@ -22,6 +22,10 @@ allowed-tools: Bash(git *)
|
|
|
22
22
|
- "Debug the notification queue — jobs are stuck"
|
|
23
23
|
</trigger_examples>
|
|
24
24
|
|
|
25
|
+
<task>
|
|
26
|
+
$ARGUMENTS
|
|
27
|
+
</task>
|
|
28
|
+
|
|
25
29
|
<workflow>
|
|
26
30
|
Follow each phase in order. Each one feeds the next.
|
|
27
31
|
|
|
@@ -233,7 +237,3 @@ When complete, produce a debug report using the template in [template.md](templa
|
|
|
233
237
|
|
|
234
238
|
Do not add explanations, caveats, or follow-up suggestions unless the user asks.
|
|
235
239
|
</output>
|
|
236
|
-
|
|
237
|
-
<task>
|
|
238
|
-
$ARGUMENTS
|
|
239
|
-
</task>
|
|
@@ -0,0 +1,319 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: yolo
|
|
3
|
+
description: "Sets up and launches a Claude Code devcontainer with --dangerously-skip-permissions for fully autonomous, unattended operation inside a sandboxed environment. Use when: (1) user wants to run Claude Code without permission prompts, (2) setting up a secure development container for autonomous coding, (3) user says 'yolo', 'yolo mode', 'run without permissions', 'autonomous mode', 'skip permissions safely', or 'devcontainer setup'."
|
|
4
|
+
argument-hint: "[optional: path to project root — defaults to current directory]"
|
|
5
|
+
allowed-tools: Bash(docker *), Bash(ls *), Bash(cat *), Bash(cp *), Bash(mkdir *), Bash(which *), Bash(open *), Bash(code *), Bash(command *)
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
<live_context>
|
|
9
|
+
**Project root:**
|
|
10
|
+
!`pwd`
|
|
11
|
+
|
|
12
|
+
**Devcontainer present:**
|
|
13
|
+
!`ls -la .devcontainer/devcontainer.json 2>/dev/null && echo "YES" || echo "NO"`
|
|
14
|
+
|
|
15
|
+
**Docker daemon status:**
|
|
16
|
+
!`docker info --format '{{.ServerVersion}}' 2>/dev/null && echo "RUNNING" || echo "NOT RUNNING"`
|
|
17
|
+
|
|
18
|
+
**Docker Desktop installed:**
|
|
19
|
+
!`command -v docker 2>/dev/null || echo "NOT FOUND"`
|
|
20
|
+
|
|
21
|
+
**VS Code installed:**
|
|
22
|
+
!`command -v code 2>/dev/null || echo "NOT FOUND"`
|
|
23
|
+
</live_context>
|
|
24
|
+
|
|
25
|
+
<trigger_examples>
|
|
26
|
+
- "yolo"
|
|
27
|
+
- "Enter yolo mode"
|
|
28
|
+
- "Set up a devcontainer so Claude can run autonomously"
|
|
29
|
+
- "I want to run Claude Code without permission prompts"
|
|
30
|
+
- "Skip permissions safely with a devcontainer"
|
|
31
|
+
- "Set up autonomous mode"
|
|
32
|
+
</trigger_examples>
|
|
33
|
+
|
|
34
|
+
<system>
|
|
35
|
+
You are a devcontainer setup specialist. Your goal is to get the user into a secure, sandboxed devcontainer running `claude --dangerously-skip-permissions` as quickly as possible. You follow a strict decision tree and never skip safety checks.
|
|
36
|
+
|
|
37
|
+
**IMPORTANT SECURITY NOTE:** While the devcontainer provides substantial protections (network firewall, isolation), it is NOT immune to all attacks. Only use devcontainers with **trusted repositories**. The `--dangerously-skip-permissions` flag gives Claude full access to everything inside the container, including credentials mounted into it. Always inform the user of this trade-off.
|
|
38
|
+
</system>
|
|
39
|
+
|
|
40
|
+
<task>
|
|
41
|
+
$ARGUMENTS
|
|
42
|
+
</task>
|
|
43
|
+
|
|
44
|
+
<workflow>
|
|
45
|
+
Follow this decision tree strictly. Each phase gates the next.
|
|
46
|
+
|
|
47
|
+
```
|
|
48
|
+
START
|
|
49
|
+
│
|
|
50
|
+
├─ Phase 1: Detect devcontainer
|
|
51
|
+
│ ├─ EXISTS → Phase 2
|
|
52
|
+
│ └─ MISSING → Phase 5 (install) → Phase 2
|
|
53
|
+
│
|
|
54
|
+
├─ Phase 2: Check Docker
|
|
55
|
+
│ ├─ RUNNING → Phase 3
|
|
56
|
+
│ └─ NOT RUNNING → Phase 4 (start Docker) → Phase 3
|
|
57
|
+
│
|
|
58
|
+
├─ Phase 3: Launch devcontainer
|
|
59
|
+
│ └─ claude --dangerously-skip-permissions
|
|
60
|
+
│
|
|
61
|
+
├─ Phase 4: Start Docker
|
|
62
|
+
│ └─ Start Docker Desktop/daemon → Phase 3
|
|
63
|
+
│
|
|
64
|
+
└─ Phase 5: Install devcontainer
|
|
65
|
+
└─ Scaffold .devcontainer/ → Phase 2
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
<phase_1_detect>
|
|
69
|
+
|
|
70
|
+
## Phase 1 — Detect devcontainer
|
|
71
|
+
|
|
72
|
+
Check whether `.devcontainer/devcontainer.json` exists in the project root.
|
|
73
|
+
|
|
74
|
+
1. Read the `<live_context>` above for the pre-injected detection result.
|
|
75
|
+
2. If **YES** — announce to the user and proceed to Phase 2:
|
|
76
|
+
|
|
77
|
+
> **DEVCONTAINER DETECTED** — `.devcontainer/` found. Checking Docker status...
|
|
78
|
+
|
|
79
|
+
3. If **NO** — announce and proceed to Phase 5:
|
|
80
|
+
|
|
81
|
+
> **NO DEVCONTAINER FOUND** — I'll set one up using the Claude Code reference implementation.
|
|
82
|
+
|
|
83
|
+
</phase_1_detect>
|
|
84
|
+
|
|
85
|
+
<phase_2_check_docker>
|
|
86
|
+
|
|
87
|
+
## Phase 2 — Check Docker
|
|
88
|
+
|
|
89
|
+
Verify the Docker daemon is running and accessible.
|
|
90
|
+
|
|
91
|
+
1. Read the `<live_context>` above for the pre-injected Docker status.
|
|
92
|
+
2. If `docker` command is **NOT FOUND**:
|
|
93
|
+
|
|
94
|
+
> **DOCKER NOT INSTALLED** — Docker Desktop is required for devcontainers. Please install it from https://www.docker.com/products/docker-desktop/ and re-run `/yolo`.
|
|
95
|
+
|
|
96
|
+
Stop here. Do not proceed.
|
|
97
|
+
|
|
98
|
+
3. If Docker is **RUNNING** — proceed to Phase 3:
|
|
99
|
+
|
|
100
|
+
> **DOCKER RUNNING** (version X.X.X) — Ready to launch devcontainer.
|
|
101
|
+
|
|
102
|
+
4. If Docker is **NOT RUNNING** — proceed to Phase 4.
|
|
103
|
+
|
|
104
|
+
</phase_2_check_docker>
|
|
105
|
+
|
|
106
|
+
<phase_3_launch>
|
|
107
|
+
|
|
108
|
+
## Phase 3 — Launch devcontainer
|
|
109
|
+
|
|
110
|
+
Open the project in VS Code with the devcontainer.
|
|
111
|
+
|
|
112
|
+
**Prerequisites check:**
|
|
113
|
+
|
|
114
|
+
1. Verify VS Code is installed (`command -v code`).
|
|
115
|
+
2. If VS Code is not installed, inform the user:
|
|
116
|
+
|
|
117
|
+
> **VS Code is required** for devcontainer support. Install it from https://code.visualstudio.com/ and the [Dev Containers extension](https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers).
|
|
118
|
+
|
|
119
|
+
**Launch sequence:**
|
|
120
|
+
|
|
121
|
+
1. Open the project in VS Code:
|
|
122
|
+
|
|
123
|
+
```bash
|
|
124
|
+
code .
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
2. Instruct the user:
|
|
128
|
+
|
|
129
|
+
> **LAUNCHING DEVCONTAINER**
|
|
130
|
+
>
|
|
131
|
+
> VS Code is opening your project. Follow these steps:
|
|
132
|
+
>
|
|
133
|
+
> 1. VS Code should prompt **"Reopen in Container"** — click it.
|
|
134
|
+
> - If no prompt appears: open Command Palette (`Cmd+Shift+P` / `Ctrl+Shift+P`) → type **"Dev Containers: Reopen in Container"** → select it.
|
|
135
|
+
> 2. Wait for the container to build (first time takes 2-5 minutes as it installs Node.js 20, Claude Code, ZSH, and configures the firewall).
|
|
136
|
+
> 3. Once inside the container, open a terminal and run:
|
|
137
|
+
> ```
|
|
138
|
+
> claude --dangerously-skip-permissions
|
|
139
|
+
> ```
|
|
140
|
+
>
|
|
141
|
+
> **What's happening under the hood:**
|
|
142
|
+
>
|
|
143
|
+
> - The container runs Node.js 20 with Claude Code pre-installed
|
|
144
|
+
> - A firewall restricts outbound traffic to: npm registry, GitHub, Claude API, Sentry, and VS Code marketplace only
|
|
145
|
+
> - All other internet access is blocked (verified on startup)
|
|
146
|
+
> - Your project is mounted at `/workspace`
|
|
147
|
+
> - Shell history and Claude config persist between container restarts
|
|
148
|
+
>
|
|
149
|
+
> **Security trade-off:** `--dangerously-skip-permissions` means Claude can execute ANY command inside the container without asking. The firewall and container isolation are your safety net. Only use this with trusted code.
|
|
150
|
+
|
|
151
|
+
</phase_3_launch>
|
|
152
|
+
|
|
153
|
+
<phase_4_start_docker>
|
|
154
|
+
|
|
155
|
+
## Phase 4 — Start Docker
|
|
156
|
+
|
|
157
|
+
The Docker daemon is installed but not running.
|
|
158
|
+
|
|
159
|
+
**macOS:**
|
|
160
|
+
|
|
161
|
+
```bash
|
|
162
|
+
open -a Docker
|
|
163
|
+
```
|
|
164
|
+
|
|
165
|
+
**Linux:**
|
|
166
|
+
|
|
167
|
+
```bash
|
|
168
|
+
sudo systemctl start docker
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
After issuing the start command:
|
|
172
|
+
|
|
173
|
+
1. Wait for Docker to become ready (poll up to 30 seconds):
|
|
174
|
+
|
|
175
|
+
```bash
|
|
176
|
+
for i in $(seq 1 15); do
|
|
177
|
+
docker info --format '{{.ServerVersion}}' 2>/dev/null && break
|
|
178
|
+
sleep 2
|
|
179
|
+
done
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
2. Re-verify Docker is running:
|
|
183
|
+
|
|
184
|
+
```bash
|
|
185
|
+
docker info --format '{{.ServerVersion}}' 2>/dev/null
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
3. If Docker started successfully — proceed to Phase 3:
|
|
189
|
+
|
|
190
|
+
> **DOCKER STARTED** — Docker daemon is now running. Launching devcontainer...
|
|
191
|
+
|
|
192
|
+
4. If Docker failed to start after 30 seconds:
|
|
193
|
+
|
|
194
|
+
> **DOCKER FAILED TO START** — Please start Docker Desktop manually and re-run `/yolo`.
|
|
195
|
+
|
|
196
|
+
Stop here.
|
|
197
|
+
|
|
198
|
+
</phase_4_start_docker>
|
|
199
|
+
|
|
200
|
+
<phase_5_install_devcontainer>
|
|
201
|
+
|
|
202
|
+
## Phase 5 — Install devcontainer
|
|
203
|
+
|
|
204
|
+
Scaffold the `.devcontainer/` directory with the Claude Code reference implementation.
|
|
205
|
+
|
|
206
|
+
### Step 1 — Create directory
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
mkdir -p .devcontainer
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
### Step 2 — Create `devcontainer.json`
|
|
213
|
+
|
|
214
|
+
Write the file using the reference config. See [references/devcontainer-json.md](references/devcontainer-json.md) for the full file content.
|
|
215
|
+
|
|
216
|
+
Key settings:
|
|
217
|
+
|
|
218
|
+
- Name: `"Claude Code Sandbox"`
|
|
219
|
+
- Dockerfile build with Node.js 20
|
|
220
|
+
- `NET_ADMIN` + `NET_RAW` capabilities (required for firewall)
|
|
221
|
+
- Claude Code VS Code extension pre-installed
|
|
222
|
+
- Volume mounts for bash history and Claude config persistence
|
|
223
|
+
- Firewall initialization via `postStartCommand`
|
|
224
|
+
- Workspace mounted at `/workspace`
|
|
225
|
+
|
|
226
|
+
### Step 3 — Create `Dockerfile`
|
|
227
|
+
|
|
228
|
+
Write the file using the reference Dockerfile. See [references/dockerfile.md](references/dockerfile.md) for the full file content.
|
|
229
|
+
|
|
230
|
+
Key features:
|
|
231
|
+
|
|
232
|
+
- Base: `node:20`
|
|
233
|
+
- Installs: git, ZSH, fzf, gh CLI, iptables/ipset, nano, vim
|
|
234
|
+
- Installs `@anthropic-ai/claude-code` globally
|
|
235
|
+
- Sets up non-root `node` user
|
|
236
|
+
- Configures ZSH with Powerlevel10k theme
|
|
237
|
+
- Copies and configures firewall script with sudo access
|
|
238
|
+
|
|
239
|
+
### Step 4 — Create `init-firewall.sh`
|
|
240
|
+
|
|
241
|
+
Write the file using the reference firewall script. See [references/init-firewall.sh.md](references/init-firewall.sh.md) for the full file content.
|
|
242
|
+
|
|
243
|
+
Key security features:
|
|
244
|
+
|
|
245
|
+
- Default-deny policy (DROP all INPUT, OUTPUT, FORWARD)
|
|
246
|
+
- Whitelisted outbound only: npm registry, GitHub (dynamic IP fetch), Claude API, Sentry, StatsIG, VS Code marketplace
|
|
247
|
+
- DNS and SSH allowed
|
|
248
|
+
- Localhost and host network allowed
|
|
249
|
+
- Startup verification: confirms `example.com` is blocked and `api.github.com` is reachable
|
|
250
|
+
- Docker DNS rules preserved
|
|
251
|
+
|
|
252
|
+
### Step 5 — Add `.devcontainer` to `.gitignore` (optional)
|
|
253
|
+
|
|
254
|
+
Check if `.gitignore` exists and whether `.devcontainer/` is already listed. If not, ask the user:
|
|
255
|
+
|
|
256
|
+
> **Should I add `.devcontainer/` to `.gitignore`?**
|
|
257
|
+
>
|
|
258
|
+
> - **Yes** — keep devcontainer config local to your machine
|
|
259
|
+
> - **No** — commit it so the whole team can use it (recommended for teams)
|
|
260
|
+
|
|
261
|
+
### Step 6 — Verify installation
|
|
262
|
+
|
|
263
|
+
```bash
|
|
264
|
+
ls -la .devcontainer/
|
|
265
|
+
cat .devcontainer/devcontainer.json | head -5
|
|
266
|
+
```
|
|
267
|
+
|
|
268
|
+
Announce completion:
|
|
269
|
+
|
|
270
|
+
> **DEVCONTAINER INSTALLED** — `.devcontainer/` is ready with Dockerfile, firewall script, and configuration. Proceeding to check Docker...
|
|
271
|
+
|
|
272
|
+
Return to Phase 2.
|
|
273
|
+
|
|
274
|
+
</phase_5_install_devcontainer>
|
|
275
|
+
|
|
276
|
+
</workflow>
|
|
277
|
+
|
|
278
|
+
<customization_notes>
|
|
279
|
+
|
|
280
|
+
## Customizing the devcontainer
|
|
281
|
+
|
|
282
|
+
After installation, the user may want to customize:
|
|
283
|
+
|
|
284
|
+
**Adding allowed domains to the firewall:**
|
|
285
|
+
Edit `.devcontainer/init-firewall.sh` and add domains to the `for domain in` loop:
|
|
286
|
+
|
|
287
|
+
```bash
|
|
288
|
+
for domain in \
|
|
289
|
+
"registry.npmjs.org" \
|
|
290
|
+
"api.anthropic.com" \
|
|
291
|
+
"your-custom-domain.com" \ # <-- add here
|
|
292
|
+
...
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
**Adding VS Code extensions:**
|
|
296
|
+
Edit `.devcontainer/devcontainer.json` → `customizations.vscode.extensions[]`:
|
|
297
|
+
|
|
298
|
+
```json
|
|
299
|
+
"extensions": [
|
|
300
|
+
"anthropic.claude-code",
|
|
301
|
+
"your-extension.id"
|
|
302
|
+
]
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
**Changing Node.js version:**
|
|
306
|
+
Edit `.devcontainer/Dockerfile` — change the `FROM` line:
|
|
307
|
+
|
|
308
|
+
```dockerfile
|
|
309
|
+
FROM node:22
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
**Adding project-specific dependencies:**
|
|
313
|
+
Add `RUN` commands to the Dockerfile before the `USER node` line:
|
|
314
|
+
|
|
315
|
+
```dockerfile
|
|
316
|
+
RUN apt-get update && apt-get install -y your-package
|
|
317
|
+
```
|
|
318
|
+
|
|
319
|
+
</customization_notes>
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# devcontainer.json — Reference
|
|
2
|
+
|
|
3
|
+
Write this file to `.devcontainer/devcontainer.json`:
|
|
4
|
+
|
|
5
|
+
```json
|
|
6
|
+
{
|
|
7
|
+
"name": "Claude Code Sandbox",
|
|
8
|
+
"build": {
|
|
9
|
+
"dockerfile": "Dockerfile",
|
|
10
|
+
"args": {
|
|
11
|
+
"TZ": "${localEnv:TZ:America/Los_Angeles}",
|
|
12
|
+
"CLAUDE_CODE_VERSION": "latest",
|
|
13
|
+
"GIT_DELTA_VERSION": "0.18.2",
|
|
14
|
+
"ZSH_IN_DOCKER_VERSION": "1.2.0"
|
|
15
|
+
}
|
|
16
|
+
},
|
|
17
|
+
"runArgs": [
|
|
18
|
+
"--cap-add=NET_ADMIN",
|
|
19
|
+
"--cap-add=NET_RAW"
|
|
20
|
+
],
|
|
21
|
+
"customizations": {
|
|
22
|
+
"vscode": {
|
|
23
|
+
"extensions": [
|
|
24
|
+
"anthropic.claude-code",
|
|
25
|
+
"dbaeumer.vscode-eslint",
|
|
26
|
+
"esbenp.prettier-vscode",
|
|
27
|
+
"eamodio.gitlens"
|
|
28
|
+
],
|
|
29
|
+
"settings": {
|
|
30
|
+
"editor.formatOnSave": true,
|
|
31
|
+
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
|
32
|
+
"editor.codeActionsOnSave": {
|
|
33
|
+
"source.fixAll.eslint": "explicit"
|
|
34
|
+
},
|
|
35
|
+
"terminal.integrated.defaultProfile.linux": "zsh",
|
|
36
|
+
"terminal.integrated.profiles.linux": {
|
|
37
|
+
"bash": {
|
|
38
|
+
"path": "bash",
|
|
39
|
+
"icon": "terminal-bash"
|
|
40
|
+
},
|
|
41
|
+
"zsh": {
|
|
42
|
+
"path": "zsh"
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
},
|
|
48
|
+
"remoteUser": "node",
|
|
49
|
+
"mounts": [
|
|
50
|
+
"source=claude-code-bashhistory-${devcontainerId},target=/commandhistory,type=volume",
|
|
51
|
+
"source=claude-code-config-${devcontainerId},target=/home/node/.claude,type=volume"
|
|
52
|
+
],
|
|
53
|
+
"containerEnv": {
|
|
54
|
+
"NODE_OPTIONS": "--max-old-space-size=4096",
|
|
55
|
+
"CLAUDE_CONFIG_DIR": "/home/node/.claude",
|
|
56
|
+
"POWERLEVEL9K_DISABLE_GITSTATUS": "true"
|
|
57
|
+
},
|
|
58
|
+
"workspaceMount": "source=${localWorkspaceFolder},target=/workspace,type=bind,consistency=delegated",
|
|
59
|
+
"workspaceFolder": "/workspace",
|
|
60
|
+
"postStartCommand": "sudo /usr/local/bin/init-firewall.sh",
|
|
61
|
+
"waitFor": "postStartCommand"
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
## Key settings explained
|
|
66
|
+
|
|
67
|
+
| Setting | Purpose |
|
|
68
|
+
|---------|---------|
|
|
69
|
+
| `runArgs: --cap-add=NET_ADMIN,NET_RAW` | Required for the firewall script to configure iptables |
|
|
70
|
+
| `remoteUser: node` | Runs as non-root for security |
|
|
71
|
+
| `mounts` (bashhistory, config) | Persists shell history and Claude config between container restarts |
|
|
72
|
+
| `postStartCommand` | Initializes the firewall on every container start |
|
|
73
|
+
| `waitFor: postStartCommand` | Ensures firewall is active before VS Code connects |
|
|
74
|
+
| `workspaceMount` | Binds the local project into `/workspace` with delegated consistency for performance |
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
# Dockerfile — Reference
|
|
2
|
+
|
|
3
|
+
Write this file to `.devcontainer/Dockerfile`:
|
|
4
|
+
|
|
5
|
+
```dockerfile
|
|
6
|
+
FROM node:20
|
|
7
|
+
|
|
8
|
+
ARG TZ
|
|
9
|
+
ENV TZ="$TZ"
|
|
10
|
+
|
|
11
|
+
ARG CLAUDE_CODE_VERSION=latest
|
|
12
|
+
|
|
13
|
+
# Install basic development tools and iptables/ipset
|
|
14
|
+
RUN apt-get update && apt-get install -y --no-install-recommends \
|
|
15
|
+
less \
|
|
16
|
+
git \
|
|
17
|
+
procps \
|
|
18
|
+
sudo \
|
|
19
|
+
fzf \
|
|
20
|
+
zsh \
|
|
21
|
+
man-db \
|
|
22
|
+
unzip \
|
|
23
|
+
gnupg2 \
|
|
24
|
+
gh \
|
|
25
|
+
iptables \
|
|
26
|
+
ipset \
|
|
27
|
+
iproute2 \
|
|
28
|
+
dnsutils \
|
|
29
|
+
aggregate \
|
|
30
|
+
jq \
|
|
31
|
+
nano \
|
|
32
|
+
vim \
|
|
33
|
+
&& apt-get clean && rm -rf /var/lib/apt/lists/*
|
|
34
|
+
|
|
35
|
+
# Ensure default node user has access to /usr/local/share
|
|
36
|
+
RUN mkdir -p /usr/local/share/npm-global && \
|
|
37
|
+
chown -R node:node /usr/local/share
|
|
38
|
+
|
|
39
|
+
ARG USERNAME=node
|
|
40
|
+
|
|
41
|
+
# Persist bash history.
|
|
42
|
+
RUN SNIPPET="export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
|
|
43
|
+
&& mkdir /commandhistory \
|
|
44
|
+
&& touch /commandhistory/.bash_history \
|
|
45
|
+
&& chown -R $USERNAME /commandhistory
|
|
46
|
+
|
|
47
|
+
# Set `DEVCONTAINER` environment variable to help with orientation
|
|
48
|
+
ENV DEVCONTAINER=true
|
|
49
|
+
|
|
50
|
+
# Create workspace and config directories and set permissions
|
|
51
|
+
RUN mkdir -p /workspace /home/node/.claude && \
|
|
52
|
+
chown -R node:node /workspace /home/node/.claude
|
|
53
|
+
|
|
54
|
+
WORKDIR /workspace
|
|
55
|
+
|
|
56
|
+
ARG GIT_DELTA_VERSION=0.18.2
|
|
57
|
+
RUN ARCH=$(dpkg --print-architecture) && \
|
|
58
|
+
wget "https://github.com/dandavison/delta/releases/download/${GIT_DELTA_VERSION}/git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
|
|
59
|
+
sudo dpkg -i "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb" && \
|
|
60
|
+
rm "git-delta_${GIT_DELTA_VERSION}_${ARCH}.deb"
|
|
61
|
+
|
|
62
|
+
# Set up non-root user
|
|
63
|
+
USER node
|
|
64
|
+
|
|
65
|
+
# Install global packages
|
|
66
|
+
ENV NPM_CONFIG_PREFIX=/usr/local/share/npm-global
|
|
67
|
+
ENV PATH=$PATH:/usr/local/share/npm-global/bin
|
|
68
|
+
|
|
69
|
+
# Set the default shell to zsh rather than sh
|
|
70
|
+
ENV SHELL=/bin/zsh
|
|
71
|
+
|
|
72
|
+
# Set the default editor and visual
|
|
73
|
+
ENV EDITOR=nano
|
|
74
|
+
ENV VISUAL=nano
|
|
75
|
+
|
|
76
|
+
# Default powerline10k theme
|
|
77
|
+
ARG ZSH_IN_DOCKER_VERSION=1.2.0
|
|
78
|
+
RUN sh -c "$(wget -O- https://github.com/deluan/zsh-in-docker/releases/download/v${ZSH_IN_DOCKER_VERSION}/zsh-in-docker.sh)" -- \
|
|
79
|
+
-p git \
|
|
80
|
+
-p fzf \
|
|
81
|
+
-a "source /usr/share/doc/fzf/examples/key-bindings.zsh" \
|
|
82
|
+
-a "source /usr/share/doc/fzf/examples/completion.zsh" \
|
|
83
|
+
-a "export PROMPT_COMMAND='history -a' && export HISTFILE=/commandhistory/.bash_history" \
|
|
84
|
+
-x
|
|
85
|
+
|
|
86
|
+
# Install Claude
|
|
87
|
+
RUN npm install -g @anthropic-ai/claude-code@${CLAUDE_CODE_VERSION}
|
|
88
|
+
|
|
89
|
+
# Copy and set up firewall script
|
|
90
|
+
COPY init-firewall.sh /usr/local/bin/
|
|
91
|
+
USER root
|
|
92
|
+
RUN chmod +x /usr/local/bin/init-firewall.sh && \
|
|
93
|
+
echo "node ALL=(root) NOPASSWD: /usr/local/bin/init-firewall.sh" > /etc/sudoers.d/node-firewall && \
|
|
94
|
+
chmod 0440 /etc/sudoers.d/node-firewall
|
|
95
|
+
USER node
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## What this Dockerfile provides
|
|
99
|
+
|
|
100
|
+
| Layer | Details |
|
|
101
|
+
|-------|---------|
|
|
102
|
+
| **Base image** | Node.js 20 (LTS) |
|
|
103
|
+
| **Dev tools** | git, ZSH, fzf, gh CLI, jq, nano, vim, delta (git diff) |
|
|
104
|
+
| **Network tools** | iptables, ipset, iproute2, dnsutils, aggregate (for firewall) |
|
|
105
|
+
| **User** | Non-root `node` user with sudo for firewall script only |
|
|
106
|
+
| **Shell** | ZSH with Powerlevel10k theme, fzf key bindings |
|
|
107
|
+
| **Claude Code** | Installed globally via npm (version configurable via build arg) |
|
|
108
|
+
| **Firewall** | `init-firewall.sh` copied and configured with passwordless sudo |
|
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
# init-firewall.sh — Reference
|
|
2
|
+
|
|
3
|
+
Write this file to `.devcontainer/init-firewall.sh`:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
#!/bin/bash
|
|
7
|
+
set -euo pipefail # Exit on error, undefined vars, and pipeline failures
|
|
8
|
+
IFS=$'\n\t' # Stricter word splitting
|
|
9
|
+
|
|
10
|
+
# 1. Extract Docker DNS info BEFORE any flushing
|
|
11
|
+
DOCKER_DNS_RULES=$(iptables-save -t nat | grep "127\.0\.0\.11" || true)
|
|
12
|
+
|
|
13
|
+
# Flush existing rules and delete existing ipsets
|
|
14
|
+
iptables -F
|
|
15
|
+
iptables -X
|
|
16
|
+
iptables -t nat -F
|
|
17
|
+
iptables -t nat -X
|
|
18
|
+
iptables -t mangle -F
|
|
19
|
+
iptables -t mangle -X
|
|
20
|
+
ipset destroy allowed-domains 2>/dev/null || true
|
|
21
|
+
|
|
22
|
+
# 2. Selectively restore ONLY internal Docker DNS resolution
|
|
23
|
+
if [ -n "$DOCKER_DNS_RULES" ]; then
|
|
24
|
+
echo "Restoring Docker DNS rules..."
|
|
25
|
+
iptables -t nat -N DOCKER_OUTPUT 2>/dev/null || true
|
|
26
|
+
iptables -t nat -N DOCKER_POSTROUTING 2>/dev/null || true
|
|
27
|
+
echo "$DOCKER_DNS_RULES" | xargs -L 1 iptables -t nat
|
|
28
|
+
else
|
|
29
|
+
echo "No Docker DNS rules to restore"
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# First allow DNS and localhost before any restrictions
|
|
33
|
+
# Allow outbound DNS
|
|
34
|
+
iptables -A OUTPUT -p udp --dport 53 -j ACCEPT
|
|
35
|
+
# Allow inbound DNS responses
|
|
36
|
+
iptables -A INPUT -p udp --sport 53 -j ACCEPT
|
|
37
|
+
# Allow outbound SSH
|
|
38
|
+
iptables -A OUTPUT -p tcp --dport 22 -j ACCEPT
|
|
39
|
+
# Allow inbound SSH responses
|
|
40
|
+
iptables -A INPUT -p tcp --sport 22 -m state --state ESTABLISHED -j ACCEPT
|
|
41
|
+
# Allow localhost
|
|
42
|
+
iptables -A INPUT -i lo -j ACCEPT
|
|
43
|
+
iptables -A OUTPUT -o lo -j ACCEPT
|
|
44
|
+
|
|
45
|
+
# Create ipset with CIDR support
|
|
46
|
+
ipset create allowed-domains hash:net
|
|
47
|
+
|
|
48
|
+
# Fetch GitHub meta information and aggregate + add their IP ranges
|
|
49
|
+
echo "Fetching GitHub IP ranges..."
|
|
50
|
+
gh_ranges=$(curl -s https://api.github.com/meta)
|
|
51
|
+
if [ -z "$gh_ranges" ]; then
|
|
52
|
+
echo "ERROR: Failed to fetch GitHub IP ranges"
|
|
53
|
+
exit 1
|
|
54
|
+
fi
|
|
55
|
+
|
|
56
|
+
if ! echo "$gh_ranges" | jq -e '.web and .api and .git' >/dev/null; then
|
|
57
|
+
echo "ERROR: GitHub API response missing required fields"
|
|
58
|
+
exit 1
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
echo "Processing GitHub IPs..."
|
|
62
|
+
while read -r cidr; do
|
|
63
|
+
if [[ ! "$cidr" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}/[0-9]{1,2}$ ]]; then
|
|
64
|
+
echo "ERROR: Invalid CIDR range from GitHub meta: $cidr"
|
|
65
|
+
exit 1
|
|
66
|
+
fi
|
|
67
|
+
echo "Adding GitHub range $cidr"
|
|
68
|
+
ipset add allowed-domains "$cidr"
|
|
69
|
+
done < <(echo "$gh_ranges" | jq -r '(.web + .api + .git)[]' | aggregate -q)
|
|
70
|
+
|
|
71
|
+
# Resolve and add other allowed domains
|
|
72
|
+
for domain in \
|
|
73
|
+
"registry.npmjs.org" \
|
|
74
|
+
"api.anthropic.com" \
|
|
75
|
+
"sentry.io" \
|
|
76
|
+
"statsig.anthropic.com" \
|
|
77
|
+
"statsig.com" \
|
|
78
|
+
"marketplace.visualstudio.com" \
|
|
79
|
+
"vscode.blob.core.windows.net" \
|
|
80
|
+
"update.code.visualstudio.com"; do
|
|
81
|
+
echo "Resolving $domain..."
|
|
82
|
+
ips=$(dig +noall +answer A "$domain" | awk '$4 == "A" {print $5}')
|
|
83
|
+
if [ -z "$ips" ]; then
|
|
84
|
+
echo "ERROR: Failed to resolve $domain"
|
|
85
|
+
exit 1
|
|
86
|
+
fi
|
|
87
|
+
|
|
88
|
+
while read -r ip; do
|
|
89
|
+
if [[ ! "$ip" =~ ^[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$ ]]; then
|
|
90
|
+
echo "ERROR: Invalid IP from DNS for $domain: $ip"
|
|
91
|
+
exit 1
|
|
92
|
+
fi
|
|
93
|
+
echo "Adding $ip for $domain"
|
|
94
|
+
ipset add allowed-domains "$ip"
|
|
95
|
+
done < <(echo "$ips")
|
|
96
|
+
done
|
|
97
|
+
|
|
98
|
+
# Get host IP from default route
|
|
99
|
+
HOST_IP=$(ip route | grep default | cut -d" " -f3)
|
|
100
|
+
if [ -z "$HOST_IP" ]; then
|
|
101
|
+
echo "ERROR: Failed to detect host IP"
|
|
102
|
+
exit 1
|
|
103
|
+
fi
|
|
104
|
+
|
|
105
|
+
HOST_NETWORK=$(echo "$HOST_IP" | sed "s/\.[0-9]*$/.0\/24/")
|
|
106
|
+
echo "Host network detected as: $HOST_NETWORK"
|
|
107
|
+
|
|
108
|
+
# Set up remaining iptables rules
|
|
109
|
+
iptables -A INPUT -s "$HOST_NETWORK" -j ACCEPT
|
|
110
|
+
iptables -A OUTPUT -d "$HOST_NETWORK" -j ACCEPT
|
|
111
|
+
|
|
112
|
+
# Set default policies to DROP first
|
|
113
|
+
iptables -P INPUT DROP
|
|
114
|
+
iptables -P FORWARD DROP
|
|
115
|
+
iptables -P OUTPUT DROP
|
|
116
|
+
|
|
117
|
+
# First allow established connections for already approved traffic
|
|
118
|
+
iptables -A INPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
119
|
+
iptables -A OUTPUT -m state --state ESTABLISHED,RELATED -j ACCEPT
|
|
120
|
+
|
|
121
|
+
# Then allow only specific outbound traffic to allowed domains
|
|
122
|
+
iptables -A OUTPUT -m set --match-set allowed-domains dst -j ACCEPT
|
|
123
|
+
|
|
124
|
+
# Explicitly REJECT all other outbound traffic for immediate feedback
|
|
125
|
+
iptables -A OUTPUT -j REJECT --reject-with icmp-admin-prohibited
|
|
126
|
+
|
|
127
|
+
echo "Firewall configuration complete"
|
|
128
|
+
echo "Verifying firewall rules..."
|
|
129
|
+
if curl --connect-timeout 5 https://example.com >/dev/null 2>&1; then
|
|
130
|
+
echo "ERROR: Firewall verification failed - was able to reach https://example.com"
|
|
131
|
+
exit 1
|
|
132
|
+
else
|
|
133
|
+
echo "Firewall verification passed - unable to reach https://example.com as expected"
|
|
134
|
+
fi
|
|
135
|
+
|
|
136
|
+
# Verify GitHub API access
|
|
137
|
+
if ! curl --connect-timeout 5 https://api.github.com/zen >/dev/null 2>&1; then
|
|
138
|
+
echo "ERROR: Firewall verification failed - unable to reach https://api.github.com"
|
|
139
|
+
exit 1
|
|
140
|
+
else
|
|
141
|
+
echo "Firewall verification passed - able to reach https://api.github.com as expected"
|
|
142
|
+
fi
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Firewall rules summary
|
|
146
|
+
|
|
147
|
+
| Rule | Direction | Purpose |
|
|
148
|
+
|------|-----------|---------|
|
|
149
|
+
| DNS (UDP 53) | Outbound | Domain name resolution |
|
|
150
|
+
| SSH (TCP 22) | Outbound | Git over SSH |
|
|
151
|
+
| Localhost | Both | Container-internal communication |
|
|
152
|
+
| Host network | Both | Docker host ↔ container communication |
|
|
153
|
+
| GitHub IPs | Outbound | Git operations, GitHub API (dynamically fetched from api.github.com/meta) |
|
|
154
|
+
| npm registry | Outbound | Package installation |
|
|
155
|
+
| api.anthropic.com | Outbound | Claude API calls |
|
|
156
|
+
| sentry.io | Outbound | Error reporting |
|
|
157
|
+
| statsig.anthropic.com | Outbound | Feature flags |
|
|
158
|
+
| VS Code marketplace | Outbound | Extension downloads |
|
|
159
|
+
| **Everything else** | **BLOCKED** | Default-deny with REJECT |
|
|
160
|
+
|
|
161
|
+
## Verification on startup
|
|
162
|
+
|
|
163
|
+
The script verifies the firewall by:
|
|
164
|
+
1. Confirming `https://example.com` is **blocked** (should fail)
|
|
165
|
+
2. Confirming `https://api.github.com` is **reachable** (should succeed)
|
|
166
|
+
|
|
167
|
+
If either check fails, the script exits with an error and the container will not start.
|