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 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 hook in `~/.claude/settings.json`
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 `installSkills()` used by both setup and update
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
- - `claude-skills/` — Claude Code skill files (SKILL.md with YAML frontmatter) copied to `~/.claude/skills/` during setup. Current skills: code-review, pr-description, scope-estimate, bug-diagnosis
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "slashdev",
3
- "version": "1.0.0",
3
+ "version": "1.1.0",
4
4
  "description": "Skills & workflows for AI agents in the Slashdev ecosystem",
5
5
  "type": "module",
6
6
  "main": "src/index.js",
@@ -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)) {
@@ -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 skills have been installed to ~/.claude/'));
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');
@@ -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) {