From 078d55982b495c456e9f8859469331e44fec7bdf Mon Sep 17 00:00:00 2001 From: Flo Date: Mon, 30 Mar 2026 13:39:40 +0200 Subject: [PATCH] chore: Add agentic coding tooling --- .claude/settings.json | 76 +++++++ .crosslink/.gitignore | 11 + .crosslink/.last-hydrated-ref | 1 + .crosslink/hook-config.json | 80 +++++++ .crosslink/rules/c.md | 43 ++++ .crosslink/rules/cpp.md | 39 ++++ .crosslink/rules/csharp.md | 51 +++++ .crosslink/rules/elixir-phoenix.md | 57 +++++ .crosslink/rules/elixir.md | 39 ++++ .crosslink/rules/global.md | 195 ++++++++++++++++ .crosslink/rules/go.md | 44 ++++ .crosslink/rules/java.md | 42 ++++ .crosslink/rules/javascript-react.md | 44 ++++ .crosslink/rules/javascript.md | 36 +++ .crosslink/rules/knowledge.md | 53 +++++ .crosslink/rules/kotlin.md | 44 ++++ .crosslink/rules/odin.md | 53 +++++ .crosslink/rules/php.md | 46 ++++ .crosslink/rules/project.md | 7 + .crosslink/rules/python.md | 44 ++++ .crosslink/rules/quality.md | 89 ++++++++ .crosslink/rules/rigor.md | 46 ++++ .crosslink/rules/ruby.md | 47 ++++ .crosslink/rules/rust.md | 48 ++++ .crosslink/rules/sanitize-patterns.txt | 22 ++ .crosslink/rules/scala.md | 45 ++++ .crosslink/rules/swift.md | 50 +++++ .crosslink/rules/tracking-normal.md | 101 +++++++++ .crosslink/rules/tracking-relaxed.md | 11 + .crosslink/rules/tracking-strict.md | 209 ++++++++++++++++++ .crosslink/rules/typescript-react.md | 39 ++++ .crosslink/rules/typescript.md | 93 ++++++++ .crosslink/rules/web.md | 80 +++++++ .crosslink/rules/zig.md | 48 ++++ .design/git-harden.md | 119 ++++++++++ .design/git-harden.pipeline.json | 8 + .mcp.json | 25 +++ GEMINI.md | 21 ++ .../specs/2026-03-25-git-harden-design.md | 0 ...2026-03-25-git-harden-design.pipeline.json | 25 +++ 40 files changed, 2131 insertions(+) create mode 100644 .claude/settings.json create mode 100644 .crosslink/.gitignore create mode 100644 .crosslink/.last-hydrated-ref create mode 100644 .crosslink/hook-config.json create mode 100644 .crosslink/rules/c.md create mode 100644 .crosslink/rules/cpp.md create mode 100644 .crosslink/rules/csharp.md create mode 100644 .crosslink/rules/elixir-phoenix.md create mode 100644 .crosslink/rules/elixir.md create mode 100644 .crosslink/rules/global.md create mode 100644 .crosslink/rules/go.md create mode 100644 .crosslink/rules/java.md create mode 100644 .crosslink/rules/javascript-react.md create mode 100644 .crosslink/rules/javascript.md create mode 100644 .crosslink/rules/knowledge.md create mode 100644 .crosslink/rules/kotlin.md create mode 100644 .crosslink/rules/odin.md create mode 100644 .crosslink/rules/php.md create mode 100644 .crosslink/rules/project.md create mode 100644 .crosslink/rules/python.md create mode 100644 .crosslink/rules/quality.md create mode 100644 .crosslink/rules/rigor.md create mode 100644 .crosslink/rules/ruby.md create mode 100644 .crosslink/rules/rust.md create mode 100644 .crosslink/rules/sanitize-patterns.txt create mode 100644 .crosslink/rules/scala.md create mode 100644 .crosslink/rules/swift.md create mode 100644 .crosslink/rules/tracking-normal.md create mode 100644 .crosslink/rules/tracking-relaxed.md create mode 100644 .crosslink/rules/tracking-strict.md create mode 100644 .crosslink/rules/typescript-react.md create mode 100644 .crosslink/rules/typescript.md create mode 100644 .crosslink/rules/web.md create mode 100644 .crosslink/rules/zig.md create mode 100644 .design/git-harden.md create mode 100644 .design/git-harden.pipeline.json create mode 100644 .mcp.json create mode 100644 GEMINI.md rename docs/{superpowers => }/specs/2026-03-25-git-harden-design.md (100%) create mode 100644 docs/specs/2026-03-25-git-harden-design.pipeline.json diff --git a/.claude/settings.json b/.claude/settings.json new file mode 100644 index 0000000..551ff7b --- /dev/null +++ b/.claude/settings.json @@ -0,0 +1,76 @@ +{ + "allowedTools": [ + "Bash(tmux *)", + "Bash(git worktree *)", + "Bash(shellcheck:*)" + ], + "enableAllProjectMcpServers": true, + "hooks": { + "PostToolUse": [ + { + "hooks": [ + { + "command": "HOOK=\"$(git rev-parse --show-toplevel 2>/dev/null)/.claude/hooks/post-edit-check.py\"; if [ -f \"$HOOK\" ]; then python3 \"$HOOK\"; else exit 0; fi", + "timeout": 5, + "type": "command" + } + ], + "matcher": "Write|Edit" + }, + { + "hooks": [ + { + "command": "HOOK=\"$(git rev-parse --show-toplevel 2>/dev/null)/.claude/hooks/heartbeat.py\"; if [ -f \"$HOOK\" ]; then python3 \"$HOOK\"; else exit 0; fi", + "timeout": 3, + "type": "command" + } + ] + } + ], + "PreToolUse": [ + { + "hooks": [ + { + "command": "HOOK=\"$(git rev-parse --show-toplevel 2>/dev/null)/.claude/hooks/pre-web-check.py\"; if [ -f \"$HOOK\" ]; then python3 \"$HOOK\"; else exit 0; fi", + "timeout": 5, + "type": "command" + } + ], + "matcher": "WebFetch|WebSearch" + }, + { + "hooks": [ + { + "command": "HOOK=\"$(git rev-parse --show-toplevel 2>/dev/null)/.claude/hooks/work-check.py\"; if [ -f \"$HOOK\" ]; then python3 \"$HOOK\"; else exit 0; fi", + "timeout": 3, + "type": "command" + } + ], + "matcher": "Write|Edit|Bash" + } + ], + "SessionStart": [ + { + "hooks": [ + { + "command": "HOOK=\"$(git rev-parse --show-toplevel 2>/dev/null)/.claude/hooks/session-start.py\"; if [ -f \"$HOOK\" ]; then python3 \"$HOOK\"; else exit 0; fi", + "timeout": 10, + "type": "command" + } + ], + "matcher": "startup|resume" + } + ], + "UserPromptSubmit": [ + { + "hooks": [ + { + "command": "HOOK=\"$(git rev-parse --show-toplevel 2>/dev/null)/.claude/hooks/prompt-guard.py\"; if [ -f \"$HOOK\" ]; then python3 \"$HOOK\"; else exit 0; fi", + "timeout": 5, + "type": "command" + } + ] + } + ] + } +} diff --git a/.crosslink/.gitignore b/.crosslink/.gitignore new file mode 100644 index 0000000..5cc6f02 --- /dev/null +++ b/.crosslink/.gitignore @@ -0,0 +1,11 @@ +# Multi-agent collaboration (machine-local) +agent.json +repo-id +.hub-cache/ +.knowledge-cache/ +keys/ +integrations/ + +# Machine-local overrides +hook-config.local.json +rules.local/ diff --git a/.crosslink/.last-hydrated-ref b/.crosslink/.last-hydrated-ref new file mode 100644 index 0000000..72cf279 --- /dev/null +++ b/.crosslink/.last-hydrated-ref @@ -0,0 +1 @@ +137aca828adf41849200b3d93a476b06e1796aa0 \ No newline at end of file diff --git a/.crosslink/hook-config.json b/.crosslink/hook-config.json new file mode 100644 index 0000000..14df7d0 --- /dev/null +++ b/.crosslink/hook-config.json @@ -0,0 +1,80 @@ +{ + "agent_overrides": { + "agent_lint_commands": [], + "agent_test_commands": [], + "blocked_git_commands": [ + "git push --force", + "git push -f", + "git reset --hard", + "git clean -f", + "git clean -fd", + "git clean -fdx", + "git checkout .", + "git restore ." + ], + "gated_git_commands": [], + "tracking_mode": "relaxed" + }, + "allowed_bash_prefixes": [ + "crosslink ", + "git status", + "git diff", + "git log", + "git branch", + "git show", + "jj log", + "jj diff", + "jj status", + "jj show", + "jj bookmark list", + "cargo test", + "cargo build", + "cargo check", + "cargo clippy", + "cargo fmt", + "npm test", + "npm run", + "npx ", + "tsc", + "node ", + "python ", + "bash ", + "shellcheck ", + "chmod ", + "mkdir ", + "cat ", + "ls", + "dir", + "pwd", + "echo" + ], + "auto_steal_stale_locks": "false", + "blocked_git_commands": [ + "git push", + "git merge", + "git rebase", + "git cherry-pick", + "git reset", + "git checkout .", + "git restore .", + "git clean", + "git stash", + "git tag", + "git am", + "git apply", + "git branch -d", + "git branch -D", + "git branch -m" + ], + "comment_discipline": "encouraged", + "cpitd_auto_install": true, + "gated_git_commands": [ + "git commit" + ], + "intervention_tracking": true, + "kickoff_verification": "local", + "reminder_drift_threshold": "0", + "signing_enforcement": "disabled", + "tracking_mode": "relaxed", + "tracker_remote": "origin" +} diff --git a/.crosslink/rules/c.md b/.crosslink/rules/c.md new file mode 100644 index 0000000..df7c1c9 --- /dev/null +++ b/.crosslink/rules/c.md @@ -0,0 +1,43 @@ +### C Best Practices + +#### Memory Safety +- Always check return values of malloc/calloc +- Free all allocated memory (use tools like valgrind) +- Initialize all variables before use +- Use sizeof() with the variable, not the type + +```c +// GOOD: Safe memory allocation +int *arr = malloc(n * sizeof(*arr)); +if (arr == NULL) { + return -1; // Handle allocation failure +} +// ... use arr ... +free(arr); + +// BAD: Unchecked allocation +int *arr = malloc(n * sizeof(int)); +arr[0] = 1; // Crash if malloc failed +``` + +#### Buffer Safety +- Always bounds-check array access +- Use `strncpy`/`snprintf` instead of `strcpy`/`sprintf` +- Validate string lengths before copying + +```c +// GOOD: Safe string copy +char dest[64]; +strncpy(dest, src, sizeof(dest) - 1); +dest[sizeof(dest) - 1] = '\0'; + +// BAD: Buffer overflow risk +char dest[64]; +strcpy(dest, src); // No bounds check +``` + +#### Security +- Never use `gets()` (use `fgets()`) +- Validate all external input +- Use constant-time comparison for secrets +- Avoid integer overflow in size calculations diff --git a/.crosslink/rules/cpp.md b/.crosslink/rules/cpp.md new file mode 100644 index 0000000..f454a71 --- /dev/null +++ b/.crosslink/rules/cpp.md @@ -0,0 +1,39 @@ +### C++ Best Practices + +#### Modern C++ (C++17+) +- Use smart pointers (`unique_ptr`, `shared_ptr`) over raw pointers +- Use RAII for resource management +- Prefer `std::string` and `std::vector` over C arrays +- Use `auto` for complex types, explicit types for clarity + +```cpp +// GOOD: Modern C++ with smart pointers +auto config = std::make_unique(); +auto users = std::vector{}; + +// BAD: Manual memory management +Config* config = new Config(); +// ... forgot to delete +``` + +#### Error Handling +- Use exceptions for exceptional cases +- Use `std::optional` for values that may not exist +- Use `std::expected` (C++23) or result types for expected failures + +```cpp +// GOOD: Optional for missing values +std::optional findUser(const std::string& id) { + auto it = users.find(id); + if (it == users.end()) { + return std::nullopt; + } + return it->second; +} +``` + +#### Security +- Validate all input boundaries +- Use `std::string_view` for non-owning string references +- Avoid C-style casts; use `static_cast`, `dynamic_cast` +- Never use `sprintf`; use `std::format` or streams diff --git a/.crosslink/rules/csharp.md b/.crosslink/rules/csharp.md new file mode 100644 index 0000000..c87c946 --- /dev/null +++ b/.crosslink/rules/csharp.md @@ -0,0 +1,51 @@ +### C# Best Practices + +#### Code Style +- Follow .NET naming conventions (PascalCase for public, camelCase for private) +- Use `var` when type is obvious from right side +- Use expression-bodied members for simple methods +- Enable nullable reference types + +```csharp +// GOOD: Modern C# style +public class UserService +{ + private readonly IUserRepository _repository; + + public UserService(IUserRepository repository) + => _repository = repository; + + public async Task GetUserAsync(string id) + => await _repository.FindByIdAsync(id); +} +``` + +#### Error Handling +- Use specific exception types +- Never catch and swallow exceptions silently +- Use `try-finally` or `using` for cleanup + +```csharp +// GOOD: Proper async error handling +public async Task> GetUserAsync(string id) +{ + try + { + var user = await _repository.FindByIdAsync(id); + return user is null + ? Result.NotFound() + : Result.Ok(user); + } + catch (DbException ex) + { + _logger.LogError(ex, "Database error fetching user {Id}", id); + throw; + } +} +``` + +#### Security +- Use parameterized queries (never string interpolation for SQL) +- Validate all input with data annotations or FluentValidation +- Use ASP.NET's built-in anti-forgery tokens +- Store secrets in Azure Key Vault or similar diff --git a/.crosslink/rules/elixir-phoenix.md b/.crosslink/rules/elixir-phoenix.md new file mode 100644 index 0000000..c6003ad --- /dev/null +++ b/.crosslink/rules/elixir-phoenix.md @@ -0,0 +1,57 @@ +# Phoenix & LiveView Rules + +## HEEx Template Syntax (Critical) +- **Attributes use `{}`**: `
` — never `<%= %>` in attributes +- **Body values use `{}`**: `{@value}` — use `<%= %>` only for blocks (if/for/cond) +- **Class lists require `[]`**: `class={["base", @flag && "active"]}` — bare `{}` is invalid +- **No `else if`**: Use `cond` for multiple conditions +- **Comments**: `<%!-- comment --%>` +- **Literal curlies**: Use `phx-no-curly-interpolation` on parent tag + +## Phoenix v1.8 +- Wrap templates with `` (already aliased) +- `current_scope` errors → move routes to proper `live_session`, pass to Layouts.app +- `<.flash_group>` only in layouts.ex +- Use `<.icon name="hero-x-mark">` for icons, `<.input>` for form fields + +## LiveView +- Use `<.link navigate={}>` / `push_navigate`, not deprecated `live_redirect` +- Hooks with own DOM need `phx-update="ignore"` +- Avoid LiveComponents unless necessary +- No inline `