refactor: reorganize, add package (opencode, osgrep, beads), add ai agent shell

This commit is contained in:
Rasyidan Akbar F. 2025-12-09 15:09:05 +07:00
commit a0f0f39399
58 changed files with 7867 additions and 42 deletions

View file

@ -1,8 +1,8 @@
{ config, pkgs, lib, ... }: {
# Import shared cross-platform home configuration
imports = [
../../home/rsydn/base.nix
../../home/rsydn/shell/nushell.nix
../../home/base.nix
../../home/shell/nushell.nix
./programs/aerospace
./programs/ghostty.nix
];

View file

@ -9,6 +9,7 @@
./devtools/languages.nix
./devtools/default.nix
./devtools/try.nix
./devtools/opencode.nix
./secrets.nix
];
@ -34,6 +35,13 @@
path = "~/src/tries";
};
rsydn.opencode = {
enable = lib.mkDefault true;
theme = "catppuccin";
model = "anthropic/claude-sonnet-4-5";
smallModel = "anthropic/claude-haiku-3-5";
};
rsydn.devTools = {
enable = lib.mkDefault true;
packages = with pkgs; [

View file

@ -10,8 +10,6 @@ let
crushDefault = getPkgsPackage "crush";
opencodeDefault = getPkgsPackage "opencode";
claudeDefault = getPkgsPackage "claude-code";
toolOption = { name, description, defaultPackage, extraOptions ? { }
@ -61,10 +59,6 @@ let
name = "crush";
cfg = cfg.crush;
}
{
name = "opencode";
cfg = cfg.opencode;
}
{
name = "claude";
cfg = cfg.claude;
@ -121,11 +115,7 @@ in {
defaultPackage = crushDefault;
};
opencode = toolOption {
name = "OpenCode";
description = "OpenCode AI collaborative CLI packaged in nixpkgs.";
defaultPackage = opencodeDefault;
};
# Note: OpenCode is now managed by its own module at ./opencode.nix
claude = toolOption {
name = "Claude Code";

View file

@ -0,0 +1,107 @@
{ config, lib, pkgs, customPkgs, ... }:
let
inherit (lib) mkEnableOption mkOption types mkIf;
cfg = config.rsydn.opencode;
# Build the config JSON, filtering out null/empty values
opencodeConfig = lib.filterAttrs (_: v: v != null && v != { }) {
"$schema" = "https://opencode.ai/config.json";
theme = cfg.theme;
model = cfg.model;
small_model = cfg.smallModel;
autoupdate = false; # Nix manages updates
mcp = if cfg.mcpServers != { } then cfg.mcpServers else null;
keybinds = if cfg.keybinds != { } then cfg.keybinds else null;
};
in {
options.rsydn.opencode = {
enable = mkEnableOption "OpenCode CLI with managed configuration";
package = mkOption {
type = types.nullOr types.package;
default = customPkgs.opencode;
description = ''
OpenCode package to use. Defaults to the custom-built package
from packages/opencode.nix via customPkgs.
'';
};
theme = mkOption {
type = types.str;
default = "catppuccin";
description = "UI theme for OpenCode TUI";
};
model = mkOption {
type = types.str;
default = "anthropic/claude-sonnet-4-5";
description = "Primary LLM model identifier";
};
smallModel = mkOption {
type = types.str;
default = "anthropic/claude-haiku-3-5";
description = "Lightweight model for tasks like title generation";
};
mcpServers = mkOption {
type = types.attrs;
default = { };
example = {
nixos = {
type = "stdio";
command = "uvx";
args = [ "mcp-nixos" ];
};
};
description = "MCP server configurations";
};
keybinds = mkOption {
type = types.attrs;
default = { };
description = "Custom keybinding overrides";
};
# Future extension points (joelhooks-style)
agents = mkOption {
type = types.attrsOf types.path;
default = { };
description = ''
Custom agent markdown files.
Keys become filenames under ~/.config/opencode/agent/
'';
};
commands = mkOption {
type = types.attrsOf types.path;
default = { };
description = ''
Custom command markdown files.
Keys become filenames under ~/.config/opencode/command/
'';
};
extraConfig = mkOption {
type = types.attrs;
default = { };
description = "Additional config options to merge into opencode.json";
};
};
config = mkIf cfg.enable {
home.packages = lib.optional (cfg.package != null) cfg.package;
xdg.configFile = {
# Global config file
"opencode/opencode.json".text =
builtins.toJSON (opencodeConfig // cfg.extraConfig);
} // lib.mapAttrs' (name: path:
# Agent markdown files
lib.nameValuePair "opencode/agent/${name}.md" { source = path; })
cfg.agents // lib.mapAttrs' (name: path:
# Command markdown files
lib.nameValuePair "opencode/command/${name}.md" { source = path; })
cfg.commands;
};
}

View file

@ -0,0 +1,74 @@
{
"bufferline.nvim": {
"branch": "main",
"commit": "655133c3b4c3e5e05ec549b9f8cc2894ac6f51b3"
},
"catppuccin": {
"branch": "main",
"commit": "8c4125e3c746976ba025dc5d908fa22c6aa09486"
},
"gruvbox.nvim": {
"branch": "main",
"commit": "5e0a460d8e0f7f669c158dedd5f9ae2bcac31437"
},
"indent-blankline.nvim": {
"branch": "master",
"commit": "005b56001b2cb30bfa61b7986bc50657816ba4ba"
},
"lazy.nvim": {
"branch": "main",
"commit": "ed4dc336a73c18da6fea6e1cf7ad6e1b76d281eb"
},
"lazydev.nvim": {
"branch": "main",
"commit": "12532f81ef8aa35dd4a44713ea32e760d643675c"
},
"lualine.nvim": {
"branch": "master",
"commit": "3946f0122255bc377d14a59b27b609fb3ab25768"
},
"mini.ai": {
"branch": "main",
"commit": "11c57180bc9084089206e211ac7aa598bedc9673"
},
"mini.bufremove": {
"branch": "main",
"commit": "1b5d7e7bc81cd02476bdd964c3d850c48eb91a07"
},
"mini.pairs": {
"branch": "main",
"commit": "b9aada8c0e59f2b938e98fbf4eae0799eba96ad9"
},
"noice.nvim": {
"branch": "main",
"commit": "d14d02cb709e3bb2da88363c32f8b4250bced52d"
},
"nui.nvim": {
"branch": "main",
"commit": "de740991c12411b663994b2860f1a4fd0937c130"
},
"nvim-lint": {
"branch": "master",
"commit": "9da1fb942dd0668d5182f9c8dee801b9c190e2bb"
},
"nvim-lspconfig": {
"branch": "master",
"commit": "2b52bc2190c8efde2e4de02d829a138666774c7c"
},
"nvim-treesitter": {
"branch": "master",
"commit": "42fc28ba918343ebfd5565147a42a26580579482"
},
"nvim-web-devicons": {
"branch": "master",
"commit": "8dcb311b0c92d460fac00eac706abd43d94d68af"
},
"tokyonight.nvim": {
"branch": "main",
"commit": "4fe1b0c44f5d6ee769cdfbdffc7ccb703f53feda"
},
"ts-comments.nvim": {
"branch": "main",
"commit": "217ab9cc137fceb6659b53790bd25e608219abe1"
}
}

View file

@ -67,6 +67,71 @@ in {
print $"(ansi green_bold) Core QA checks passed!(ansi reset)"
}
# Beads helper functions for AI agent workflows
# Safe Beads initialization with git repository check
def bd-init [] {
if not (".git" | path exists) and not (".git" | path type) == "dir" {
print $"(ansi red_bold)Error: Not in a git repository(ansi reset)"
print "Beads requires a git repository. Initialize git first:"
print " git init"
return
}
if (".beads" | path exists) {
print $"(ansi yellow)Beads already initialized in this project(ansi reset)"
return
}
print $"(ansi green)Initializing Beads...(ansi reset)"
bd init
if $env.LAST_EXIT_CODE == 0 {
print $"(ansi green_bold) Beads initialized successfully(ansi reset)"
print ""
print "Next steps:"
print " bd create --title \"Your task\" --type feature"
print " bd ready"
}
}
# View ready tasks with structured output
def bd-ready [] {
if not (".beads" | path exists) {
print $"(ansi red)Not a Beads project. Run 'bd-init' first.(ansi reset)"
return
}
let ready_tasks = (bd ready --json | from json)
if ($ready_tasks | is-empty) {
print $"(ansi yellow)No ready tasks(ansi reset)"
return
}
print $"(ansi green_bold)Ready Tasks:(ansi reset)\n"
$ready_tasks | each {|task|
print $"(ansi cyan)[$($task.id)](ansi reset) ($task.title)"
print $" Type: ($task.type) | Priority: ($task.priority) | Status: ($task.status)"
if ($task.description? != null) and ($task.description | str length) > 0 {
print $" ($task.description)"
}
print ""
} | ignore
}
# Sync Beads database with git
def bd-sync [] {
if not (".beads" | path exists) {
print $"(ansi red)Not a Beads project(ansi reset)"
return
}
print $"(ansi blue)Syncing Beads database...(ansi reset)"
bd sync
if $env.LAST_EXIT_CODE == 0 {
print $"(ansi green) Sync complete(ansi reset)"
}
}
'';
programs.nushell.envFile.text = ''

View file

@ -4,9 +4,9 @@
# Desktop configs are handled directly in nixos/desktops/* modules
imports = [
../../home/rsydn/base.nix # Base CLI tools (git, tmux, etc.)
../../home/rsydn/shell/fish.nix # Shell configuration
../../home/rsydn/shell/nushell.nix # Secondary Nushell setup
../../home/base.nix # Base CLI tools (git, tmux, etc.)
../../home/shell/fish.nix # Shell configuration
../../home/shell/nushell.nix # Secondary Nushell setup
];
config.programs.tmux.extraConfig = lib.mkAfter ''

View file

@ -1,48 +0,0 @@
{ lib, pkgs, ... }:
let
hardwareConfigPath = builtins.toString ./.
+ "/../desktops/hardware-configuration.nix";
hardwareModule = if builtins.pathExists hardwareConfigPath then
import hardwareConfigPath
else
(_: { });
in {
# Unified desktop configuration for gaming and development
# Includes both KDE Plasma and Hyprland - switch at login screen
# Import hardware and desktop configurations
imports = [
# Hardware
hardwareModule
# Audio & Graphics
../audio.nix
../graphics.nix
# Desktop Environments
../desktops/plasma.nix # KDE Plasma (for gaming)
../desktops/hyprland # Hyprland (for development)
# Desktop Apps & Theme
../desktops/apps/browsers.nix
../desktops/apps/terminals.nix
../desktops/themes/catppuccin.nix # Or use themes/default.nix for Adwaita
# Gaming & Features
../desktops/gaming.nix
../bluetooth.nix
];
# Boot configuration with CachyOS LTS kernel
boot = {
loader.systemd-boot.enable = true;
loader.efi.canTouchEfiVariables = true;
kernelPackages = pkgs.linuxPackages_cachyos-lts;
};
# Hostname
networking.hostName = "desktop";
# This value determines the NixOS release compatibility
system.stateVersion = "24.05";
}

View file

@ -1,51 +0,0 @@
{ lib, pkgs, ... }: {
networking.hostName = "dev-vm";
# Set the baseline NixOS release used for stateful data; bump on rebuilds.
system.stateVersion = lib.mkDefault "24.05";
# Boot loader configuration for VM
boot.loader.grub.enable = lib.mkDefault true;
boot.loader.grub.device = lib.mkDefault "/dev/vda";
# Root filesystem configuration for VM
fileSystems."/" = lib.mkDefault {
device = "/dev/vda1";
fsType = "ext4";
};
environment.systemPackages = with pkgs; [
# Core utilities
coreutils
ast-grep
ripgrep
bat
tree
zip
unzip
jq
psmisc # provides killall and friends
gh
tree-sitter
# System monitoring & debugging tools
htop # Process viewer
iotop # I/O monitoring
lsof # List open files
strace # System call tracing
ltrace # Library call tracing
gdb # GNU debugger
tcpdump # Network packet analyzer
ncdu # Disk usage analyzer
];
services.qemuGuest.enable = true;
services.openssh.openFirewall = false;
networking.firewall = {
allowedTCPPorts = [ ];
interfaces."tailscale0".allowedTCPPorts = [ 22 ];
};
imports = [ ../virtualization.nix ];
}