uv-suite 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.
Files changed (70) hide show
  1. package/README.md +180 -0
  2. package/agents/claude-code/anti-slop-guard.md +84 -0
  3. package/agents/claude-code/architect.md +68 -0
  4. package/agents/claude-code/cartographer.md +99 -0
  5. package/agents/claude-code/devops.md +43 -0
  6. package/agents/claude-code/eval-writer.md +57 -0
  7. package/agents/claude-code/prototype-builder.md +59 -0
  8. package/agents/claude-code/reviewer.md +76 -0
  9. package/agents/claude-code/security.md +69 -0
  10. package/agents/claude-code/spec-writer.md +81 -0
  11. package/agents/claude-code/test-writer.md +54 -0
  12. package/agents/codex/anti-slop-guard.toml +12 -0
  13. package/agents/codex/architect.toml +11 -0
  14. package/agents/codex/cartographer.toml +16 -0
  15. package/agents/codex/devops.toml +8 -0
  16. package/agents/codex/eval-writer.toml +11 -0
  17. package/agents/codex/prototype-builder.toml +10 -0
  18. package/agents/codex/reviewer.toml +16 -0
  19. package/agents/codex/security.toml +14 -0
  20. package/agents/codex/spec-writer.toml +11 -0
  21. package/agents/codex/test-writer.toml +13 -0
  22. package/agents/cursor/anti-slop-guard.mdc +22 -0
  23. package/agents/cursor/architect.mdc +24 -0
  24. package/agents/cursor/cartographer.mdc +28 -0
  25. package/agents/cursor/devops.mdc +16 -0
  26. package/agents/cursor/eval-writer.mdc +21 -0
  27. package/agents/cursor/prototype-builder.mdc +25 -0
  28. package/agents/cursor/reviewer.mdc +26 -0
  29. package/agents/cursor/security.mdc +20 -0
  30. package/agents/cursor/spec-writer.mdc +27 -0
  31. package/agents/cursor/test-writer.mdc +28 -0
  32. package/agents/portable/anti-slop-guard.md +71 -0
  33. package/agents/portable/architect.md +83 -0
  34. package/agents/portable/cartographer.md +64 -0
  35. package/agents/portable/devops.md +56 -0
  36. package/agents/portable/eval-writer.md +70 -0
  37. package/agents/portable/prototype-builder.md +70 -0
  38. package/agents/portable/reviewer.md +79 -0
  39. package/agents/portable/security.md +63 -0
  40. package/agents/portable/spec-writer.md +89 -0
  41. package/agents/portable/test-writer.md +56 -0
  42. package/bin/cli.js +84 -0
  43. package/guardrails/architecture-slop.md +60 -0
  44. package/guardrails/comment-slop.md +53 -0
  45. package/guardrails/doc-slop.md +62 -0
  46. package/guardrails/error-handling-slop.md +65 -0
  47. package/guardrails/overengineering-slop.md +56 -0
  48. package/guardrails/test-slop.md +72 -0
  49. package/hooks/auto-lint.sh +41 -0
  50. package/hooks/block-destructive.sh +34 -0
  51. package/hooks/danger-zone-check.sh +42 -0
  52. package/hooks/session-review-reminder.sh +35 -0
  53. package/install.sh +230 -0
  54. package/package.json +39 -0
  55. package/personas/auto.json +80 -0
  56. package/personas/professional.json +109 -0
  57. package/personas/spike.json +54 -0
  58. package/personas/sport.json +39 -0
  59. package/settings.json +108 -0
  60. package/skills/architect/SKILL.md +26 -0
  61. package/skills/map-codebase/SKILL.md +50 -0
  62. package/skills/persona/SKILL.md +4 -0
  63. package/skills/prototype/SKILL.md +27 -0
  64. package/skills/review/SKILL.md +39 -0
  65. package/skills/security-review/SKILL.md +73 -0
  66. package/skills/slop-check/SKILL.md +30 -0
  67. package/skills/spec/SKILL.md +33 -0
  68. package/skills/write-evals/SKILL.md +28 -0
  69. package/skills/write-tests/SKILL.md +40 -0
  70. package/uv.sh +56 -0
