refactor: reorganize, add package (opencode, osgrep, beads), add ai agent shell
This commit is contained in:
parent
7e0a207778
commit
a0f0f39399
58 changed files with 7867 additions and 42 deletions
16
AGENTS.md
16
AGENTS.md
|
|
@ -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
59
flake.lock
generated
|
|
@ -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": [
|
||||||
|
|
|
||||||
49
flake.nix
49
flake.nix
|
|
@ -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; });
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
|
|
@ -47,5 +47,5 @@
|
||||||
interfaces."tailscale0".allowedTCPPorts = [ 22 ];
|
interfaces."tailscale0".allowedTCPPorts = [ 22 ];
|
||||||
};
|
};
|
||||||
|
|
||||||
imports = [ ../virtualization.nix ];
|
imports = [ ../modules/nixos/virtualization.nix ];
|
||||||
}
|
}
|
||||||
|
|
@ -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
|
||||||
];
|
];
|
||||||
|
|
|
||||||
|
|
@ -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; [
|
||||||
|
|
@ -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";
|
||||||
107
modules/home/devtools/opencode.nix
Normal file
107
modules/home/devtools/opencode.nix
Normal 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;
|
||||||
|
};
|
||||||
|
}
|
||||||
74
modules/home/programs/nvim/lazy-lock.json
Normal file
74
modules/home/programs/nvim/lazy-lock.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -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 = ''
|
||||||
|
|
@ -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
8
packages/default.nix
Normal 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
59
packages/opencode.nix
Normal 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
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
32
packages/osgrep.nix
Normal 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
98
shells/ai-agent.nix
Normal 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"
|
||||||
|
'';
|
||||||
|
}
|
||||||
Loading…
Add table
Add a link
Reference in a new issue