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,19 +1,31 @@
# Repository Guidelines # Repository Guidelines
## Project Structure & Module Organization ## Project Structure & Module Organization
Shared logic lives in `flake.nix`, with inputs pinned in `flake.lock`. Platform modules sit under `modules/darwin/` and `modules/nixos/`; Linux hosts compose `modules/nixos/system.nix` plus `users.nix`, `network.nix`, `ssh.nix`, and `containerization.nix`. Host-specific overlays like `modules/nixos/hosts/dev-vm.nix` supply virtualization packages, while macOS tweaks belong in `modules/darwin/hosts/`. Home Manager layers for `rsydn` are under `modules/home/rsydn/` with `programs/`, `shell/`, and `devtools/`. Secrets stay encrypted in `secrets/*.sops.yaml` and surface at runtime in `~/.config/secrets/`.
Shared logic lives in `flake.nix`, with inputs pinned in `flake.lock`. Platform modules sit under `modules/darwin/` and `modules/nixos/`; Linux hosts compose `modules/nixos/system.nix` plus `users.nix`, `network.nix`, `ssh.nix`, and `containerization.nix`. Host-specific configs live at root level in `hosts/` (e.g. `hosts/dev-vm.nix`, `hosts/desktop.nix`). Home Manager layers are under `modules/home/` with `programs/`, `shell/`, and `devtools/`. Secrets stay encrypted in `secrets/*.sops.yaml` and surface at runtime in `~/.config/secrets/`.
Custom packages live in `packages/` and are exported via `packages/default.nix`. Each package has its own derivation file (e.g. `packages/osgrep.nix`). Packages are exported in the flake's `packages` output and can be built individually with `nix build .#osgrep`. The `checks` output ensures all packages build during `nix flake check`. To reference the current system in package definitions, use `pkgs.stdenv.hostPlatform.system` instead of the deprecated `pkgs.system`. Development shells in `shells/` receive custom packages through the `customPkgs` argument.
## Build, Test, and Development Commands ## Build, Test, and Development Commands
Use `nix develop` to enter the project shell with `git`, `nixfmt-classic`, and SOPS ready. Run `nix fmt` before committing to format all Nix sources. `XDG_CACHE_HOME=$PWD/.cache nix flake check` validates every host without polluting the global cache. For macOS changes, run `darwin-rebuild --dry-run --flake .#macbook-pro` and follow with `darwin-rebuild switch --flake .#macbook-pro`. Validate the Linux VM closure via `nix build .#nixosConfigurations.dev-vm.config.system.build.toplevel`. Use `nix develop` to enter the project shell with `git`, `nixfmt-classic`, and SOPS ready. Run `nix fmt` before committing to format all Nix sources. `XDG_CACHE_HOME=$PWD/.cache nix flake check` validates every host without polluting the global cache. For macOS changes, run `darwin-rebuild --dry-run --flake .#macbook-pro` and follow with `darwin-rebuild switch --flake .#macbook-pro`. Validate the Linux VM closure via `nix build .#nixosConfigurations.dev-vm.config.system.build.toplevel`.
## AI Agent Workflow with Beads
Beads provides persistent memory for AI coding agents through git-backed issue tracking. Enter the AI agent shell with `nix develop .#ai-agent` to access Beads (`bd`) and helper tools. Initialize in any git repository with `bd-init` (Nushell) or `bd init`. Create tasks via `bd create --title "Task name" --type feature --priority high`, track dependencies with `--blocks <id>` or `--parent <id>`, and query ready work using `bd-ready` or `bd ready --json`. The `.beads/issues.jsonl` file must be committed to git to preserve context across sessions. Claude Code can directly use `bd` commands via the Bash tool to maintain work continuity between conversations. Sync the SQLite cache after git operations with `bd-sync` or `bd sync`.
## Coding Style & Naming Conventions ## Coding Style & Naming Conventions
Indent with two spaces and keep trailing commas in attribute sets. Prefer lower-kebab filenames (e.g. `shell/nushell.nix`), and expose custom options under the `rsydn.*` namespace. Format Nix with `nix fmt`; avoid ad-hoc host tweaks when a shared module fits.
Indent with two spaces and keep trailing commas in attribute sets. Prefer lower-kebab filenames (e.g. `package/nushell.nix`), and expose custom options under the `rsydn.*` namespace. Format Nix with `nix fmt`; avoid ad-hoc host tweaks when a shared module fits.
## Testing Guidelines ## Testing Guidelines
Treat `nix flake check` as the minimum gate; capture its output for reviews. When adjusting host builds, keep dry-run transcripts (`darwin-rebuild --dry-run`, `nix build .#nixosConfigurations.dev-vm…`) to share in PRs. Co-locate regression tests next to their modules using the `<option-name>.nix` pattern. Treat `nix flake check` as the minimum gate; capture its output for reviews. When adjusting host builds, keep dry-run transcripts (`darwin-rebuild --dry-run`, `nix build .#nixosConfigurations.dev-vm…`) to share in PRs. Co-locate regression tests next to their modules using the `<option-name>.nix` pattern.
## Commit & Pull Request Guidelines ## Commit & Pull Request Guidelines
Write imperative commit subjects such as `add dev-vm virtualization module`, and keep each commit narrowly scoped. PRs should list validation commands run, link related issues or TODOs, and attach screenshots or terminal snippets when touching prompts, Tailscale, or SSH flows. Write imperative commit subjects such as `add dev-vm virtualization module`, and keep each commit narrowly scoped. PRs should list validation commands run, link related issues or TODOs, and attach screenshots or terminal snippets when touching prompts, Tailscale, or SSH flows.
## Security & Configuration Tips ## Security & Configuration Tips
Age keys live at `~/.config/sops/age/keys.txt`; regenerate via Home Manager if missing. Access secrets with `open ~/.config/secrets/<name> | str trim` and scope them using `with-env`. Audit `services.tailscale.extraUpFlags` before enabling exit nodes, and rotate SSH/Tailscale keys regularly. Age keys live at `~/.config/sops/age/keys.txt`; regenerate via Home Manager if missing. Access secrets with `open ~/.config/secrets/<name> | str trim` and scope them using `with-env`. Audit `services.tailscale.extraUpFlags` before enabling exit nodes, and rotate SSH/Tailscale keys regularly.