package/bin/cli.js ADDED
@@ -0,0 +1,84 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { execSync } = require('child_process');
4
+ const path = require('path');
5
+ const fs = require('fs');
6
+
7
+ const UV_SUITE_DIR = path.resolve(__dirname, '..');
8
+ const args = process.argv.slice(2);
9
+ const command = args[0];
10
+
11
+ function usage() {
12
+ console.log(`
13
+ uv-suite - AI-assisted development framework
14
+
15
+ Usage:
16
+ npx uv-suite install [--persona sport|professional|auto|spike] [--global]
17
+ npx uv-suite info
18
+
19
+ Commands:
20
+ install Install agents, skills, hooks, guardrails, and personas
21
+ info Show what would be installed
22
+
23
+ Personas:
24
+ spike Research & documentation (Opus, max effort)
25
+ sport New projects, prototyping (Sonnet, high effort)
26
+ professional Production code (default, all hooks + guardrails)
27
+ auto Fully autonomous (max effort, everything approved)
28
+
29
+ Examples:
30
+ npx uv-suite install Install with Professional persona
31
+ npx uv-suite install --persona sport Install with Sport persona
32
+ npx uv-suite install --global Install to ~/.claude/
33
+ `);
34
+ }
35
+
36
+ function info() {
37
+ console.log(`
38
+ UV Suite v0.1.0
39
+
40
+ Contents:
41
+ 10 agents Claude Code (.md), Cursor (.mdc), Codex (.toml)
42
+ 9 skills Slash commands for Claude Code
43
+ 4 hooks auto-lint, slop-check, danger-zone, block-destructive
44
+ 6 guardrails Anti-slop rules (comment, overengineering, error, test, doc, architecture)
45
+ 4 personas Spike, Sport, Professional, Auto
46
+ 1 launcher uv.sh (session launcher with persona selection)
47
+
48
+ Source: ${UV_SUITE_DIR}
49
+ `);
50
+ }
51
+
52
+ function install() {
53
+ const installScript = path.join(UV_SUITE_DIR, 'install.sh');
54
+ if (!fs.existsSync(installScript)) {
55
+ console.error('Error: install.sh not found at', installScript);
56
+ process.exit(1);
57
+ }
58
+
59
+ // Pass through all args after "install"
60
+ const installArgs = args.slice(1).join(' ');
61
+ try {
62
+ execSync(`bash "${installScript}" ${installArgs}`, { stdio: 'inherit' });
63
+ } catch (e) {
64
+ process.exit(e.status || 1);
65
+ }
66
+ }
67
+
68
+ switch (command) {
69
+ case 'install':
70
+ install();
71
+ break;
72
+ case 'info':
73
+ info();
74
+ break;
75
+ case '--help':
76
+ case '-h':
77
+ case undefined:
78
+ usage();
79
+ break;
80
+ default:
81
+ console.error(`Unknown command: ${command}`);
82
+ usage();
83
+ process.exit(1);
84
+ }
@@ -0,0 +1,60 @@
1
+ # Guardrail: Architecture Slop
2
+
3
+ **Severity:** High (unjustified complexity has compounding cost)
4
+ **Category:** Architecture quality
5
+
6
+ ## Pattern
7
+
8
+ Architecture decisions that sound sophisticated but don't match actual requirements. Buzzword-driven design. Complexity assumed rather than earned.
9
+
10
+ ## Detection Rules
11
+
12
+ - Does the proposed architecture match the actual scale? (10 users don't need microservices)
13
+ - Is the complexity justified by a specific requirement, or just "best practice"?
14
+ - Could a simpler approach work? If yes, why isn't it the recommendation?
15
+ - Are buzzwords being used as reasoning? ("event-driven" is not a reason, it's a pattern)
16
+ - Is the "future-proofing" based on concrete plans or speculation?
17
+
18
+ ## Examples
19
+
20
+ ### Slop
21
+
22
+ ```
23
+ "We should use an event-driven microservices architecture with CQRS
24
+ and event sourcing" (for a CRUD app with 3 endpoints and 100 users)
25
+
26
+ "Let's implement a service mesh with circuit breakers and distributed
27
+ tracing" (for a monolith on a single server)
28
+
29
+ "We need a Kafka cluster for message passing" (for 10 events per minute)
30
+
31
+ "Let's use GraphQL for maximum flexibility" (for 5 fixed queries)
32
+ ```
33
+
34
+ ### Not Slop
35
+
36
+ ```
37
+ "A monolith with 3 REST endpoints is sufficient. We'll extract
38
+ services if/when we need independent scaling or deployment."
39
+
40
+ "PostgreSQL handles our query patterns well. We'll add read replicas
41
+ if/when we exceed 10k queries per second."
42
+
43
+ "A simple task queue (Redis + BullMQ) handles our async processing.
44
+ We'll move to Kafka if/when we need multi-consumer event streaming."
45
+
46
+ "REST with OpenAPI spec. GraphQL adds complexity we don't need for
47
+ our fixed client requirements."
48
+ ```
49
+
50
+ ## The Challenge Test
51
+
52
+ For every architectural component, ask: **"What breaks if we don't have this?"**
53
+
54
+ - If the answer is "nothing for the next 6 months" → remove it
55
+ - If the answer is "we'd need to add it in 2 months when X happens" → document the upgrade path, don't build it now
56
+ - If the answer is "users can't complete the core flow" → keep it
57
+
58
+ ## Fix
59
+
60
+ Start with the simplest architecture that meets current requirements. Document the concrete triggers that would justify more complexity. "We'll add caching when p99 latency exceeds 500ms" is better than "we're adding Redis for performance."
@@ -0,0 +1,53 @@
1
+ # Guardrail: Comment Slop
2
+
3
+ **Severity:** High (actively obscures code)
4
+ **Category:** Code quality
5
+
6
+ ## Pattern
7
+
8
+ Comments that restate what the code already says, adding no information.
9
+
10
+ ## Detection Rule
11
+
12
+ If deleting the comment and reading the code tells you the same thing, the comment is slop. Comments should explain **why**, not **what**.
13
+
14
+ ## Examples
15
+
16
+ ### Slop
17
+
18
+ ```typescript
19
+ // Initialize the database connection
20
+ const db = initDatabase();
21
+
22
+ // Set the user's name
23
+ user.name = newName;
24
+
25
+ // Loop through the items
26
+ for (const item of items) {
27
+
28
+ // Return the result
29
+ return result;
30
+
31
+ // Check if the user is valid
32
+ if (isValid(user)) {
33
+ ```
34
+
35
+ ### Not Slop
36
+
37
+ ```typescript
38
+ // Retry with exponential backoff because the API rate-limits
39
+ // at 100 req/min and our batch size exceeds that
40
+ const db = initDatabase({ retries: 3, backoff: 'exponential' });
41
+
42
+ // Normalize Unicode before comparison — we've seen cases where
43
+ // visually identical names fail equality checks (NFD vs NFC)
44
+ user.name = newName.normalize('NFC');
45
+
46
+ // Skip soft-deleted records intentionally — the reports team
47
+ // needs historical data including deleted entries
48
+ const records = db.query('SELECT * FROM orders');
49
+ ```
50
+
51
+ ## Fix
52
+
53
+ Delete the comment. If the code genuinely needs explanation, rename the variable or function to be self-documenting. Only add a comment when there's context the code can't express (business decisions, workarounds, non-obvious constraints).
@@ -0,0 +1,62 @@
1
+ # Guardrail: Documentation Slop
2
+
3
+ **Severity:** Medium (misleads readers, creates false sense of documentation)
4
+ **Category:** Documentation quality
5
+
6
+ ## Pattern
7
+
8
+ Documentation that uses words without saying anything specific. Vague adjectives, appeals to authority, feature lists that could describe any system.
9
+
10
+ ## Detection Rules
11
+
12
+ **Vague adjectives (red flags):**
13
+ - "Robust", "scalable", "maintainable", "comprehensive", "extensive"
14
+ - "Elegant", "clean", "modern", "cutting-edge", "state-of-the-art"
15
+ - "Enterprise-grade", "production-ready", "battle-tested"
16
+
17
+ **Evasive verbs:**
18
+ - "Leverages", "utilizes", "facilitates", "empowers", "enables"
19
+
20
+ **Authority appeals:**
21
+ - "Best practices", "industry-standard", "widely adopted"
22
+ - Without naming the specific practice
23
+
24
+ **Generic feature lists:**
25
+ - "Comprehensive error handling" — what errors? What handling?
26
+ - "Flexible configuration options" — what options? What's configurable?
27
+ - "Extensive logging" — what's logged? Where? What format?
28
+
29
+ ## Examples
30
+
31
+ ### Slop
32
+
33
+ ```markdown
34
+ ## Overview
35
+ This module provides a robust, scalable, and maintainable solution for
36
+ handling user authentication. It leverages industry-standard best practices
37
+ to ensure secure and efficient credential management.
38
+
39
+ ## Features
40
+ - Comprehensive authentication flow
41
+ - Secure credential storage
42
+ - Flexible configuration options
43
+ - Extensive error handling
44
+ ```
45
+
46
+ ### Not Slop
47
+
48
+ ```markdown
49
+ ## What This Does
50
+ Handles login, signup, and session management using bcrypt for password
51
+ hashing and JWT for session tokens. Sessions expire after 24 hours.
52
+
53
+ ## Endpoints
54
+ - POST /auth/signup — Create account (email + password)
55
+ - POST /auth/login — Get JWT token
56
+ - POST /auth/logout — Invalidate session
57
+ - GET /auth/me — Get current user (requires valid JWT)
58
+ ```
59
+
60
+ ## Fix
61
+
62
+ Replace every vague adjective with a specific fact. "Robust error handling" becomes "Retries 3 times with exponential backoff on 5xx errors." If you can't make it specific, delete it — the absence of vague claims is better than their presence.
@@ -0,0 +1,65 @@
1
+ # Guardrail: Error Handling Slop
2
+
3
+ **Severity:** Medium (adds noise, obscures real error paths)
4
+ **Category:** Code quality
5
+
6
+ ## Pattern
7
+
8
+ Try/catch blocks around code that can't throw, or that catch and re-throw without adding value. Defensive checks for impossible states.
9
+
10
+ ## Detection Rules
11
+
12
+ - Try/catch where the try block can't throw
13
+ - Catch that only logs and re-throws (the caller handles it)
14
+ - Error handling for impossible states (e.g., validating a non-null TypeScript param isn't null)
15
+ - Defensive checks deep inside internal functions (trust the caller at internal boundaries)
16
+ - Catching generic `Error` when only specific errors are possible
17
+
18
+ ## Examples
19
+
20
+ ### Slop
21
+
22
+ ```typescript
23
+ // JSON.stringify doesn't throw on a plain object
24
+ try {
25
+ const json = JSON.stringify(user);
26
+ return json;
27
+ } catch (error) {
28
+ console.error('Failed to stringify user:', error);
29
+ throw error;
30
+ }
31
+
32
+ // Catch, log, re-throw — adds nothing
33
+ try {
34
+ const result = await fetchUser(id);
35
+ return result;
36
+ } catch (error) {
37
+ console.error('Error fetching user:', error);
38
+ throw error;
39
+ }
40
+
41
+ // Null check on TypeScript non-null parameter
42
+ function processUser(user: User): void {
43
+ if (!user) throw new Error('User is required'); // TypeScript already prevents this
44
+ }
45
+ ```
46
+
47
+ ### Not Slop
48
+
49
+ ```typescript
50
+ // No try/catch needed — let errors propagate naturally
51
+ const json = JSON.stringify(user);
52
+ return json;
53
+
54
+ // Handle the error meaningfully at the system boundary
55
+ try {
56
+ const result = await fetchUser(id);
57
+ return result;
58
+ } catch (error) {
59
+ return { error: 'User not found', status: 404 };
60
+ }
61
+ ```
62
+
63
+ ## Fix
64
+
65
+ Remove the try/catch. Only add error handling at system boundaries (user input, network calls, file I/O) or when converting the error to a different type/format. Let internal errors propagate naturally.
@@ -0,0 +1,56 @@
1
+ # Guardrail: Over-Engineering Slop
2
+
3
+ **Severity:** High (inflates maintenance burden)
4
+ **Category:** Code quality
5
+
6
+ ## Pattern
7
+
8
+ Abstractions, patterns, and indirection that serve no current purpose. AI models love creating "clean architecture" with factories, interfaces, and wrappers that only have one implementation.
9
+
10
+ ## Detection Rules
11
+
12
+ - Interface with only one implementation
13
+ - Factory that creates only one type
14
+ - Abstract class with only one subclass
15
+ - Wrapper that adds no behavior
16
+ - Configuration for values that never change
17
+ - Generic type parameter that's always the same concrete type
18
+ - "Strategy pattern" with one strategy
19
+ - Builder pattern for objects with 2-3 fields
20
+
21
+ ## Examples
22
+
23
+ ### Slop
24
+
25
+ ```typescript
26
+ // Factory for a single implementation
27
+ interface PaymentProcessor {
28
+ process(payment: Payment): Promise<Result>;
29
+ }
30
+
31
+ class PaymentProcessorFactory {
32
+ static create(type: string): PaymentProcessor {
33
+ switch (type) {
34
+ case 'stripe': return new StripePaymentProcessor();
35
+ default: throw new Error(`Unknown type: ${type}`);
36
+ }
37
+ }
38
+ }
39
+
40
+ class StripePaymentProcessor implements PaymentProcessor {
41
+ async process(payment: Payment): Promise<Result> {
42
+ return stripe.charges.create({ amount: payment.amount });
43
+ }
44
+ }
45
+ ```
46
+
47
+ ### Not Slop
48
+
49
+ ```typescript
50
+ // Direct call — add abstraction when you need a second processor
51
+ await stripe.charges.create({ amount: payment.amount });
52
+ ```
53
+
54
+ ## Fix
55
+
56
+ Delete the abstraction. Call the thing directly. Add abstraction when (not before) you need it. If you have two implementations, then the interface earns its place.
@@ -0,0 +1,72 @@
1
+ # Guardrail: Test Slop
2
+
3
+ **Severity:** High (creates false sense of coverage)
4
+ **Category:** Testing quality
5
+
6
+ ## Pattern
7
+
8
+ Tests that pass but don't actually verify behavior. They inflate coverage metrics without catching bugs.
9
+
10
+ ## Detection Rules
11
+
12
+ - `expect(x).toBeTruthy()` or `expect(x).toBeDefined()` — almost always slop
13
+ - Tests where the mock is the only thing being tested (you're testing your setup, not your code)
14
+ - Snapshot tests on simple/trivial components
15
+ - Tests with no assertions
16
+ - Tests that test framework behavior ("does React render a component?")
17
+ - Test name doesn't match what's actually being tested
18
+ - Copy-pasted tests with only minor variations (use parameterized tests)
19
+
20
+ ## Examples
21
+
22
+ ### Slop
23
+
24
+ ```typescript
25
+ // Tests existence, not behavior
26
+ test('should create a user', () => {
27
+ const user = createUser({ name: 'Alice' });
28
+ expect(user).toBeTruthy();
29
+ });
30
+
31
+ // Tests the mock, not the code
32
+ test('should fetch user', async () => {
33
+ mockFetch.mockResolvedValue({ name: 'Alice' });
34
+ const user = await fetchUser(1);
35
+ expect(user.name).toBe('Alice'); // You told the mock to return this
36
+ });
37
+
38
+ // Snapshot of a trivial component
39
+ test('should render', () => {
40
+ const tree = renderer.create(<Button>Click</Button>);
41
+ expect(tree.toJSON()).toMatchSnapshot();
42
+ });
43
+ ```
44
+
45
+ ### Not Slop
46
+
47
+ ```typescript
48
+ // Tests specific behavior
49
+ test('createUser hashes password before storing', async () => {
50
+ const user = await createUser({ name: 'Alice', password: 'secret123' });
51
+ expect(user.passwordHash).not.toBe('secret123');
52
+ expect(await bcrypt.compare('secret123', user.passwordHash)).toBe(true);
53
+ });
54
+
55
+ // Tests real behavior with real dependency
56
+ test('fetchUser returns null for non-existent user', async () => {
57
+ const user = await fetchUser(999);
58
+ expect(user).toBeNull();
59
+ });
60
+
61
+ // Tests meaningful interaction
62
+ test('Button calls onClick when clicked', () => {
63
+ const onClick = jest.fn();
64
+ render(<Button onClick={onClick}>Click</Button>);
65
+ fireEvent.click(screen.getByText('Click'));
66
+ expect(onClick).toHaveBeenCalledTimes(1);
67
+ });
68
+ ```
69
+
70
+ ## Fix
71
+
72
+ Delete tests that can't fail meaningfully. Rewrite to test actual behavior: specific values, specific side effects, specific error conditions. Ask: "Would this test catch a real bug?"
@@ -0,0 +1,41 @@
1
+ #!/bin/bash
2
+ # UV Suite Hook: Auto-lint after file writes
3
+ # Event: PostToolUse (Write|Edit)
4
+ # Reads the tool input from stdin, extracts the file path, runs the appropriate linter.
5
+
6
+ INPUT=$(cat)
7
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
8
+
9
+ if [ -z "$FILE_PATH" ] || [ ! -f "$FILE_PATH" ]; then
10
+ exit 0
11
+ fi
12
+
13
+ EXT="${FILE_PATH##*.}"
14
+
15
+ case "$EXT" in
16
+ ts|tsx|js|jsx|mjs|cjs)
17
+ # Try prettier first, fall back to eslint
18
+ if command -v npx &>/dev/null; then
19
+ npx prettier --write "$FILE_PATH" 2>/dev/null || true
20
+ fi
21
+ ;;
22
+ py)
23
+ if command -v ruff &>/dev/null; then
24
+ ruff format "$FILE_PATH" 2>/dev/null || true
25
+ elif command -v black &>/dev/null; then
26
+ black --quiet "$FILE_PATH" 2>/dev/null || true
27
+ fi
28
+ ;;
29
+ go)
30
+ if command -v gofmt &>/dev/null; then
31
+ gofmt -w "$FILE_PATH" 2>/dev/null || true
32
+ fi
33
+ ;;
34
+ rs)
35
+ if command -v rustfmt &>/dev/null; then
36
+ rustfmt "$FILE_PATH" 2>/dev/null || true
37
+ fi
38
+ ;;
39
+ esac
40
+
41
+ exit 0
@@ -0,0 +1,34 @@
1
+ #!/bin/bash
2
+ # UV Suite Hook: Block dangerous bash commands
3
+ # Event: PreToolUse (Bash)
4
+ # Blocks rm -rf, DROP TABLE, force push to main, etc.
5
+
6
+ INPUT=$(cat)
7
+ COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty')
8
+
9
+ if [ -z "$COMMAND" ]; then
10
+ exit 0
11
+ fi
12
+
13
+ # Block patterns
14
+ if echo "$COMMAND" | grep -qEi "rm\s+-rf\s+/|rm\s+-rf\s+~|rm\s+-rf\s+\.\s*$"; then
15
+ echo "Blocked: recursive delete of root, home, or current directory" >&2
16
+ exit 2
17
+ fi
18
+
19
+ if echo "$COMMAND" | grep -qEi "drop\s+(table|database)|truncate\s+table"; then
20
+ echo "Blocked: destructive database operation — get human approval first" >&2
21
+ exit 2
22
+ fi
23
+
24
+ if echo "$COMMAND" | grep -qEi "git\s+push\s+.*--force.*\s+(main|master)"; then
25
+ echo "Blocked: force push to main/master" >&2
26
+ exit 2
27
+ fi
28
+
29
+ if echo "$COMMAND" | grep -qEi "git\s+reset\s+--hard\s+origin"; then
30
+ echo "Blocked: hard reset to origin — this discards local work" >&2
31
+ exit 2
32
+ fi
33
+
34
+ exit 0
@@ -0,0 +1,42 @@
1
+ #!/bin/bash
2
+ # UV Suite Hook: Check if a file being modified is in a danger zone
3
+ # Event: PreToolUse (Edit|Write)
4
+ # If the file appears in DANGER-ZONES.md, warns Claude via systemMessage.
5
+
6
+ INPUT=$(cat)
7
+ FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // empty')
8
+
9
+ if [ -z "$FILE_PATH" ]; then
10
+ exit 0
11
+ fi
12
+
13
+ DANGER_FILE="DANGER-ZONES.md"
14
+ if [ ! -f "$DANGER_FILE" ]; then
15
+ # Check project root
16
+ DANGER_FILE="${CLAUDE_PROJECT_DIR:-$(git rev-parse --show-toplevel 2>/dev/null)}/DANGER-ZONES.md"
17
+ fi
18
+
19
+ if [ ! -f "$DANGER_FILE" ]; then
20
+ exit 0
21
+ fi
22
+
23
+ # Get just the filename/relative path to search for
24
+ BASENAME=$(basename "$FILE_PATH")
25
+ RELPATH=$(realpath --relative-to="${CLAUDE_PROJECT_DIR:-.}" "$FILE_PATH" 2>/dev/null || echo "$FILE_PATH")
26
+
27
+ # Search for the file in DANGER-ZONES.md
28
+ MATCH=$(grep -i "$BASENAME\|$RELPATH" "$DANGER_FILE" 2>/dev/null)
29
+
30
+ if [ -n "$MATCH" ]; then
31
+ # File is in a danger zone — warn but don't block
32
+ cat <<EOF
33
+ {
34
+ "continue": true,
35
+ "systemMessage": "WARNING: This file is in a DANGER ZONE. Check DANGER-ZONES.md before proceeding. Relevant entry:\n\n${MATCH}\n\nConsider flagging this modification to the human before continuing."
36
+ }
37
+ EOF
38
+ else
39
+ echo '{"continue": true}'
40
+ fi
41
+
42
+ exit 0
@@ -0,0 +1,35 @@
1
+ #!/bin/bash
2
+ # UV Suite Hook: Remind to review before ending session
3
+ # Event: Stop
4
+ # If there are uncommitted changes, reminds the user to run /review and /slop-check.
5
+
6
+ # Check for uncommitted changes
7
+ STAGED=$(git diff --cached --stat 2>/dev/null)
8
+ UNSTAGED=$(git diff --stat 2>/dev/null)
9
+ UNTRACKED=$(git ls-files --others --exclude-standard 2>/dev/null | head -5)
10
+
11
+ if [ -z "$STAGED" ] && [ -z "$UNSTAGED" ] && [ -z "$UNTRACKED" ]; then
12
+ # No changes — nothing to remind about
13
+ exit 0
14
+ fi
15
+
16
+ # Build a summary of what's pending
17
+ SUMMARY=""
18
+ if [ -n "$STAGED" ]; then
19
+ SUMMARY="Staged changes:\n$STAGED\n"
20
+ fi
21
+ if [ -n "$UNSTAGED" ]; then
22
+ SUMMARY="${SUMMARY}Unstaged changes:\n$UNSTAGED\n"
23
+ fi
24
+ if [ -n "$UNTRACKED" ]; then
25
+ SUMMARY="${SUMMARY}Untracked files:\n$UNTRACKED\n"
26
+ fi
27
+
28
+ cat <<EOF
29
+ {
30
+ "continue": true,
31
+ "systemMessage": "SESSION END REMINDER: There are uncommitted changes in the working tree.\n\n${SUMMARY}\nConsider running /review and /slop-check before committing."
32
+ }
33
+ EOF
34
+
35
+ exit 0