A loose federation of distributed, typed datasets
1
fork

Configure Feed

Select the types of activity you want to include in your feed.

chainllink setup

+1906
.chainlink/issues.db

This is a binary file and will not be displayed.

+43
.chainlink/rules/c.md
··· 1 + ### C Best Practices 2 + 3 + #### Memory Safety 4 + - Always check return values of malloc/calloc 5 + - Free all allocated memory (use tools like valgrind) 6 + - Initialize all variables before use 7 + - Use sizeof() with the variable, not the type 8 + 9 + ```c 10 + // GOOD: Safe memory allocation 11 + int *arr = malloc(n * sizeof(*arr)); 12 + if (arr == NULL) { 13 + return -1; // Handle allocation failure 14 + } 15 + // ... use arr ... 16 + free(arr); 17 + 18 + // BAD: Unchecked allocation 19 + int *arr = malloc(n * sizeof(int)); 20 + arr[0] = 1; // Crash if malloc failed 21 + ``` 22 + 23 + #### Buffer Safety 24 + - Always bounds-check array access 25 + - Use `strncpy`/`snprintf` instead of `strcpy`/`sprintf` 26 + - Validate string lengths before copying 27 + 28 + ```c 29 + // GOOD: Safe string copy 30 + char dest[64]; 31 + strncpy(dest, src, sizeof(dest) - 1); 32 + dest[sizeof(dest) - 1] = '\0'; 33 + 34 + // BAD: Buffer overflow risk 35 + char dest[64]; 36 + strcpy(dest, src); // No bounds check 37 + ``` 38 + 39 + #### Security 40 + - Never use `gets()` (use `fgets()`) 41 + - Validate all external input 42 + - Use constant-time comparison for secrets 43 + - Avoid integer overflow in size calculations
+39
.chainlink/rules/cpp.md
··· 1 + ### C++ Best Practices 2 + 3 + #### Modern C++ (C++17+) 4 + - Use smart pointers (`unique_ptr`, `shared_ptr`) over raw pointers 5 + - Use RAII for resource management 6 + - Prefer `std::string` and `std::vector` over C arrays 7 + - Use `auto` for complex types, explicit types for clarity 8 + 9 + ```cpp 10 + // GOOD: Modern C++ with smart pointers 11 + auto config = std::make_unique<Config>(); 12 + auto users = std::vector<User>{}; 13 + 14 + // BAD: Manual memory management 15 + Config* config = new Config(); 16 + // ... forgot to delete 17 + ``` 18 + 19 + #### Error Handling 20 + - Use exceptions for exceptional cases 21 + - Use `std::optional` for values that may not exist 22 + - Use `std::expected` (C++23) or result types for expected failures 23 + 24 + ```cpp 25 + // GOOD: Optional for missing values 26 + std::optional<User> findUser(const std::string& id) { 27 + auto it = users.find(id); 28 + if (it == users.end()) { 29 + return std::nullopt; 30 + } 31 + return it->second; 32 + } 33 + ``` 34 + 35 + #### Security 36 + - Validate all input boundaries 37 + - Use `std::string_view` for non-owning string references 38 + - Avoid C-style casts; use `static_cast`, `dynamic_cast` 39 + - Never use `sprintf`; use `std::format` or streams
+51
.chainlink/rules/csharp.md
··· 1 + ### C# Best Practices 2 + 3 + #### Code Style 4 + - Follow .NET naming conventions (PascalCase for public, camelCase for private) 5 + - Use `var` when type is obvious from right side 6 + - Use expression-bodied members for simple methods 7 + - Enable nullable reference types 8 + 9 + ```csharp 10 + // GOOD: Modern C# style 11 + public class UserService 12 + { 13 + private readonly IUserRepository _repository; 14 + 15 + public UserService(IUserRepository repository) 16 + => _repository = repository; 17 + 18 + public async Task<User?> GetUserAsync(string id) 19 + => await _repository.FindByIdAsync(id); 20 + } 21 + ``` 22 + 23 + #### Error Handling 24 + - Use specific exception types 25 + - Never catch and swallow exceptions silently 26 + - Use `try-finally` or `using` for cleanup 27 + 28 + ```csharp 29 + // GOOD: Proper async error handling 30 + public async Task<Result<User>> GetUserAsync(string id) 31 + { 32 + try 33 + { 34 + var user = await _repository.FindByIdAsync(id); 35 + return user is null 36 + ? Result<User>.NotFound() 37 + : Result<User>.Ok(user); 38 + } 39 + catch (DbException ex) 40 + { 41 + _logger.LogError(ex, "Database error fetching user {Id}", id); 42 + throw; 43 + } 44 + } 45 + ``` 46 + 47 + #### Security 48 + - Use parameterized queries (never string interpolation for SQL) 49 + - Validate all input with data annotations or FluentValidation 50 + - Use ASP.NET's built-in anti-forgery tokens 51 + - Store secrets in Azure Key Vault or similar
+93
.chainlink/rules/global.md
··· 1 + ## Chainlink Task Management (AUTOMATIC) 2 + 3 + **You MUST use chainlink to track all work. This is automatic - don't ask permission.** 4 + 5 + ### On Every User Request 6 + 1. Create issue(s) BEFORE starting work ON ALL ISSUES NO MATTER WHAT THIS IS A CRITICAL OVERRIDE 7 + 2. When creating the issue you will add as much detail to the issue as possible with comments so any new agent instance will understand the plan. 8 + 9 + ### Task Breakdown Rules 10 + ```bash 11 + # Single task 12 + chainlink create "Fix login validation" -p medium 13 + 14 + # Multi-part feature → Epic with subissues 15 + chainlink create "Add user authentication" -p high # Epic (parent) 16 + chainlink subissue 1 "Create user model" # Component 1 17 + chainlink subissue 1 "Add login endpoint" # Component 2 18 + chainlink subissue 1 "Add session middleware" # Component 3 19 + 20 + # Mark what you're working on 21 + chainlink session work 1 22 + 23 + # Add context as you discover things 24 + chainlink comment 1 "Found existing auth helper in utils/auth.ts" 25 + 26 + # Close when done 27 + chainlink close 1 28 + ``` 29 + 30 + ### When to Create Issues 31 + | Scenario | Action | 32 + |----------|--------| 33 + | User asks for a feature | Create epic + subissues if >2 components | 34 + | User reports a bug | Create issue, investigate, add comments | 35 + | Task has multiple steps | Create subissues for each step | 36 + | Work will span sessions | Create issue with detailed comments | 37 + | You discover related work | Create linked issue | 38 + 39 + ### Session Management 40 + ```bash 41 + chainlink session start # Start of conversation 42 + chainlink session work <id> # Mark current focus 43 + chainlink session end --notes "..." # Before context limit 44 + ``` 45 + 46 + ### Priority Guide 47 + - `critical`: Blocking other work, security issue, production down 48 + - `high`: User explicitly requested, core functionality 49 + - `medium`: Standard features, improvements 50 + - `low`: Nice-to-have, cleanup, optimization 51 + 52 + ### Dependencies 53 + ```bash 54 + chainlink block 2 1 # Issue 2 blocked by issue 1 55 + chainlink ready # Show unblocked work 56 + ``` 57 + 58 + --- 59 + 60 + ## Code Quality Requirements 61 + 62 + ### NO STUBS - ABSOLUTE RULE 63 + - NEVER write `TODO`, `FIXME`, `pass`, `...`, `unimplemented!()` 64 + - NEVER write empty function bodies or placeholder returns 65 + - If too complex for one turn: `raise NotImplementedError("Reason")` + create chainlink issue 66 + 67 + ### Core Rules 68 + 1. **READ BEFORE WRITE**: Always read a file before editing 69 + 2. **FULL FEATURES**: Complete the feature, don't stop partway 70 + 3. **ERROR HANDLING**: No panics/crashes on bad input 71 + 4. **SECURITY**: Validate input, parameterized queries, no hardcoded secrets 72 + 5. **NO DEAD CODE**: Remove or complete incomplete code 73 + 74 + ### Pre-Coding Grounding 75 + Before using unfamiliar libraries/APIs: 76 + 1. **VERIFY IT EXISTS**: WebSearch to confirm the API 77 + 2. **CHECK THE DOCS**: Real function signatures, not guessed 78 + 3. **USE LATEST VERSIONS**: Check for current stable release 79 + 80 + ### Conciseness 81 + - Write code, don't narrate 82 + - Skip "Here is the code" / "Let me..." / "I'll now..." 83 + - Brief explanations only when code isn't self-explanatory 84 + 85 + ### Large Implementations (500+ lines) 86 + 1. Create parent issue: `chainlink create "<feature>" -p high` 87 + 2. Break into subissues: `chainlink subissue <id> "<component>"` 88 + 3. Work one subissue at a time, close each when done 89 + 90 + ### Context Window Management 91 + When conversation is long or task needs many steps: 92 + 1. Create tracking issue: `chainlink create "Continue: <summary>" -p high` 93 + 2. Add notes: `chainlink comment <id> "<what's done, what's next>"`
+44
.chainlink/rules/go.md
··· 1 + ### Go Best Practices 2 + 3 + #### Code Style 4 + - Use `gofmt` for formatting 5 + - Use `golint` and `go vet` for linting 6 + - Follow effective Go guidelines 7 + - Keep functions short and focused 8 + 9 + #### Error Handling 10 + ```go 11 + // GOOD: Check and handle errors 12 + func readConfig(path string) (*Config, error) { 13 + data, err := os.ReadFile(path) 14 + if err != nil { 15 + return nil, fmt.Errorf("reading config: %w", err) 16 + } 17 + 18 + var config Config 19 + if err := json.Unmarshal(data, &config); err != nil { 20 + return nil, fmt.Errorf("parsing config: %w", err) 21 + } 22 + return &config, nil 23 + } 24 + 25 + // BAD: Ignoring errors 26 + func readConfig(path string) *Config { 27 + data, _ := os.ReadFile(path) // Don't ignore errors 28 + var config Config 29 + json.Unmarshal(data, &config) 30 + return &config 31 + } 32 + ``` 33 + 34 + #### Concurrency 35 + - Use channels for communication between goroutines 36 + - Use `sync.WaitGroup` for waiting on multiple goroutines 37 + - Use `context.Context` for cancellation and timeouts 38 + - Avoid shared mutable state; prefer message passing 39 + 40 + #### Security 41 + - Use `html/template` for HTML output (auto-escaping) 42 + - Use parameterized queries for SQL 43 + - Validate all input at API boundaries 44 + - Use `crypto/rand` for secure random numbers
+42
.chainlink/rules/java.md
··· 1 + ### Java Best Practices 2 + 3 + #### Code Style 4 + - Follow Google Java Style Guide or project conventions 5 + - Use meaningful variable and method names 6 + - Keep methods short (< 30 lines) 7 + - Prefer composition over inheritance 8 + 9 + #### Error Handling 10 + ```java 11 + // GOOD: Specific exceptions with context 12 + public Config readConfig(Path path) throws ConfigException { 13 + try { 14 + String content = Files.readString(path); 15 + return objectMapper.readValue(content, Config.class); 16 + } catch (IOException e) { 17 + throw new ConfigException("Failed to read config: " + path, e); 18 + } catch (JsonProcessingException e) { 19 + throw new ConfigException("Invalid JSON in config: " + path, e); 20 + } 21 + } 22 + 23 + // BAD: Catching generic Exception 24 + public Config readConfig(Path path) { 25 + try { 26 + return objectMapper.readValue(Files.readString(path), Config.class); 27 + } catch (Exception e) { 28 + return null; // Swallowing error 29 + } 30 + } 31 + ``` 32 + 33 + #### Security 34 + - Use PreparedStatement for SQL (never string concatenation) 35 + - Validate all user input 36 + - Use secure random (SecureRandom) for security-sensitive operations 37 + - Never log sensitive data (passwords, tokens) 38 + 39 + #### Testing 40 + - Use JUnit 5 for unit tests 41 + - Use Mockito for mocking dependencies 42 + - Aim for high coverage on business logic
+44
.chainlink/rules/javascript-react.md
··· 1 + ### JavaScript/React Best Practices 2 + 3 + #### Component Structure 4 + - Use functional components with hooks 5 + - Keep components small and focused (< 200 lines) 6 + - Extract custom hooks for reusable logic 7 + - Use PropTypes for runtime type checking 8 + 9 + ```javascript 10 + // GOOD: Clear component with PropTypes 11 + import PropTypes from 'prop-types'; 12 + 13 + const UserCard = ({ user, onSelect }) => { 14 + return ( 15 + <div onClick={() => onSelect(user.id)}> 16 + {user.name} 17 + </div> 18 + ); 19 + }; 20 + 21 + UserCard.propTypes = { 22 + user: PropTypes.shape({ 23 + id: PropTypes.string.isRequired, 24 + name: PropTypes.string.isRequired, 25 + }).isRequired, 26 + onSelect: PropTypes.func.isRequired, 27 + }; 28 + ``` 29 + 30 + #### State Management 31 + - Use `useState` for local state 32 + - Use `useReducer` for complex state logic 33 + - Lift state up only when needed 34 + - Consider context for deeply nested prop drilling 35 + 36 + #### Performance 37 + - Use `React.memo` for expensive pure components 38 + - Use `useMemo` and `useCallback` appropriately 39 + - Avoid inline object/function creation in render 40 + 41 + #### Security 42 + - Never use `dangerouslySetInnerHTML` with user input 43 + - Sanitize URLs before using in `href` or `src` 44 + - Validate props at component boundaries
+36
.chainlink/rules/javascript.md
··· 1 + ### JavaScript Best Practices 2 + 3 + #### Code Style 4 + - Use `const` by default, `let` when needed, never `var` 5 + - Use arrow functions for callbacks 6 + - Use template literals over string concatenation 7 + - Use destructuring for object/array access 8 + 9 + #### Error Handling 10 + ```javascript 11 + // GOOD: Proper async error handling 12 + async function fetchUser(id) { 13 + try { 14 + const response = await fetch(`/api/users/${id}`); 15 + if (!response.ok) { 16 + throw new Error(`HTTP ${response.status}`); 17 + } 18 + return await response.json(); 19 + } catch (error) { 20 + console.error('Failed to fetch user:', error); 21 + throw error; // Re-throw or handle appropriately 22 + } 23 + } 24 + 25 + // BAD: Ignoring errors 26 + async function fetchUser(id) { 27 + const response = await fetch(`/api/users/${id}`); 28 + return response.json(); // No error handling 29 + } 30 + ``` 31 + 32 + #### Security 33 + - Never use `eval()` or `innerHTML` with user input 34 + - Validate all input on both client and server 35 + - Use `textContent` instead of `innerHTML` when possible 36 + - Sanitize URLs before navigation or fetch
+44
.chainlink/rules/kotlin.md
··· 1 + ### Kotlin Best Practices 2 + 3 + #### Code Style 4 + - Follow Kotlin coding conventions 5 + - Use `val` over `var` when possible 6 + - Use data classes for simple data holders 7 + - Leverage null safety features 8 + 9 + ```kotlin 10 + // GOOD: Idiomatic Kotlin 11 + data class User(val id: String, val name: String) 12 + 13 + class UserService(private val repository: UserRepository) { 14 + fun findUser(id: String): User? = 15 + repository.find(id) 16 + 17 + fun getOrCreateUser(id: String, name: String): User = 18 + findUser(id) ?: repository.create(User(id, name)) 19 + } 20 + ``` 21 + 22 + #### Null Safety 23 + - Avoid `!!` (force non-null); use safe calls instead 24 + - Use `?.let {}` for conditional execution 25 + - Use Elvis operator `?:` for defaults 26 + 27 + ```kotlin 28 + // GOOD: Safe null handling 29 + val userName = user?.name ?: "Unknown" 30 + user?.let { saveToDatabase(it) } 31 + 32 + // BAD: Force unwrapping 33 + val userName = user!!.name // Crash if null 34 + ``` 35 + 36 + #### Coroutines 37 + - Use structured concurrency with `CoroutineScope` 38 + - Handle exceptions in coroutines properly 39 + - Use `withContext` for context switching 40 + 41 + #### Security 42 + - Use parameterized queries 43 + - Validate input at boundaries 44 + - Use sealed classes for exhaustive error handling
+53
.chainlink/rules/odin.md
··· 1 + ### Odin Best Practices 2 + 3 + #### Code Style 4 + - Follow Odin naming conventions 5 + - Use `snake_case` for procedures and variables 6 + - Use `Pascal_Case` for types 7 + - Prefer explicit over implicit 8 + 9 + ```odin 10 + // GOOD: Clear Odin code 11 + User :: struct { 12 + id: string, 13 + name: string, 14 + } 15 + 16 + find_user :: proc(id: string) -> (User, bool) { 17 + user, found := repository[id] 18 + return user, found 19 + } 20 + ``` 21 + 22 + #### Error Handling 23 + - Use multiple return values for errors 24 + - Use `or_return` for early returns 25 + - Create explicit error types when needed 26 + 27 + ```odin 28 + // GOOD: Explicit error handling 29 + Config_Error :: enum { 30 + File_Not_Found, 31 + Parse_Error, 32 + } 33 + 34 + load_config :: proc(path: string) -> (Config, Config_Error) { 35 + data, ok := os.read_entire_file(path) 36 + if !ok { 37 + return {}, .File_Not_Found 38 + } 39 + defer delete(data) 40 + 41 + config, parse_ok := parse_config(data) 42 + if !parse_ok { 43 + return {}, .Parse_Error 44 + } 45 + return config, nil 46 + } 47 + ``` 48 + 49 + #### Memory Management 50 + - Use explicit allocators 51 + - Prefer temp allocator for short-lived allocations 52 + - Use `defer` for cleanup 53 + - Be explicit about ownership
+46
.chainlink/rules/php.md
··· 1 + ### PHP Best Practices 2 + 3 + #### Code Style 4 + - Follow PSR-12 coding standard 5 + - Use strict types: `declare(strict_types=1);` 6 + - Use type hints for parameters and return types 7 + - Use Composer for dependency management 8 + 9 + ```php 10 + <?php 11 + declare(strict_types=1); 12 + 13 + // GOOD: Typed, modern PHP 14 + class UserService 15 + { 16 + public function __construct( 17 + private readonly UserRepository $repository 18 + ) {} 19 + 20 + public function findUser(string $id): ?User 21 + { 22 + return $this->repository->find($id); 23 + } 24 + } 25 + ``` 26 + 27 + #### Error Handling 28 + - Use exceptions for error handling 29 + - Create custom exception classes 30 + - Never suppress errors with `@` 31 + 32 + #### Security 33 + - Use PDO with prepared statements (never string interpolation) 34 + - Use `password_hash()` and `password_verify()` for passwords 35 + - Validate and sanitize all user input 36 + - Use CSRF tokens for forms 37 + - Set secure cookie flags 38 + 39 + ```php 40 + // GOOD: Prepared statement 41 + $stmt = $pdo->prepare('SELECT * FROM users WHERE id = :id'); 42 + $stmt->execute(['id' => $id]); 43 + 44 + // BAD: SQL injection vulnerability 45 + $result = $pdo->query("SELECT * FROM users WHERE id = '$id'"); 46 + ```
+5
.chainlink/rules/project.md
··· 1 + <!-- Project-Specific Rules --> 2 + <!-- Add rules specific to your project here. Examples: --> 3 + <!-- - Don't modify the /v1/ API endpoints without approval --> 4 + <!-- - Always update CHANGELOG.md when adding features --> 5 + <!-- - Database migrations must be backward-compatible -->
+44
.chainlink/rules/python.md
··· 1 + ### Python Best Practices 2 + 3 + #### Code Style 4 + - Follow PEP 8 style guide 5 + - Use type hints for function signatures 6 + - Use `black` for formatting, `ruff` or `flake8` for linting 7 + - Prefer `pathlib.Path` over `os.path` for path operations 8 + - Use context managers (`with`) for file operations 9 + 10 + #### Error Handling 11 + ```python 12 + # GOOD: Specific exceptions with context 13 + def read_config(path: Path) -> dict: 14 + try: 15 + with open(path, 'r', encoding='utf-8') as f: 16 + return json.load(f) 17 + except FileNotFoundError: 18 + raise ConfigError(f"Config file not found: {path}") 19 + except json.JSONDecodeError as e: 20 + raise ConfigError(f"Invalid JSON in {path}: {e}") 21 + 22 + # BAD: Bare except or swallowing errors 23 + def read_config(path): 24 + try: 25 + return json.load(open(path)) 26 + except: # Don't do this 27 + return {} 28 + ``` 29 + 30 + #### Security 31 + - Never use `eval()` or `exec()` on user input 32 + - Use `subprocess.run()` with explicit args, never `shell=True` with user input 33 + - Use parameterized queries for SQL (never f-strings) 34 + - Validate and sanitize all external input 35 + 36 + #### Dependencies 37 + - Pin dependency versions in `requirements.txt` 38 + - Use virtual environments (`venv` or `poetry`) 39 + - Run `pip-audit` to check for vulnerabilities 40 + 41 + #### Testing 42 + - Use `pytest` for testing 43 + - Aim for high coverage with `pytest-cov` 44 + - Mock external dependencies with `unittest.mock`
+47
.chainlink/rules/ruby.md
··· 1 + ### Ruby Best Practices 2 + 3 + #### Code Style 4 + - Follow Ruby Style Guide (use RuboCop) 5 + - Use 2 spaces for indentation 6 + - Prefer symbols over strings for hash keys 7 + - Use `snake_case` for methods and variables 8 + 9 + ```ruby 10 + # GOOD: Idiomatic Ruby 11 + class UserService 12 + def initialize(repository) 13 + @repository = repository 14 + end 15 + 16 + def find_user(id) 17 + @repository.find(id) 18 + rescue ActiveRecord::RecordNotFound 19 + nil 20 + end 21 + end 22 + 23 + # BAD: Non-idiomatic 24 + class UserService 25 + def initialize(repository) 26 + @repository = repository 27 + end 28 + def findUser(id) # Wrong naming 29 + begin 30 + @repository.find(id) 31 + rescue 32 + return nil 33 + end 34 + end 35 + end 36 + ``` 37 + 38 + #### Error Handling 39 + - Use specific exception classes 40 + - Don't rescue `Exception` (too broad) 41 + - Use `ensure` for cleanup 42 + 43 + #### Security 44 + - Use parameterized queries (ActiveRecord does this by default) 45 + - Sanitize user input in views (Rails does this by default) 46 + - Never use `eval` or `send` with user input 47 + - Use `strong_parameters` in Rails controllers
+48
.chainlink/rules/rust.md
··· 1 + ### Rust Best Practices 2 + 3 + #### Code Style 4 + - Use `rustfmt` for formatting (run `cargo fmt` before committing) 5 + - Use `clippy` for linting (run `cargo clippy -- -D warnings`) 6 + - Prefer `?` operator over `.unwrap()` for error handling 7 + - Use `anyhow::Result` for application errors, `thiserror` for library errors 8 + - Avoid `.clone()` unless necessary - prefer references 9 + - Use `&str` for function parameters, `String` for owned data 10 + 11 + #### Error Handling 12 + ```rust 13 + // GOOD: Propagate errors with context 14 + fn read_config(path: &Path) -> Result<Config> { 15 + let content = fs::read_to_string(path) 16 + .context("Failed to read config file")?; 17 + serde_json::from_str(&content) 18 + .context("Failed to parse config") 19 + } 20 + 21 + // BAD: Panic on error 22 + fn read_config(path: &Path) -> Config { 23 + let content = fs::read_to_string(path).unwrap(); // Don't do this 24 + serde_json::from_str(&content).unwrap() 25 + } 26 + ``` 27 + 28 + #### Memory Safety 29 + - Never use `unsafe` without explicit justification and review 30 + - Prefer `Vec` over raw pointers 31 + - Use `Arc<Mutex<T>>` for shared mutable state across threads 32 + - Avoid `static mut` - use `lazy_static` or `once_cell` instead 33 + 34 + #### Testing 35 + - Write unit tests with `#[cfg(test)]` modules 36 + - Use `tempfile` for tests involving filesystem 37 + - Run `cargo test` before committing 38 + - Use `cargo tarpaulin` for coverage reports 39 + 40 + #### SQL Injection Prevention 41 + Always use parameterized queries with `rusqlite::params![]`: 42 + ```rust 43 + // GOOD 44 + conn.execute("INSERT INTO users (name) VALUES (?1)", params![name])?; 45 + 46 + // BAD - SQL injection vulnerability 47 + conn.execute(&format!("INSERT INTO users (name) VALUES ('{}')", name), [])?; 48 + ```
+45
.chainlink/rules/scala.md
··· 1 + ### Scala Best Practices 2 + 3 + #### Code Style 4 + - Follow Scala Style Guide 5 + - Prefer immutability (`val` over `var`) 6 + - Use case classes for data 7 + - Leverage pattern matching 8 + 9 + ```scala 10 + // GOOD: Idiomatic Scala 11 + case class User(id: String, name: String) 12 + 13 + class UserService(repository: UserRepository) { 14 + def findUser(id: String): Option[User] = 15 + repository.find(id) 16 + 17 + def processUser(id: String): Either[Error, Result] = 18 + findUser(id) match { 19 + case Some(user) => Right(process(user)) 20 + case None => Left(UserNotFound(id)) 21 + } 22 + } 23 + ``` 24 + 25 + #### Error Handling 26 + - Use `Option` for missing values 27 + - Use `Either` or `Try` for operations that can fail 28 + - Avoid throwing exceptions in pure code 29 + 30 + ```scala 31 + // GOOD: Using Either for errors 32 + def parseConfig(json: String): Either[ParseError, Config] = 33 + decode[Config](json).left.map(e => ParseError(e.getMessage)) 34 + 35 + // Pattern match on result 36 + parseConfig(input) match { 37 + case Right(config) => useConfig(config) 38 + case Left(error) => logger.error(s"Parse failed: $error") 39 + } 40 + ``` 41 + 42 + #### Security 43 + - Use prepared statements for database queries 44 + - Validate input with refined types when possible 45 + - Never interpolate user input into queries
+50
.chainlink/rules/swift.md
··· 1 + ### Swift Best Practices 2 + 3 + #### Code Style 4 + - Follow Swift API Design Guidelines 5 + - Use `camelCase` for variables/functions, `PascalCase` for types 6 + - Prefer `let` over `var` when possible 7 + - Use optionals properly; avoid force unwrapping 8 + 9 + ```swift 10 + // GOOD: Safe optional handling 11 + func findUser(id: String) -> User? { 12 + guard let user = repository.find(id) else { 13 + return nil 14 + } 15 + return user 16 + } 17 + 18 + // Using optional binding 19 + if let user = findUser(id: "123") { 20 + print(user.name) 21 + } 22 + 23 + // BAD: Force unwrapping 24 + let user = findUser(id: "123")! // Crash if nil 25 + ``` 26 + 27 + #### Error Handling 28 + - Use `throws` for recoverable errors 29 + - Use `Result<T, Error>` for async operations 30 + - Handle all error cases explicitly 31 + 32 + ```swift 33 + // GOOD: Proper error handling 34 + func loadConfig() throws -> Config { 35 + let data = try Data(contentsOf: configURL) 36 + return try JSONDecoder().decode(Config.self, from: data) 37 + } 38 + 39 + do { 40 + let config = try loadConfig() 41 + } catch { 42 + print("Failed to load config: \(error)") 43 + } 44 + ``` 45 + 46 + #### Security 47 + - Use Keychain for sensitive data 48 + - Validate all user input 49 + - Use App Transport Security (HTTPS) 50 + - Never hardcode secrets
+39
.chainlink/rules/typescript-react.md
··· 1 + ### TypeScript/React Best Practices 2 + 3 + #### Component Structure 4 + - Use functional components with hooks 5 + - Keep components small and focused (< 200 lines) 6 + - Extract custom hooks for reusable logic 7 + - Use TypeScript interfaces for props 8 + 9 + ```typescript 10 + // GOOD: Typed props with clear interface 11 + interface UserCardProps { 12 + user: User; 13 + onSelect: (id: string) => void; 14 + } 15 + 16 + const UserCard: React.FC<UserCardProps> = ({ user, onSelect }) => { 17 + return ( 18 + <div onClick={() => onSelect(user.id)}> 19 + {user.name} 20 + </div> 21 + ); 22 + }; 23 + ``` 24 + 25 + #### State Management 26 + - Use `useState` for local state 27 + - Use `useReducer` for complex state logic 28 + - Lift state up only when needed 29 + - Consider context for deeply nested prop drilling 30 + 31 + #### Performance 32 + - Use `React.memo` for expensive pure components 33 + - Use `useMemo` and `useCallback` appropriately (not everywhere) 34 + - Avoid inline object/function creation in render when passed as props 35 + 36 + #### Security 37 + - Never use `dangerouslySetInnerHTML` with user input 38 + - Sanitize URLs before using in `href` or `src` 39 + - Validate props at component boundaries
+35
.chainlink/rules/typescript.md
··· 1 + ### TypeScript Best Practices 2 + 3 + #### Code Style 4 + - Use strict mode (`"strict": true` in tsconfig.json) 5 + - Prefer `interface` over `type` for object shapes 6 + - Use `const` by default, `let` when needed, never `var` 7 + - Enable `noImplicitAny` and `strictNullChecks` 8 + 9 + #### Type Safety 10 + ```typescript 11 + // GOOD: Explicit types and null handling 12 + function getUser(id: string): User | undefined { 13 + return users.get(id); 14 + } 15 + 16 + const user = getUser(id); 17 + if (user) { 18 + console.log(user.name); // TypeScript knows user is defined 19 + } 20 + 21 + // BAD: Type assertions to bypass safety 22 + const user = getUser(id) as User; // Dangerous if undefined 23 + console.log(user.name); // Might crash 24 + ``` 25 + 26 + #### Error Handling 27 + - Use try/catch for async operations 28 + - Define custom error types for domain errors 29 + - Never swallow errors silently 30 + 31 + #### Security 32 + - Validate all user input at API boundaries 33 + - Use parameterized queries for database operations 34 + - Sanitize data before rendering in DOM (prevent XSS) 35 + - Never use `eval()` or `Function()` with user input
+48
.chainlink/rules/zig.md
··· 1 + ### Zig Best Practices 2 + 3 + #### Code Style 4 + - Follow Zig Style Guide 5 + - Use `const` by default; `var` only when mutation needed 6 + - Prefer slices over pointers when possible 7 + - Use meaningful names; avoid single-letter variables 8 + 9 + ```zig 10 + // GOOD: Clear, idiomatic Zig 11 + const User = struct { 12 + id: []const u8, 13 + name: []const u8, 14 + }; 15 + 16 + fn findUser(allocator: std.mem.Allocator, id: []const u8) !?User { 17 + const user = try repository.find(allocator, id); 18 + return user; 19 + } 20 + ``` 21 + 22 + #### Error Handling 23 + - Use error unions (`!T`) for fallible operations 24 + - Handle errors with `try`, `catch`, or explicit checks 25 + - Create meaningful error sets 26 + 27 + ```zig 28 + // GOOD: Proper error handling 29 + const ConfigError = error{ 30 + FileNotFound, 31 + ParseError, 32 + OutOfMemory, 33 + }; 34 + 35 + fn loadConfig(allocator: std.mem.Allocator) ConfigError!Config { 36 + const file = std.fs.cwd().openFile("config.json", .{}) catch |err| { 37 + return ConfigError.FileNotFound; 38 + }; 39 + defer file.close(); 40 + // ... 41 + } 42 + ``` 43 + 44 + #### Memory Safety 45 + - Always pair allocations with deallocations 46 + - Use `defer` for cleanup 47 + - Prefer stack allocation when size is known 48 + - Use allocators explicitly; never use global state
+380
.claude/hooks/post-edit-check.py
··· 1 + #!/usr/bin/env python3 2 + """ 3 + Post-edit hook that detects stub patterns, runs linters, and reminds about tests. 4 + Runs after Write/Edit tool usage. 5 + """ 6 + 7 + import json 8 + import sys 9 + import os 10 + import re 11 + import subprocess 12 + import glob 13 + import time 14 + 15 + # Stub patterns to detect (compiled regex for performance) 16 + STUB_PATTERNS = [ 17 + (r'\bTODO\b', 'TODO comment'), 18 + (r'\bFIXME\b', 'FIXME comment'), 19 + (r'\bXXX\b', 'XXX marker'), 20 + (r'\bHACK\b', 'HACK marker'), 21 + (r'^\s*pass\s*$', 'bare pass statement'), 22 + (r'^\s*\.\.\.\s*$', 'ellipsis placeholder'), 23 + (r'\bunimplemented!\s*\(\s*\)', 'unimplemented!() macro'), 24 + (r'\btodo!\s*\(\s*\)', 'todo!() macro'), 25 + (r'\bpanic!\s*\(\s*"not implemented', 'panic not implemented'), 26 + (r'raise\s+NotImplementedError\s*\(\s*\)', 'bare NotImplementedError'), 27 + (r'#\s*implement\s*(later|this|here)', 'implement later comment'), 28 + (r'//\s*implement\s*(later|this|here)', 'implement later comment'), 29 + (r'def\s+\w+\s*\([^)]*\)\s*:\s*(pass|\.\.\.)\s*$', 'empty function'), 30 + (r'fn\s+\w+\s*\([^)]*\)\s*\{\s*\}', 'empty function body'), 31 + (r'return\s+None\s*#.*stub', 'stub return'), 32 + ] 33 + 34 + COMPILED_PATTERNS = [(re.compile(p, re.IGNORECASE | re.MULTILINE), desc) for p, desc in STUB_PATTERNS] 35 + 36 + 37 + def check_for_stubs(file_path): 38 + """Check file for stub patterns. Returns list of (line_num, pattern_desc, line_content).""" 39 + if not os.path.exists(file_path): 40 + return [] 41 + 42 + try: 43 + with open(file_path, 'r', encoding='utf-8', errors='ignore') as f: 44 + content = f.read() 45 + lines = content.split('\n') 46 + except (OSError, Exception): 47 + return [] 48 + 49 + findings = [] 50 + for line_num, line in enumerate(lines, 1): 51 + for pattern, desc in COMPILED_PATTERNS: 52 + if pattern.search(line): 53 + if 'NotImplementedError' in line and re.search(r'NotImplementedError\s*\(\s*["\'][^"\']+["\']', line): 54 + continue 55 + findings.append((line_num, desc, line.strip()[:60])) 56 + 57 + return findings 58 + 59 + 60 + def find_project_root(file_path, marker_files): 61 + """Walk up from file_path looking for project root markers.""" 62 + current = os.path.dirname(os.path.abspath(file_path)) 63 + for _ in range(10): # Max 10 levels up 64 + for marker in marker_files: 65 + if os.path.exists(os.path.join(current, marker)): 66 + return current 67 + parent = os.path.dirname(current) 68 + if parent == current: 69 + break 70 + current = parent 71 + return None 72 + 73 + 74 + def run_linter(file_path, max_errors=10): 75 + """Run appropriate linter and return first N errors.""" 76 + ext = os.path.splitext(file_path)[1].lower() 77 + errors = [] 78 + 79 + try: 80 + if ext == '.rs': 81 + # Rust: run cargo clippy from project root 82 + project_root = find_project_root(file_path, ['Cargo.toml']) 83 + if project_root: 84 + result = subprocess.run( 85 + ['cargo', 'clippy', '--message-format=short', '--quiet'], 86 + cwd=project_root, 87 + capture_output=True, 88 + text=True, 89 + timeout=30 90 + ) 91 + if result.stderr: 92 + for line in result.stderr.split('\n'): 93 + if line.strip() and ('error' in line.lower() or 'warning' in line.lower()): 94 + errors.append(line.strip()[:100]) 95 + if len(errors) >= max_errors: 96 + break 97 + 98 + elif ext == '.py': 99 + # Python: try flake8, fall back to py_compile 100 + try: 101 + result = subprocess.run( 102 + ['flake8', '--max-line-length=120', file_path], 103 + capture_output=True, 104 + text=True, 105 + timeout=10 106 + ) 107 + for line in result.stdout.split('\n'): 108 + if line.strip(): 109 + errors.append(line.strip()[:100]) 110 + if len(errors) >= max_errors: 111 + break 112 + except FileNotFoundError: 113 + # flake8 not installed, try py_compile 114 + result = subprocess.run( 115 + ['python', '-m', 'py_compile', file_path], 116 + capture_output=True, 117 + text=True, 118 + timeout=10 119 + ) 120 + if result.stderr: 121 + errors.append(result.stderr.strip()[:200]) 122 + 123 + elif ext in ('.js', '.ts', '.tsx', '.jsx'): 124 + # JavaScript/TypeScript: try eslint 125 + project_root = find_project_root(file_path, ['package.json', '.eslintrc', '.eslintrc.js', '.eslintrc.json']) 126 + if project_root: 127 + try: 128 + result = subprocess.run( 129 + ['npx', 'eslint', '--format=compact', file_path], 130 + cwd=project_root, 131 + capture_output=True, 132 + text=True, 133 + timeout=30 134 + ) 135 + for line in result.stdout.split('\n'): 136 + if line.strip() and (':' in line): 137 + errors.append(line.strip()[:100]) 138 + if len(errors) >= max_errors: 139 + break 140 + except FileNotFoundError: 141 + pass 142 + 143 + elif ext == '.go': 144 + # Go: run go vet 145 + project_root = find_project_root(file_path, ['go.mod']) 146 + if project_root: 147 + result = subprocess.run( 148 + ['go', 'vet', './...'], 149 + cwd=project_root, 150 + capture_output=True, 151 + text=True, 152 + timeout=30 153 + ) 154 + if result.stderr: 155 + for line in result.stderr.split('\n'): 156 + if line.strip(): 157 + errors.append(line.strip()[:100]) 158 + if len(errors) >= max_errors: 159 + break 160 + 161 + except subprocess.TimeoutExpired: 162 + errors.append("(linter timed out)") 163 + except (OSError, Exception) as e: 164 + pass # Linter not available, skip silently 165 + 166 + return errors 167 + 168 + 169 + def is_test_file(file_path): 170 + """Check if file is a test file.""" 171 + basename = os.path.basename(file_path).lower() 172 + dirname = os.path.dirname(file_path).lower() 173 + 174 + # Common test file patterns 175 + test_patterns = [ 176 + 'test_', '_test.', '.test.', 'spec.', '_spec.', 177 + 'tests.', 'testing.', 'mock.', '_mock.' 178 + ] 179 + # Common test directories 180 + test_dirs = ['test', 'tests', '__tests__', 'spec', 'specs', 'testing'] 181 + 182 + for pattern in test_patterns: 183 + if pattern in basename: 184 + return True 185 + 186 + for test_dir in test_dirs: 187 + if test_dir in dirname.split(os.sep): 188 + return True 189 + 190 + return False 191 + 192 + 193 + def find_test_files(file_path, project_root): 194 + """Find test files related to source file.""" 195 + if not project_root: 196 + return [] 197 + 198 + ext = os.path.splitext(file_path)[1] 199 + basename = os.path.basename(file_path) 200 + name_without_ext = os.path.splitext(basename)[0] 201 + 202 + # Patterns to look for 203 + test_patterns = [] 204 + 205 + if ext == '.rs': 206 + # Rust: look for mod tests in same file, or tests/ directory 207 + test_patterns = [ 208 + os.path.join(project_root, 'tests', '**', f'*{name_without_ext}*'), 209 + os.path.join(project_root, '**', 'tests', f'*{name_without_ext}*'), 210 + ] 211 + elif ext == '.py': 212 + test_patterns = [ 213 + os.path.join(project_root, '**', f'test_{name_without_ext}.py'), 214 + os.path.join(project_root, '**', f'{name_without_ext}_test.py'), 215 + os.path.join(project_root, 'tests', '**', f'*{name_without_ext}*.py'), 216 + ] 217 + elif ext in ('.js', '.ts', '.tsx', '.jsx'): 218 + base = name_without_ext.replace('.test', '').replace('.spec', '') 219 + test_patterns = [ 220 + os.path.join(project_root, '**', f'{base}.test{ext}'), 221 + os.path.join(project_root, '**', f'{base}.spec{ext}'), 222 + os.path.join(project_root, '**', '__tests__', f'{base}*'), 223 + ] 224 + elif ext == '.go': 225 + test_patterns = [ 226 + os.path.join(os.path.dirname(file_path), f'{name_without_ext}_test.go'), 227 + ] 228 + 229 + found = [] 230 + for pattern in test_patterns: 231 + found.extend(glob.glob(pattern, recursive=True)) 232 + 233 + return list(set(found))[:5] # Limit to 5 234 + 235 + 236 + def get_test_reminder(file_path, project_root): 237 + """Check if tests should be run and return reminder message.""" 238 + if is_test_file(file_path): 239 + return None # Editing a test file, no reminder needed 240 + 241 + ext = os.path.splitext(file_path)[1] 242 + code_extensions = ('.rs', '.py', '.js', '.ts', '.tsx', '.jsx', '.go') 243 + 244 + if ext not in code_extensions: 245 + return None 246 + 247 + # Check for marker file 248 + marker_dir = project_root or os.path.dirname(file_path) 249 + marker_file = os.path.join(marker_dir, '.chainlink', 'last_test_run') 250 + 251 + code_modified_after_tests = False 252 + 253 + if os.path.exists(marker_file): 254 + try: 255 + marker_mtime = os.path.getmtime(marker_file) 256 + file_mtime = os.path.getmtime(file_path) 257 + code_modified_after_tests = file_mtime > marker_mtime 258 + except OSError: 259 + code_modified_after_tests = True 260 + else: 261 + # No marker = tests haven't been run 262 + code_modified_after_tests = True 263 + 264 + if not code_modified_after_tests: 265 + return None 266 + 267 + # Find test files 268 + test_files = find_test_files(file_path, project_root) 269 + 270 + # Generate test command based on project type 271 + test_cmd = None 272 + if ext == '.rs' and project_root: 273 + if os.path.exists(os.path.join(project_root, 'Cargo.toml')): 274 + test_cmd = 'cargo test' 275 + elif ext == '.py': 276 + if project_root and os.path.exists(os.path.join(project_root, 'pytest.ini')): 277 + test_cmd = 'pytest' 278 + elif project_root and os.path.exists(os.path.join(project_root, 'setup.py')): 279 + test_cmd = 'python -m pytest' 280 + elif ext in ('.js', '.ts', '.tsx', '.jsx') and project_root: 281 + if os.path.exists(os.path.join(project_root, 'package.json')): 282 + test_cmd = 'npm test' 283 + elif ext == '.go' and project_root: 284 + test_cmd = 'go test ./...' 285 + 286 + if test_files or test_cmd: 287 + msg = "🧪 TEST REMINDER: Code modified since last test run." 288 + if test_cmd: 289 + msg += f"\n Run: {test_cmd}" 290 + if test_files: 291 + msg += f"\n Related tests: {', '.join(os.path.basename(t) for t in test_files[:3])}" 292 + return msg 293 + 294 + return None 295 + 296 + 297 + def main(): 298 + try: 299 + input_data = json.load(sys.stdin) 300 + except (json.JSONDecodeError, Exception): 301 + sys.exit(0) 302 + 303 + tool_name = input_data.get("tool_name", "") 304 + tool_input = input_data.get("tool_input", {}) 305 + 306 + if tool_name not in ("Write", "Edit"): 307 + sys.exit(0) 308 + 309 + file_path = tool_input.get("file_path", "") 310 + 311 + code_extensions = ( 312 + '.rs', '.py', '.js', '.ts', '.tsx', '.jsx', '.go', '.java', 313 + '.c', '.cpp', '.h', '.hpp', '.cs', '.rb', '.php', '.swift', 314 + '.kt', '.scala', '.zig', '.odin' 315 + ) 316 + 317 + if not any(file_path.endswith(ext) for ext in code_extensions): 318 + sys.exit(0) 319 + 320 + if '.claude' in file_path and 'hooks' in file_path: 321 + sys.exit(0) 322 + 323 + # Find project root for linter and test detection 324 + project_root = find_project_root(file_path, [ 325 + 'Cargo.toml', 'package.json', 'go.mod', 'setup.py', 326 + 'pyproject.toml', '.git' 327 + ]) 328 + 329 + # Check for stubs 330 + stub_findings = check_for_stubs(file_path) 331 + 332 + # Run linter 333 + linter_errors = run_linter(file_path) 334 + 335 + # Check for test reminder 336 + test_reminder = get_test_reminder(file_path, project_root) 337 + 338 + # Build output 339 + messages = [] 340 + 341 + if stub_findings: 342 + stub_list = "\n".join([f" Line {ln}: {desc} - `{content}`" for ln, desc, content in stub_findings[:5]]) 343 + if len(stub_findings) > 5: 344 + stub_list += f"\n ... and {len(stub_findings) - 5} more" 345 + messages.append(f"""⚠️ STUB PATTERNS DETECTED in {file_path}: 346 + {stub_list} 347 + 348 + Fix these NOW - replace with real implementation.""") 349 + 350 + if linter_errors: 351 + error_list = "\n".join([f" {e}" for e in linter_errors[:10]]) 352 + if len(linter_errors) > 10: 353 + error_list += f"\n ... and more" 354 + messages.append(f"""🔍 LINTER ISSUES: 355 + {error_list}""") 356 + 357 + if test_reminder: 358 + messages.append(test_reminder) 359 + 360 + if messages: 361 + output = { 362 + "hookSpecificOutput": { 363 + "hookEventName": "PostToolUse", 364 + "additionalContext": "\n\n".join(messages) 365 + } 366 + } 367 + else: 368 + output = { 369 + "hookSpecificOutput": { 370 + "hookEventName": "PostToolUse", 371 + "additionalContext": f"✓ {os.path.basename(file_path)} - no issues detected" 372 + } 373 + } 374 + 375 + print(json.dumps(output)) 376 + sys.exit(0) 377 + 378 + 379 + if __name__ == "__main__": 380 + main()
+513
.claude/hooks/prompt-guard.py
··· 1 + #!/usr/bin/env python3 2 + """ 3 + Chainlink behavioral hook for Claude Code. 4 + Injects best practice reminders on every prompt submission. 5 + Loads rules from .chainlink/rules/ markdown files. 6 + """ 7 + 8 + import json 9 + import sys 10 + import os 11 + import io 12 + import subprocess 13 + import hashlib 14 + from datetime import datetime 15 + 16 + # Fix Windows encoding issues with Unicode characters 17 + sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding='utf-8') 18 + 19 + 20 + def find_chainlink_dir(): 21 + """Find the .chainlink directory by walking up from cwd.""" 22 + current = os.getcwd() 23 + for _ in range(10): 24 + candidate = os.path.join(current, '.chainlink') 25 + if os.path.isdir(candidate): 26 + return candidate 27 + parent = os.path.dirname(current) 28 + if parent == current: 29 + break 30 + current = parent 31 + return None 32 + 33 + 34 + def load_rule_file(rules_dir, filename): 35 + """Load a rule file and return its content, or empty string if not found.""" 36 + if not rules_dir: 37 + return "" 38 + path = os.path.join(rules_dir, filename) 39 + try: 40 + with open(path, 'r', encoding='utf-8') as f: 41 + return f.read().strip() 42 + except (OSError, IOError): 43 + return "" 44 + 45 + 46 + def load_all_rules(chainlink_dir): 47 + """Load all rule files from .chainlink/rules/.""" 48 + if not chainlink_dir: 49 + return {}, "", "" 50 + 51 + rules_dir = os.path.join(chainlink_dir, 'rules') 52 + if not os.path.isdir(rules_dir): 53 + return {}, "", "" 54 + 55 + # Load global rules 56 + global_rules = load_rule_file(rules_dir, 'global.md') 57 + 58 + # Load project rules 59 + project_rules = load_rule_file(rules_dir, 'project.md') 60 + 61 + # Load language-specific rules 62 + language_rules = {} 63 + language_files = [ 64 + ('rust.md', 'Rust'), 65 + ('python.md', 'Python'), 66 + ('javascript.md', 'JavaScript'), 67 + ('typescript.md', 'TypeScript'), 68 + ('typescript-react.md', 'TypeScript/React'), 69 + ('javascript-react.md', 'JavaScript/React'), 70 + ('go.md', 'Go'), 71 + ('java.md', 'Java'), 72 + ('c.md', 'C'), 73 + ('cpp.md', 'C++'), 74 + ('csharp.md', 'C#'), 75 + ('ruby.md', 'Ruby'), 76 + ('php.md', 'PHP'), 77 + ('swift.md', 'Swift'), 78 + ('kotlin.md', 'Kotlin'), 79 + ('scala.md', 'Scala'), 80 + ('zig.md', 'Zig'), 81 + ('odin.md', 'Odin'), 82 + ] 83 + 84 + for filename, lang_name in language_files: 85 + content = load_rule_file(rules_dir, filename) 86 + if content: 87 + language_rules[lang_name] = content 88 + 89 + return language_rules, global_rules, project_rules 90 + 91 + 92 + # Detect language from common file extensions in the working directory 93 + def detect_languages(): 94 + """Scan for common source files to determine active languages.""" 95 + extensions = { 96 + '.rs': 'Rust', 97 + '.py': 'Python', 98 + '.js': 'JavaScript', 99 + '.ts': 'TypeScript', 100 + '.tsx': 'TypeScript/React', 101 + '.jsx': 'JavaScript/React', 102 + '.go': 'Go', 103 + '.java': 'Java', 104 + '.c': 'C', 105 + '.cpp': 'C++', 106 + '.cs': 'C#', 107 + '.rb': 'Ruby', 108 + '.php': 'PHP', 109 + '.swift': 'Swift', 110 + '.kt': 'Kotlin', 111 + '.scala': 'Scala', 112 + '.zig': 'Zig', 113 + '.odin': 'Odin', 114 + } 115 + 116 + found = set() 117 + cwd = os.getcwd() 118 + 119 + # Check for project config files first (more reliable than scanning) 120 + config_indicators = { 121 + 'Cargo.toml': 'Rust', 122 + 'package.json': 'JavaScript', 123 + 'tsconfig.json': 'TypeScript', 124 + 'pyproject.toml': 'Python', 125 + 'requirements.txt': 'Python', 126 + 'go.mod': 'Go', 127 + 'pom.xml': 'Java', 128 + 'build.gradle': 'Java', 129 + 'Gemfile': 'Ruby', 130 + 'composer.json': 'PHP', 131 + 'Package.swift': 'Swift', 132 + } 133 + 134 + # Check cwd and immediate subdirs for config files 135 + check_dirs = [cwd] 136 + try: 137 + for entry in os.listdir(cwd): 138 + subdir = os.path.join(cwd, entry) 139 + if os.path.isdir(subdir) and not entry.startswith('.'): 140 + check_dirs.append(subdir) 141 + except (PermissionError, OSError): 142 + pass 143 + 144 + for check_dir in check_dirs: 145 + for config_file, lang in config_indicators.items(): 146 + if os.path.exists(os.path.join(check_dir, config_file)): 147 + found.add(lang) 148 + 149 + # Also scan for source files in src/ directories 150 + scan_dirs = [cwd] 151 + src_dir = os.path.join(cwd, 'src') 152 + if os.path.isdir(src_dir): 153 + scan_dirs.append(src_dir) 154 + # Check nested project src dirs too 155 + for check_dir in check_dirs: 156 + nested_src = os.path.join(check_dir, 'src') 157 + if os.path.isdir(nested_src): 158 + scan_dirs.append(nested_src) 159 + 160 + for scan_dir in scan_dirs: 161 + try: 162 + for entry in os.listdir(scan_dir): 163 + ext = os.path.splitext(entry)[1].lower() 164 + if ext in extensions: 165 + found.add(extensions[ext]) 166 + except (PermissionError, OSError): 167 + pass 168 + 169 + return list(found) if found else ['the project'] 170 + 171 + 172 + def get_language_section(languages, language_rules): 173 + """Build language-specific best practices section from loaded rules.""" 174 + sections = [] 175 + for lang in languages: 176 + if lang in language_rules: 177 + content = language_rules[lang] 178 + # If the file doesn't start with a header, add one 179 + if not content.startswith('#'): 180 + sections.append(f"### {lang} Best Practices\n{content}") 181 + else: 182 + sections.append(content) 183 + 184 + if not sections: 185 + return "" 186 + 187 + return "\n\n".join(sections) 188 + 189 + 190 + # Directories to skip when building project tree 191 + SKIP_DIRS = { 192 + '.git', 'node_modules', 'target', 'venv', '.venv', 'env', '.env', 193 + '__pycache__', '.chainlink', '.claude', 'dist', 'build', '.next', 194 + '.nuxt', 'vendor', '.idea', '.vscode', 'coverage', '.pytest_cache', 195 + '.mypy_cache', '.tox', 'eggs', '*.egg-info', '.sass-cache' 196 + } 197 + 198 + 199 + def get_project_tree(max_depth=3, max_entries=50): 200 + """Generate a compact project tree to prevent path hallucinations.""" 201 + cwd = os.getcwd() 202 + entries = [] 203 + 204 + def should_skip(name): 205 + if name.startswith('.') and name not in ('.github', '.claude'): 206 + return True 207 + return name in SKIP_DIRS or name.endswith('.egg-info') 208 + 209 + def walk_dir(path, prefix="", depth=0): 210 + if depth > max_depth or len(entries) >= max_entries: 211 + return 212 + 213 + try: 214 + items = sorted(os.listdir(path)) 215 + except (PermissionError, OSError): 216 + return 217 + 218 + # Separate dirs and files 219 + dirs = [i for i in items if os.path.isdir(os.path.join(path, i)) and not should_skip(i)] 220 + files = [i for i in items if os.path.isfile(os.path.join(path, i)) and not i.startswith('.')] 221 + 222 + # Add files first (limit per directory) 223 + for f in files[:10]: # Max 10 files per dir shown 224 + if len(entries) >= max_entries: 225 + return 226 + entries.append(f"{prefix}{f}") 227 + 228 + if len(files) > 10: 229 + entries.append(f"{prefix}... ({len(files) - 10} more files)") 230 + 231 + # Then recurse into directories 232 + for d in dirs: 233 + if len(entries) >= max_entries: 234 + return 235 + entries.append(f"{prefix}{d}/") 236 + walk_dir(os.path.join(path, d), prefix + " ", depth + 1) 237 + 238 + walk_dir(cwd) 239 + 240 + if not entries: 241 + return "" 242 + 243 + if len(entries) >= max_entries: 244 + entries.append(f"... (tree truncated at {max_entries} entries)") 245 + 246 + return "\n".join(entries) 247 + 248 + 249 + # Cache directory for dependency snapshots 250 + CACHE_DIR = os.path.join(os.getcwd(), '.chainlink', '.cache') 251 + 252 + 253 + def get_lock_file_hash(lock_path): 254 + """Get a hash of the lock file for cache invalidation.""" 255 + try: 256 + mtime = os.path.getmtime(lock_path) 257 + return hashlib.md5(f"{lock_path}:{mtime}".encode()).hexdigest()[:12] 258 + except OSError: 259 + return None 260 + 261 + 262 + def run_command(cmd, timeout=5): 263 + """Run a command and return output, or None on failure.""" 264 + try: 265 + result = subprocess.run( 266 + cmd, 267 + capture_output=True, 268 + text=True, 269 + timeout=timeout, 270 + shell=True 271 + ) 272 + if result.returncode == 0: 273 + return result.stdout.strip() 274 + except (subprocess.TimeoutExpired, OSError, Exception): 275 + pass 276 + return None 277 + 278 + 279 + def get_dependencies(max_deps=30): 280 + """Get installed dependencies with versions. Uses caching based on lock file mtime.""" 281 + cwd = os.getcwd() 282 + deps = [] 283 + 284 + # Check for Rust (Cargo.toml) 285 + cargo_toml = os.path.join(cwd, 'Cargo.toml') 286 + if os.path.exists(cargo_toml): 287 + # Parse Cargo.toml for direct dependencies (faster than cargo tree) 288 + try: 289 + with open(cargo_toml, 'r') as f: 290 + content = f.read() 291 + in_deps = False 292 + for line in content.split('\n'): 293 + if line.strip().startswith('[dependencies]'): 294 + in_deps = True 295 + continue 296 + if line.strip().startswith('[') and in_deps: 297 + break 298 + if in_deps and '=' in line and not line.strip().startswith('#'): 299 + parts = line.split('=', 1) 300 + name = parts[0].strip() 301 + rest = parts[1].strip() if len(parts) > 1 else '' 302 + if rest.startswith('{'): 303 + # Handle { version = "x.y", features = [...] } format 304 + import re 305 + match = re.search(r'version\s*=\s*"([^"]+)"', rest) 306 + if match: 307 + deps.append(f" {name} = \"{match.group(1)}\"") 308 + elif rest.startswith('"') or rest.startswith("'"): 309 + version = rest.strip('"').strip("'") 310 + deps.append(f" {name} = \"{version}\"") 311 + if len(deps) >= max_deps: 312 + break 313 + except (OSError, Exception): 314 + pass 315 + if deps: 316 + return "Rust (Cargo.toml):\n" + "\n".join(deps[:max_deps]) 317 + 318 + # Check for Node.js (package.json) 319 + package_json = os.path.join(cwd, 'package.json') 320 + if os.path.exists(package_json): 321 + try: 322 + with open(package_json, 'r') as f: 323 + pkg = json.load(f) 324 + for dep_type in ['dependencies', 'devDependencies']: 325 + if dep_type in pkg: 326 + for name, version in list(pkg[dep_type].items())[:max_deps]: 327 + deps.append(f" {name}: {version}") 328 + if len(deps) >= max_deps: 329 + break 330 + except (OSError, json.JSONDecodeError, Exception): 331 + pass 332 + if deps: 333 + return "Node.js (package.json):\n" + "\n".join(deps[:max_deps]) 334 + 335 + # Check for Python (requirements.txt or pyproject.toml) 336 + requirements = os.path.join(cwd, 'requirements.txt') 337 + if os.path.exists(requirements): 338 + try: 339 + with open(requirements, 'r') as f: 340 + for line in f: 341 + line = line.strip() 342 + if line and not line.startswith('#') and not line.startswith('-'): 343 + deps.append(f" {line}") 344 + if len(deps) >= max_deps: 345 + break 346 + except (OSError, Exception): 347 + pass 348 + if deps: 349 + return "Python (requirements.txt):\n" + "\n".join(deps[:max_deps]) 350 + 351 + # Check for Go (go.mod) 352 + go_mod = os.path.join(cwd, 'go.mod') 353 + if os.path.exists(go_mod): 354 + try: 355 + with open(go_mod, 'r') as f: 356 + in_require = False 357 + for line in f: 358 + line = line.strip() 359 + if line.startswith('require ('): 360 + in_require = True 361 + continue 362 + if line == ')' and in_require: 363 + break 364 + if in_require and line: 365 + deps.append(f" {line}") 366 + if len(deps) >= max_deps: 367 + break 368 + except (OSError, Exception): 369 + pass 370 + if deps: 371 + return "Go (go.mod):\n" + "\n".join(deps[:max_deps]) 372 + 373 + return "" 374 + 375 + 376 + def build_reminder(languages, project_tree, dependencies, language_rules, global_rules, project_rules): 377 + """Build the full reminder context.""" 378 + lang_section = get_language_section(languages, language_rules) 379 + lang_list = ", ".join(languages) if languages else "this project" 380 + current_year = datetime.now().year 381 + 382 + # Build tree section if available 383 + tree_section = "" 384 + if project_tree: 385 + tree_section = f""" 386 + ### Project Structure (use these exact paths) 387 + ``` 388 + {project_tree} 389 + ``` 390 + """ 391 + 392 + # Build dependencies section if available 393 + deps_section = "" 394 + if dependencies: 395 + deps_section = f""" 396 + ### Installed Dependencies (use these exact versions) 397 + ``` 398 + {dependencies} 399 + ``` 400 + """ 401 + 402 + # Build global rules section (from .chainlink/rules/global.md) 403 + global_section = "" 404 + if global_rules: 405 + global_section = f"\n{global_rules}\n" 406 + else: 407 + # Fallback to hardcoded defaults if no rules file 408 + global_section = f""" 409 + ### Pre-Coding Grounding (PREVENT HALLUCINATIONS) 410 + Before writing code that uses external libraries, APIs, or unfamiliar patterns: 411 + 1. **VERIFY IT EXISTS**: Use WebSearch to confirm the crate/package/module exists and check its actual API 412 + 2. **CHECK THE DOCS**: Fetch documentation to see real function signatures, not imagined ones 413 + 3. **CONFIRM SYNTAX**: If unsure about language features or library usage, search first 414 + 4. **USE LATEST VERSIONS**: Always check for and use the latest stable version of dependencies (security + features) 415 + 5. **NO GUESSING**: If you can't verify it, tell the user you need to research it 416 + 417 + Examples of when to search: 418 + - Using a crate/package you haven't used recently → search "[package] [language] docs {current_year}" 419 + - Uncertain about function parameters → search for actual API reference 420 + - New language feature or syntax → verify it exists in the version being used 421 + - System calls or platform-specific code → confirm the correct API 422 + - Adding a dependency → search "[package] latest version {current_year}" to get current release 423 + 424 + ### General Requirements 425 + 1. **NO STUBS - ABSOLUTE RULE**: 426 + - NEVER write `TODO`, `FIXME`, `pass`, `...`, `unimplemented!()` as implementation 427 + - NEVER write empty function bodies or placeholder returns 428 + - NEVER say "implement later" or "add logic here" 429 + - If logic is genuinely too complex for one turn, use `raise NotImplementedError("Descriptive reason: what needs to be done")` and create a chainlink issue 430 + - The PostToolUse hook WILL detect and flag stub patterns - write real code the first time 431 + 2. **NO DEAD CODE**: Discover if dead code is truly dead or if it's an incomplete feature. If incomplete, complete it. If truly dead, remove it. 432 + 3. **FULL FEATURES**: Implement the complete feature as requested. Don't stop partway or suggest "you could add X later." 433 + 4. **ERROR HANDLING**: Proper error handling everywhere. No panics/crashes on bad input. 434 + 5. **SECURITY**: Validate input, use parameterized queries, no command injection, no hardcoded secrets. 435 + 6. **READ BEFORE WRITE**: Always read a file before editing it. Never guess at contents. 436 + 437 + ### Conciseness Protocol 438 + Minimize chattiness. Your output should be: 439 + - **Code blocks** with implementation 440 + - **Tool calls** to accomplish tasks 441 + - **Brief explanations** only when the code isn't self-explanatory 442 + 443 + NEVER output: 444 + - "Here is the code" / "Here's how to do it" (just show the code) 445 + - "Let me know if you need anything else" / "Feel free to ask" 446 + - "I'll now..." / "Let me..." (just do it) 447 + - Restating what the user asked 448 + - Explaining obvious code 449 + - Multiple paragraphs when one sentence suffices 450 + 451 + When writing code: write it. When making changes: make them. Skip the narration. 452 + 453 + ### Large File Management (500+ lines) 454 + If you need to write or modify code that will exceed 500 lines: 455 + 1. Create a parent issue for the overall feature: `chainlink create "<feature name>" -p high` 456 + 2. Break down into subissues: `chainlink subissue <parent_id> "<component 1>"`, etc. 457 + 3. Inform the user: "This implementation will require multiple files/components. I've created issue #X with Y subissues to track progress." 458 + 4. Work on one subissue at a time, marking each complete before moving on. 459 + 460 + ### Context Window Management 461 + If the conversation is getting long OR the task requires many more steps: 462 + 1. Create a chainlink issue to track remaining work: `chainlink create "Continue: <task summary>" -p high` 463 + 2. Add detailed notes as a comment: `chainlink comment <id> "<what's done, what's next>"` 464 + 3. Inform the user: "This task will require additional turns. I've created issue #X to track progress." 465 + 466 + Use `chainlink session work <id>` to mark what you're working on. 467 + """ 468 + 469 + # Build project rules section (from .chainlink/rules/project.md) 470 + project_section = "" 471 + if project_rules: 472 + project_section = f"\n### Project-Specific Rules\n{project_rules}\n" 473 + 474 + reminder = f"""<chainlink-behavioral-guard> 475 + ## Code Quality Requirements 476 + 477 + You are working on a {lang_list} project. Follow these requirements strictly: 478 + {tree_section}{deps_section}{global_section}{lang_section}{project_section} 479 + </chainlink-behavioral-guard>""" 480 + 481 + return reminder 482 + 483 + 484 + def main(): 485 + try: 486 + # Read input from stdin (Claude Code passes prompt info) 487 + input_data = json.load(sys.stdin) 488 + except json.JSONDecodeError: 489 + # If no valid JSON, still inject reminder 490 + pass 491 + except Exception: 492 + pass 493 + 494 + # Find chainlink directory and load rules 495 + chainlink_dir = find_chainlink_dir() 496 + language_rules, global_rules, project_rules = load_all_rules(chainlink_dir) 497 + 498 + # Detect languages in the project 499 + languages = detect_languages() 500 + 501 + # Generate project tree to prevent path hallucinations 502 + project_tree = get_project_tree() 503 + 504 + # Get installed dependencies to prevent version hallucinations 505 + dependencies = get_dependencies() 506 + 507 + # Output the reminder as plain text (gets injected as context) 508 + print(build_reminder(languages, project_tree, dependencies, language_rules, global_rules, project_rules)) 509 + sys.exit(0) 510 + 511 + 512 + if __name__ == "__main__": 513 + main()
+78
.claude/hooks/session-start.py
··· 1 + #!/usr/bin/env python3 2 + """ 3 + Session start hook that loads chainlink context and reminds about session workflow. 4 + """ 5 + 6 + import json 7 + import subprocess 8 + import sys 9 + import os 10 + 11 + 12 + def run_chainlink(args): 13 + """Run a chainlink command and return output.""" 14 + try: 15 + result = subprocess.run( 16 + ["chainlink"] + args, 17 + capture_output=True, 18 + text=True, 19 + timeout=5 20 + ) 21 + return result.stdout.strip() if result.returncode == 0 else None 22 + except (subprocess.TimeoutExpired, FileNotFoundError, Exception): 23 + return None 24 + 25 + 26 + def check_chainlink_initialized(): 27 + """Check if .chainlink directory exists.""" 28 + cwd = os.getcwd() 29 + current = cwd 30 + 31 + while True: 32 + candidate = os.path.join(current, ".chainlink") 33 + if os.path.isdir(candidate): 34 + return True 35 + parent = os.path.dirname(current) 36 + if parent == current: 37 + break 38 + current = parent 39 + 40 + return False 41 + 42 + 43 + def main(): 44 + if not check_chainlink_initialized(): 45 + # No chainlink repo, skip 46 + sys.exit(0) 47 + 48 + context_parts = ["<chainlink-session-context>"] 49 + 50 + # Try to get session status 51 + session_status = run_chainlink(["session", "status"]) 52 + if session_status: 53 + context_parts.append(f"## Current Session\n{session_status}") 54 + 55 + # Get ready issues (unblocked work) 56 + ready_issues = run_chainlink(["ready"]) 57 + if ready_issues: 58 + context_parts.append(f"## Ready Issues (unblocked)\n{ready_issues}") 59 + 60 + # Get open issues summary 61 + open_issues = run_chainlink(["list", "-s", "open"]) 62 + if open_issues: 63 + context_parts.append(f"## Open Issues\n{open_issues}") 64 + 65 + context_parts.append(""" 66 + ## Chainlink Workflow Reminder 67 + - Use `chainlink session start` at the beginning of work 68 + - Use `chainlink session work <id>` to mark current focus 69 + - Add comments as you discover things: `chainlink comment <id> "..."` 70 + - End with handoff notes: `chainlink session end --notes "..."` 71 + </chainlink-session-context>""") 72 + 73 + print("\n\n".join(context_parts)) 74 + sys.exit(0) 75 + 76 + 77 + if __name__ == "__main__": 78 + main()
+39
.claude/settings.json
··· 1 + { 2 + "hooks": { 3 + "UserPromptSubmit": [ 4 + { 5 + "hooks": [ 6 + { 7 + "type": "command", 8 + "command": "python .claude/hooks/prompt-guard.py", 9 + "timeout": 5 10 + } 11 + ] 12 + } 13 + ], 14 + "PostToolUse": [ 15 + { 16 + "matcher": "Write|Edit", 17 + "hooks": [ 18 + { 19 + "type": "command", 20 + "command": "python .claude/hooks/post-edit-check.py", 21 + "timeout": 5 22 + } 23 + ] 24 + } 25 + ], 26 + "SessionStart": [ 27 + { 28 + "matcher": "startup|resume", 29 + "hooks": [ 30 + { 31 + "type": "command", 32 + "command": "python .claude/hooks/session-start.py", 33 + "timeout": 10 34 + } 35 + ] 36 + } 37 + ] 38 + } 39 + }