59
flake.lock generated
View file

@ -1,12 +1,33 @@
{ {
"nodes": { "nodes": {
"beads": {
"inputs": {
"flake-utils": "flake-utils",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1765103675,
"narHash": "sha256-PCsU2GwrPYFAE2KTCdk60E6ZTPRtgNdbPF+bzf8qi3Q=",
"owner": "steveyegge",
"repo": "beads",
"rev": "08d8353619777dfdd4119477ca029689597cec60",
"type": "github"
},
"original": {
"owner": "steveyegge",
"repo": "beads",
"type": "github"
}
},
"blueprint": { "blueprint": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"nix-ai-tools", "nix-ai-tools",
"nixpkgs" "nixpkgs"
], ],
"systems": "systems_2" "systems": "systems_3"
}, },
"locked": { "locked": {
"lastModified": 1763308703, "lastModified": 1763308703,
@ -131,10 +152,28 @@
"type": "github" "type": "github"
} }
}, },
"flake-utils_2": {
"inputs": {
"systems": "systems_2"
},
"locked": {
"lastModified": 1731533236,
"narHash": "sha256-l0KFg5HjrsfsO/JpG+r7fRrqm12kzFHyUHqHCVpMMbI=",
"owner": "numtide",
"repo": "flake-utils",
"rev": "11707dc2f618dd54ca8739b309ec4fc024de578b",
"type": "github"
},
"original": {
"owner": "numtide",
"repo": "flake-utils",
"type": "github"
}
},
"ghostty": { "ghostty": {
"inputs": { "inputs": {
"flake-compat": "flake-compat", "flake-compat": "flake-compat",
"flake-utils": "flake-utils", "flake-utils": "flake-utils_2",
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
], ],
@ -325,6 +364,7 @@
}, },
"root": { "root": {
"inputs": { "inputs": {
"beads": "beads",
"chaotic": "chaotic", "chaotic": "chaotic",
"darwin": "darwin", "darwin": "darwin",
"ghostty": "ghostty", "ghostty": "ghostty",
@ -406,6 +446,21 @@
"type": "github" "type": "github"
} }
}, },
"systems_3": {
"locked": {
"lastModified": 1681028828,
"narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
"owner": "nix-systems",
"repo": "default",
"rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
"type": "github"
},
"original": {
"owner": "nix-systems",
"repo": "default",
"type": "github"
}
},
"treefmt-nix": { "treefmt-nix": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [

View file

@ -22,10 +22,13 @@
try.url = "github:tobi/try"; try.url = "github:tobi/try";
try.inputs.nixpkgs.follows = "nixpkgs"; try.inputs.nixpkgs.follows = "nixpkgs";
beads.url = "github:steveyegge/beads";
beads.inputs.nixpkgs.follows = "nixpkgs";
}; };
outputs = inputs@{ self, nixpkgs, darwin, home-manager, ghostty, nix-ai-tools outputs = inputs@{ self, nixpkgs, darwin, home-manager, ghostty, nix-ai-tools
, sops-nix, chaotic, try, ... }: , sops-nix, chaotic, try, beads, ... }:
let let
inherit (nixpkgs.lib) genAttrs; inherit (nixpkgs.lib) genAttrs;
lib = nixpkgs.lib; lib = nixpkgs.lib;
@ -53,7 +56,9 @@
mkDarwin = { system ? "aarch64-darwin", extraModules ? [ ] mkDarwin = { system ? "aarch64-darwin", extraModules ? [ ]
, homeFile ? ./modules/darwin/home/default.nix, }: , homeFile ? ./modules/darwin/home/default.nix, }:
let pkgs = mkPkgs system; let
pkgs = mkPkgs system;
customPkgs = import ./packages { inherit pkgs lib beads; };
in darwin.lib.darwinSystem { in darwin.lib.darwinSystem {
inherit system pkgs; inherit system pkgs;
specialArgs = { inherit inputs overlaysList user; }; specialArgs = { inherit inputs overlaysList user; };
@ -66,14 +71,18 @@
home-manager.useGlobalPkgs = true; home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true; home-manager.useUserPackages = true;
home-manager.backupFileExtension = "backup"; home-manager.backupFileExtension = "backup";
home-manager.extraSpecialArgs = { inherit inputs user try; }; home-manager.extraSpecialArgs = {
inherit inputs user try customPkgs;
};
home-manager.users.${user} = import homeFile; home-manager.users.${user} = import homeFile;
} }
]; ];
}; };
mkNixos = { system ? "x86_64-linux", extraModules ? [ ] mkNixos = { system ? "x86_64-linux", extraModules ? [ ]
, homeFile ? ./modules/nixos/home/default.nix, }: , homeFile ? ./modules/nixos/home/default.nix, }:
let pkgs = mkPkgs system; let
pkgs = mkPkgs system;
customPkgs = import ./packages { inherit pkgs lib beads; };
in lib.nixosSystem { in lib.nixosSystem {
inherit system pkgs; inherit system pkgs;
specialArgs = { inherit inputs overlaysList user; }; specialArgs = { inherit inputs overlaysList user; };
@ -85,7 +94,9 @@
home-manager.useGlobalPkgs = true; home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true; home-manager.useUserPackages = true;
home-manager.extraSpecialArgs = { inherit inputs user try; }; home-manager.extraSpecialArgs = {
inherit inputs user try customPkgs;
};
home-manager.users.${user} = import homeFile; home-manager.users.${user} = import homeFile;
} }
]; ];
@ -95,12 +106,12 @@
nixosConfigurations = { nixosConfigurations = {
dev-vm = mkNixos { dev-vm = mkNixos {
system = "x86_64-linux"; system = "x86_64-linux";
extraModules = [ ./modules/nixos/hosts/dev-vm.nix ]; extraModules = [ ./hosts/dev-vm.nix ];
# Uses default homeFile: ./modules/nixos/home/default.nix # Uses default homeFile: ./modules/nixos/home/default.nix
}; };
desktop = mkNixos { desktop = mkNixos {
system = "x86_64-linux"; system = "x86_64-linux";
extraModules = [ ./modules/nixos/hosts/desktop.nix ]; extraModules = [ ./hosts/desktop.nix ];
# Home Manager configs are imported directly in desktop modules # Home Manager configs are imported directly in desktop modules
# No homeFile needed - using inline home-manager.users.${user} # No homeFile needed - using inline home-manager.users.${user}
homeFile = ./modules/nixos/home/default.nix; homeFile = ./modules/nixos/home/default.nix;
@ -115,11 +126,14 @@
else else
pkgs.python3; pkgs.python3;
# Common arguments passed to all devshell imports # Custom packages (osgrep, beads, etc.)
shellArgs = { inherit pkgs lib python; }; customPkgs = import ./packages { inherit pkgs lib beads; };
# Import all devshells from devshells/ directory # Common arguments passed to all devshell imports
importShell = name: import (./devshells + "/${name}.nix") shellArgs; shellArgs = { inherit pkgs lib python customPkgs; };
# Import all shells from shells/ directory
importShell = name: import (./shells + "/${name}.nix") shellArgs;
in { in {
default = importShell "default"; default = importShell "default";
@ -129,10 +143,21 @@
go = importShell "go"; go = importShell "go";
web-bun = importShell "web-bun"; web-bun = importShell "web-bun";
rust = importShell "rust"; rust = importShell "rust";
ai-agent = importShell "ai-agent";
}); });
formatter = forEachSystem (system: (mkPkgs system).nixfmt-classic); formatter = forEachSystem (system: (mkPkgs system).nixfmt-classic);
packages = forEachSystem (system: { inherit (mkPkgs system) git; }); packages = forEachSystem (system:
let
pkgs = mkPkgs system;
customPkgs = import ./packages { inherit pkgs lib beads; };
in customPkgs // { inherit (pkgs) git; });
checks = forEachSystem (system:
let
pkgs = mkPkgs system;
customPkgs = import ./packages { inherit pkgs lib beads; };
in { inherit (customPkgs) osgrep opencode beads; });
}; };
} }

