From 661fb3791b33764d2a4906d9d89a793200987944 Mon Sep 17 00:00:00 2001 From: karitham Date: Thu, 5 Mar 2026 18:01:23 +0100 Subject: [PATCH] opencode/agents: new ask agent --- modules/opencode/agents/ask.md | 42 ++++ modules/opencode/default.nix | 6 +- modules/opencode/rules/global.md | 56 ----- modules/opencode/rules/go.md | 95 --------- modules/opencode/rules/javascript.md | 36 ---- modules/opencode/rules/nix.md | 306 --------------------------- modules/opencode/rules/typescript.md | 35 --- 7 files changed, 43 insertions(+), 533 deletions(-) create mode 100644 modules/opencode/agents/ask.md delete mode 100644 modules/opencode/rules/global.md delete mode 100644 modules/opencode/rules/go.md delete mode 100644 modules/opencode/rules/javascript.md delete mode 100644 modules/opencode/rules/nix.md delete mode 100644 modules/opencode/rules/typescript.md diff --git a/modules/opencode/agents/ask.md b/modules/opencode/agents/ask.md new file mode 100644 index 0000000..7b1a693 --- /dev/null +++ b/modules/opencode/agents/ask.md @@ -0,0 +1,42 @@ +--- +description: Exploratory read-only agent for searching, analyzing, and asking questions about the codebase. +mode: primary +temperature: 0.1 +tools: +tools: + read: true + glob: true + grep: true + edit: false + bash: true + todowrite: false + todoread: false +permission: + edit: deny + bash: + "*": ask + "ls *": allow + "cat *": allow + "grep *": allow + "find *": allow + "rg *": allow + "fd *": allow + "git diff*": allow + "git log*": allow + "git status": allow + "git show*": allow +--- + +You are the "Ask" agent, a senior software architect and codebase explorer. Your purpose is entirely exploratory and analytical. The user interacting with you is a domain expert and fellow senior engineer. They will ask you to search for things, trace execution paths, and analyze high-level architectural patterns or complex implementation details. + +# Core Directives: + +1. **Strictly Read-Only:** You operate in a purely exploratory environment. Do not attempt to modify, refactor, or write new code to the file system. + +2. **Active Exploration & Precision:** Leverage read-only bash commands (`grep`, `rg`, `find`, `cat`, `ls`) to actively traverse the codebase. Do not guess—base all your answers on actual code reality. Always ground your analysis with precise, valid file paths and line references. + +3. **Expert-Level Analysis:** Communicate peer-to-peer. Assume the user has deep technical fluency. Focus your explanations on architectural patterns, system constraints, data flow, and idiomatic usage. Gloss over basic syntax or trivial lexical differences. Get straight to the point without introductory fluff or over-explaining standard concepts. + +4. **Proactive Investigation:** If given a high-level query (e.g., "Where is the auth middleware?"), autonomously locate the relevant implementations, trace the usage patterns, and present a concise, highly technical summary of the underlying mechanics. + +5. **Boundary Enforcement:** If the user requests code modifications, politely decline, remind them of your read-only constraints, and advise them to use the `build` agent to apply changes. diff --git a/modules/opencode/default.nix b/modules/opencode/default.nix index 867791b..9d853de 100644 --- a/modules/opencode/default.nix +++ b/modules/opencode/default.nix @@ -5,16 +5,13 @@ ... }: let - rulesDir = ./rules; - opencodePkg = pkgs.symlinkJoin { name = "opencode.wrapped"; paths = [ pkgs.opencode ]; buildInputs = [ pkgs.makeWrapper ]; postBuild = '' wrapProgram $out/bin/opencode \ - --set SHELL ${lib.getExe pkgs.bash} \ - --set-default LANGRULES_DIR ${rulesDir} + --set SHELL ${lib.getExe pkgs.bash} ''; }; @@ -34,7 +31,6 @@ lib.mkIf cfg.enable { enableMcpIntegration = cfg.enableMcp; settings = { inherit (cfg) theme; - # plugin = [ "git@tangled.org:karitham.dev/langrules-opencode" ]; permission = { todoread = "deny"; todowrite = "deny"; diff --git a/modules/opencode/rules/global.md b/modules/opencode/rules/global.md deleted file mode 100644 index c16328f..0000000 --- a/modules/opencode/rules/global.md +++ /dev/null @@ -1,56 +0,0 @@ -# Global Coding Rules - -## NO STUBS - ABSOLUTE RULE - -- NEVER write `TODO`, `FIXME`, `pass`, `...`, `unimplemented!()` -- NEVER write empty function bodies or placeholder returns -- If too complex for one turn: throw an error/exception with a clear reason - -## Core Rules - -1. **READ BEFORE WRITE**: Always read a file before editing. -2. **FULL FEATURES**: Complete the feature, don't stop partway. -3. **ERROR HANDLING**: No panics/crashes on bad input. -4. **SECURITY**: Validate input, parameterized queries, no hardcoded secrets. -5. **NO DEAD CODE**: Remove or complete incomplete code. - -## Chainlink Lifecycle Management - -You MUST use Chainlink to track all work: - -- **Session Start**: `chainlink session start` - shows previous handoff -- **Session Work**: `chainlink session work ` - mark current focus -- **Progress**: `chainlink comment "..."` - update regularly -- **Session End**: `chainlink session end --notes "..."` - REQUIRED before stopping - -### Handoff Notes Format - -```markdown -**Accomplished:** -- Completed feature X -- Added tests for Y - -**In Progress:** -- Working on feature Z - -**Next:** -- Complete feature Z -- Run tests - -**Blockers:** -- None -``` - -## Code Quality Requirements - -- **NO DEAD CODE**: Remove unused functions, variables, imports -- **NO HARDCODED SECRETS**: Use environment variables, configs -- **COMMIT FREQUENTLY**: Small, focused commits -- **DESCRIPTIVE COMMITS**: Explain WHY, not just WHAT - -## Security - -- Validate ALL inputs (user, network, config) -- Use parameterized queries for database access -- Never log sensitive data (passwords, tokens, keys) -- Follow principle of least privilege diff --git a/modules/opencode/rules/go.md b/modules/opencode/rules/go.md deleted file mode 100644 index 3db7d0d..0000000 --- a/modules/opencode/rules/go.md +++ /dev/null @@ -1,95 +0,0 @@ -### Go Best Practices - -Follow [Effective Go](https://go.dev/doc/effective_go) and [Go Code Review Comments](https://github.com/golang/go/wiki/CodeReviewComments). - -#### Code Style & Structure - -- **Existing Style**: The best Go style is the existing style. Adapt to existing patterns and conventions in the codebase as much as possible. -- **Small Interfaces**: Keep interfaces small (often 1-3 methods). "The bigger the interface, the weaker the abstraction." -- **Deep Modules**: Design modules with simple interfaces that hide significant complexity (deep vs. shallow). -- **Align Left**: Keep the "happy path" aligned to the left. -- **Early Returns**: Use guard clauses to handle errors and edge cases early, reducing nesting. -- **Avoid Else**: Avoid `else` blocks; they often indicate complex branching that can be flattened with early returns. -- **No Naked Returns**: Avoid using named return parameters for actual returns. -- **Latest Features**: Leverage modern Go features (generics, `any`, `net/http` routing enhancements, `log/slog`, `t.Context()`). -- **Simple Functions**: Write small, focused functions that do one thing well. -- **Avoid God Objects**: Don't create types that try to do everything. Use composition. -- **Pure Code**: Prefer pure functions where possible. -- **Easy to Mock**: Accept interfaces, return structs. Use dependency injection. - -#### Error Handling - -Always check and handle errors immediately. Wrap errors with context. - -```go -// GOOD: Early returns, aligned left, no else -func readConfig(path string) (*Config, error) { - data, err := os.ReadFile(path) - if err != nil { - return nil, fmt.Errorf("read config: %w", err) - } - - var config Config - if err := json.Unmarshal(data, &config); err != nil { - return nil, fmt.Errorf("parse config: %w", err) - } - - return &config, nil -} - -// BAD: Nested, use of else, harder to follow -func readConfig(path string) (*Config, error) { - data, err := os.ReadFile(path) - if err == nil { - var config Config - err = json.Unmarshal(data, &config) - if err == nil { - return &config, nil - } else { - return nil, err - } - } else { - return nil, err - } -} -``` - -#### Concurrency & Sync - -- **Package sync**: Use `sync.Mutex`, `sync.RWMutex`, `sync.Once`, and `sync.Pool` extensively. -- **sync.WaitGroup**: Use for waiting on multiple goroutines. -- **Channels**: Use for coordination between goroutines. -- **Context**: Always propagate `context.Context`. Use `t.Context()` in tests for automatic cleanup. -- **Atomic**: Use `sync/atomic` for simple counter/state updates. - -#### Testing - -Use the standard `testing` package effectively. - -- **Table-Driven Tests**: Use anonymous structs for test cases with clear `name`, `got`, and `want` fields. -- **t.Context()**: Use `t.Context()` for test-scoped context that cancels when the test finishes. -- **Few Functions, Good Tables**: Prefer one robust test function with a comprehensive table over many small test functions. -- **Helper Functions**: Use `t.Helper()` in functions that assist in testing to keep stack traces clean. - -```go -func TestSum(t *testing.T) { - tests := []struct { - name string - args []int - want int - }{ - {"empty", []int{}, 0}, - {"single", []int{1}, 1}, - {"multiple", []int{1, 2, 3}, 6}, - {"negative", []int{1, -1}, 0}, - } - - for _, tt := range tests { - t.Run(tt.name, func(t *testing.T) { - if got := Sum(tt.args); got != tt.want { - t.Errorf("Sum() = %v, want %v", got, tt.want) - } - }) - } -} -``` diff --git a/modules/opencode/rules/javascript.md b/modules/opencode/rules/javascript.md deleted file mode 100644 index df368de..0000000 --- a/modules/opencode/rules/javascript.md +++ /dev/null @@ -1,36 +0,0 @@ -### JavaScript Best Practices - -#### Code Style -- Use `const` by default, `let` when needed, never `var` -- Use arrow functions for callbacks -- Use template literals over string concatenation -- Use destructuring for object/array access - -#### Error Handling -```javascript -// GOOD: Proper async error handling -async function fetchUser(id) { - try { - const response = await fetch(`/api/users/${id}`); - if (!response.ok) { - throw new Error(`HTTP ${response.status}`); - } - return await response.json(); - } catch (error) { - console.error('Failed to fetch user:', error); - throw error; // Re-throw or handle appropriately - } -} - -// BAD: Ignoring errors -async function fetchUser(id) { - const response = await fetch(`/api/users/${id}`); - return response.json(); // No error handling -} -``` - -#### Security -- Never use `eval()` or `innerHTML` with user input -- Validate all input on both client and server -- Use `textContent` instead of `innerHTML` when possible -- Sanitize URLs before navigation or fetch diff --git a/modules/opencode/rules/nix.md b/modules/opencode/rules/nix.md deleted file mode 100644 index 2cb3edb..0000000 --- a/modules/opencode/rules/nix.md +++ /dev/null @@ -1,306 +0,0 @@ -# Nix Best Practices - -## Code Style - -- Use `nixfmt` for formatting (set as formatter in flake, run `nix fmt` before committing) -- Follow the [Nixpkgs contributing guide](https://github.com/NixOS/nixpkgs/blob/master/CONTRIBUTING.md) for style conventions -- Use descriptive variable names that explain purpose -- Prefer `let ... in` blocks for local bindings over deep nesting -- Keep derivations and functions small and composable -- Use `lib.mkIf`, `lib.mkWhen`, `lib.optionals` for conditional logic -- Use `lib.getExe` and `lib.getExe'` for accessing package executables - -## Error Handling - -```nix -# GOOD: Proper error handling with builtins.tryEval -let - configPath = ./config.json; - config = builtins.tryEval (builtins.fromJSON (builtins.readFile configPath)); -in -if config.success then config.value else throw "Failed to parse config" - -# GOOD: Using assert statements for validation -assert lib.assertMsg (cfg.port > 0 && cfg.port < 65536) "Port must be between 1 and 65535"; -cfg - -# GOOD: Using lib.mapAttrs' with lib.nameValuePair for building attribute sets -lib.mapAttrs' (argName: argValue: - lib.nameValuePair "${argName}+${dir.name}" { - action = { - "${argValue}-${dir.value}" = [ ]; - }; - } -) act - -# BAD: Silent failures or incomplete error messages -let - data = builtins.readFile configPath; - config = builtins.fromJSON data; # Will throw cryptic error if invalid -in config -``` - -## Nix Module System - -- Use `lib.mkOptionType`, `lib.mkDefault`, `lib.mkForce` appropriately -- Prefer `mkOption` with proper `type`, `default`, and `description` fields -- Use `mkIf` for conditional module options rather than deep nesting -- Keep modules focused - one module per responsibility -- Use `imports` to compose modules rather than duplicating code -- Use `lib.getExe pkgs.packageName` for executable paths in configuration - -```nix -# GOOD: Well-structured module option -{ lib, config, ... }: -{ - options.my.username = lib.mkOption { - type = lib.types.str; - description = "The username for the current user."; - default = "kar"; - }; - - config = lib.mkIf (config.my.username != "root") { - users.users.${config.my.username} = { - home = "/home/${config.my.username}"; - initialPassword = ""; - isNormalUser = true; - extraGroups = [ - "networkmanager" - "docker" - "wheel" - ]; - }; - }; -} -``` - -## Flake Patterns - -- Use `flake-parts` for modular flake structure -- Use `withSystem` for system-specific configuration -- Provide `devShells`, `packages`, `checks`, `formatter` per system -- Lock `nixpkgs` reference using channel URLs or specific revisions -- Provide formatter and linter in `devShells` -- Use `nixConfig` for Nix configuration (experimental features, substituters) - -## System Management - -- Use `easy-hosts` for organizing hosts by class (desktop, server, wsl) and tags -- Define shared modules for all systems in `config.easy-hosts.shared.modules` -- Use perClass and perTag for class-specific and tag-specific modules -- Use `mkSystem'` for creating individual systems with proper specialArgs - -## Overlay Patterns - -- Use `overrideAttrs` for patching existing packages -- Use `fetchurl` or `fetchFromGitHub` for patches with hash verification -- Use `callPackage` to add packages to overlay -- Keep overlays focused and well-documented - -## Package Building - -- Use `pkgs.buildGoModule` for Go packages with proper vendorHash -- Use `pkgs.fetchFromGitHub` for GitHub sources with hash verification -- Set `env.CGO_ENABLED`, `flags`, and `ldflags` appropriately for Go -- Include proper `meta` with description, homepage, license, maintainers -- Use `trimpath` and static linking flags for production binaries - -## Security - -- Never commit secrets or API keys in Nix files -- Use `sops-nix` for secrets management in production -- Use sandbox mode in `nix.conf` (`sandbox = true`) for builds -- Review network access in derivations - use `allowedRequisites` for purity -- Use `fetchFromGitHub` or `fetchurl` with hash verification - -## Dependency Management - -- Use specific Git revisions/commits for Git dependencies (not branches) -- Always include `sha256` hash for fetch functions -- Use `inputs.nixpkgs.follows` to avoid duplicating inputs -- Pin `nixpkgs` in production configurations using `flake.lock` -- Prefer `flake-inputs` over `builtins.fetchTarball` for external sources -- Use `callPackage` pattern for package dependencies - -## Testing - -- Use `nix-instantiate --parse` to check for syntax errors -- Use `nix-build` with `-A` to test specific attributes -- Write checks in `perSystem.checks` for package testing -- Use `nix flake check` for flake validation -- Test modules with `nixos-rebuild dry-build` or `nixos-rebuild test` - -## Evaluation - -- Use `lib.warnIf` and `lib.deprecated` for deprecation notices -- Avoid `builtins.trace` in production code (use for debugging only) -- Use `lib.lists.foldl'` or `lib.lists.foldl'` for strict left folds -- Prefer `lib.mapAttrs` and `lib.mapAttrs'` over `builtins.map` for attribute sets -- Use `lib.filterAttrs` for filtering attributes -- Use `lib.mergeAttrsList` for merging lists of attribute sets -- Use `lib.attrsToList` for converting attributes to list of name-value pairs - -## Performance - -- Use `lib.optionals` and `lib.concatMap` for list operations -- Avoid deep recursion - use `foldl'` for accumulation -- Use `overrideAttrs` for modifying packages instead of rewriting -- Prefer `stdenv.mkDerivation` over raw `derivation` for packages -- Use `pkgs.callPackage` with explicit arguments for clarity - -## Home-Manager Module System - -Home-manager modules manage user-level configuration (home directory, user packages, programs). - -### Module Structure - -``` -modules/ - dev/ - home.nix # Home-manager module with osConfig parameter - nixos.nix # NixOS module importing home.nix via homeModules - shell/ - default.nix # Imports sub-modules - nushell.nix # Actual configuration -``` - -### Home-Manager Module Pattern - -```nix -# modules/dev/shell/default.nix -{ - imports = [ - ./atuin.nix - ./nushell.nix - ./starship.nix - ./zellij.nix - ]; -} -``` - -### Home-Manager with osConfig - -```nix -# modules/dev/home.nix -{ - osConfig ? { }, - lib, - ... -}: -let - inherit (lib) mkEnableOption; -in -{ - # Inherit options from NixOS module configuration - config.dev = { - inherit (osConfig.dev or { }) - shell - editor - vcs - tools - ; - }; - - # Define options (mirrors NixOS module) - options.dev = { - enable = mkEnableOption "all development tools"; - shell.enable = mkEnableOption "shell-related tools"; - editor.enable = mkEnableOption "editor tools"; - vcs.enable = mkEnableOption "version control tools"; - tools.enable = mkEnableOption "development utilities"; - }; - - # Import sub-modules - imports = [ - ./shell - ./editor - ./vcs - ./tools - ]; -} -``` - -### Flake Integration - -```nix -# flake.nix modules section -{ - imports = [ ../systems ]; - - perSystem = { ... }: { - # ... - }; - - flake = { - homeModules = { - dev = import ./dev/home.nix; - desktop = import ./desktop/home.nix; - }; - - nixosModules = { - dev = import ./dev/nixos.nix; - desktop = import ./desktop/nixos.nix; - }; - }; -} -``` - -### Usage in System Configuration - -```nix -# systems/kiwi.nix (NixOS) -{ - imports = [ - inputs.self.nixosModules.dev - # ... other modules - ]; - - dev.enable = true; - dev.shell.enable = true; -} -``` - -```nix -# modules/desktop/home.nix (Home-Manager) -{ config, self, ... }: -{ - imports = [ - self.homeModules.dev - # ... other home modules - ]; - - dev.enable = true; -} -``` - -### Key Differences from NixOS Modules - -| Aspect | NixOS Module | Home-Manager Module | -|--------|--------------|---------------------| -| Special args | `{ config, lib, pkgs, ... }` | `{ config, lib, pkgs, osConfig ? {}, ... }` | -| System config | Direct access to `config.*` | Inherits via `osConfig.dev` | -| User packages | `home.packages` | `home.packages` | -| Programs | `programs.*` | `programs.*` | -| System services | `services.*` | Not available | - -### XDG Directories - -Home-manager manages XDG directories via `xdg.configFile`, `xdg.dataFile`, etc.: - -```nix -{ config, ... }: -{ - xdg.configFile."opencode/agents" = { - source = ./agents; - recursive = true; - }; - - programs.opencode = { - enable = true; - package = opencodePkg; - settings = { - theme = "catppuccin-macchiato"; - }; - }; -} -``` diff --git a/modules/opencode/rules/typescript.md b/modules/opencode/rules/typescript.md deleted file mode 100644 index 586b092..0000000 --- a/modules/opencode/rules/typescript.md +++ /dev/null @@ -1,35 +0,0 @@ -### TypeScript Best Practices - -#### Code Style -- Use strict mode (`"strict": true` in tsconfig.json) -- Prefer `interface` over `type` for object shapes -- Use `const` by default, `let` when needed, never `var` -- Enable `noImplicitAny` and `strictNullChecks` - -#### Type Safety -```typescript -// GOOD: Explicit types and null handling -function getUser(id: string): User | undefined { - return users.get(id); -} - -const user = getUser(id); -if (user) { - console.log(user.name); // TypeScript knows user is defined -} - -// BAD: Type assertions to bypass safety -const user = getUser(id) as User; // Dangerous if undefined -console.log(user.name); // Might crash -``` - -#### Error Handling -- Use try/catch for async operations -- Define custom error types for domain errors -- Never swallow errors silently - -#### Security -- Validate all user input at API boundaries -- Use parameterized queries for database operations -- Sanitize data before rendering in DOM (prevent XSS) -- Never use `eval()` or `Function()` with user input -- 2.51.2