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
## 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
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
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
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
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
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": {
"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": {
"inputs": {
"nixpkgs": [
"nix-ai-tools",
"nixpkgs"
],
"systems": "systems_2"
"systems": "systems_3"
},
"locked": {
"lastModified": 1763308703,
@ -131,10 +152,28 @@
"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": {
"inputs": {
"flake-compat": "flake-compat",
"flake-utils": "flake-utils",
"flake-utils": "flake-utils_2",
"nixpkgs": [
"nixpkgs"
],
@ -325,6 +364,7 @@
},
"root": {
"inputs": {
"beads": "beads",
"chaotic": "chaotic",
"darwin": "darwin",
"ghostty": "ghostty",
@ -406,6 +446,21 @@
"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": {
"inputs": {
"nixpkgs": [

View file

@ -22,10 +22,13 @@
try.url = "github:tobi/try";
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
, sops-nix, chaotic, try, ... }:
, sops-nix, chaotic, try, beads, ... }:
let
inherit (nixpkgs.lib) genAttrs;
lib = nixpkgs.lib;
@ -53,7 +56,9 @@
mkDarwin = { system ? "aarch64-darwin", extraModules ? [ ]
, 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 {
inherit system pkgs;
specialArgs = { inherit inputs overlaysList user; };
@ -66,14 +71,18 @@
home-manager.useGlobalPkgs = true;
home-manager.useUserPackages = true;
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;
}
];
};
mkNixos = { system ? "x86_64-linux", extraModules ? [ ]
, homeFile ? ./modules/nixos/home/default.nix, }:
let pkgs = mkPkgs system;
let
pkgs = mkPkgs system;
customPkgs = import ./packages { inherit pkgs lib beads; };
in lib.nixosSystem {
inherit system pkgs;
specialArgs = { inherit inputs overlaysList user; };
@ -85,7 +94,9 @@
home-manager.useGlobalPkgs = 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;
}
];
@ -95,12 +106,12 @@
nixosConfigurations = {
dev-vm = mkNixos {
system = "x86_64-linux";
extraModules = [ ./modules/nixos/hosts/dev-vm.nix ];
extraModules = [ ./hosts/dev-vm.nix ];
# Uses default homeFile: ./modules/nixos/home/default.nix
};
desktop = mkNixos {
system = "x86_64-linux";
extraModules = [ ./modules/nixos/hosts/desktop.nix ];
extraModules = [ ./hosts/desktop.nix ];
# Home Manager configs are imported directly in desktop modules
# No homeFile needed - using inline home-manager.users.${user}
homeFile = ./modules/nixos/home/default.nix;
@ -115,11 +126,14 @@
else
pkgs.python3;
# Common arguments passed to all devshell imports
shellArgs = { inherit pkgs lib python; };
# Custom packages (osgrep, beads, etc.)
customPkgs = import ./packages { inherit pkgs lib beads; };
# Import all devshells from devshells/ directory
importShell = name: import (./devshells + "/${name}.nix") shellArgs;
# Common arguments passed to all devshell imports
shellArgs = { inherit pkgs lib python customPkgs; };
# Import all shells from shells/ directory
importShell = name: import (./shells + "/${name}.nix") shellArgs;
in {
default = importShell "default";
@ -129,10 +143,21 @@
go = importShell "go";
web-bun = importShell "web-bun";
rust = importShell "rust";
ai-agent = importShell "ai-agent";
});
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, ... }:
let
hardwareConfigPath = builtins.toString ./.
+ "/../desktops/hardware-configuration.nix";
+ "/../modules/nixos/desktops/hardware-configuration.nix";
hardwareModule = if builtins.pathExists hardwareConfigPath then
import hardwareConfigPath
else
@ -16,21 +16,21 @@ in {
hardwareModule
# Audio & Graphics
../audio.nix
../graphics.nix
../modules/nixos/audio.nix
../modules/nixos/graphics.nix
# Desktop Environments
../desktops/plasma.nix # KDE Plasma (for gaming)
../desktops/hyprland # Hyprland (for development)
../modules/nixos/desktops/plasma.nix # KDE Plasma (for gaming)
../modules/nixos/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
../modules/nixos/desktops/apps/browsers.nix
../modules/nixos/desktops/apps/terminals.nix
../modules/nixos/desktops/themes/catppuccin.nix # Or use themes/default.nix for Adwaita
# Gaming & Features
../desktops/gaming.nix
../bluetooth.nix
../modules/nixos/desktops/gaming.nix
../modules/nixos/bluetooth.nix
];
# Boot configuration with CachyOS LTS kernel

View file

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

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 ''

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"
'';
}