slashdev 1.0.0 → 1.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.md +5 -4
- package/GETTING-STARTED.md +68 -0
- package/claude-skills/secure-deploy/SKILL.md +219 -0
- package/hooks/pre-install-guard.sh +88 -0
- package/package.json +1 -1
- package/src/commands/setup-internals.js +84 -0
- package/src/commands/setup.js +8 -2
- package/src/commands/update.js +5 -1
package/CLAUDE.md
CHANGED
|
@@ -45,11 +45,12 @@ The skill runner loads the system prompt, gathers project context via `codebase.
|
|
|
45
45
|
**Auth (`src/auth/`):** Interactive login with API key validation, stored separately in credentials config.
|
|
46
46
|
|
|
47
47
|
**Claude Code integration (`src/commands/` + `hooks/` + `claude-skills/`):**
|
|
48
|
-
- `src/commands/setup.js` — Prompts for `@slashdev.io` email, writes `~/.slashdev/config.json`, copies hooks to `~/.claude/hooks/`, copies skills to `~/.claude/skills/`, registers Stop
|
|
49
|
-
- `src/commands/update.js` — Re-copies hooks/skills without re-prompting for email, updates packageVersion in config
|
|
50
|
-
- `src/commands/setup-internals.js` — Shared `installHooks()` and `
|
|
48
|
+
- `src/commands/setup.js` — Prompts for `@slashdev.io` email, writes `~/.slashdev/config.json`, copies hooks to `~/.claude/hooks/`, copies skills to `~/.claude/skills/`, registers Stop + PreToolUse hooks in `~/.claude/settings.json`, writes Slashdev rules section to `~/.claude/CLAUDE.md`
|
|
49
|
+
- `src/commands/update.js` — Re-copies hooks/skills without re-prompting for email, updates packageVersion in config, refreshes the Slashdev rules section in `~/.claude/CLAUDE.md`
|
|
50
|
+
- `src/commands/setup-internals.js` — Shared `installHooks()`, `installSkills()`, `installGstack()`, and `writeGlobalClaudeMd()` used by both setup and update. The global CLAUDE.md writer is idempotent — it manages a single section bracketed by `<!-- SLASHDEV:RULES:START -->` / `<!-- SLASHDEV:RULES:END -->` markers, leaving any other user content untouched.
|
|
51
51
|
- `hooks/post-response.sh` — Claude Code Stop hook that reads JSON from stdin, extracts session/prompt/response, POSTs to Slashdev logging API (fire-and-forget, async curl)
|
|
52
|
-
- `
|
|
52
|
+
- `hooks/pre-install-guard.sh` — Claude Code PreToolUse hook for the Bash tool. Reads tool-call JSON from stdin, detects bare dependency install commands (`npm/pnpm/yarn/bun install`, `composer install`, `pip install`, etc.), and returns `{"permissionDecision":"deny","message":"..."}` to force a retry with the `sfw` (Socket Firewall) prefix. Strips leading `cd <dir> &&` and env-var prefixes before matching. Fails open on parse errors so it can never break Claude Code. Smoke tests: `echo '{"tool_name":"Bash","tool_input":{"command":"npm install lodash"}}' | bash hooks/pre-install-guard.sh`.
|
|
53
|
+
- `claude-skills/` — Claude Code skill files (SKILL.md with YAML frontmatter) copied to `~/.claude/skills/` during setup. Current skills: agentswarm, code-review, pr-description, scope-estimate, bug-diagnosis, frontend-design, secure-deploy. The `secure-deploy` skill walks Claude through retrofitting CI/CD configs (GitHub Actions, Dockerfiles, GitLab CI, deploy scripts, package.json) to wrap dependency installs with `sfw`.
|
|
53
54
|
- `bin/check-setup.js` — Postinstall script that prints setup reminder if `~/.slashdev/config.json` is missing
|
|
54
55
|
|
|
55
56
|
**Connections system (`src/connections/`):**
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
# Slashdev CLI — Getting Started
|
|
2
|
+
|
|
3
|
+
## Install
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm install -g slashdev
|
|
7
|
+
slashdev setup
|
|
8
|
+
```
|
|
9
|
+
|
|
10
|
+
You'll be prompted for your `@slashdev.io` email. After setup, all skills are immediately available in Claude Code.
|
|
11
|
+
|
|
12
|
+
## The Big One: Agentswarm
|
|
13
|
+
|
|
14
|
+
In any project, open Claude Code and run:
|
|
15
|
+
|
|
16
|
+
```
|
|
17
|
+
/agentswarm "describe your task here"
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
This launches a full agent team that automatically:
|
|
21
|
+
1. **Analyzes** your codebase and detects the stack
|
|
22
|
+
2. **Plans** the implementation with test coverage
|
|
23
|
+
3. **Implements** the code (backend + frontend in parallel)
|
|
24
|
+
4. **Reviews** code quality, security, and performance
|
|
25
|
+
5. **QA tests** — verifies test quality and audits API request counts per page
|
|
26
|
+
|
|
27
|
+
It works with Laravel, React, Flutter, and whatever else is in the project.
|
|
28
|
+
|
|
29
|
+
## Other Slashdev Skills
|
|
30
|
+
|
|
31
|
+
| Command | What it does |
|
|
32
|
+
|---------|-------------|
|
|
33
|
+
| `/frontend-design` | Distinctive UI design — avoids generic AI aesthetics |
|
|
34
|
+
| `/code-review` | Security, performance, and maintainability review |
|
|
35
|
+
| `/pr-description` | Generate PR descriptions from your diff |
|
|
36
|
+
| `/scope-estimate` | Effort estimation for tasks |
|
|
37
|
+
| `/bug-diagnosis` | Systematic root-cause analysis |
|
|
38
|
+
|
|
39
|
+
## gstack Skills (Bundled)
|
|
40
|
+
|
|
41
|
+
We've bundled [gstack](https://github.com/garrytan/gstack) — 29 additional skills from Garry Tan. Highlights:
|
|
42
|
+
|
|
43
|
+
| Command | What it does |
|
|
44
|
+
|---------|-------------|
|
|
45
|
+
| `/plan-ceo-review` | CEO-level plan review — challenges premises, finds the 10-star product |
|
|
46
|
+
| `/plan-eng-review` | Eng manager review — locks in architecture and edge cases |
|
|
47
|
+
| `/qa` | Opens a real browser, finds bugs, fixes them, re-verifies |
|
|
48
|
+
| `/ship` | Automates testing, review, version bump, and PR creation |
|
|
49
|
+
| `/review` | Pre-landing PR review for production bugs |
|
|
50
|
+
| `/cso` | Security audit with OWASP Top 10 and secrets archaeology |
|
|
51
|
+
| `/investigate` | Systematic debugging — no fixes without root cause |
|
|
52
|
+
| `/design-consultation` | Builds a complete design system from scratch |
|
|
53
|
+
| `/retro` | Weekly engineering retrospective with metrics |
|
|
54
|
+
| `/careful` | Warns before destructive commands |
|
|
55
|
+
|
|
56
|
+
All gstack skills are available immediately after `slashdev setup`.
|
|
57
|
+
|
|
58
|
+
## Heads Up: Prompt Logging
|
|
59
|
+
|
|
60
|
+
All prompts and responses in Claude Code are logged to our API so we can run analytics and help improve the team's prompting. This happens automatically via a background hook — no action needed on your part.
|
|
61
|
+
|
|
62
|
+
## Updating
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
slashdev update
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This pulls the latest skills and hooks. If you run into issues, `slashdev setup` will re-initialize everything.
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: secure-deploy
|
|
3
|
+
description: Retrofit a project's CI/CD pipelines, Dockerfiles, and deploy scripts to use Socket Firewall (sfw) for all dependency installs. Use when the user asks to "secure deploy", "set up sfw in CI", "add socket firewall to production", or whenever you're editing a CI workflow / Dockerfile / deploy script that installs dependencies.
|
|
4
|
+
version: "1.0"
|
|
5
|
+
owner: michael@slashdev.io
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Secure Deploy — Retrofit projects to use Socket Firewall in production
|
|
9
|
+
|
|
10
|
+
Slashdev requires every dependency install — local AND production — to be wrapped
|
|
11
|
+
with Socket Firewall (`sfw`). This protects against supply-chain attacks
|
|
12
|
+
(compromised packages, typosquats, dependency confusion) by scanning every package
|
|
13
|
+
against Socket's vulnerability/malware database before it's allowed to install.
|
|
14
|
+
|
|
15
|
+
When a developer asks you to set up production deployment, edit a CI pipeline, write
|
|
16
|
+
a Dockerfile, or deploy a project, **you must ensure every install command uses `sfw`**.
|
|
17
|
+
|
|
18
|
+
This skill walks you through the retrofit systematically.
|
|
19
|
+
|
|
20
|
+
## When to use this skill
|
|
21
|
+
|
|
22
|
+
Trigger automatically (without being asked) whenever you're about to:
|
|
23
|
+
- Create or edit a `.github/workflows/*.yml` file with an `npm`/`pnpm`/`yarn`/`bun`/`composer`/`pip` install step
|
|
24
|
+
- Create or edit a `Dockerfile` that runs an install command
|
|
25
|
+
- Create or edit `.gitlab-ci.yml`, `bitbucket-pipelines.yml`, `circleci/config.yml`, or any other CI config
|
|
26
|
+
- Create or edit a `deploy.sh`, `Procfile`, or `Makefile` install target
|
|
27
|
+
- Modify the `scripts` section of `package.json` to add an install-related step
|
|
28
|
+
|
|
29
|
+
Trigger explicitly when the user says: "secure deploy", "add sfw to CI", "set up socket firewall in production", "retrofit production for socket firewall", "audit deploy for sfw".
|
|
30
|
+
|
|
31
|
+
## Step-by-step retrofit playbook
|
|
32
|
+
|
|
33
|
+
### Step 1 — Discover deployment surface
|
|
34
|
+
|
|
35
|
+
Use Glob to find every file that could install dependencies in CI or production:
|
|
36
|
+
|
|
37
|
+
```
|
|
38
|
+
.github/workflows/*.yml
|
|
39
|
+
.github/workflows/*.yaml
|
|
40
|
+
.gitlab-ci.yml
|
|
41
|
+
.gitlab/*.yml
|
|
42
|
+
bitbucket-pipelines.yml
|
|
43
|
+
.circleci/config.yml
|
|
44
|
+
azure-pipelines.yml
|
|
45
|
+
Jenkinsfile
|
|
46
|
+
Dockerfile
|
|
47
|
+
Dockerfile.*
|
|
48
|
+
*.dockerfile
|
|
49
|
+
docker-compose*.yml
|
|
50
|
+
Procfile
|
|
51
|
+
deploy.sh
|
|
52
|
+
deploy/*.sh
|
|
53
|
+
scripts/deploy*.sh
|
|
54
|
+
Makefile
|
|
55
|
+
package.json
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
For each file found, Read it and look for any of these patterns:
|
|
59
|
+
|
|
60
|
+
- `npm install`, `npm i`, `npm ci`, `npm add`
|
|
61
|
+
- `pnpm install`, `pnpm i`, `pnpm add`
|
|
62
|
+
- `yarn`, `yarn install`, `yarn add`
|
|
63
|
+
- `bun install`, `bun i`, `bun add`
|
|
64
|
+
- `composer install`, `composer require`, `composer update`
|
|
65
|
+
- `pip install`, `pip3 install`, `uv install`, `uv sync`, `uv add`
|
|
66
|
+
|
|
67
|
+
Note any line that already starts with `sfw ` — those are already secured and don't
|
|
68
|
+
need to be touched.
|
|
69
|
+
|
|
70
|
+
### Step 2 — Plan the changes
|
|
71
|
+
|
|
72
|
+
For each install command found, plan one of these rewrites:
|
|
73
|
+
|
|
74
|
+
**a) GitHub Actions workflow YAML**
|
|
75
|
+
|
|
76
|
+
Before:
|
|
77
|
+
```yaml
|
|
78
|
+
- name: Install dependencies
|
|
79
|
+
run: npm ci
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
After:
|
|
83
|
+
```yaml
|
|
84
|
+
- name: Install Socket Firewall
|
|
85
|
+
run: npm install -g @socketsecurity/cli
|
|
86
|
+
- name: Install dependencies
|
|
87
|
+
run: sfw npm ci
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
If a job has multiple install steps, only one `Install Socket Firewall` step is needed
|
|
91
|
+
per job (it persists across steps because they share the same runner).
|
|
92
|
+
|
|
93
|
+
**b) Dockerfile**
|
|
94
|
+
|
|
95
|
+
Before:
|
|
96
|
+
```dockerfile
|
|
97
|
+
RUN npm ci
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
After:
|
|
101
|
+
```dockerfile
|
|
102
|
+
RUN npm install -g @socketsecurity/cli && sfw npm ci
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
For Composer:
|
|
106
|
+
```dockerfile
|
|
107
|
+
RUN npm install -g @socketsecurity/cli && sfw composer install --no-dev --optimize-autoloader
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
(npm is the simplest install vector for sfw itself; if the base image doesn't have
|
|
111
|
+
Node, install Node first or use the official Socket installer.)
|
|
112
|
+
|
|
113
|
+
**c) Shell deploy scripts (`deploy.sh`, etc.)**
|
|
114
|
+
|
|
115
|
+
Before:
|
|
116
|
+
```bash
|
|
117
|
+
npm ci --omit=dev
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
After:
|
|
121
|
+
```bash
|
|
122
|
+
# Ensure sfw is available
|
|
123
|
+
if ! command -v sfw >/dev/null 2>&1; then
|
|
124
|
+
npm install -g @socketsecurity/cli
|
|
125
|
+
fi
|
|
126
|
+
sfw npm ci --omit=dev
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
**d) `package.json` scripts**
|
|
130
|
+
|
|
131
|
+
Before:
|
|
132
|
+
```json
|
|
133
|
+
{
|
|
134
|
+
"scripts": {
|
|
135
|
+
"postinstall": "npm install --prefix ./client"
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
After:
|
|
141
|
+
```json
|
|
142
|
+
{
|
|
143
|
+
"scripts": {
|
|
144
|
+
"postinstall": "sfw npm install --prefix ./client"
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
(Don't add the `npm install -g @socketsecurity/cli` step inside `package.json` — assume
|
|
150
|
+
sfw is already installed wherever this script runs. If it's running in CI, the workflow
|
|
151
|
+
or Dockerfile should have installed sfw at the top.)
|
|
152
|
+
|
|
153
|
+
### Step 3 — Show the diff and confirm
|
|
154
|
+
|
|
155
|
+
Before writing any changes, show the user a clear summary:
|
|
156
|
+
|
|
157
|
+
> Found N install commands across M files. Proposed changes:
|
|
158
|
+
>
|
|
159
|
+
> 1. `.github/workflows/ci.yml` — wrap `npm ci` in build job (+ add sfw install step)
|
|
160
|
+
> 2. `Dockerfile` — wrap `RUN npm ci` with sfw install
|
|
161
|
+
> 3. `package.json` — wrap postinstall script
|
|
162
|
+
>
|
|
163
|
+
> Apply these changes?
|
|
164
|
+
|
|
165
|
+
Use AskUserQuestion if there's any ambiguity (e.g. multiple jobs in a workflow, unclear
|
|
166
|
+
whether a step is part of the build or local-only). For Dockerfiles especially, ask
|
|
167
|
+
about the base image — if it doesn't have Node, the sfw install command needs to be
|
|
168
|
+
different.
|
|
169
|
+
|
|
170
|
+
### Step 4 — Apply the changes
|
|
171
|
+
|
|
172
|
+
Use Edit to apply each change. After applying:
|
|
173
|
+
|
|
174
|
+
1. **Validate YAML/JSON syntax** — for any modified `.yml` or `.json` file, run a quick
|
|
175
|
+
parse check (e.g. `python3 -c 'import yaml,sys; yaml.safe_load(open("path"))'` for
|
|
176
|
+
YAML, or `python3 -m json.tool path` for JSON). Fix any syntax errors.
|
|
177
|
+
2. **Run a build smoke test if possible** — for Dockerfiles, suggest the user run
|
|
178
|
+
`docker build .` locally. For GitHub Actions, suggest pushing to a feature branch
|
|
179
|
+
and watching the workflow run.
|
|
180
|
+
3. **Document what was changed** — print a final summary listing every file modified
|
|
181
|
+
and what was added.
|
|
182
|
+
|
|
183
|
+
### Step 5 — Verify CI doesn't already use a different security tool
|
|
184
|
+
|
|
185
|
+
If during Step 1 you find references to other security scanners (Snyk, Dependabot,
|
|
186
|
+
Trivy, npm audit, etc.), don't remove them — they complement Socket Firewall, they
|
|
187
|
+
don't replace it. Mention to the user: "I found existing security scanning via X. I
|
|
188
|
+
left it in place — Socket Firewall complements it by blocking malicious packages
|
|
189
|
+
*before* they install, while X scans the installed tree."
|
|
190
|
+
|
|
191
|
+
## Important conventions
|
|
192
|
+
|
|
193
|
+
- **Never silently change install commands.** Always show the diff and get
|
|
194
|
+
confirmation before writing CI/deploy files. These are production-critical files.
|
|
195
|
+
- **Don't add sfw to dev-only `node_modules` install steps in `package.json`** unless
|
|
196
|
+
the user explicitly asks. The PreToolUse hook handles dev-side enforcement
|
|
197
|
+
automatically; only retrofit `package.json` scripts that run in CI/deploy contexts.
|
|
198
|
+
- **Don't try to install Socket Firewall globally on the user's machine** as part of
|
|
199
|
+
this skill. That's the developer's responsibility, and the slashdev hook will warn
|
|
200
|
+
them if it's missing. This skill only modifies project files.
|
|
201
|
+
- **Don't touch `.lock` files** (`package-lock.json`, `pnpm-lock.yaml`, `yarn.lock`,
|
|
202
|
+
`composer.lock`, `bun.lockb`). They're generated.
|
|
203
|
+
- **Preserve existing flags** — when wrapping `npm ci --omit=dev` with `sfw`, keep
|
|
204
|
+
the `--omit=dev` flag.
|
|
205
|
+
|
|
206
|
+
## Output format
|
|
207
|
+
|
|
208
|
+
Always end the skill with:
|
|
209
|
+
|
|
210
|
+
```
|
|
211
|
+
✓ Secured N install commands across M files:
|
|
212
|
+
- <file>: <change summary>
|
|
213
|
+
- <file>: <change summary>
|
|
214
|
+
|
|
215
|
+
Next steps:
|
|
216
|
+
- Test locally (if applicable)
|
|
217
|
+
- Push to a feature branch and watch CI
|
|
218
|
+
- Confirm sfw is available in your CI environment (the install step is now in place)
|
|
219
|
+
```
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# pre-install-guard.sh — PreToolUse hook for Bash tool calls.
|
|
3
|
+
#
|
|
4
|
+
# Blocks bare dependency install commands and forces them to be re-run with
|
|
5
|
+
# Socket Firewall (`sfw`) prefix to protect against supply-chain attacks.
|
|
6
|
+
#
|
|
7
|
+
# Reads tool-call JSON from stdin, returns:
|
|
8
|
+
# {} → allow
|
|
9
|
+
# {"permissionDecision":"deny","message":"..."} → block
|
|
10
|
+
#
|
|
11
|
+
# Patterns matched (anchored at the start of the actual command, after any
|
|
12
|
+
# leading `cd ... &&` prefix):
|
|
13
|
+
# - npm/pnpm/yarn/bun (install|i|ci|add|update|upgrade)
|
|
14
|
+
# - composer (install|require|update)
|
|
15
|
+
# - pip/pip3/uv (install|sync)
|
|
16
|
+
set -euo pipefail
|
|
17
|
+
|
|
18
|
+
INPUT=$(cat)
|
|
19
|
+
|
|
20
|
+
# Extract tool_input.command from the JSON. Try grep/sed first for speed,
|
|
21
|
+
# fall back to python3 for properly-escaped JSON.
|
|
22
|
+
COMMAND=$(printf '%s' "$INPUT" \
|
|
23
|
+
| grep -o '"command"[[:space:]]*:[[:space:]]*"[^"]*"' \
|
|
24
|
+
| head -1 \
|
|
25
|
+
| sed 's/.*:[[:space:]]*"//;s/"$//' \
|
|
26
|
+
|| true)
|
|
27
|
+
|
|
28
|
+
if [ -z "$COMMAND" ]; then
|
|
29
|
+
COMMAND=$(printf '%s' "$INPUT" \
|
|
30
|
+
| python3 -c 'import sys,json; print(json.loads(sys.stdin.read()).get("tool_input",{}).get("command",""))' \
|
|
31
|
+
2>/dev/null || true)
|
|
32
|
+
fi
|
|
33
|
+
|
|
34
|
+
# No command extracted → allow (don't block on parse failure).
|
|
35
|
+
if [ -z "$COMMAND" ]; then
|
|
36
|
+
echo '{}'
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Strip leading whitespace.
|
|
41
|
+
TRIMMED=$(printf '%s' "$COMMAND" | sed 's/^[[:space:]]*//')
|
|
42
|
+
|
|
43
|
+
# Strip a leading `cd <dir> && ` prefix (or `cd <dir>; `) so we examine the
|
|
44
|
+
# actual command being run, not the directory change.
|
|
45
|
+
ACTUAL_CMD="$TRIMMED"
|
|
46
|
+
while printf '%s' "$ACTUAL_CMD" | grep -qE '^cd[[:space:]]+[^&;]+[[:space:]]*(&&|;)[[:space:]]*'; do
|
|
47
|
+
ACTUAL_CMD=$(printf '%s' "$ACTUAL_CMD" \
|
|
48
|
+
| sed -E 's/^cd[[:space:]]+[^&;]+[[:space:]]*(&&|;)[[:space:]]*//')
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
# Strip leading env-var assignments (e.g. `NODE_ENV=production npm ci`).
|
|
52
|
+
while printf '%s' "$ACTUAL_CMD" | grep -qE '^[A-Z_][A-Z0-9_]*=[^[:space:]]+[[:space:]]+'; do
|
|
53
|
+
ACTUAL_CMD=$(printf '%s' "$ACTUAL_CMD" \
|
|
54
|
+
| sed -E 's/^[A-Z_][A-Z0-9_]*=[^[:space:]]+[[:space:]]+//')
|
|
55
|
+
done
|
|
56
|
+
|
|
57
|
+
# Already wrapped with sfw → allow immediately.
|
|
58
|
+
if printf '%s' "$ACTUAL_CMD" | grep -qE '^sfw([[:space:]]|$)'; then
|
|
59
|
+
echo '{}'
|
|
60
|
+
exit 0
|
|
61
|
+
fi
|
|
62
|
+
|
|
63
|
+
# Detect install commands. Patterns are conservative — must be the first token,
|
|
64
|
+
# followed by an install-style subcommand.
|
|
65
|
+
IS_INSTALL=0
|
|
66
|
+
case "$ACTUAL_CMD" in
|
|
67
|
+
npm[[:space:]]install*|npm[[:space:]]i[[:space:]]*|"npm i"|npm[[:space:]]ci*|npm[[:space:]]add*|npm[[:space:]]update*|npm[[:space:]]upgrade*) IS_INSTALL=1 ;;
|
|
68
|
+
pnpm[[:space:]]install*|pnpm[[:space:]]i[[:space:]]*|"pnpm i"|pnpm[[:space:]]add*|pnpm[[:space:]]update*|pnpm[[:space:]]upgrade*) IS_INSTALL=1 ;;
|
|
69
|
+
yarn[[:space:]]install*|yarn[[:space:]]add*|yarn[[:space:]]upgrade*|"yarn") IS_INSTALL=1 ;;
|
|
70
|
+
bun[[:space:]]install*|bun[[:space:]]i[[:space:]]*|"bun i"|bun[[:space:]]add*|bun[[:space:]]update*) IS_INSTALL=1 ;;
|
|
71
|
+
composer[[:space:]]install*|composer[[:space:]]require*|composer[[:space:]]update*|composer[[:space:]]upgrade*) IS_INSTALL=1 ;;
|
|
72
|
+
pip[[:space:]]install*|pip3[[:space:]]install*|uv[[:space:]]install*|uv[[:space:]]sync*|uv[[:space:]]add*) IS_INSTALL=1 ;;
|
|
73
|
+
esac
|
|
74
|
+
|
|
75
|
+
if [ "$IS_INSTALL" -eq 0 ]; then
|
|
76
|
+
echo '{}'
|
|
77
|
+
exit 0
|
|
78
|
+
fi
|
|
79
|
+
|
|
80
|
+
# It's an install command without sfw — block it.
|
|
81
|
+
# Escape the original command for safe inclusion in JSON.
|
|
82
|
+
ESCAPED_CMD=$(printf '%s' "$ACTUAL_CMD" | python3 -c 'import sys,json; print(json.dumps(sys.stdin.read())[1:-1])' 2>/dev/null || printf '%s' "$ACTUAL_CMD" | sed 's/\\/\\\\/g; s/"/\\"/g')
|
|
83
|
+
|
|
84
|
+
if command -v sfw >/dev/null 2>&1; then
|
|
85
|
+
printf '{"permissionDecision":"deny","message":"[slashdev] Bare install command blocked. Re-run with the sfw prefix:\\n\\n sfw %s\\n\\nSocket Firewall scans every package against Socket'"'"'s vulnerability/malware database before installing it. This protects against supply-chain attacks."}\n' "$ESCAPED_CMD"
|
|
86
|
+
else
|
|
87
|
+
printf '{"permissionDecision":"deny","message":"[slashdev] Socket Firewall (sfw) is required for dependency installs but is not installed on this machine. Install it once:\\n\\n npm install -g @socketsecurity/cli\\n\\nThen retry the install with:\\n\\n sfw %s"}\n' "$ESCAPED_CMD"
|
|
88
|
+
fi
|
package/package.json
CHANGED
|
@@ -20,6 +20,38 @@ const CLAUDE_DIR = join(homedir(), '.claude');
|
|
|
20
20
|
const CLAUDE_HOOKS_DIR = join(CLAUDE_DIR, 'hooks');
|
|
21
21
|
const CLAUDE_SKILLS_DIR = join(CLAUDE_DIR, 'skills');
|
|
22
22
|
const CLAUDE_SETTINGS_FILE = join(CLAUDE_DIR, 'settings.json');
|
|
23
|
+
const CLAUDE_MEMORY_FILE = join(CLAUDE_DIR, 'CLAUDE.md');
|
|
24
|
+
|
|
25
|
+
const SLASHDEV_SECTION_START = '<!-- SLASHDEV:RULES:START -->';
|
|
26
|
+
const SLASHDEV_SECTION_END = '<!-- SLASHDEV:RULES:END -->';
|
|
27
|
+
|
|
28
|
+
const SLASHDEV_RULES_BLOCK = `${SLASHDEV_SECTION_START}
|
|
29
|
+
# Slashdev workspace rules
|
|
30
|
+
|
|
31
|
+
## Security: always use Socket Firewall for dependency installs
|
|
32
|
+
|
|
33
|
+
Claude must use Socket Firewall (\`sfw\`) for **every** dependency install command.
|
|
34
|
+
This applies to:
|
|
35
|
+
|
|
36
|
+
- **Local development** — never run \`npm install\`, \`pnpm install\`, \`yarn add\`,
|
|
37
|
+
\`bun install\`, \`composer install\`, \`pip install\`, or any equivalent without
|
|
38
|
+
prefixing \`sfw \`. A PreToolUse hook will block bare install commands and tell
|
|
39
|
+
you to retry with \`sfw\`.
|
|
40
|
+
- **Production deployment** — when generating or editing CI/CD config (GitHub Actions,
|
|
41
|
+
GitLab CI, CircleCI, Jenkins), Dockerfiles, deploy scripts (\`deploy.sh\`, \`Procfile\`),
|
|
42
|
+
or \`package.json\` scripts that install dependencies, **always** wrap the install
|
|
43
|
+
step with \`sfw\`. Examples:
|
|
44
|
+
- \`sfw npm ci\` instead of \`npm ci\`
|
|
45
|
+
- \`sfw composer install --no-dev\` instead of \`composer install --no-dev\`
|
|
46
|
+
- \`RUN npm install -g @socketsecurity/cli && sfw npm ci\` in a Dockerfile
|
|
47
|
+
- If \`sfw\` is not yet installed in a CI environment, install it first:
|
|
48
|
+
\`npm install -g @socketsecurity/cli\`. Document the install step inline in the workflow.
|
|
49
|
+
- Use the \`/secure-deploy\` skill to retrofit existing CI/CD configs.
|
|
50
|
+
|
|
51
|
+
Reason: Socket Firewall scans every package against Socket's vulnerability/malware
|
|
52
|
+
database before letting it install. This protects against supply-chain attacks
|
|
53
|
+
(compromised packages, typosquats, dependency confusion).
|
|
54
|
+
${SLASHDEV_SECTION_END}`;
|
|
23
55
|
|
|
24
56
|
function ensureDir(dir) {
|
|
25
57
|
if (!existsSync(dir)) {
|
|
@@ -87,12 +119,64 @@ export function installHooks() {
|
|
|
87
119
|
settings.hooks.Stop = stopHooks;
|
|
88
120
|
}
|
|
89
121
|
|
|
122
|
+
// Register PreToolUse hook for the install guard (Bash tool only).
|
|
123
|
+
const guardCommand = join(CLAUDE_HOOKS_DIR, 'pre-install-guard.sh');
|
|
124
|
+
if (existsSync(guardCommand)) {
|
|
125
|
+
const preToolUseHooks = settings.hooks.PreToolUse || [];
|
|
126
|
+
const guardRegistered = preToolUseHooks.some(
|
|
127
|
+
(entry) =>
|
|
128
|
+
entry.matcher === 'Bash' &&
|
|
129
|
+
Array.isArray(entry.hooks) &&
|
|
130
|
+
entry.hooks.some((h) => h.command && h.command.includes('pre-install-guard.sh'))
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
if (!guardRegistered) {
|
|
134
|
+
preToolUseHooks.push({
|
|
135
|
+
matcher: 'Bash',
|
|
136
|
+
hooks: [
|
|
137
|
+
{
|
|
138
|
+
type: 'command',
|
|
139
|
+
command: guardCommand,
|
|
140
|
+
statusMessage: 'Checking install safety…',
|
|
141
|
+
},
|
|
142
|
+
],
|
|
143
|
+
});
|
|
144
|
+
settings.hooks.PreToolUse = preToolUseHooks;
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
90
148
|
writeFileSync(CLAUDE_SETTINGS_FILE, JSON.stringify(settings, null, 2), 'utf-8');
|
|
91
149
|
|
|
92
150
|
const hookFiles = readdirSync(hooksSource).filter((f) => f.endsWith('.sh'));
|
|
93
151
|
return { installed: hookFiles.length };
|
|
94
152
|
}
|
|
95
153
|
|
|
154
|
+
export function writeGlobalClaudeMd() {
|
|
155
|
+
ensureDir(CLAUDE_DIR);
|
|
156
|
+
|
|
157
|
+
let existing = '';
|
|
158
|
+
if (existsSync(CLAUDE_MEMORY_FILE)) {
|
|
159
|
+
existing = readFileSync(CLAUDE_MEMORY_FILE, 'utf-8');
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
let next;
|
|
163
|
+
if (existing.includes(SLASHDEV_SECTION_START) && existing.includes(SLASHDEV_SECTION_END)) {
|
|
164
|
+
// Replace the existing managed section in place, preserving everything around it.
|
|
165
|
+
const before = existing.split(SLASHDEV_SECTION_START)[0];
|
|
166
|
+
const after = existing.split(SLASHDEV_SECTION_END)[1];
|
|
167
|
+
next = `${before}${SLASHDEV_RULES_BLOCK}${after}`;
|
|
168
|
+
} else if (existing.trim().length > 0) {
|
|
169
|
+
// Append after existing user content.
|
|
170
|
+
next = `${existing.trimEnd()}\n\n${SLASHDEV_RULES_BLOCK}\n`;
|
|
171
|
+
} else {
|
|
172
|
+
// Brand new file.
|
|
173
|
+
next = `${SLASHDEV_RULES_BLOCK}\n`;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
writeFileSync(CLAUDE_MEMORY_FILE, next, 'utf-8');
|
|
177
|
+
return { written: true, path: CLAUDE_MEMORY_FILE };
|
|
178
|
+
}
|
|
179
|
+
|
|
96
180
|
export function installSkills() {
|
|
97
181
|
const skillsSource = join(PACKAGE_ROOT, 'claude-skills');
|
|
98
182
|
if (!existsSync(skillsSource)) {
|
package/src/commands/setup.js
CHANGED
|
@@ -6,7 +6,7 @@ import { fileURLToPath } from 'url';
|
|
|
6
6
|
import { existsSync, readFileSync, mkdirSync } from 'fs';
|
|
7
7
|
import { homedir } from 'os';
|
|
8
8
|
import { getConfig, setConfig, getSlashdevDir } from '../core/config.js';
|
|
9
|
-
import { installHooks, installSkills, installGstack } from './setup-internals.js';
|
|
9
|
+
import { installHooks, installSkills, installGstack, writeGlobalClaudeMd } from './setup-internals.js';
|
|
10
10
|
|
|
11
11
|
const __filename = fileURLToPath(import.meta.url);
|
|
12
12
|
const __dirname = dirname(__filename);
|
|
@@ -82,6 +82,10 @@ export async function setup() {
|
|
|
82
82
|
spinner.text = 'Installing gstack skills...';
|
|
83
83
|
const gstackResult = installGstack();
|
|
84
84
|
|
|
85
|
+
// Write global Claude rules (Socket Firewall + workspace policy)
|
|
86
|
+
spinner.text = 'Writing global Claude rules...';
|
|
87
|
+
const rulesResult = writeGlobalClaudeMd();
|
|
88
|
+
|
|
85
89
|
spinner.succeed('Slashdev Claude tools installed');
|
|
86
90
|
|
|
87
91
|
console.log('');
|
|
@@ -92,9 +96,11 @@ export async function setup() {
|
|
|
92
96
|
if (gstackResult.warning) {
|
|
93
97
|
console.log(` ${chalk.yellow('!')} ${gstackResult.warning}`);
|
|
94
98
|
}
|
|
99
|
+
console.log(` ${chalk.green('✓')} Rules: ${rulesResult.path}`);
|
|
95
100
|
console.log(` ${chalk.green('✓')} Config: ${join(getSlashdevDir(), 'config.json')}`);
|
|
96
101
|
console.log('');
|
|
97
|
-
console.log(chalk.dim(' Hooks and
|
|
102
|
+
console.log(chalk.dim(' Hooks, skills, and global rules have been installed to ~/.claude/'));
|
|
103
|
+
console.log(chalk.dim(' Socket Firewall enforcement is active for all Bash install commands.'));
|
|
98
104
|
console.log(chalk.dim(' Prompt logs will be sent to the Slashdev API for quality review.\n'));
|
|
99
105
|
} catch (err) {
|
|
100
106
|
spinner.fail('Setup failed');
|
package/src/commands/update.js
CHANGED
|
@@ -27,7 +27,7 @@ export async function update() {
|
|
|
27
27
|
try {
|
|
28
28
|
// Re-import setup internals by calling setup's install functions
|
|
29
29
|
// We import dynamically to avoid circular deps
|
|
30
|
-
const { installHooks, installSkills, installGstack } = await import('./setup-internals.js');
|
|
30
|
+
const { installHooks, installSkills, installGstack, writeGlobalClaudeMd } = await import('./setup-internals.js');
|
|
31
31
|
|
|
32
32
|
const hookResult = installHooks();
|
|
33
33
|
|
|
@@ -37,6 +37,9 @@ export async function update() {
|
|
|
37
37
|
spinner.text = 'Updating gstack skills...';
|
|
38
38
|
const gstackResult = installGstack();
|
|
39
39
|
|
|
40
|
+
spinner.text = 'Updating global Claude rules...';
|
|
41
|
+
const rulesResult = writeGlobalClaudeMd();
|
|
42
|
+
|
|
40
43
|
// Update version in config
|
|
41
44
|
const pkg = JSON.parse(readFileSync(join(PACKAGE_ROOT, 'package.json'), 'utf-8'));
|
|
42
45
|
setConfig('packageVersion', pkg.version);
|
|
@@ -50,6 +53,7 @@ export async function update() {
|
|
|
50
53
|
if (gstackResult.warning) {
|
|
51
54
|
console.log(` ${chalk.yellow('!')} ${gstackResult.warning}`);
|
|
52
55
|
}
|
|
56
|
+
console.log(` ${chalk.green('✓')} Rules: ${rulesResult.path}`);
|
|
53
57
|
console.log(` ${chalk.green('✓')} Config: version set to ${pkg.version}`);
|
|
54
58
|
console.log('');
|
|
55
59
|
} catch (err) {
|