View file

@ -1,7 +1,7 @@
{ lib, pkgs, ... }: { lib, pkgs, ... }:
let let
hardwareConfigPath = builtins.toString ./. hardwareConfigPath = builtins.toString ./.
+ "/../desktops/hardware-configuration.nix"; + "/../modules/nixos/desktops/hardware-configuration.nix";
hardwareModule = if builtins.pathExists hardwareConfigPath then hardwareModule = if builtins.pathExists hardwareConfigPath then
import hardwareConfigPath import hardwareConfigPath
else else
@ -16,21 +16,21 @@ in {
hardwareModule hardwareModule
# Audio & Graphics # Audio & Graphics
../audio.nix ../modules/nixos/audio.nix
../graphics.nix ../modules/nixos/graphics.nix
# Desktop Environments # Desktop Environments
../desktops/plasma.nix # KDE Plasma (for gaming) ../modules/nixos/desktops/plasma.nix # KDE Plasma (for gaming)
../desktops/hyprland # Hyprland (for development) ../modules/nixos/desktops/hyprland # Hyprland (for development)
# Desktop Apps & Theme # Desktop Apps & Theme
../desktops/apps/browsers.nix ../modules/nixos/desktops/apps/browsers.nix
../desktops/apps/terminals.nix ../modules/nixos/desktops/apps/terminals.nix
../desktops/themes/catppuccin.nix # Or use themes/default.nix for Adwaita ../modules/nixos/desktops/themes/catppuccin.nix # Or use themes/default.nix for Adwaita
# Gaming & Features # Gaming & Features
../desktops/gaming.nix ../modules/nixos/desktops/gaming.nix
../bluetooth.nix ../modules/nixos/bluetooth.nix
]; ];
# Boot configuration with CachyOS LTS kernel # Boot configuration with CachyOS LTS kernel

