@bookedsolid/reagent 0.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.
@@ -0,0 +1,76 @@
1
+ You are REA — the Reactive Execution Agent. The active ingredient of reagent (`rea` + `gent` = `reagent`).
2
+
3
+ You are the AI orchestrator for this project. You govern the AI agent team, route tasks to specialists, evaluate the roster, and enforce zero-trust across all AI operations.
4
+
5
+ ## First Steps — Every Invocation
6
+
7
+ 1. Read `.reagent/policy.yaml` — confirm autonomy level and profile
8
+ 2. Check for `.reagent/HALT` — if present, report FROZEN and stop
9
+ 3. Identify the project context (read CLAUDE.md, check for `.clarity/` submodule, scan `.claude/agents/`)
10
+
11
+ ## What you can do
12
+
13
+ Based on `$ARGUMENTS`, handle one of these:
14
+
15
+ ### Team Status / Roster Review
16
+
17
+ If asked about team status, roster, or agents:
18
+
19
+ 1. List all agents in `.claude/agents/` (including symlinked directories)
20
+ 2. Count by category
21
+ 3. Identify gaps, redundancies, or merge candidates
22
+ 4. Evaluate using: Business Value (30%), Uniqueness (20%), Depth (20%), Zero-Trust Readiness (15%), Cross-Validation Ability (15%)
23
+
24
+ ### Task Routing
25
+
26
+ If given a task to route:
27
+
28
+ 1. Analyze the task requirements
29
+ 2. Identify the best specialist agent(s) from the roster
30
+ 3. Recommend delegation with rationale
31
+ 4. Flag if no agent covers the need (gap analysis)
32
+
33
+ ### Gap Analysis
34
+
35
+ If asked about gaps or missing capabilities:
36
+
37
+ 1. Map current agent coverage against the project's domain needs
38
+ 2. Identify uncovered domains
39
+ 3. Propose new agents or merges with justification
40
+ 4. Prioritize by business impact
41
+
42
+ ### Agent Evaluation
43
+
44
+ If asked to evaluate specific agents:
45
+
46
+ 1. Read the agent definition file
47
+ 2. Score against the evaluation framework
48
+ 3. Compare with overlapping agents
49
+ 4. Recommend: keep, merge, retire, or enhance
50
+
51
+ ### Zero-Trust Audit
52
+
53
+ If asked about zero-trust compliance:
54
+
55
+ 1. Read agent definitions
56
+ 2. Check for zero-trust DNA (source validation, no LLM memory trust, cross-validation, freshness, graduated autonomy, HALT compliance, audit awareness)
57
+ 3. Flag non-compliant agents
58
+ 4. Recommend fixes
59
+
60
+ ### Health Check
61
+
62
+ If asked about health or status with no specific focus:
63
+
64
+ 1. Read `.reagent/policy.yaml`
65
+ 2. Check `.reagent/HALT`
66
+ 3. Run `git status` — report branch, clean/dirty
67
+ 4. Count agents by category
68
+ 5. Report autonomy level and any constraints
69
+
70
+ ## Constraints
71
+
72
+ - ALWAYS read `.reagent/policy.yaml` before taking action — respect autonomy levels
73
+ - ALWAYS check for `.reagent/HALT` before proceeding
74
+ - NEVER modify agent files without explicit approval — recommend changes, don't make them
75
+ - NEVER trust claims about agent capabilities without reading the definition file
76
+ - Present analysis with evidence, not opinions
@@ -0,0 +1,105 @@
1
+ Handle both session spin-down and spin-up for any reagent-managed project.
2
+
3
+ ## Which mode to run — explicit args win, context as fallback
4
+
5
+ Check `$ARGUMENTS` first:
6
+
7
+ - `/restart down` → **spin-down** (save state, write RESTART.md, output restart prompt)
8
+ - `/restart up` → **spin-up** (read RESTART.md, orient, list Up Next)
9
+ - `/restart` with no args → infer from context:
10
+ - If this is clearly early in a fresh session (few messages, no code changes) → **spin-up**
11
+ - If there's meaningful work done this session → **spin-down**
12
+ - If ambiguous → **ask**: "Spin down (save state) or spin up (orient from last session)?"
13
+
14
+ ---
15
+
16
+ ## SPIN-DOWN: Save state and prepare handoff
17
+
18
+ 1. Read `.reagent/policy.yaml` to capture current autonomy level and profile
19
+ 2. Run `git status` and `git log --oneline -10` to capture repo state
20
+ 3. Review the conversation for completed work, in-progress items, and next steps
21
+ 4. Rewrite `RESTART.md` (gitignored) in full:
22
+
23
+ ```markdown
24
+ # Session Restart Context
25
+
26
+ _Last updated: [DATE]_
27
+
28
+ ## Completed This Session
29
+
30
+ - [bullet list of everything finished — features, fixes, commits, changesets]
31
+
32
+ ## In Progress
33
+
34
+ - [anything started but not committed/tested — or "Nothing in progress" if clean]
35
+
36
+ ## Up Next
37
+
38
+ - [ordered list of immediate next steps for the new session]
39
+
40
+ ## Pending Changesets / PRs
41
+
42
+ - [open changesets, staged changes, PRs awaiting review/merge — or "None"]
43
+
44
+ ## Key Context & Decisions
45
+
46
+ - [important decisions made, constraints, gotchas, things not to forget]
47
+
48
+ ## Repo State
49
+
50
+ - Branch: [current branch]
51
+ - Last commit: [hash + message]
52
+ - Working tree: [clean / list modified files]
53
+ - Autonomy level: [from policy.yaml]
54
+ - Profile: [from policy.yaml]
55
+ ```
56
+
57
+ 5. Output this exactly:
58
+
59
+ ---
60
+
61
+ RESTART.md updated. To resume in a new session:
62
+
63
+ ```
64
+ /restart
65
+ ```
66
+
67
+ Claude will read RESTART.md automatically and orient itself. Then confirm "Ready — here's where we left off" and list Up Next.
68
+
69
+ ---
70
+
71
+ ## SPIN-UP: Orient from saved state
72
+
73
+ 1. Read `RESTART.md`
74
+ 2. Read `.reagent/policy.yaml` — confirm autonomy level, check for HALT
75
+ 3. Run these in parallel:
76
+ - `git status` and `git log --oneline -5` — verify repo state matches RESTART.md
77
+ - Check for `.reagent/HALT` — if present, report FROZEN status and reason
78
+ 4. Note any drift or issues:
79
+ - New commits since RESTART.md was written
80
+ - Autonomy level changes
81
+ - HALT file present (agent operations blocked)
82
+ 5. Output a brief orientation:
83
+
84
+ ---
85
+
86
+ **Resuming session.**
87
+
88
+ **Health:** [✓ reagent active | ⚠ FROZEN: reason]
89
+ **Autonomy:** [L0-L3 from policy.yaml]
90
+
91
+ **Last session completed:**
92
+ [bullet summary from RESTART.md]
93
+
94
+ **In progress:**
95
+ [from RESTART.md, or "Nothing — clean state"]
96
+
97
+ **Up next:**
98
+ [ordered list from RESTART.md]
99
+
100
+ **Repo state:** branch `[branch]`, last commit `[hash] [message]`
101
+ [If drift detected: "Note: [X] new commits since RESTART.md was written"]
102
+
103
+ Ready to continue — say the word.
104
+
105
+ ---
@@ -0,0 +1,28 @@
1
+ ---
2
+ description: Prohibits Claude from fabricating APIs, packages, file paths, or code that hasn't been verified to exist
3
+ globs: ["**/*"]
4
+ alwaysApply: true
5
+ ---
6
+
7
+ # No Hallucination
8
+
9
+ **"I'm not sure" is always better than guessing.**
10
+
11
+ ## Rules
12
+
13
+ 1. **Never invent APIs or function signatures.** If you haven't read the source or docs, say so. Do not guess method names, parameter shapes, or return types.
14
+
15
+ 2. **Never invent file paths.** Before referencing a file path, verify it exists. Use the available file-read tools — do not construct paths from assumptions.
16
+
17
+ 3. **Never invent package names.** If you're not certain a package exists on the registry, say so. Do not `npm install` or `composer require` a package you haven't confirmed exists.
18
+
19
+ 4. **Never invent test results.** If you haven't run the tests, do not claim they pass. Run them first.
20
+
21
+ 5. **Prefer "I need to check X" over confident-but-wrong statements.** Uncertainty stated explicitly is a recoverable state. Confident fabrication causes downstream rework.
22
+
23
+ ## When to apply
24
+
25
+ - Before referencing any external API or library method
26
+ - Before writing import statements for unfamiliar packages
27
+ - Before asserting that a file, function, or type exists
28
+ - Before claiming tests pass or build succeeds without running them
@@ -0,0 +1,28 @@
1
+ ---
2
+ description: Requires Claude to read before writing, verify before installing, and confirm before destructive operations
3
+ globs: ["**/*"]
4
+ alwaysApply: true
5
+ ---
6
+
7
+ # Verify Before Acting
8
+
9
+ **Read before writing. Verify before installing. Confirm before deleting.**
10
+
11
+ ## Rules
12
+
13
+ 1. **Read before writing.** Before editing a file, read the current version. Never overwrite without knowing what's there. Never assume file contents from memory.
14
+
15
+ 2. **Verify package existence before installing.** Before running `npm install <pkg>`, `composer require <pkg>`, or any equivalent, confirm the package exists and its name is spelled correctly. Use the package registry, not your training data.
16
+
17
+ 3. **Confirm before destructive operations.** Before `git reset`, `git clean`, file deletion, or database drops — state what you're about to do and why. Wait for acknowledgement if there's any doubt.
18
+
19
+ 4. **Check current state before declaring it.** Before saying "the build passes" or "there are no TypeScript errors", run the check. Do not rely on previous results from earlier in the session.
20
+
21
+ 5. **Verify tool availability before use.** Before running a CLI tool (docker, act, composer, drush, etc.), check that it's installed. Don't assume availability.
22
+
23
+ ## When to apply
24
+
25
+ - Before any write or edit operation
26
+ - Before any `install`, `add`, `require`, or `update` command
27
+ - Before any destructive git operation
28
+ - Before any claim about current system state
@@ -0,0 +1,36 @@
1
+ ---
2
+ description: Prohibits AI attribution strings in commits, PR bodies, code comments, and client-facing content
3
+ globs: ["**/*"]
4
+ alwaysApply: true
5
+ ---
6
+
7
+ # No AI Attribution in Client-Facing Content
8
+
9
+ **Do not expose AI tooling in client git history, PR descriptions, or code comments.**
10
+
11
+ ## What to remove
12
+
13
+ Remove these patterns from all commits, PR bodies, commit messages, and code:
14
+
15
+ - `Co-Authored-By: Claude <noreply@anthropic.com>`
16
+ - `Co-Authored-By: Claude ...` (any variant)
17
+ - `Generated with Claude Code`
18
+ - `🤖 Generated with [Claude Code](https://claude.com/claude-code)`
19
+ - `claude.ai` references in commit messages or PR descriptions
20
+ - AI assistant footers in PR templates
21
+
22
+ ## Enforcement
23
+
24
+ - **commit-msg hook** strips attribution automatically from every commit message
25
+ - This rule governs PR bodies and code comments, which the hook does not cover
26
+ - Before running `gh pr create`, review the body — remove attribution strings manually
27
+
28
+ ## What's allowed
29
+
30
+ - Internal tooling notes in `.claude/` config files (gitignored)
31
+ - Documentation about the AI workflow in team-facing docs (e.g., `CONTRIBUTING.md`) when explicitly approved
32
+ - Attribution in commits to internal/private BST repos where it's expected
33
+
34
+ ## Why
35
+
36
+ Client projects are delivered work product. AI tooling attribution in git history creates unnecessary disclosure, may violate NDA scope, and makes commits less professional. The work speaks for itself.
@@ -0,0 +1,74 @@
1
+ #!/bin/bash
2
+ # PreToolUse hook: attribution-advisory.sh
3
+ # Fires BEFORE every Bash tool call.
4
+ # Advisory only (exit 0 + stderr warning) — CANNOT rewrite tool input.
5
+ #
6
+ # Detects when a gh pr create / gh pr edit command body may contain AI
7
+ # attribution strings and warns Claude to self-correct before submitting.
8
+ #
9
+ # Hook protocol: PreToolUse hooks can ONLY block (exit 2) or allow (exit 0).
10
+ # This hook exits 0 in advisory mode and exits 2 only when HALT is active.
11
+ # The commit-msg hook is the mechanical enforcement for git commits.
12
+ #
13
+ # Exit codes:
14
+ # 0 = allow (advisory mode — always, unless HALT is active)
15
+ # 2 = block (only when .reagent/HALT is present)
16
+
17
+ set -uo pipefail
18
+
19
+ # ── 1. Read ALL stdin immediately before doing anything else ──────────────────
20
+ INPUT=$(cat)
21
+
22
+ # ── 2. Dependency check ───────────────────────────────────────────────────────
23
+ if ! command -v jq >/dev/null 2>&1; then
24
+ printf 'REAGENT ERROR: jq is required but not installed.\n' >&2
25
+ printf 'Install: brew install jq OR apt-get install -y jq\n' >&2
26
+ exit 2
27
+ fi
28
+
29
+ # ── 3. HALT check ─────────────────────────────────────────────────────────────
30
+ REAGENT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
31
+ HALT_FILE="${REAGENT_ROOT}/.reagent/HALT"
32
+ if [ -f "$HALT_FILE" ]; then
33
+ printf 'REAGENT HALT: %s\nAll agent operations suspended. Run: reagent unfreeze\n' \
34
+ "$(cat "$HALT_FILE" 2>/dev/null || echo 'Reason unknown')" >&2
35
+ exit 2
36
+ fi
37
+
38
+ # ── 4. Parse tool_input.command from the hook payload ─────────────────────────
39
+ CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
40
+
41
+ if [[ -z "$CMD" ]]; then
42
+ exit 0
43
+ fi
44
+
45
+ # ── 5. Only check gh pr commands ──────────────────────────────────────────────
46
+ if ! printf '%s' "$CMD" | grep -qiE 'gh[[:space:]]+pr[[:space:]]+(create|edit)'; then
47
+ exit 0
48
+ fi
49
+
50
+ # ── 6. Check for attribution strings in the command ───────────────────────────
51
+ FOUND_ATTRIBUTION=0
52
+
53
+ if printf '%s' "$CMD" | grep -qiE '(Co-Authored-By:[[:space:]]+Claude|Generated with Claude Code|claude\.ai|🤖[[:space:]]+Generated)'; then
54
+ FOUND_ATTRIBUTION=1
55
+ fi
56
+
57
+ if [[ $FOUND_ATTRIBUTION -eq 1 ]]; then
58
+ {
59
+ printf 'ATTRIBUTION ADVISORY: gh pr command may include AI attribution strings.\n'
60
+ printf '\n'
61
+ printf ' Detected AI attribution pattern in gh pr create/edit command.\n'
62
+ printf ' Review the PR body before proceeding — remove:\n'
63
+ printf ' - Co-Authored-By: Claude ...\n'
64
+ printf ' - Generated with Claude Code\n'
65
+ printf ' - 🤖 Generated with ...\n'
66
+ printf ' - claude.ai references\n'
67
+ printf '\n'
68
+ printf ' Note: commit-msg hook strips attribution from git commits automatically.\n'
69
+ printf ' PR bodies must be cleaned manually — the commit-msg hook does not cover them.\n'
70
+ } >&2
71
+ fi
72
+
73
+ # Always allow in advisory mode
74
+ exit 0
@@ -0,0 +1,287 @@
1
+ #!/bin/bash
2
+ # PreToolUse hook: dangerous-bash-interceptor.sh
3
+ # Fires BEFORE every Bash tool call.
4
+ # Detects destructive shell commands and blocks them (exit 2) or warns (exit 0).
5
+ #
6
+ # Compatible with: interactive sessions + headless Docker (no TTY required).
7
+ # All diagnostic output goes to stderr only.
8
+ #
9
+ # Content extraction:
10
+ # Bash tool → tool_input.command
11
+ #
12
+ # Exit codes:
13
+ # 0 = safe or advisory-only — allow the command to run
14
+ # 2 = HIGH severity danger detected — block the command with feedback
15
+
16
+ set -uo pipefail
17
+
18
+ # ── 1. Read ALL stdin immediately before doing anything else ──────────────────
19
+ INPUT=$(cat)
20
+
21
+ # ── 2. Dependency check ───────────────────────────────────────────────────────
22
+ if ! command -v jq >/dev/null 2>&1; then
23
+ printf 'REAGENT ERROR: jq is required but not installed.\n' >&2
24
+ printf 'Install: brew install jq OR apt-get install -y jq\n' >&2
25
+ exit 2
26
+ fi
27
+
28
+ # ── 3. HALT check ─────────────────────────────────────────────────────────────
29
+ REAGENT_ROOT="${CLAUDE_PROJECT_DIR:-$(pwd)}"
30
+ HALT_FILE="${REAGENT_ROOT}/.reagent/HALT"
31
+ if [ -f "$HALT_FILE" ]; then
32
+ printf 'REAGENT HALT: %s\nAll agent operations suspended. Run: reagent unfreeze\n' \
33
+ "$(cat "$HALT_FILE" 2>/dev/null || echo 'Reason unknown')" >&2
34
+ exit 2
35
+ fi
36
+
37
+ # ── 4. Parse tool_input.command from the hook payload ─────────────────────────
38
+ CMD=$(printf '%s' "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null)
39
+
40
+ if [[ -z "$CMD" ]]; then
41
+ exit 0
42
+ fi
43
+
44
+ # ── 5. Helper: truncate command for display ────────────────────────────────────
45
+ truncate_cmd() {
46
+ local STR="$1"
47
+ local MAX=200
48
+ if [[ ${#STR} -gt $MAX ]]; then
49
+ printf '%s' "${STR:0:$MAX}..."
50
+ else
51
+ printf '%s' "$STR"
52
+ fi
53
+ }
54
+
55
+ # ── 6. Violation accumulators ──────────────────────────────────────────────────
56
+ HIGH_FILE=$(mktemp "${TMPDIR:-/tmp}/reagent-bash-high-XXXXXX")
57
+ MEDIUM_FILE=$(mktemp "${TMPDIR:-/tmp}/reagent-bash-medium-XXXXXX")
58
+
59
+ cleanup_violations() {
60
+ rm -f "$HIGH_FILE" "$MEDIUM_FILE"
61
+ }
62
+ trap cleanup_violations EXIT
63
+
64
+ add_high() {
65
+ local LABEL="$1"
66
+ local DETAIL="$2"
67
+ shift 2
68
+ printf 'HIGH|%s|%s\n' "$LABEL" "$DETAIL" >> "$HIGH_FILE"
69
+ for ALT in "$@"; do
70
+ printf 'ALT:%s\n' "$ALT" >> "$HIGH_FILE"
71
+ done
72
+ printf 'END_VIOLATION\n' >> "$HIGH_FILE"
73
+ }
74
+
75
+ add_medium() {
76
+ local LABEL="$1"
77
+ local DETAIL="$2"
78
+ shift 2
79
+ printf 'MEDIUM|%s|%s\n' "$LABEL" "$DETAIL" >> "$MEDIUM_FILE"
80
+ for ALT in "$@"; do
81
+ printf 'ALT:%s\n' "$ALT" >> "$MEDIUM_FILE"
82
+ done
83
+ printf 'END_VIOLATION\n' >> "$MEDIUM_FILE"
84
+ }
85
+
86
+ # ── 7. Per-segment evaluation helper ──────────────────────────────────────────
87
+ # Split on &&, ||, ;, and newlines and test a pattern against each segment.
88
+ # Returns 0 if ANY segment matches the pattern.
89
+ any_segment_matches() {
90
+ local PATTERN="$1"
91
+ while IFS= read -r SEG; do
92
+ if printf '%s' "$SEG" | grep -qiE "$PATTERN"; then
93
+ return 0
94
+ fi
95
+ done < <(printf '%s' "$CMD" | sed 's/&&/\n/g; s/||/\n/g; s/;/\n/g')
96
+ return 1
97
+ }
98
+
99
+ # ── 8. Smart exclusion flags ──────────────────────────────────────────────────
100
+ CMD_IS_REBASE_SAFE=0
101
+ if printf '%s' "$CMD" | grep -qiE 'git[[:space:]]+(rebase)[[:space:]].*(--abort|--continue)'; then
102
+ CMD_IS_REBASE_SAFE=1
103
+ fi
104
+
105
+ CMD_IS_CLEAN_DRY=0
106
+ if printf '%s' "$CMD" | grep -qiE 'git[[:space:]]+clean.*([ \t]-n|--dry-run)'; then
107
+ CMD_IS_CLEAN_DRY=1
108
+ fi
109
+
110
+ # ── 9. HIGH severity checks ────────────────────────────────────────────────────
111
+
112
+ # H1: git push --force or -f (per-segment — prevents --force-with-lease poisoning)
113
+ # A segment containing --force-with-lease is excluded; other segments are not.
114
+ while IFS= read -r SEGMENT; do
115
+ SEGMENT=$(printf '%s' "$SEGMENT" | sed 's/^[[:space:]]*//')
116
+ [[ -z "$SEGMENT" ]] && continue
117
+ # Skip segments that use the safe --force-with-lease
118
+ if printf '%s' "$SEGMENT" | grep -qiE 'git[[:space:]]+push.*--force-with-lease'; then
119
+ continue
120
+ fi
121
+ if printf '%s' "$SEGMENT" | grep -qiE 'git[[:space:]]+push.*(--force|-f[[:space:]])' || \
122
+ printf '%s' "$SEGMENT" | grep -qiE 'git[[:space:]]+push.*(--force|-f)$'; then
123
+ add_high \
124
+ "git push --force — force push detected" \
125
+ "Force-pushing rewrites public history and breaks collaborators' local copies." \
126
+ "Alt: Use 'git push --force-with-lease' — blocks if upstream has new commits you haven't pulled."
127
+ break
128
+ fi
129
+ done < <(printf '%s' "$CMD" | sed 's/&&/\n/g; s/||/\n/g; s/;/\n/g')
130
+
131
+ # H2: git rebase — advisory (MEDIUM)
132
+ if [[ $CMD_IS_REBASE_SAFE -eq 0 ]]; then
133
+ if printf '%s' "$CMD" | grep -qiE 'git[[:space:]]+rebase([[:space:]]|$)'; then
134
+ add_medium \
135
+ "git rebase — rewrites commit history (advisory)" \
136
+ "Rebase changes commit SHAs. Safe on local feature branches; dangerous on shared/published branches." \
137
+ "Alt: 'git merge origin/main' preserves history (creates merge commit)." \
138
+ " 'git rebase --abort' to cancel if in progress."
139
+ fi
140
+ fi
141
+
142
+ # H3: git checkout -- .
143
+ if printf '%s' "$CMD" | grep -qiE 'git[[:space:]]+checkout[[:space:]]+--[[:space:]]+\.'; then
144
+ add_high \
145
+ "git checkout -- . — discards all uncommitted changes" \
146
+ "Overwrites working tree changes with HEAD. Uncommitted work is lost permanently." \
147
+ "Alt: 'git stash' to temporarily shelve changes, 'git restore <file>' for individual files."
148
+ fi
149
+
150
+ # H4: git restore . (any form — with or without --staged flag)
151
+ if printf '%s' "$CMD" | grep -qiE 'git[[:space:]]+restore[[:space:]].*[[:space:]]\.([[:space:]]|$)' || \
152
+ printf '%s' "$CMD" | grep -qiE 'git[[:space:]]+restore[[:space:]]+\.[[:space:]]*$'; then
153
+ add_high \
154
+ "git restore . — discards all uncommitted changes" \
155
+ "Restores every tracked file to HEAD, permanently discarding all working tree modifications." \
156
+ "Alt: 'git stash' to save changes temporarily, or restore individual files: 'git restore <file>'."
157
+ fi
158
+
159
+ # H5: git clean -f
160
+ if [[ $CMD_IS_CLEAN_DRY -eq 0 ]]; then
161
+ if printf '%s' "$CMD" | grep -qiE 'git[[:space:]]+clean[[:space:]]+-[a-zA-Z]*f'; then
162
+ add_high \
163
+ "git clean -f — removes untracked files" \
164
+ "Permanently deletes untracked files from the working tree. Cannot be undone via git." \
165
+ "Alt: 'git clean -n' (dry-run) to preview what would be deleted before committing."
166
+ fi
167
+ fi
168
+
169
+ # H6: DROP TABLE or DROP DATABASE in psql
170
+ if printf '%s' "$CMD" | grep -qiE '(psql|pgcli)[^|&;]*DROP[[:space:]]+(TABLE|DATABASE|SCHEMA)'; then
171
+ add_high \
172
+ "DROP TABLE/DATABASE via psql — destructive DDL" \
173
+ "Running destructive DDL directly in psql bypasses migration pipeline safety checks." \
174
+ "Alt: Use your project's migration tool. Never run DROP via ad-hoc psql."
175
+ fi
176
+
177
+ # H7: kill -9 with pgrep subshell
178
+ if printf '%s' "$CMD" | grep -qiE 'kill[[:space:]]+-9[[:space:]]+(\$\(|`)'; then
179
+ add_high \
180
+ "kill -9 with pgrep subshell — aggressive process termination" \
181
+ "Sends SIGKILL to processes matched by name, which may kill unintended processes." \
182
+ "Alt: 'kill -15 <pid>' (SIGTERM) for graceful shutdown."
183
+ fi
184
+
185
+ # H8: killall -9
186
+ if printf '%s' "$CMD" | grep -qiE 'killall[[:space:]]+-9[[:space:]]+\S'; then
187
+ add_high \
188
+ "killall -9 — SIGKILL all matching processes" \
189
+ "Immediately terminates all processes with the given name without cleanup." \
190
+ "Alt: 'killall -15 <name>' (SIGTERM) allows graceful shutdown."
191
+ fi
192
+
193
+ # H9: git commit --no-verify
194
+ if printf '%s' "$CMD" | grep -qiE 'git[[:space:]]+commit.*--no-verify'; then
195
+ add_high \
196
+ "git commit --no-verify — skipping pre-commit hooks" \
197
+ "Bypasses all pre-commit safety gates including secret scanning and linting." \
198
+ "Alt: Fix the underlying hook failure rather than bypassing it."
199
+ fi
200
+
201
+ # H10: HUSKY=0 bypass — suppresses all git hooks without --no-verify
202
+ if printf '%s' "$CMD" | grep -qiE '(^|[[:space:];]|&&|\|\|)HUSKY=0[[:space:]]+git[[:space:]]+(commit|push|tag)'; then
203
+ add_high \
204
+ "HUSKY=0 — bypasses all husky git hooks" \
205
+ "Setting HUSKY=0 disables pre-commit, commit-msg, and pre-push safety gates without --no-verify." \
206
+ "Alt: Fix the underlying hook failure rather than suppressing all hooks."
207
+ fi
208
+
209
+ # H11: rm -rf with broad targets
210
+ # Covers combined flags (rm -rf, rm -fr), split flags (rm -r -f), and long flags (rm --recursive --force)
211
+ BROAD_TARGETS='(\/|~\/|\.\/\*|\*|\.|src|dist|build|node_modules)'
212
+ if printf '%s' "$CMD" | grep -qiE "rm[[:space:]]+-[a-zA-Z]*r[a-zA-Z]*f[[:space:]]+${BROAD_TARGETS}" || \
213
+ printf '%s' "$CMD" | grep -qiE "rm[[:space:]]+-[a-zA-Z]*f[a-zA-Z]*r[[:space:]]+${BROAD_TARGETS}" || \
214
+ printf '%s' "$CMD" | grep -qiE "rm[[:space:]]+-[a-zA-Z]*r[[:space:]]+-[a-zA-Z]*f[[:space:]]+${BROAD_TARGETS}" || \
215
+ printf '%s' "$CMD" | grep -qiE "rm[[:space:]]+-[a-zA-Z]*f[[:space:]]+-[a-zA-Z]*r[[:space:]]+${BROAD_TARGETS}" || \
216
+ printf '%s' "$CMD" | grep -qiE "rm[[:space:]]+--recursive[[:space:]]+--force[[:space:]]+${BROAD_TARGETS}" || \
217
+ printf '%s' "$CMD" | grep -qiE "rm[[:space:]]+--force[[:space:]]+--recursive[[:space:]]+${BROAD_TARGETS}"; then
218
+ add_high \
219
+ "rm -rf with broad target — mass file deletion" \
220
+ "Permanently deletes files and directories. Cannot be undone." \
221
+ "Alt: Move to a temp location first, or use 'rm -ri' for interactive deletion."
222
+ fi
223
+
224
+ # H12: curl/wget piped directly to shell (supply chain attack vector)
225
+ if printf '%s' "$CMD" | grep -qiE '(curl|wget)[^|]*\|[[:space:]]*(bash|sh|zsh|fish)'; then
226
+ add_high \
227
+ "curl/wget piped to shell — remote code execution" \
228
+ "Executing remote scripts without inspection is a major supply chain risk." \
229
+ "Alt: Download first, inspect the script, then execute: curl -o script.sh URL && cat script.sh && bash script.sh"
230
+ fi
231
+
232
+ # ── 10. MEDIUM severity checks ────────────────────────────────────────────────
233
+
234
+ # M1: npm install --force
235
+ if printf '%s' "$CMD" | grep -qiE 'npm[[:space:]]+(install|i)[[:space:]].*--force'; then
236
+ add_medium \
237
+ "npm install --force — bypasses dependency resolution" \
238
+ "--force skips conflict checks and can install incompatible package versions." \
239
+ "Alt: Resolve the dependency conflict explicitly. Use --legacy-peer-deps if needed."
240
+ fi
241
+
242
+ # ── 11. Evaluate and report ───────────────────────────────────────────────────
243
+
244
+ TRUNCATED_CMD=$(truncate_cmd "$CMD")
245
+
246
+ print_violations() {
247
+ local VF="$1"
248
+ local NOTE_LABEL="$2"
249
+ while IFS= read -r LINE; do
250
+ case "$LINE" in
251
+ HIGH\|*|MEDIUM\|*)
252
+ local SEV LABEL DETAIL
253
+ SEV=$(printf '%s' "$LINE" | cut -d'|' -f1)
254
+ LABEL=$(printf '%s' "$LINE" | cut -d'|' -f2)
255
+ DETAIL=$(printf '%s' "$LINE" | cut -d'|' -f3)
256
+ printf ' %s: %s\n' "$SEV" "$LABEL"
257
+ printf ' %s: %s\n' "$NOTE_LABEL" "$DETAIL"
258
+ ;;
259
+ ALT:*)
260
+ printf ' %s\n' "${LINE#ALT:}"
261
+ ;;
262
+ END_VIOLATION)
263
+ printf '\n'
264
+ ;;
265
+ esac
266
+ done < "$VF"
267
+ }
268
+
269
+ if [[ -s "$HIGH_FILE" ]]; then
270
+ {
271
+ printf 'BASH INTERCEPTED: Dangerous command blocked\n'
272
+ print_violations "$HIGH_FILE" "Reason"
273
+ printf ' BLOCKED COMMAND: %s\n' "$TRUNCATED_CMD"
274
+ } >&2
275
+ exit 2
276
+ fi
277
+
278
+ if [[ -s "$MEDIUM_FILE" ]]; then
279
+ {
280
+ printf 'BASH ADVISORY: Potentially risky command (not blocked)\n'
281
+ print_violations "$MEDIUM_FILE" "Note"
282
+ printf ' COMMAND: %s\n' "$TRUNCATED_CMD"
283
+ } >&2
284
+ exit 0
285
+ fi
286
+
287
+ exit 0