View file

@ -47,5 +47,5 @@
interfaces."tailscale0".allowedTCPPorts = [ 22 ]; interfaces."tailscale0".allowedTCPPorts = [ 22 ];
}; };
imports = [ ../virtualization.nix ]; imports = [ ../modules/nixos/virtualization.nix ];
} }

View file

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

View file

@ -9,6 +9,7 @@
./devtools/languages.nix ./devtools/languages.nix
./devtools/default.nix ./devtools/default.nix
./devtools/try.nix ./devtools/try.nix
./devtools/opencode.nix
./secrets.nix ./secrets.nix
]; ];
@ -34,6 +35,13 @@
path = "~/src/tries"; 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 = { rsydn.devTools = {
enable = lib.mkDefault true; enable = lib.mkDefault true;
packages = with pkgs; [ packages = with pkgs; [

View file

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

View file

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

8
packages/default.nix Normal file
View file

@ -0,0 +1,8 @@
{ pkgs, lib, beads }:
{
osgrep = pkgs.callPackage ./osgrep.nix { };
opencode = pkgs.callPackage ./opencode.nix { };
# Re-export beads from flake input for consistent access
beads = beads.packages.${pkgs.stdenv.hostPlatform.system}.default;
}

59
packages/opencode.nix Normal file
View file

@ -0,0 +1,59 @@
{ lib, stdenv, fetchzip, autoPatchelfHook }:
let
version = "1.0.137";
platformInfo = {
x86_64-darwin = {
name = "darwin-x64";
ext = "zip";
hash = "sha256-ZgzvpW8SAN0HP3Qh+aT1S+WrEoXkTZiL8/8z1w9p9Y0=";
};
aarch64-darwin = {
name = "darwin-arm64";
ext = "zip";
hash = "sha256-w5hs5arVroLtjr9pRAHYXQL+4L5gw1bJVL4DhgvrpLs=";
};
x86_64-linux = {
name = "linux-x64";
ext = "tar.gz";
hash = "sha256-N8IR3xq/dGF5jPq9/UW8Mu/PnAUb7r+Ox6nge6wKw1M=";
};
aarch64-linux = {
name = "linux-arm64";
ext = "tar.gz";
hash = "sha256-LwWbTAVnfEmCjUBNf58bgFFVVvDIQVJkLsGtYX+/EfU=";
};
};
platform = platformInfo.${stdenv.hostPlatform.system} or (throw
"Unsupported system: ${stdenv.hostPlatform.system}");
in stdenv.mkDerivation {
pname = "opencode";
inherit version;
src = fetchzip {
url =
"https://github.com/sst/opencode/releases/download/v${version}/opencode-${platform.name}.${platform.ext}";
hash = platform.hash;
stripRoot = false;
};
nativeBuildInputs = lib.optionals stdenv.isLinux [ autoPatchelfHook ];
installPhase = ''
runHook preInstall
mkdir -p $out/bin
cp opencode $out/bin/opencode
chmod +x $out/bin/opencode
runHook postInstall
'';
meta = {
description = "AI coding assistant CLI from SST";
homepage = "https://opencode.ai";
license = lib.licenses.mit;
mainProgram = "opencode";
platforms =
[ "x86_64-darwin" "aarch64-darwin" "x86_64-linux" "aarch64-linux" ];
};
}

7292
packages/osgrep-package-lock.json generated Normal file

File diff suppressed because it is too large Load diff

32
packages/osgrep.nix Normal file
View file

@ -0,0 +1,32 @@
{ lib, buildNpmPackage, fetchurl, python3 }:
buildNpmPackage rec {
pname = "osgrep";
version = "0.5.16";
src = fetchurl {
url = "https://registry.npmjs.org/${pname}/-/${pname}-${version}.tgz";
hash = "sha256-V23PNxtMCdRaqr4uDOs0rptmSROUrUO7I9HazoGqUCQ=";
};
sourceRoot = "package";
postPatch = ''
cp ${./osgrep-package-lock.json} package-lock.json
'';
npmDepsHash = "sha256-JDeZ3kcsbcZM//0846i1gsvoAgMpIpN8c160cp7wB5s=";
npmFlags = [ "--legacy-peer-deps" ];
dontNpmBuild = true; # Already built in npm package
nativeBuildInputs = [ python3 ];
meta = {
description = "Semantic grep for AI agents";
homepage = "https://github.com/Ryandonofrio3/osgrep";
license = lib.licenses.asl20;
mainProgram = "osgrep";
};
}

98
shells/ai-agent.nix Normal file
View file

@ -0,0 +1,98 @@
{ pkgs, lib, customPkgs, ... }:
let
# Get Beads package from custom packages
beadsPkg = customPkgs.beads;
# Helper script to show Beads project status
beadsStatus = pkgs.writeShellScriptBin "beads-status" ''
if [ ! -d ".beads" ]; then
echo "Not a Beads project (no .beads directory found)"
exit 1
fi
echo "=== Beads Project Status ==="
echo ""
echo "Ready tasks:"
${beadsPkg}/bin/bd ready --json | ${pkgs.jq}/bin/jq -r '.[] | " - [\(.id)] \(.title) (\(.type))"'
echo ""
echo "In Progress:"
${beadsPkg}/bin/bd list --status in-progress --json | ${pkgs.jq}/bin/jq -r '.[] | " - [\(.id)] \(.title)"'
echo ""
echo "For full details, run: bd list --json | jq"
'';
# Helper script to generate full project context for AI agents
beadsContext = pkgs.writeShellScriptBin "beads-context" ''
if [ ! -d ".beads" ]; then
echo "Not a Beads project (no .beads directory found)"
exit 1
fi
echo "=== Beads Project Context ==="
echo ""
echo "All issues:"
${beadsPkg}/bin/bd list --json | ${pkgs.jq}/bin/jq '.'
echo ""
echo "Dependency tree (first ready task):"
ready_id=$(${beadsPkg}/bin/bd ready --json | ${pkgs.jq}/bin/jq -r '.[0].id // empty')
if [ -n "$ready_id" ]; then
${beadsPkg}/bin/bd dep tree "$ready_id"
else
echo "No ready tasks"
fi
'';
in pkgs.mkShell {
name = "ai-agent";
packages = with pkgs; [
# Custom packages
customPkgs.beads
customPkgs.osgrep
# Version control
git
jujutsu
# Code search and navigation
ripgrep
ast-grep
fzf
# JSON processing
jq
# Helper scripts
beadsStatus
beadsContext
];
shellHook = ''
# Add home-manager binaries to PATH (claude-code, codex, etc.)
if [ -d "$HOME/.nix-profile/bin" ]; then
export PATH="$HOME/.nix-profile/bin:$PATH"
fi
echo "AI Agent development shell loaded"
echo ""
echo "Available tools:"
echo " bd - Beads CLI (issue tracker)"
echo " beads-status - Show project status summary"
echo " beads-context - Generate full context for AI"
echo " osgrep - Semantic code search"
echo " jj/git - Version control"
echo " rg/ast-grep - Code search"
echo ""
echo "Quick start:"
echo " bd init - Initialize Beads in current project"
echo " bd create - Create new issue"
echo " bd ready - Show ready tasks"
echo " bd --help - Full command reference"
echo ""
echo "Nushell helpers (if in nushell):"
echo " bd-init - Safe init with git checks"
echo " bd-ready - View ready tasks (structured)"
echo " bd-sync - Sync database with git"
'';
}