Compare commits
118 Commits
e4a20ddeb9
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
| 85eb20c4cb | |||
| 276378b57c | |||
| 03b968513b | |||
| e545d38314 | |||
| 564ccd2559 | |||
| f32f51970b | |||
| 0e50839ce0 | |||
| ed995a1edd | |||
| 2424b87b46 | |||
| 37ef245374 | |||
| ac40abe696 | |||
| a611d7fb99 | |||
| 89c430b940 | |||
| 1dd7d8a2d8 | |||
| 69697c822c | |||
| 8def00f368 | |||
| c907354a4f | |||
| 80ff1f8b03 | |||
| 9b5069693a | |||
| 2cad84ff26 | |||
| bbfcc366c2 | |||
| 8b09aa9705 | |||
| 0eaa830050 | |||
| 8577807650 | |||
| a186c136f0 | |||
| e6b5ff0fb8 | |||
| 6a8bda9031 | |||
| 073cc1aa38 | |||
| cea666f3d8 | |||
| 813fd347d5 | |||
| 66ff22f9e6 | |||
| 86afae7d6c | |||
| 5e46938488 | |||
| 8f3951522c | |||
| 6e5af04278 | |||
| 01cf320c2e | |||
| b69cc789b1 | |||
| 642598bbab | |||
| 31b87c7177 | |||
| aae96c7b5a | |||
| 980bd7c993 | |||
| dd77ed07e5 | |||
| 724abba247 | |||
| 9239e8dc6d | |||
| 11c816b2c2 | |||
| 901059d0cd | |||
| 4c69dddcd3 | |||
| 447d7f1dd7 | |||
| a8b07b0c30 | |||
| 94baea90d6 | |||
| 1bb97448a4 | |||
| 3ede8cd2c2 | |||
| c9e986121b | |||
| 067312dddf | |||
| a5e387a81d | |||
| 1005313bd0 | |||
| 2d3e15231a | |||
| a6cc5dcc4a | |||
| 958c332bf1 | |||
| 49fa4d623e | |||
| 4eefa6b337 | |||
| bef2afed66 | |||
| 5ad97d97a7 | |||
| 51b0bd4b1d | |||
| 19c770c163 | |||
| 9bd22bc5de | |||
| 85f2d5c19f | |||
| d3ceac88fc | |||
| 799207efaa | |||
| 260da9cbfc | |||
| 34ecdaf528 | |||
| d1964e8212 | |||
| 03228cfdf1 | |||
| 4defd577d3 | |||
| 4adc8329a1 | |||
| c859f5e41e | |||
| 7d93a9e09e | |||
| 0a79986914 | |||
| 9598d68a84 | |||
| 4f507d6bd1 | |||
| 04d7eda8c4 | |||
| 32fda4a7e9 | |||
| 6c816ae5ab | |||
| 7810be3cc1 | |||
| d2f1555309 | |||
| 80f24ce11f | |||
| 3b345614f7 | |||
| 4fa8026ce5 | |||
| f989e96fb8 | |||
| 0f39be78da | |||
| 1edbafd18a | |||
| e28dbf2236 | |||
| 944ee0e6e7 | |||
| ce490cacdc | |||
| 2452683a0c | |||
| 7be22a5210 | |||
| 3e8f143752 | |||
| 2b44191e73 | |||
| a25be94c48 | |||
| e829a9ff39 | |||
| 0c70cb0707 | |||
| b4a1f09841 | |||
| 227caee599 | |||
| 7045dee36e | |||
| d6d5b33d4c | |||
| 4b8e1215a5 | |||
| b21a150452 | |||
| b24065bf5c | |||
| 5d9f25747d | |||
| b882adc7ea | |||
| e88b11d8bb | |||
| 09d9501427 | |||
| 8f893a7216 | |||
| 12a24b83f4 | |||
| 0d9213e461 | |||
| 8c84c70152 | |||
| a92d0cfe8b | |||
| d19a0ffb81 |
@@ -1,13 +1,15 @@
|
|||||||
keys:
|
keys:
|
||||||
|
- &user_cschmatzler age1xate984yhl9qk9d4q99pyxmzz48sq56nfhu8weyzkgum4ed5tc5shjmrs7
|
||||||
- &host_tahani age1njjegjjdqzfnrr54f536yl4lduqgna3wuv7ef6vtl9jw5cju0grsgy62tm
|
- &host_tahani age1njjegjjdqzfnrr54f536yl4lduqgna3wuv7ef6vtl9jw5cju0grsgy62tm
|
||||||
- &host_michael age187jl7e4k9n4guygkmpuqzeh0wenefwrfkpvuyhvwjrjwxqpzassqq3x67j
|
- &host_michael age187jl7e4k9n4guygkmpuqzeh0wenefwrfkpvuyhvwjrjwxqpzassqq3x67j
|
||||||
- &host_jason age1ez6j3r5wdp0tjy7n5qzv5vfakdc2nh2zeu388zu7a80l0thv052syxq5e2
|
- &host_janet age1f9h725ewwwwwkelnrvdvrurg6fcsn3zxrxdt0v6v8ys0nzngcsvqu77nc8
|
||||||
- &host_chidi age1tlymdmaukhwupzrhszspp26lgd8s64rw4vu9lwc7gsgrjm78095s9fe9l3
|
- &host_chidi age1tlymdmaukhwupzrhszspp26lgd8s64rw4vu9lwc7gsgrjm78095s9fe9l3
|
||||||
creation_rules:
|
creation_rules:
|
||||||
- path_regex: secrets/[^/]+$
|
- path_regex: secrets/[^/]+$
|
||||||
key_groups:
|
key_groups:
|
||||||
- age:
|
- age:
|
||||||
|
- *user_cschmatzler
|
||||||
- *host_tahani
|
- *host_tahani
|
||||||
- *host_michael
|
- *host_michael
|
||||||
- *host_jason
|
- *host_janet
|
||||||
- *host_chidi
|
- *host_chidi
|
||||||
|
|||||||
20
AGENTS.md
20
AGENTS.md
@@ -5,7 +5,7 @@
|
|||||||
### Local Development
|
### Local Development
|
||||||
```bash
|
```bash
|
||||||
nix run .#build # Build current host config
|
nix run .#build # Build current host config
|
||||||
nix run .#build -- <hostname> # Build specific host (chidi, jason, michael, tahani)
|
nix run .#build -- <hostname> # Build specific host (chidi, janet, michael, tahani)
|
||||||
nix run .#apply # Build and apply locally (darwin-rebuild/nixos-rebuild switch)
|
nix run .#apply # Build and apply locally (darwin-rebuild/nixos-rebuild switch)
|
||||||
nix flake check # Validate flake
|
nix flake check # Validate flake
|
||||||
```
|
```
|
||||||
@@ -33,11 +33,12 @@ alejandra . # Format all Nix files
|
|||||||
|
|
||||||
### File Structure
|
### File Structure
|
||||||
- **Modules**: `modules/` - All configuration (flake-parts modules, auto-imported by import-tree)
|
- **Modules**: `modules/` - All configuration (flake-parts modules, auto-imported by import-tree)
|
||||||
|
- `hosts/` - Per-host composition modules
|
||||||
|
- `profiles/` - Shared host and user profile bundles
|
||||||
- `_lib/` - Utility functions (underscore = ignored by import-tree)
|
- `_lib/` - Utility functions (underscore = ignored by import-tree)
|
||||||
- `_darwin/` - Darwin-specific sub-modules
|
- `_darwin/` - Darwin-specific sub-modules
|
||||||
- `_neovim/` - Neovim plugin configs
|
- `_neovim/` - Neovim plugin configs
|
||||||
- `_opencode/` - OpenCode agent/command/skill configs
|
- `hosts/_parts/` - Host-specific leaf files (disk-config, hardware, service fragments, etc.)
|
||||||
- `_hosts/` - Host-specific sub-files (disk-config, hardware, etc.)
|
|
||||||
- **Apps**: `apps/` - Per-system app scripts (Nushell)
|
- **Apps**: `apps/` - Per-system app scripts (Nushell)
|
||||||
- **Secrets**: `secrets/` - SOPS-encrypted secrets (`.sops.yaml` for config)
|
- **Secrets**: `secrets/` - SOPS-encrypted secrets (`.sops.yaml` for config)
|
||||||
|
|
||||||
@@ -53,13 +54,15 @@ alejandra . # Format all Nix files
|
|||||||
- `homeManager` - Home Manager configuration
|
- `homeManager` - Home Manager configuration
|
||||||
- `os` - Applies to both NixOS and darwin
|
- `os` - Applies to both NixOS and darwin
|
||||||
|
|
||||||
**Hosts**: `den.hosts.<system>.<name>` defined in `modules/hosts.nix`
|
**Hosts**: `den.hosts.<system>.<name>` declared in `modules/inventory.nix`
|
||||||
|
|
||||||
|
**Profiles**: shared bundles live under `modules/profiles/{host,user}` and are exposed as `den.aspects.host-*` and `den.aspects.user-*`
|
||||||
|
|
||||||
**Defaults**: `den.default.*` defined in `modules/defaults.nix`
|
**Defaults**: `den.default.*` defined in `modules/defaults.nix`
|
||||||
|
|
||||||
**Imports**: Auto-imported by import-tree; underscore-prefixed dirs (`_lib/`, `_darwin/`, etc.) are excluded from auto-import
|
**Imports**: Auto-imported by import-tree; underscore-prefixed dirs (`_lib/`, `_darwin/`, etc.) are excluded from auto-import
|
||||||
|
|
||||||
**Deployment**: deploy-rs for NixOS hosts (michael, tahani); darwin hosts (chidi, jason) are local-only
|
**Deployment**: deploy-rs for NixOS hosts (michael, tahani); darwin hosts (chidi, janet) are local-only
|
||||||
|
|
||||||
### Nix Language Conventions
|
### Nix Language Conventions
|
||||||
|
|
||||||
@@ -67,7 +70,7 @@ alejandra . # Format all Nix files
|
|||||||
```nix
|
```nix
|
||||||
{inputs, pkgs, lib, ...}:
|
{inputs, pkgs, lib, ...}:
|
||||||
```
|
```
|
||||||
Destructure arguments on separate lines. Use `...` to capture remaining args.
|
Use `...` to capture remaining args. Let Alejandra control the exact layout.
|
||||||
|
|
||||||
**Attribute Sets**:
|
**Attribute Sets**:
|
||||||
```nix
|
```nix
|
||||||
@@ -108,7 +111,6 @@ in {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
- Destructure args on separate lines
|
|
||||||
- Use `with lib;` for brevity with NixOS lib functions
|
- Use `with lib;` for brevity with NixOS lib functions
|
||||||
- Define `cfg` for config options
|
- Define `cfg` for config options
|
||||||
- Use `mkIf`, `mkForce`, `mkDefault` appropriately
|
- Use `mkIf`, `mkForce`, `mkDefault` appropriately
|
||||||
@@ -126,13 +128,13 @@ in {
|
|||||||
|
|
||||||
### Naming Conventions
|
### Naming Conventions
|
||||||
- **Aspect names**: `den.aspects.<name>.<class>` for feature configuration
|
- **Aspect names**: `den.aspects.<name>.<class>` for feature configuration
|
||||||
- **Hostnames**: Lowercase, descriptive (e.g., `michael`, `tahani`, `chidi`, `jason`)
|
- **Hostnames**: Lowercase, descriptive (e.g., `michael`, `tahani`, `chidi`, `janet`)
|
||||||
- **Module files**: Descriptive, lowercase with hyphens (e.g., `neovim-config.nix`)
|
- **Module files**: Descriptive, lowercase with hyphens (e.g., `neovim-config.nix`)
|
||||||
|
|
||||||
### Secrets Management
|
### Secrets Management
|
||||||
- Use SOPS for secrets (see `.sops.yaml`)
|
- Use SOPS for secrets (see `.sops.yaml`)
|
||||||
- Never commit unencrypted secrets
|
- Never commit unencrypted secrets
|
||||||
- Secret definitions live in per-host modules (`modules/michael.nix`, `modules/tahani.nix`, etc.)
|
- Secret definitions live in per-host modules (`modules/hosts/michael.nix`, `modules/hosts/tahani.nix`, etc.)
|
||||||
- Shared SOPS defaults (module imports, key paths) in `modules/secrets.nix`
|
- Shared SOPS defaults (module imports, key paths) in `modules/secrets.nix`
|
||||||
|
|
||||||
### Aspect Composition
|
### Aspect Composition
|
||||||
|
|||||||
23
README.md
23
README.md
@@ -5,13 +5,16 @@ Personal Nix flake for four machines:
|
|||||||
- `michael` - x86_64 Linux server
|
- `michael` - x86_64 Linux server
|
||||||
- `tahani` - x86_64 Linux home server / workstation
|
- `tahani` - x86_64 Linux home server / workstation
|
||||||
- `chidi` - aarch64 Darwin work laptop
|
- `chidi` - aarch64 Darwin work laptop
|
||||||
- `jason` - aarch64 Darwin personal laptop
|
- `janet` - aarch64 Darwin personal laptop
|
||||||
|
|
||||||
## Repository Map
|
## Repository Map
|
||||||
|
|
||||||
- `modules/` - flake-parts modules, auto-imported via `import-tree`
|
- `modules/` - flake-parts modules, auto-imported via `import-tree`
|
||||||
- `modules/_hosts/` - host-specific submodules like hardware, disks, and services
|
- `modules/hosts/` - per-host composition modules
|
||||||
|
- `modules/hosts/_parts/` - host-private leaf modules like hardware, disks, and literal networking
|
||||||
|
- `modules/profiles/` - shared host and user profile bundles
|
||||||
- `modules/_lib/` - local helper functions
|
- `modules/_lib/` - local helper functions
|
||||||
|
- `modules/_notability/`, `modules/_paperless/` - feature-owned scripts and templates
|
||||||
- `apps/` - Nushell apps exposed through the flake
|
- `apps/` - Nushell apps exposed through the flake
|
||||||
- `secrets/` - SOPS-encrypted secrets
|
- `secrets/` - SOPS-encrypted secrets
|
||||||
- `flake.nix` - generated flake entrypoint
|
- `flake.nix` - generated flake entrypoint
|
||||||
@@ -21,9 +24,12 @@ Personal Nix flake for four machines:
|
|||||||
|
|
||||||
This repo uses `den` and organizes configuration around aspects instead of putting everything directly in host files.
|
This repo uses `den` and organizes configuration around aspects instead of putting everything directly in host files.
|
||||||
|
|
||||||
- shared behavior lives in `den.aspects.<name>.<class>` modules
|
- shared behavior lives in `den.aspects.<name>.<class>` modules under `modules/*.nix`
|
||||||
- hosts are declared in `modules/hosts.nix`
|
- the machine inventory lives in `modules/inventory.nix`
|
||||||
- host composition happens in `modules/<host>.nix`
|
- shared bundles live in `modules/profiles/{host,user}/`
|
||||||
|
- host composition happens in `modules/hosts/<host>.nix`
|
||||||
|
- host-private imports live in `modules/hosts/_parts/<host>/` and stay limited to true machine leaf files
|
||||||
|
- feature-owned services live in top-level modules like `modules/gitea.nix`, `modules/notability.nix`, and `modules/paperless.nix`
|
||||||
- user-level config mostly lives in Home Manager aspects
|
- user-level config mostly lives in Home Manager aspects
|
||||||
|
|
||||||
Common examples:
|
Common examples:
|
||||||
@@ -31,8 +37,11 @@ Common examples:
|
|||||||
- `modules/core.nix` - shared Nix and shell foundation
|
- `modules/core.nix` - shared Nix and shell foundation
|
||||||
- `modules/dev-tools.nix` - VCS, language, and developer tooling
|
- `modules/dev-tools.nix` - VCS, language, and developer tooling
|
||||||
- `modules/network.nix` - SSH, fail2ban, and tailscale aspects
|
- `modules/network.nix` - SSH, fail2ban, and tailscale aspects
|
||||||
- `modules/michael.nix` - server composition for `michael`
|
- `modules/gitea.nix` - Gitea, Litestream, and backup stack for `michael`
|
||||||
- `modules/tahani.nix` - server/workstation composition for `tahani`
|
- `modules/notability.nix` - Notability ingest services and user tooling for `tahani`
|
||||||
|
- `modules/profiles/user/workstation.nix` - shared developer workstation user bundle
|
||||||
|
- `modules/hosts/michael.nix` - server composition for `michael`
|
||||||
|
- `modules/hosts/tahani.nix` - server/workstation composition for `tahani`
|
||||||
|
|
||||||
## Common Commands
|
## Common Commands
|
||||||
|
|
||||||
|
|||||||
@@ -3,17 +3,5 @@
|
|||||||
use ../common.nu *
|
use ../common.nu *
|
||||||
|
|
||||||
def main [hostname?: string, ...rest: string] {
|
def main [hostname?: string, ...rest: string] {
|
||||||
let host = if ($hostname | is-empty) {
|
build-config "darwin" $hostname ...$rest
|
||||||
try { scutil --get LocalHostName | str trim } catch { hostname -s | str trim }
|
|
||||||
} else { $hostname }
|
|
||||||
|
|
||||||
print_info $"Building configuration for ($host)"
|
|
||||||
|
|
||||||
nix build $".#darwinConfigurations.($host).system" --show-trace ...$rest
|
|
||||||
|
|
||||||
if ("./result" | path exists) {
|
|
||||||
rm ./result
|
|
||||||
}
|
|
||||||
|
|
||||||
print_success "Build completed successfully"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/env nu
|
|
||||||
|
|
||||||
use ../common.nu *
|
|
||||||
|
|
||||||
def main [...inputs: string] {
|
|
||||||
if ($inputs | is-empty) {
|
|
||||||
print_info "Updating all flake inputs"
|
|
||||||
nix flake update
|
|
||||||
} else {
|
|
||||||
print_info $"Updating flake inputs: ($inputs | str join ', ')"
|
|
||||||
nix flake update ...$inputs
|
|
||||||
}
|
|
||||||
|
|
||||||
print_info "Regenerating flake.nix"
|
|
||||||
nix run .#write-flake
|
|
||||||
|
|
||||||
print_info "Formatting"
|
|
||||||
alejandra .
|
|
||||||
|
|
||||||
print_success "Flake updated"
|
|
||||||
}
|
|
||||||
12
apps/apply
12
apps/apply
@@ -2,18 +2,8 @@
|
|||||||
|
|
||||||
use ./common.nu *
|
use ./common.nu *
|
||||||
|
|
||||||
def get-hostname [] {
|
|
||||||
if $nu.os-info.name == "macos" {
|
|
||||||
try { ^scutil --get LocalHostName | str trim } catch { ^hostname -s | str trim }
|
|
||||||
} else {
|
|
||||||
^hostname | str trim
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
def main [hostname?: string, ...rest: string] {
|
def main [hostname?: string, ...rest: string] {
|
||||||
let host = if ($hostname | is-empty) {
|
let host = resolve-host $hostname
|
||||||
get-hostname
|
|
||||||
} else { $hostname }
|
|
||||||
|
|
||||||
print_info $"Applying configuration for ($host)"
|
print_info $"Applying configuration for ($host)"
|
||||||
|
|
||||||
|
|||||||
@@ -15,3 +15,58 @@ export def print_error [msg: string] {
|
|||||||
export def print_warning [msg: string] {
|
export def print_warning [msg: string] {
|
||||||
print $"(ansi yellow)[WARN](ansi reset) ($msg)"
|
print $"(ansi yellow)[WARN](ansi reset) ($msg)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export def get-hostname [] {
|
||||||
|
if $nu.os-info.name == "macos" {
|
||||||
|
try { ^scutil --get LocalHostName | str trim } catch { ^hostname -s | str trim }
|
||||||
|
} else {
|
||||||
|
^hostname | str trim
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export def resolve-host [hostname?: string] {
|
||||||
|
if ($hostname | is-empty) {
|
||||||
|
get-hostname
|
||||||
|
} else {
|
||||||
|
$hostname
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export def cleanup-result-link [] {
|
||||||
|
if ("./result" | path exists) {
|
||||||
|
rm ./result
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export def build-config [kind: string, hostname?: string, ...rest: string] {
|
||||||
|
let host = resolve-host $hostname
|
||||||
|
|
||||||
|
print_info $"Building configuration for ($host)"
|
||||||
|
|
||||||
|
if $kind == "darwin" {
|
||||||
|
nix build $".#darwinConfigurations.($host).system" --show-trace ...$rest
|
||||||
|
} else {
|
||||||
|
nix build $".#nixosConfigurations.($host).config.system.build.toplevel" --show-trace ...$rest
|
||||||
|
}
|
||||||
|
|
||||||
|
cleanup-result-link
|
||||||
|
print_success "Build completed successfully"
|
||||||
|
}
|
||||||
|
|
||||||
|
export def update-flake [inputs: list<string>] {
|
||||||
|
if ($inputs | is-empty) {
|
||||||
|
print_info "Updating all flake inputs"
|
||||||
|
nix flake update
|
||||||
|
} else {
|
||||||
|
print_info $"Updating flake inputs: ($inputs | str join ', ')"
|
||||||
|
nix flake update ...$inputs
|
||||||
|
}
|
||||||
|
|
||||||
|
print_info "Regenerating flake.nix"
|
||||||
|
nix run .#write-flake
|
||||||
|
|
||||||
|
print_info "Formatting"
|
||||||
|
alejandra .
|
||||||
|
|
||||||
|
print_success "Flake updated"
|
||||||
|
}
|
||||||
|
|||||||
7
apps/update
Executable file
7
apps/update
Executable file
@@ -0,0 +1,7 @@
|
|||||||
|
#!/usr/bin/env nu
|
||||||
|
|
||||||
|
use ./common.nu *
|
||||||
|
|
||||||
|
def main [...inputs: string] {
|
||||||
|
update-flake $inputs
|
||||||
|
}
|
||||||
@@ -3,17 +3,5 @@
|
|||||||
use ../common.nu *
|
use ../common.nu *
|
||||||
|
|
||||||
def main [hostname?: string, ...rest: string] {
|
def main [hostname?: string, ...rest: string] {
|
||||||
let host = if ($hostname | is-empty) {
|
build-config "nixos" $hostname ...$rest
|
||||||
hostname | str trim
|
|
||||||
} else { $hostname }
|
|
||||||
|
|
||||||
print_info $"Building configuration for ($host)"
|
|
||||||
|
|
||||||
nix build $".#nixosConfigurations.($host).config.system.build.toplevel" --show-trace ...$rest
|
|
||||||
|
|
||||||
if ("./result" | path exists) {
|
|
||||||
rm ./result
|
|
||||||
}
|
|
||||||
|
|
||||||
print_success "Build completed successfully"
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,21 +0,0 @@
|
|||||||
#!/usr/bin/env nu
|
|
||||||
|
|
||||||
use ../common.nu *
|
|
||||||
|
|
||||||
def main [...inputs: string] {
|
|
||||||
if ($inputs | is-empty) {
|
|
||||||
print_info "Updating all flake inputs"
|
|
||||||
nix flake update
|
|
||||||
} else {
|
|
||||||
print_info $"Updating flake inputs: ($inputs | str join ', ')"
|
|
||||||
nix flake update ...$inputs
|
|
||||||
}
|
|
||||||
|
|
||||||
print_info "Regenerating flake.nix"
|
|
||||||
nix run .#write-flake
|
|
||||||
|
|
||||||
print_info "Formatting"
|
|
||||||
alejandra .
|
|
||||||
|
|
||||||
print_success "Flake updated"
|
|
||||||
}
|
|
||||||
360
flake.lock
generated
360
flake.lock
generated
@@ -28,16 +28,16 @@
|
|||||||
"brew-src": {
|
"brew-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769363988,
|
"lastModified": 1774235677,
|
||||||
"narHash": "sha256-BiGPeulrDVetXP+tjxhMcGLUROZAtZIhU5m4MqawCfM=",
|
"narHash": "sha256-0ryNYmzDAeRlrzPTAgmzGH/Cgc8iv/LBN6jWGUANvIk=",
|
||||||
"owner": "Homebrew",
|
"owner": "Homebrew",
|
||||||
"repo": "brew",
|
"repo": "brew",
|
||||||
"rev": "d01011cac6d72032c75fd2cd9489909e95d9faf2",
|
"rev": "894a3d23ac0c8aaf561b9874b528b9cb2e839201",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
"owner": "Homebrew",
|
"owner": "Homebrew",
|
||||||
"ref": "5.0.12",
|
"ref": "5.1.1",
|
||||||
"repo": "brew",
|
"repo": "brew",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
@@ -76,6 +76,22 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"code-review-nvim": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1765543014,
|
||||||
|
"narHash": "sha256-WpbQswkUpB4Nblos8+5UE5I/PHUQOi+RQ+hj4CCdL4o=",
|
||||||
|
"owner": "choplin",
|
||||||
|
"repo": "code-review.nvim",
|
||||||
|
"rev": "ed91462e20bd08c3be71efb11a4a7d00459f0b47",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "choplin",
|
||||||
|
"repo": "code-review.nvim",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"crane": {
|
"crane": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1765739568,
|
"lastModified": 1765739568,
|
||||||
@@ -98,11 +114,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773000227,
|
"lastModified": 1775037210,
|
||||||
"narHash": "sha256-zm3ftUQw0MPumYi91HovoGhgyZBlM4o3Zy0LhPNwzXE=",
|
"narHash": "sha256-KM2WYj6EA7M/FVZVCl3rqWY+TFV5QzSyyGE2gQxeODU=",
|
||||||
"owner": "LnL7",
|
"owner": "LnL7",
|
||||||
"repo": "nix-darwin",
|
"repo": "nix-darwin",
|
||||||
"rev": "da529ac9e46f25ed5616fd634079a5f3c579135f",
|
"rev": "06648f4902343228ce2de79f291dd5a58ee12146",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -114,11 +130,11 @@
|
|||||||
},
|
},
|
||||||
"den": {
|
"den": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773344028,
|
"lastModified": 1775702491,
|
||||||
"narHash": "sha256-11LNYUxMqV1X/AwNURpGpJWnKYNiwQGnFSQof/pZ8S8=",
|
"narHash": "sha256-5BCNtE/zCLSheltliy4hTdwsq0Boj/W1XRIX8n89nqA=",
|
||||||
"owner": "vic",
|
"owner": "vic",
|
||||||
"repo": "den",
|
"repo": "den",
|
||||||
"rev": "edaa0b03639ec6485f6862c035ed10974d8f0008",
|
"rev": "d267c458e384b57317d06d45f7c65f7fb03fae4b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -154,11 +170,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773025010,
|
"lastModified": 1773889306,
|
||||||
"narHash": "sha256-khlHllTsovXgT2GZ0WxT4+RvuMjNeR5OW0UYeEHPYQo=",
|
"narHash": "sha256-PAqwnsBSI9SVC2QugvQ3xeYCB0otOwCacB1ueQj2tgw=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "disko",
|
"repo": "disko",
|
||||||
"rev": "7b9f7f88ab3b339f8142dc246445abb3c370d3d3",
|
"rev": "5ad85c82cc52264f4beddc934ba57f3789f28347",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -170,11 +186,32 @@
|
|||||||
"fenix": {
|
"fenix": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"himalaya",
|
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"rust-analyzer-src": "rust-analyzer-src"
|
"rust-analyzer-src": "rust-analyzer-src"
|
||||||
},
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775721346,
|
||||||
|
"narHash": "sha256-ogqjruvVBYEj8sWM3viOucSo1Pna9c147EKQOfA+p3I=",
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"rev": "99fde43dfee2a672e4e37ef211e0844337e5b725",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "nix-community",
|
||||||
|
"repo": "fenix",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"fenix_2": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"himalaya",
|
||||||
|
"nixpkgs"
|
||||||
|
],
|
||||||
|
"rust-analyzer-src": "rust-analyzer-src_2"
|
||||||
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767250179,
|
"lastModified": 1767250179,
|
||||||
"narHash": "sha256-PnQdWvPZqHp+7yaHWDFX3NYSKaOy0fjkwpR+rIQC7AY=",
|
"narHash": "sha256-PnQdWvPZqHp+7yaHWDFX3NYSKaOy0fjkwpR+rIQC7AY=",
|
||||||
@@ -190,32 +227,9 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"fenix_2": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"naersk",
|
|
||||||
"nixpkgs"
|
|
||||||
],
|
|
||||||
"rust-analyzer-src": "rust-analyzer-src_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1752475459,
|
|
||||||
"narHash": "sha256-z6QEu4ZFuHiqdOPbYss4/Q8B0BFhacR8ts6jO/F/aOU=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "fenix",
|
|
||||||
"rev": "bf0d6f70f4c9a9cf8845f992105652173f4b617f",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "fenix",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"fenix_3": {
|
"fenix_3": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"tuicr",
|
|
||||||
"naersk",
|
"naersk",
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
@@ -237,11 +251,11 @@
|
|||||||
},
|
},
|
||||||
"flake-aspects": {
|
"flake-aspects": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773339018,
|
"lastModified": 1773552804,
|
||||||
"narHash": "sha256-sPPriYJpLmukixc6/KBzeOhw83OSodco5YbeayHzAgo=",
|
"narHash": "sha256-a0kjpCZGnD5lt7yW6C3hzPhSf5KjnTyvX6XZ2NuhGs4=",
|
||||||
"owner": "vic",
|
"owner": "vic",
|
||||||
"repo": "flake-aspects",
|
"repo": "flake-aspects",
|
||||||
"rev": "b51a00bc0140e7708e421a64f20170a2e8ccae93",
|
"rev": "56fa8d4d4772d58eb7f9d66c28c1cd2762b60423",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -268,11 +282,11 @@
|
|||||||
},
|
},
|
||||||
"flake-file": {
|
"flake-file": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773224147,
|
"lastModified": 1774886516,
|
||||||
"narHash": "sha256-w9RQyKZSTfqoZPRzIf7H4qVHy2N6uFk1MUU+c1K4c40=",
|
"narHash": "sha256-w2LoQVM6DXrSdGUZBZqa1nYkMzHoB0t82DrptzZKhTs=",
|
||||||
"owner": "vic",
|
"owner": "vic",
|
||||||
"repo": "flake-file",
|
"repo": "flake-file",
|
||||||
"rev": "97bd69ff570dddccd704077830446ec1ca3a6988",
|
"rev": "3daadf37de2bb85b0ff34e2a7ab0d71e077c2b9e",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -288,11 +302,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772408722,
|
"lastModified": 1775087534,
|
||||||
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
|
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
|
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -309,11 +323,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772408722,
|
"lastModified": 1775087534,
|
||||||
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
|
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
|
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -330,11 +344,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772408722,
|
"lastModified": 1775087534,
|
||||||
"narHash": "sha256-rHuJtdcOjK7rAHpHphUb1iCvgkU3GpfvicLMwwnfMT0=",
|
"narHash": "sha256-91qqW8lhL7TLwgQWijoGBbiD4t7/q75KTi8NxjVmSmA=",
|
||||||
"owner": "hercules-ci",
|
"owner": "hercules-ci",
|
||||||
"repo": "flake-parts",
|
"repo": "flake-parts",
|
||||||
"rev": "f20dc5d9b8027381c474144ecabc9034d6a839a3",
|
"rev": "3107b77cd68437b9a76194f0f7f9c55f2329ca5b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -384,7 +398,7 @@
|
|||||||
},
|
},
|
||||||
"flake-utils_2": {
|
"flake-utils_2": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems_6"
|
"systems": "systems_5"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1731533236,
|
"lastModified": 1731533236,
|
||||||
@@ -402,7 +416,7 @@
|
|||||||
},
|
},
|
||||||
"himalaya": {
|
"himalaya": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"fenix": "fenix",
|
"fenix": "fenix_2",
|
||||||
"nixpkgs": "nixpkgs_2",
|
"nixpkgs": "nixpkgs_2",
|
||||||
"pimalaya": "pimalaya"
|
"pimalaya": "pimalaya"
|
||||||
},
|
},
|
||||||
@@ -427,11 +441,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773367248,
|
"lastModified": 1775683737,
|
||||||
"narHash": "sha256-FFMc1uAwy2GYasd0rdNDVxKyAgzuoJH2M+GglBQbqf0=",
|
"narHash": "sha256-oBYyowo6yfgb95Z78s3uTnAd9KkpJpwzjJbfnpLaM2Y=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "home-manager",
|
"repo": "home-manager",
|
||||||
"rev": "be0c641a6a5564caa33982faa1fe2c60d92131c7",
|
"rev": "7ba4ee4228ed36123c7cb75d50524b43514ef992",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -443,11 +457,11 @@
|
|||||||
"homebrew-cask": {
|
"homebrew-cask": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773390491,
|
"lastModified": 1775700724,
|
||||||
"narHash": "sha256-pNVKLd8Z0TmhUFfoUU4+iACsWLQuhkDaQBIYsGNgcrM=",
|
"narHash": "sha256-qQm9uIF+tI7gamLMa7DSXSQQzLQalEtOa7PHPxNkbr8=",
|
||||||
"owner": "homebrew",
|
"owner": "homebrew",
|
||||||
"repo": "homebrew-cask",
|
"repo": "homebrew-cask",
|
||||||
"rev": "4ebe4674412a7867b1fab47aca19d2c5098cea63",
|
"rev": "c622bff3b88557e3c870104db0426b93e0767a8f",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -459,11 +473,11 @@
|
|||||||
"homebrew-core": {
|
"homebrew-core": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773390003,
|
"lastModified": 1775721921,
|
||||||
"narHash": "sha256-3oEXbfs3G6oYBwviyj71BvvDt0n0k/qzHA4p+9mGpQM=",
|
"narHash": "sha256-s6K2QbKa4OJlblFp3zMSh0/2PM2zpWpAd4ZnREirj/I=",
|
||||||
"owner": "homebrew",
|
"owner": "homebrew",
|
||||||
"repo": "homebrew-core",
|
"repo": "homebrew-core",
|
||||||
"rev": "ea9540a5e8d64a3442b5f9708f9398130352b454",
|
"rev": "70028a68b515145bbeccb2961240275ab6eb9e82",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -474,11 +488,11 @@
|
|||||||
},
|
},
|
||||||
"import-tree": {
|
"import-tree": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772999353,
|
"lastModified": 1773693634,
|
||||||
"narHash": "sha256-dPb0WxUhFaz6wuR3B6ysqFJpsu8txKDPZvS47AT2XLI=",
|
"narHash": "sha256-BtZ2dtkBdSUnFPPFc+n0kcMbgaTxzFNPv2iaO326Ffg=",
|
||||||
"owner": "vic",
|
"owner": "vic",
|
||||||
"repo": "import-tree",
|
"repo": "import-tree",
|
||||||
"rev": "545a4df146fce44d155573e47f5a777985acf912",
|
"rev": "c41e7d58045f9057880b0d85e1152d6a4430dbf1",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -502,6 +516,38 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
"jj-diffconflicts": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1774204449,
|
||||||
|
"narHash": "sha256-CDLOo07tGOg/7Sowb1d39k9Nq/RW50axGj8L1D3Be70=",
|
||||||
|
"owner": "rafikdraoui",
|
||||||
|
"repo": "jj-diffconflicts",
|
||||||
|
"rev": "58163ae8fe7646179dfd7741206dd9a2b4cdadc0",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "rafikdraoui",
|
||||||
|
"repo": "jj-diffconflicts",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"jj-nvim": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1775551442,
|
||||||
|
"narHash": "sha256-hoU+DenrgxNwvLNmDtIsJ5yB5fhRjDRPOOL8WW9bpZM=",
|
||||||
|
"owner": "NicolasGB",
|
||||||
|
"repo": "jj.nvim",
|
||||||
|
"rev": "2dbe2c73c599a29e86e4123b42e430828b1f01d9",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "NicolasGB",
|
||||||
|
"repo": "jj.nvim",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"jj-ryu": {
|
"jj-ryu": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -547,11 +593,11 @@
|
|||||||
"treefmt-nix": "treefmt-nix"
|
"treefmt-nix": "treefmt-nix"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773372117,
|
"lastModified": 1775705124,
|
||||||
"narHash": "sha256-i23+1xDYCC0Oi2RKSiGB8Qp4WLziWKDPKEaR+Q+Bdvc=",
|
"narHash": "sha256-OUtgrn0k7DYnAP9skY2rOJSWJyn4w5tnUcF3lSJdfME=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "llm-agents.nix",
|
"repo": "llm-agents.nix",
|
||||||
"rev": "fb16f8035819135fb0dc6c276fb75a3db2da3378",
|
"rev": "ca76524952b00135dba57da62ce2dd123a1ba4be",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -561,32 +607,9 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"naersk": {
|
"naersk": {
|
||||||
"inputs": {
|
|
||||||
"fenix": "fenix_2",
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1769799857,
|
|
||||||
"narHash": "sha256-88IFXZ7Sa1vxbz5pty0Io5qEaMQMMUPMonLa3Ls/ss4=",
|
|
||||||
"owner": "nix-community",
|
|
||||||
"repo": "naersk",
|
|
||||||
"rev": "9d4ed44d8b8cecdceb1d6fd76e74123d90ae6339",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "nix-community",
|
|
||||||
"ref": "master",
|
|
||||||
"repo": "naersk",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"naersk_2": {
|
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"fenix": "fenix_3",
|
"fenix": "fenix_3",
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
"tuicr",
|
|
||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
@@ -614,11 +637,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773360308,
|
"lastModified": 1775693082,
|
||||||
"narHash": "sha256-Asr6gDwzxvcglRaXEZSTL4lEA6braemURJc6wKBhKrs=",
|
"narHash": "sha256-nnhkpfWsRutQh//KmVoIV7e9Gk90tBezjcoRr775BfU=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "neovim-nightly-overlay",
|
"repo": "neovim-nightly-overlay",
|
||||||
"rev": "b550599eedc54514f5b71cee8e480e337d104d84",
|
"rev": "21b2795e6aeb4a0110bdc7bd81bad59c022c9986",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -630,11 +653,11 @@
|
|||||||
"neovim-src": {
|
"neovim-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773359104,
|
"lastModified": 1775689880,
|
||||||
"narHash": "sha256-vWu0zLThxpsx6vbPK5eAgvkMKu1LV8tbybS/sjWn8S8=",
|
"narHash": "sha256-savZYhFAaBm3BQUdTrPOv7i5K18JFANJvyHv0uuvaWM=",
|
||||||
"owner": "neovim",
|
"owner": "neovim",
|
||||||
"repo": "neovim",
|
"repo": "neovim",
|
||||||
"rev": "957eb1fde04496b4a2c07fd8427a8d78844d1bf7",
|
"rev": "eefb50e352a689ec1a0a55d6827abea79960cd3d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -648,11 +671,11 @@
|
|||||||
"brew-src": "brew-src"
|
"brew-src": "brew-src"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1769437432,
|
"lastModified": 1774720267,
|
||||||
"narHash": "sha256-8d7KnCpT2LweRvSzZYEGd9IM3eFX+A78opcnDM0+ndk=",
|
"narHash": "sha256-YYftFe8jyfpQI649yfr0E+dqEXE2jznZNcYvy/lKV1U=",
|
||||||
"owner": "zhaofengli-wip",
|
"owner": "zhaofengli-wip",
|
||||||
"repo": "nix-homebrew",
|
"repo": "nix-homebrew",
|
||||||
"rev": "a5409abd0d5013d79775d3419bcac10eacb9d8c5",
|
"rev": "a7760a3a83f7609f742861afb5732210fdc437ed",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -711,11 +734,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_4": {
|
"nixpkgs_4": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773201692,
|
"lastModified": 1775639890,
|
||||||
"narHash": "sha256-NXrKzNMniu4Oam2kAFvqJ3GB2kAvlAFIriTAheaY8hw=",
|
"narHash": "sha256-9O9gNidrdzcb7vgKGtff7QiLtr0IsVaCi0pAXm8anhQ=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b6067cc0127d4db9c26c79e4de0513e58d0c40c9",
|
"rev": "456e8a9468b9d46bd8c9524425026c00745bc4d2",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -727,11 +750,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs_5": {
|
"nixpkgs_5": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773389992,
|
"lastModified": 1775722436,
|
||||||
"narHash": "sha256-wvfdLLWJ2I9oEpDd9PfMA8osfIZicoQ5MT1jIwNs9Tk=",
|
"narHash": "sha256-Z7QmfL80jmUPoSQkMlCc+1MGfkugf7bG47H3UTsyi7Q=",
|
||||||
"owner": "nixos",
|
"owner": "nixos",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "c06b4ae3d6599a672a6210b7021d699c351eebda",
|
"rev": "e73a61d035ee91f95bb0a6b95ce0b9d2866bd332",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -758,22 +781,6 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nixpkgs_7": {
|
"nixpkgs_7": {
|
||||||
"locked": {
|
|
||||||
"lastModified": 1771923393,
|
|
||||||
"narHash": "sha256-Fy0+UXELv9hOE8WjYhJt8fMDLYTU2Dqn3cX4BwoGBos=",
|
|
||||||
"owner": "NixOS",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"rev": "ea7f1f06811ce7fcc81d6c6fd4213150c23edcf2",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "NixOS",
|
|
||||||
"ref": "nixpkgs-unstable",
|
|
||||||
"repo": "nixpkgs",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"nixpkgs_8": {
|
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1765934234,
|
"lastModified": 1765934234,
|
||||||
"narHash": "sha256-pJjWUzNnjbIAMIc5gRFUuKCDQ9S1cuh3b2hKgA7Mc4A=",
|
"narHash": "sha256-pJjWUzNnjbIAMIc5gRFUuKCDQ9S1cuh3b2hKgA7Mc4A=",
|
||||||
@@ -796,11 +803,11 @@
|
|||||||
"systems": "systems_4"
|
"systems": "systems_4"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772402258,
|
"lastModified": 1775307257,
|
||||||
"narHash": "sha256-3DmCFOdmbkFML1/G9gj8Wb+rCCZFPOQtNoMCpqOF8SA=",
|
"narHash": "sha256-y9hEecHH4ennFwIcw1n480YCGh73DkEmizmQnyXuvgg=",
|
||||||
"owner": "nix-community",
|
"owner": "nix-community",
|
||||||
"repo": "nixvim",
|
"repo": "nixvim",
|
||||||
"rev": "21ae25e13b01d3b4cdc750b5f9e7bad68b150c10",
|
"rev": "2e008bb941f72379d5b935d5bfe70ed8b7c793ff",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -809,22 +816,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"nono": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1773328997,
|
|
||||||
"narHash": "sha256-O5g/6tpLDVJPmIgqCtSQBTEDjAg1/1PHON5mWBgI9t4=",
|
|
||||||
"owner": "always-further",
|
|
||||||
"repo": "nono",
|
|
||||||
"rev": "0be494284aadffd82633ce6ad1fa76301d5645b3",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "always-further",
|
|
||||||
"repo": "nono",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"pimalaya": {
|
"pimalaya": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
@@ -843,10 +834,12 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"code-review-nvim": "code-review-nvim",
|
||||||
"darwin": "darwin",
|
"darwin": "darwin",
|
||||||
"den": "den",
|
"den": "den",
|
||||||
"deploy-rs": "deploy-rs",
|
"deploy-rs": "deploy-rs",
|
||||||
"disko": "disko",
|
"disko": "disko",
|
||||||
|
"fenix": "fenix",
|
||||||
"flake-aspects": "flake-aspects",
|
"flake-aspects": "flake-aspects",
|
||||||
"flake-file": "flake-file",
|
"flake-file": "flake-file",
|
||||||
"flake-parts": "flake-parts",
|
"flake-parts": "flake-parts",
|
||||||
@@ -855,6 +848,8 @@
|
|||||||
"homebrew-cask": "homebrew-cask",
|
"homebrew-cask": "homebrew-cask",
|
||||||
"homebrew-core": "homebrew-core",
|
"homebrew-core": "homebrew-core",
|
||||||
"import-tree": "import-tree",
|
"import-tree": "import-tree",
|
||||||
|
"jj-diffconflicts": "jj-diffconflicts",
|
||||||
|
"jj-nvim": "jj-nvim",
|
||||||
"jj-ryu": "jj-ryu",
|
"jj-ryu": "jj-ryu",
|
||||||
"jj-starship": "jj-starship",
|
"jj-starship": "jj-starship",
|
||||||
"llm-agents": "llm-agents",
|
"llm-agents": "llm-agents",
|
||||||
@@ -866,20 +861,18 @@
|
|||||||
"nixpkgs"
|
"nixpkgs"
|
||||||
],
|
],
|
||||||
"nixvim": "nixvim",
|
"nixvim": "nixvim",
|
||||||
"nono": "nono",
|
|
||||||
"sops-nix": "sops-nix",
|
"sops-nix": "sops-nix",
|
||||||
"tuicr": "tuicr",
|
|
||||||
"zjstatus": "zjstatus"
|
"zjstatus": "zjstatus"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"rust-analyzer-src": {
|
"rust-analyzer-src": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1767191410,
|
"lastModified": 1775663707,
|
||||||
"narHash": "sha256-cCZGjubgDWmstvFkS6eAw2qk2ihgWkycw55u2dtLd70=",
|
"narHash": "sha256-3cSvpBETRa8aDSrUCX1jGc6FSse3OWB7cXACIZW8BYI=",
|
||||||
"owner": "rust-lang",
|
"owner": "rust-lang",
|
||||||
"repo": "rust-analyzer",
|
"repo": "rust-analyzer",
|
||||||
"rev": "a9026e6d5068172bf5a0d52a260bb290961d1cb4",
|
"rev": "8c5af725817905e462052d91a8d229b85ffa83a5",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -892,11 +885,11 @@
|
|||||||
"rust-analyzer-src_2": {
|
"rust-analyzer-src_2": {
|
||||||
"flake": false,
|
"flake": false,
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1752428706,
|
"lastModified": 1767191410,
|
||||||
"narHash": "sha256-EJcdxw3aXfP8Ex1Nm3s0awyH9egQvB2Gu+QEnJn2Sfg=",
|
"narHash": "sha256-cCZGjubgDWmstvFkS6eAw2qk2ihgWkycw55u2dtLd70=",
|
||||||
"owner": "rust-lang",
|
"owner": "rust-lang",
|
||||||
"repo": "rust-analyzer",
|
"repo": "rust-analyzer",
|
||||||
"rev": "591e3b7624be97e4443ea7b5542c191311aa141d",
|
"rev": "a9026e6d5068172bf5a0d52a260bb290961d1cb4",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -951,11 +944,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773096132,
|
"lastModified": 1775682595,
|
||||||
"narHash": "sha256-M3zEnq9OElB7zqc+mjgPlByPm1O5t2fbUrH3t/Hm5Ag=",
|
"narHash": "sha256-0E9PohY/VuESLq0LR4doaH7hTag513sDDW5n5qmHd1Q=",
|
||||||
"owner": "Mic92",
|
"owner": "Mic92",
|
||||||
"repo": "sops-nix",
|
"repo": "sops-nix",
|
||||||
"rev": "d1ff3b1034d5bab5d7d8086a7803c5a5968cd784",
|
"rev": "d2e8438d5886e92bc5e7c40c035ab6cae0c41f76",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1039,21 +1032,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"systems_6": {
|
|
||||||
"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": [
|
||||||
@@ -1062,11 +1040,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773297127,
|
"lastModified": 1775636079,
|
||||||
"narHash": "sha256-6E/yhXP7Oy/NbXtf1ktzmU8SdVqJQ09HC/48ebEGBpk=",
|
"narHash": "sha256-pc20NRoMdiar8oPQceQT47UUZMBTiMdUuWrYu2obUP0=",
|
||||||
"owner": "numtide",
|
"owner": "numtide",
|
||||||
"repo": "treefmt-nix",
|
"repo": "treefmt-nix",
|
||||||
"rev": "71b125cd05fbfd78cab3e070b73544abe24c5016",
|
"rev": "790751ff7fd3801feeaf96d7dc416a8d581265ba",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -1075,26 +1053,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"tuicr": {
|
|
||||||
"inputs": {
|
|
||||||
"naersk": "naersk_2",
|
|
||||||
"nixpkgs": "nixpkgs_7",
|
|
||||||
"utils": "utils_2"
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1773331838,
|
|
||||||
"narHash": "sha256-MaWHEvzAM5nBlMJiQ8E79hYb13WpWrhhN6YuWandgec=",
|
|
||||||
"owner": "agavra",
|
|
||||||
"repo": "tuicr",
|
|
||||||
"rev": "bb92720d4000bb991028ecfe11872e31e8b62303",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "agavra",
|
|
||||||
"repo": "tuicr",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"utils": {
|
"utils": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"systems": "systems"
|
"systems": "systems"
|
||||||
@@ -1113,29 +1071,11 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"utils_2": {
|
|
||||||
"inputs": {
|
|
||||||
"systems": "systems_5"
|
|
||||||
},
|
|
||||||
"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"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"zjstatus": {
|
"zjstatus": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"crane": "crane",
|
"crane": "crane",
|
||||||
"flake-utils": "flake-utils_2",
|
"flake-utils": "flake-utils_2",
|
||||||
"nixpkgs": "nixpkgs_8",
|
"nixpkgs": "nixpkgs_7",
|
||||||
"rust-overlay": "rust-overlay"
|
"rust-overlay": "rust-overlay"
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
|
|||||||
21
flake.nix
21
flake.nix
@@ -4,6 +4,10 @@
|
|||||||
outputs = inputs: inputs.flake-parts.lib.mkFlake {inherit inputs;} (inputs.import-tree ./modules);
|
outputs = inputs: inputs.flake-parts.lib.mkFlake {inherit inputs;} (inputs.import-tree ./modules);
|
||||||
|
|
||||||
inputs = {
|
inputs = {
|
||||||
|
code-review-nvim = {
|
||||||
|
url = "github:choplin/code-review.nvim";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
darwin = {
|
darwin = {
|
||||||
url = "github:LnL7/nix-darwin/master";
|
url = "github:LnL7/nix-darwin/master";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
@@ -14,6 +18,10 @@
|
|||||||
url = "github:nix-community/disko";
|
url = "github:nix-community/disko";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
fenix = {
|
||||||
|
url = "github:nix-community/fenix";
|
||||||
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
|
};
|
||||||
flake-aspects.url = "github:vic/flake-aspects";
|
flake-aspects.url = "github:vic/flake-aspects";
|
||||||
flake-file.url = "github:vic/flake-file";
|
flake-file.url = "github:vic/flake-file";
|
||||||
flake-parts = {
|
flake-parts = {
|
||||||
@@ -34,6 +42,14 @@
|
|||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
import-tree.url = "github:vic/import-tree";
|
import-tree.url = "github:vic/import-tree";
|
||||||
|
jj-diffconflicts = {
|
||||||
|
url = "github:rafikdraoui/jj-diffconflicts";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
jj-nvim = {
|
||||||
|
url = "github:NicolasGB/jj.nvim";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
jj-ryu = {
|
jj-ryu = {
|
||||||
url = "github:dmmulroy/jj-ryu";
|
url = "github:dmmulroy/jj-ryu";
|
||||||
flake = false;
|
flake = false;
|
||||||
@@ -52,15 +68,10 @@
|
|||||||
nixpkgs.url = "github:nixos/nixpkgs/master";
|
nixpkgs.url = "github:nixos/nixpkgs/master";
|
||||||
nixpkgs-lib.follows = "nixpkgs";
|
nixpkgs-lib.follows = "nixpkgs";
|
||||||
nixvim.url = "github:nix-community/nixvim";
|
nixvim.url = "github:nix-community/nixvim";
|
||||||
nono = {
|
|
||||||
url = "github:always-further/nono";
|
|
||||||
flake = false;
|
|
||||||
};
|
|
||||||
sops-nix = {
|
sops-nix = {
|
||||||
url = "github:Mic92/sops-nix";
|
url = "github:Mic92/sops-nix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
tuicr.url = "github:agavra/tuicr";
|
|
||||||
zjstatus.url = "github:dj95/zjstatus";
|
zjstatus.url = "github:dj95/zjstatus";
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,7 +6,9 @@
|
|||||||
}:
|
}:
|
||||||
with lib; let
|
with lib; let
|
||||||
cfg = config.local.dock;
|
cfg = config.local.dock;
|
||||||
inherit (pkgs) stdenv dockutil;
|
inherit (pkgs) dockutil stdenv;
|
||||||
|
local = import ../_lib/local.nix;
|
||||||
|
userHome = "/Users/${local.user.name}";
|
||||||
in {
|
in {
|
||||||
options = {
|
options = {
|
||||||
local.dock = {
|
local.dock = {
|
||||||
@@ -37,7 +39,7 @@ in {
|
|||||||
};
|
};
|
||||||
});
|
});
|
||||||
default = [
|
default = [
|
||||||
{path = "/Applications/Helium.app/";}
|
{path = "/Applications/Safari.app/";}
|
||||||
{path = "/Applications/Ghostty.app/";}
|
{path = "/Applications/Ghostty.app/";}
|
||||||
{path = "/System/Applications/Calendar.app/";}
|
{path = "/System/Applications/Calendar.app/";}
|
||||||
{path = "/System/Applications/Mail.app/";}
|
{path = "/System/Applications/Mail.app/";}
|
||||||
@@ -45,7 +47,7 @@ in {
|
|||||||
{path = "/System/Applications/Music.app/";}
|
{path = "/System/Applications/Music.app/";}
|
||||||
{path = "/System/Applications/System Settings.app/";}
|
{path = "/System/Applications/System Settings.app/";}
|
||||||
{
|
{
|
||||||
path = "/Users/cschmatzler/Downloads";
|
path = "${userHome}/Downloads";
|
||||||
section = "others";
|
section = "others";
|
||||||
options = "--sort name --view grid --display stack";
|
options = "--sort name --view grid --display stack";
|
||||||
}
|
}
|
||||||
@@ -56,7 +58,7 @@ in {
|
|||||||
mkOption {
|
mkOption {
|
||||||
description = "Username to apply the dock settings to";
|
description = "Username to apply the dock settings to";
|
||||||
type = types.str;
|
type = types.str;
|
||||||
default = "cschmatzler";
|
default = local.user.name;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
67
modules/_dev-tools/tuist-pr.nu
Normal file
67
modules/_dev-tools/tuist-pr.nu
Normal file
@@ -0,0 +1,67 @@
|
|||||||
|
#!/usr/bin/env nu
|
||||||
|
|
||||||
|
const repo = "tuist/tuist"
|
||||||
|
|
||||||
|
def fail [msg: string] {
|
||||||
|
error make {msg: $msg}
|
||||||
|
}
|
||||||
|
|
||||||
|
def clean [s: string] {
|
||||||
|
$s | str replace --all "\t" " " | str replace --all "\n" " "
|
||||||
|
}
|
||||||
|
|
||||||
|
def pick-pr [] {
|
||||||
|
let prs = (
|
||||||
|
gh pr list --repo $repo --state open --limit 200 --json number,title,headRefName,author
|
||||||
|
| from json
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($prs | is-empty) {
|
||||||
|
fail $"No open PRs found for ($repo)"
|
||||||
|
}
|
||||||
|
|
||||||
|
let choice = (
|
||||||
|
$prs
|
||||||
|
| each {|pr|
|
||||||
|
let title = (clean $pr.title)
|
||||||
|
let branch = (clean $pr.headRefName)
|
||||||
|
let author = $pr.author.login
|
||||||
|
$"($pr.number)\t($title)\t($author)\t($branch)"
|
||||||
|
}
|
||||||
|
| str join (char newline)
|
||||||
|
| fzf --prompt "tuist pr > " --delimiter "\t" --with-nth "1,2,3,4" --preview "gh pr view --repo tuist/tuist {1}" --preview-window "right:70%"
|
||||||
|
)
|
||||||
|
|
||||||
|
if ($choice | str trim | is-empty) {
|
||||||
|
exit 130
|
||||||
|
}
|
||||||
|
|
||||||
|
$choice | split row "\t" | first | into int
|
||||||
|
}
|
||||||
|
|
||||||
|
def main [pr_number?: int] {
|
||||||
|
let number = if ($pr_number | is-empty) { pick-pr } else { $pr_number }
|
||||||
|
let pr = (
|
||||||
|
gh pr view --repo $repo $number --json number,title,url
|
||||||
|
| from json
|
||||||
|
)
|
||||||
|
|
||||||
|
let base = ([$env.HOME "Projects" "Work"] | path join)
|
||||||
|
let dest = ([$base $"tuist-pr-($pr.number)"] | path join)
|
||||||
|
|
||||||
|
if ($dest | path exists) {
|
||||||
|
fail $"Destination already exists: ($dest)"
|
||||||
|
}
|
||||||
|
|
||||||
|
^mkdir -p $base
|
||||||
|
|
||||||
|
print $"Cloning ($repo) PR #($pr.number): ($pr.title)"
|
||||||
|
jj git clone $"https://github.com/($repo).git" $dest
|
||||||
|
|
||||||
|
do {
|
||||||
|
cd $dest
|
||||||
|
gh pr checkout $pr.number
|
||||||
|
}
|
||||||
|
|
||||||
|
print $"Ready: ($dest)"
|
||||||
|
}
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
pkgs,
|
|
||||||
...
|
|
||||||
}: {
|
|
||||||
services.restic.backups.gitea = {
|
|
||||||
repository = "s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories";
|
|
||||||
paths = ["/var/lib/gitea"];
|
|
||||||
exclude = [
|
|
||||||
"/var/lib/gitea/log"
|
|
||||||
"/var/lib/gitea/data/gitea.db"
|
|
||||||
"/var/lib/gitea/data/gitea.db-shm"
|
|
||||||
"/var/lib/gitea/data/gitea.db-wal"
|
|
||||||
];
|
|
||||||
passwordFile = config.sops.secrets.michael-gitea-restic-password.path;
|
|
||||||
environmentFile = config.sops.secrets.michael-gitea-restic-env.path;
|
|
||||||
pruneOpts = [
|
|
||||||
"--keep-daily 7"
|
|
||||||
"--keep-weekly 4"
|
|
||||||
"--keep-monthly 6"
|
|
||||||
];
|
|
||||||
timerConfig = {
|
|
||||||
OnCalendar = "daily";
|
|
||||||
Persistent = true;
|
|
||||||
RandomizedDelaySec = "1h";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.restic-backups-gitea = {
|
|
||||||
wants = ["restic-init-gitea.service"];
|
|
||||||
after = ["restic-init-gitea.service"];
|
|
||||||
serviceConfig = {
|
|
||||||
User = lib.mkForce "gitea";
|
|
||||||
Group = lib.mkForce "gitea";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.restic-init-gitea = {
|
|
||||||
description = "Initialize Restic repository for Gitea backups";
|
|
||||||
wantedBy = ["multi-user.target"];
|
|
||||||
after = ["network-online.target"];
|
|
||||||
wants = ["network-online.target"];
|
|
||||||
path = [pkgs.restic];
|
|
||||||
serviceConfig = {
|
|
||||||
Type = "oneshot";
|
|
||||||
User = "gitea";
|
|
||||||
Group = "gitea";
|
|
||||||
RemainAfterExit = true;
|
|
||||||
EnvironmentFile = config.sops.secrets.michael-gitea-restic-env.path;
|
|
||||||
};
|
|
||||||
script = ''
|
|
||||||
export RESTIC_PASSWORD=$(cat ${config.sops.secrets.michael-gitea-restic-password.path})
|
|
||||||
restic -r s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories snapshots &>/dev/null || \
|
|
||||||
restic -r s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories init
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,114 +0,0 @@
|
|||||||
{
|
|
||||||
config,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}: {
|
|
||||||
sops.secrets = {
|
|
||||||
michael-gitea-litestream = {
|
|
||||||
sopsFile = ../../../secrets/michael-gitea-litestream;
|
|
||||||
format = "binary";
|
|
||||||
owner = "gitea";
|
|
||||||
group = "gitea";
|
|
||||||
path = "/run/secrets/michael-gitea-litestream";
|
|
||||||
};
|
|
||||||
michael-gitea-restic-password = {
|
|
||||||
sopsFile = ../../../secrets/michael-gitea-restic-password;
|
|
||||||
format = "binary";
|
|
||||||
owner = "gitea";
|
|
||||||
group = "gitea";
|
|
||||||
path = "/run/secrets/michael-gitea-restic-password";
|
|
||||||
};
|
|
||||||
michael-gitea-restic-env = {
|
|
||||||
sopsFile = ../../../secrets/michael-gitea-restic-env;
|
|
||||||
format = "binary";
|
|
||||||
owner = "gitea";
|
|
||||||
group = "gitea";
|
|
||||||
path = "/run/secrets/michael-gitea-restic-env";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
networking.firewall.allowedTCPPorts = [80 443];
|
|
||||||
|
|
||||||
services.redis.servers.gitea = {
|
|
||||||
enable = true;
|
|
||||||
port = 6380;
|
|
||||||
bind = "127.0.0.1";
|
|
||||||
settings = {
|
|
||||||
maxmemory = "64mb";
|
|
||||||
maxmemory-policy = "allkeys-lru";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.gitea = {
|
|
||||||
enable = true;
|
|
||||||
database = {
|
|
||||||
type = "sqlite3";
|
|
||||||
path = "/var/lib/gitea/data/gitea.db";
|
|
||||||
};
|
|
||||||
settings = {
|
|
||||||
server = {
|
|
||||||
ROOT_URL = "https://git.schmatzler.com/";
|
|
||||||
DOMAIN = "git.schmatzler.com";
|
|
||||||
HTTP_ADDR = "127.0.0.1";
|
|
||||||
HTTP_PORT = 3000;
|
|
||||||
LANDING_PAGE = "explore";
|
|
||||||
};
|
|
||||||
service.DISABLE_REGISTRATION = true;
|
|
||||||
security.INSTALL_LOCK = true;
|
|
||||||
cache = {
|
|
||||||
ADAPTER = "redis";
|
|
||||||
HOST = "redis://127.0.0.1:6380/0?pool_size=100&idle_timeout=180s";
|
|
||||||
ITEM_TTL = "16h";
|
|
||||||
};
|
|
||||||
"cache.last_commit" = {
|
|
||||||
ITEM_TTL = "8760h";
|
|
||||||
COMMITS_COUNT = 100;
|
|
||||||
};
|
|
||||||
session = {
|
|
||||||
PROVIDER = "redis";
|
|
||||||
PROVIDER_CONFIG = "redis://127.0.0.1:6380/1?pool_size=100&idle_timeout=180s";
|
|
||||||
COOKIE_SECURE = true;
|
|
||||||
SAME_SITE = "strict";
|
|
||||||
};
|
|
||||||
api.ENABLE_SWAGGER = false;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.litestream = {
|
|
||||||
enable = true;
|
|
||||||
environmentFile = config.sops.secrets.michael-gitea-litestream.path;
|
|
||||||
settings = {
|
|
||||||
dbs = [
|
|
||||||
{
|
|
||||||
path = "/var/lib/gitea/data/gitea.db";
|
|
||||||
replicas = [
|
|
||||||
{
|
|
||||||
type = "s3";
|
|
||||||
bucket = "michael-gitea-litestream";
|
|
||||||
path = "gitea";
|
|
||||||
endpoint = "s3.eu-central-003.backblazeb2.com";
|
|
||||||
}
|
|
||||||
];
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
systemd.services.litestream.serviceConfig = {
|
|
||||||
User = lib.mkForce "gitea";
|
|
||||||
Group = lib.mkForce "gitea";
|
|
||||||
};
|
|
||||||
|
|
||||||
services.caddy = {
|
|
||||||
enable = true;
|
|
||||||
virtualHosts."git.schmatzler.com".extraConfig = ''
|
|
||||||
header {
|
|
||||||
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
|
||||||
X-Content-Type-Options "nosniff"
|
|
||||||
X-Frame-Options "DENY"
|
|
||||||
Referrer-Policy "strict-origin-when-cross-origin"
|
|
||||||
}
|
|
||||||
reverse_proxy localhost:3000
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,69 +0,0 @@
|
|||||||
{config, ...}: {
|
|
||||||
services.adguardhome = {
|
|
||||||
enable = true;
|
|
||||||
host = "127.0.0.1";
|
|
||||||
port = 10000;
|
|
||||||
settings = {
|
|
||||||
dhcp = {
|
|
||||||
enabled = false;
|
|
||||||
};
|
|
||||||
dns = {
|
|
||||||
upstream_dns = [
|
|
||||||
"1.1.1.1"
|
|
||||||
"1.0.0.1"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
filtering = {
|
|
||||||
protection_enabled = true;
|
|
||||||
filtering_enabled = true;
|
|
||||||
safe_search = {
|
|
||||||
enabled = false;
|
|
||||||
};
|
|
||||||
safebrowsing_enabled = true;
|
|
||||||
blocked_response_ttl = 10;
|
|
||||||
filters_update_interval = 24;
|
|
||||||
blocked_services = {
|
|
||||||
ids = [
|
|
||||||
"reddit"
|
|
||||||
"twitter"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
filters = [
|
|
||||||
{
|
|
||||||
enabled = true;
|
|
||||||
url = "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/adblock/pro.txt";
|
|
||||||
name = "HaGeZi Multi PRO";
|
|
||||||
id = 1;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
enabled = true;
|
|
||||||
url = "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/adblock/tif.txt";
|
|
||||||
name = "HaGeZi Threat Intelligence Feeds";
|
|
||||||
id = 2;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
enabled = true;
|
|
||||||
url = "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/adblock/gambling.txt";
|
|
||||||
name = "HaGeZi Gambling";
|
|
||||||
id = 3;
|
|
||||||
}
|
|
||||||
{
|
|
||||||
enabled = true;
|
|
||||||
url = "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/adblock/nsfw.txt";
|
|
||||||
name = "HaGeZi NSFW";
|
|
||||||
id = 4;
|
|
||||||
}
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.caddy.virtualHosts."adguard.manticore-hippocampus.ts.net" = {
|
|
||||||
extraConfig = ''
|
|
||||||
tls {
|
|
||||||
get_certificate tailscale
|
|
||||||
}
|
|
||||||
reverse_proxy localhost:${toString config.services.adguardhome.port}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
{...}: {
|
|
||||||
services.caddy.virtualHosts."cache.manticore-hippocampus.ts.net" = {
|
|
||||||
extraConfig = ''
|
|
||||||
tls {
|
|
||||||
get_certificate tailscale
|
|
||||||
}
|
|
||||||
reverse_proxy localhost:32843
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,76 +0,0 @@
|
|||||||
{config, ...}: {
|
|
||||||
services.caddy = {
|
|
||||||
enable = true;
|
|
||||||
enableReload = false;
|
|
||||||
globalConfig = ''
|
|
||||||
admin off
|
|
||||||
'';
|
|
||||||
virtualHosts."docs.manticore-hippocampus.ts.net" = {
|
|
||||||
extraConfig = ''
|
|
||||||
tls {
|
|
||||||
get_certificate tailscale
|
|
||||||
}
|
|
||||||
reverse_proxy localhost:${toString config.services.paperless.port}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
virtualHosts."docs-ai.manticore-hippocampus.ts.net" = {
|
|
||||||
extraConfig = ''
|
|
||||||
tls {
|
|
||||||
get_certificate tailscale
|
|
||||||
}
|
|
||||||
reverse_proxy localhost:3000
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
virtualisation.oci-containers = {
|
|
||||||
backend = "docker";
|
|
||||||
containers.paperless-ai = {
|
|
||||||
image = "clusterzx/paperless-ai:v3.0.9";
|
|
||||||
autoStart = true;
|
|
||||||
ports = [
|
|
||||||
"127.0.0.1:3000:3000"
|
|
||||||
];
|
|
||||||
volumes = [
|
|
||||||
"paperless-ai-data:/app/data"
|
|
||||||
];
|
|
||||||
environment = {
|
|
||||||
PUID = "1000";
|
|
||||||
PGID = "1000";
|
|
||||||
PAPERLESS_AI_PORT = "3000";
|
|
||||||
# Initial setup wizard will configure the rest
|
|
||||||
PAPERLESS_AI_INITIAL_SETUP = "yes";
|
|
||||||
PAPERLESS_API_URL = "http://host.docker.internal:${toString config.services.paperless.port}/api";
|
|
||||||
};
|
|
||||||
extraOptions = [
|
|
||||||
"--add-host=host.docker.internal:host-gateway"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.redis.servers.paperless = {
|
|
||||||
enable = true;
|
|
||||||
port = 6379;
|
|
||||||
bind = "127.0.0.1";
|
|
||||||
settings = {
|
|
||||||
maxmemory = "256mb";
|
|
||||||
maxmemory-policy = "allkeys-lru";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
services.paperless = {
|
|
||||||
enable = true;
|
|
||||||
address = "127.0.0.1";
|
|
||||||
passwordFile = config.sops.secrets.tahani-paperless-password.path;
|
|
||||||
settings = {
|
|
||||||
PAPERLESS_DBENGINE = "sqlite";
|
|
||||||
PAPERLESS_REDIS = "redis://127.0.0.1:6379";
|
|
||||||
PAPERLESS_CONSUMER_IGNORE_PATTERN = [
|
|
||||||
".DS_STORE/*"
|
|
||||||
"desktop.ini"
|
|
||||||
];
|
|
||||||
PAPERLESS_OCR_LANGUAGE = "deu+eng";
|
|
||||||
PAPERLESS_CSRF_TRUSTED_ORIGINS = "https://docs.manticore-hippocampus.ts.net";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
19
modules/_lib/caddy.nix
Normal file
19
modules/_lib/caddy.nix
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
let
|
||||||
|
local = import ./local.nix;
|
||||||
|
in {
|
||||||
|
inherit (local) tailscaleHost;
|
||||||
|
|
||||||
|
mkTailscaleVHost = {
|
||||||
|
name,
|
||||||
|
configText,
|
||||||
|
}: {
|
||||||
|
"${local.tailscaleHost name}" = {
|
||||||
|
extraConfig = ''
|
||||||
|
tls {
|
||||||
|
get_certificate tailscale
|
||||||
|
}
|
||||||
|
${configText}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
37
modules/_lib/hosts.nix
Normal file
37
modules/_lib/hosts.nix
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
{
|
||||||
|
den,
|
||||||
|
lib,
|
||||||
|
}: let
|
||||||
|
merge = lib.recursiveUpdate;
|
||||||
|
in {
|
||||||
|
mkUserHost = {
|
||||||
|
system,
|
||||||
|
host,
|
||||||
|
user,
|
||||||
|
userAspect ? "${host}-${user}",
|
||||||
|
includes ? [],
|
||||||
|
homeManager ? null,
|
||||||
|
}:
|
||||||
|
merge
|
||||||
|
(lib.setAttrByPath ["den" "hosts" system host "users" user "aspect"] userAspect)
|
||||||
|
(lib.setAttrByPath ["den" "aspects" userAspect] ({inherit includes;}
|
||||||
|
// lib.optionalAttrs (homeManager != null) {
|
||||||
|
inherit homeManager;
|
||||||
|
}));
|
||||||
|
|
||||||
|
mkPerHostAspect = {
|
||||||
|
host,
|
||||||
|
includes ? [],
|
||||||
|
darwin ? null,
|
||||||
|
nixos ? null,
|
||||||
|
}:
|
||||||
|
lib.setAttrByPath ["den" "aspects" host "includes"] [
|
||||||
|
(den.lib.perHost ({inherit includes;}
|
||||||
|
// lib.optionalAttrs (darwin != null) {
|
||||||
|
inherit darwin;
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (nixos != null) {
|
||||||
|
inherit nixos;
|
||||||
|
}))
|
||||||
|
];
|
||||||
|
}
|
||||||
33
modules/_lib/local.nix
Normal file
33
modules/_lib/local.nix
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
rec {
|
||||||
|
user = {
|
||||||
|
name = "cschmatzler";
|
||||||
|
fullName = "Christoph Schmatzler";
|
||||||
|
emails = {
|
||||||
|
personal = "christoph@schmatzler.com";
|
||||||
|
work = "christoph@tuist.dev";
|
||||||
|
icloud = "christoph.schmatzler@icloud.com";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
secretPath = name: "/run/secrets/${name}";
|
||||||
|
|
||||||
|
mkHome = system:
|
||||||
|
if builtins.match ".*-darwin" system != null
|
||||||
|
then "/Users/${user.name}"
|
||||||
|
else "/home/${user.name}";
|
||||||
|
|
||||||
|
mkHost = system: {
|
||||||
|
inherit system;
|
||||||
|
home = mkHome system;
|
||||||
|
};
|
||||||
|
|
||||||
|
hosts = {
|
||||||
|
chidi = mkHost "aarch64-darwin";
|
||||||
|
janet = mkHost "aarch64-darwin";
|
||||||
|
michael = mkHost "x86_64-linux";
|
||||||
|
tahani = mkHost "x86_64-linux";
|
||||||
|
};
|
||||||
|
|
||||||
|
tailscaleDomain = "manticore-hippocampus.ts.net";
|
||||||
|
tailscaleHost = name: "${name}.${tailscaleDomain}";
|
||||||
|
}
|
||||||
44
modules/_lib/secrets.nix
Normal file
44
modules/_lib/secrets.nix
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{lib}: let
|
||||||
|
local = import ./local.nix;
|
||||||
|
in rec {
|
||||||
|
mkBinarySecret = {
|
||||||
|
name,
|
||||||
|
sopsFile,
|
||||||
|
owner ? null,
|
||||||
|
group ? null,
|
||||||
|
path ? local.secretPath name,
|
||||||
|
}:
|
||||||
|
{
|
||||||
|
inherit path sopsFile;
|
||||||
|
format = "binary";
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (owner != null) {
|
||||||
|
inherit owner;
|
||||||
|
}
|
||||||
|
// lib.optionalAttrs (group != null) {
|
||||||
|
inherit group;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkUserBinarySecret = {
|
||||||
|
name,
|
||||||
|
sopsFile,
|
||||||
|
owner ? local.user.name,
|
||||||
|
path ? local.secretPath name,
|
||||||
|
}:
|
||||||
|
mkBinarySecret {
|
||||||
|
inherit name owner path sopsFile;
|
||||||
|
};
|
||||||
|
|
||||||
|
mkServiceBinarySecret = {
|
||||||
|
name,
|
||||||
|
sopsFile,
|
||||||
|
serviceUser,
|
||||||
|
serviceGroup ? serviceUser,
|
||||||
|
path ? local.secretPath name,
|
||||||
|
}:
|
||||||
|
mkBinarySecret {
|
||||||
|
inherit name path sopsFile;
|
||||||
|
group = serviceGroup;
|
||||||
|
owner = serviceUser;
|
||||||
|
};
|
||||||
|
}
|
||||||
46
modules/_lib/theme.nix
Normal file
46
modules/_lib/theme.nix
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
{
|
||||||
|
rosePineDawn = {
|
||||||
|
slug = "rose-pine-dawn";
|
||||||
|
displayName = "Rosé Pine Dawn";
|
||||||
|
ghosttyName = "Rose Pine Dawn";
|
||||||
|
|
||||||
|
hex = {
|
||||||
|
love = "#b4637a";
|
||||||
|
gold = "#ea9d34";
|
||||||
|
rose = "#d7827e";
|
||||||
|
pine = "#286983";
|
||||||
|
foam = "#56949f";
|
||||||
|
iris = "#907aa9";
|
||||||
|
leaf = "#6d8f89";
|
||||||
|
text = "#575279";
|
||||||
|
subtle = "#797593";
|
||||||
|
muted = "#9893a5";
|
||||||
|
highlightHigh = "#cecacd";
|
||||||
|
highlightMed = "#dfdad9";
|
||||||
|
highlightLow = "#f4ede8";
|
||||||
|
overlay = "#f2e9e1";
|
||||||
|
surface = "#fffaf3";
|
||||||
|
base = "#faf4ed";
|
||||||
|
};
|
||||||
|
|
||||||
|
rgb = {
|
||||||
|
love = "180 99 122";
|
||||||
|
gold = "234 157 52";
|
||||||
|
rose = "215 130 126";
|
||||||
|
pine = "40 105 131";
|
||||||
|
foam = "86 148 159";
|
||||||
|
iris = "144 122 169";
|
||||||
|
leaf = "109 143 137";
|
||||||
|
text = "87 82 121";
|
||||||
|
subtle = "121 117 147";
|
||||||
|
muted = "152 147 165";
|
||||||
|
highlightHigh = "206 202 205";
|
||||||
|
highlightMed = "223 218 217";
|
||||||
|
highlightLow = "244 237 232";
|
||||||
|
overlay = "242 233 225";
|
||||||
|
surface = "255 250 243";
|
||||||
|
base = "250 244 237";
|
||||||
|
black = "0 0 0";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -16,7 +16,6 @@
|
|||||||
./plugins/lsp.nix
|
./plugins/lsp.nix
|
||||||
./plugins/mini.nix
|
./plugins/mini.nix
|
||||||
./plugins/oil.nix
|
./plugins/oil.nix
|
||||||
./plugins/opencode.nix
|
|
||||||
./plugins/render-markdown.nix
|
./plugins/render-markdown.nix
|
||||||
./plugins/toggleterm.nix
|
./plugins/toggleterm.nix
|
||||||
./plugins/treesitter.nix
|
./plugins/treesitter.nix
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
{pkgs, ...}: let
|
{
|
||||||
|
pkgs,
|
||||||
|
nvim-plugin-sources,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
code-review-nvim =
|
code-review-nvim =
|
||||||
pkgs.vimUtils.buildVimPlugin {
|
pkgs.vimUtils.buildVimPlugin {
|
||||||
pname = "code-review-nvim";
|
pname = "code-review-nvim";
|
||||||
version = "unstable-2026-03-10";
|
version = "unstable";
|
||||||
src =
|
src = nvim-plugin-sources.code-review-nvim;
|
||||||
pkgs.fetchFromGitHub {
|
|
||||||
owner = "choplin";
|
|
||||||
repo = "code-review.nvim";
|
|
||||||
rev = "ed91462e20bd08c3be71efb11a4a7d00459f0b47";
|
|
||||||
hash = "sha256-WpbQswkUpB4Nblos8+5UE5I/PHUQOi+RQ+hj4CCdL4o=";
|
|
||||||
};
|
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
@@ -20,6 +18,11 @@ in {
|
|||||||
extraConfigLua = ''
|
extraConfigLua = ''
|
||||||
require('code-review').setup({
|
require('code-review').setup({
|
||||||
keymaps = false,
|
keymaps = false,
|
||||||
|
comment = {
|
||||||
|
storage = {
|
||||||
|
backend = "file",
|
||||||
|
},
|
||||||
|
},
|
||||||
ui = {
|
ui = {
|
||||||
input_window = {
|
input_window = {
|
||||||
border = "single",
|
border = "single",
|
||||||
|
|||||||
@@ -1,14 +1,12 @@
|
|||||||
{pkgs, ...}: {
|
{
|
||||||
|
pkgs,
|
||||||
|
nvim-plugin-sources,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
programs.nixvim.extraPlugins = [
|
programs.nixvim.extraPlugins = [
|
||||||
(pkgs.vimUtils.buildVimPlugin {
|
(pkgs.vimUtils.buildVimPlugin {
|
||||||
name = "jj-diffconflicts";
|
name = "jj-diffconflicts";
|
||||||
src =
|
src = nvim-plugin-sources.jj-diffconflicts;
|
||||||
pkgs.fetchFromGitHub {
|
|
||||||
owner = "rafikdraoui";
|
|
||||||
repo = "jj-diffconflicts";
|
|
||||||
rev = "main";
|
|
||||||
hash = "sha256-nzjRWHrE2jIcaDoPbixzpvflrtLhPZrihOEQWwqqU0s=";
|
|
||||||
};
|
|
||||||
})
|
})
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,15 +1,13 @@
|
|||||||
{pkgs, ...}: let
|
{
|
||||||
|
pkgs,
|
||||||
|
nvim-plugin-sources,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
jj-nvim =
|
jj-nvim =
|
||||||
pkgs.vimUtils.buildVimPlugin {
|
pkgs.vimUtils.buildVimPlugin {
|
||||||
pname = "jj-nvim";
|
pname = "jj-nvim";
|
||||||
version = "unstable-2026-03-10";
|
version = "unstable";
|
||||||
src =
|
src = nvim-plugin-sources.jj-nvim;
|
||||||
pkgs.fetchFromGitHub {
|
|
||||||
owner = "NicolasGB";
|
|
||||||
repo = "jj.nvim";
|
|
||||||
rev = "bbba4051c862473637e98277f284d12b050588ca";
|
|
||||||
hash = "sha256-nokftWcAmmHX6UcH6O79xkLwbUpq1W8N9lf1e+NyGqE=";
|
|
||||||
};
|
|
||||||
doCheck = false;
|
doCheck = false;
|
||||||
};
|
};
|
||||||
in {
|
in {
|
||||||
|
|||||||
@@ -31,8 +31,6 @@
|
|||||||
{ mode = 'n', keys = '<Leader>v', desc = '+VCS' },
|
{ mode = 'n', keys = '<Leader>v', desc = '+VCS' },
|
||||||
{ mode = 'n', keys = '<Leader>l', desc = '+LSP' },
|
{ mode = 'n', keys = '<Leader>l', desc = '+LSP' },
|
||||||
{ mode = 'x', keys = '<Leader>l', desc = '+LSP' },
|
{ mode = 'x', keys = '<Leader>l', desc = '+LSP' },
|
||||||
{ mode = 'n', keys = '<Leader>o', desc = '+OpenCode' },
|
|
||||||
{ mode = 'x', keys = '<Leader>o', desc = '+OpenCode' },
|
|
||||||
{ mode = 'n', keys = '<Leader>r', desc = '+Review' },
|
{ mode = 'n', keys = '<Leader>r', desc = '+Review' },
|
||||||
{ mode = 'v', keys = '<Leader>r', desc = '+Review' },
|
{ mode = 'v', keys = '<Leader>r', desc = '+Review' },
|
||||||
{ mode = 'n', keys = '<Leader>t', desc = '+Tab' },
|
{ mode = 'n', keys = '<Leader>t', desc = '+Tab' },
|
||||||
|
|||||||
@@ -1,99 +0,0 @@
|
|||||||
{pkgs, ...}: let
|
|
||||||
opencode-nvim =
|
|
||||||
pkgs.vimUtils.buildVimPlugin {
|
|
||||||
pname = "opencode-nvim";
|
|
||||||
version = "unstable-2026-03-12";
|
|
||||||
src =
|
|
||||||
pkgs.fetchFromGitHub {
|
|
||||||
owner = "sudo-tee";
|
|
||||||
repo = "opencode.nvim";
|
|
||||||
rev = "800c4f891f5d940f2805780a39872a0207b5a446";
|
|
||||||
hash = "sha256-3xyZux5S8ThBsi7AC4AWnd2h2LEI5L+I5Am2PNWKu64=";
|
|
||||||
};
|
|
||||||
doCheck = false;
|
|
||||||
postPatch = ''
|
|
||||||
# Widen sign column and move border further left for more padding
|
|
||||||
sed -i "s/signcolumn', 'yes'/signcolumn', 'yes:2'/" lua/opencode/ui/output_window.lua
|
|
||||||
sed -i "s/, -3)/, -5)/g" lua/opencode/ui/formatter.lua
|
|
||||||
sed -i "s/win_col = -3/win_col = -5/g" lua/opencode/ui/formatter.lua
|
|
||||||
# Fix off-by-one: user border starts 1 line too early (bleeds into header empty line)
|
|
||||||
sed -i 's/start_line = output:get_line_count() *$/start_line = output:get_line_count() + 1/' lua/opencode/ui/formatter.lua
|
|
||||||
# Fix file mention border starting 1 line too early
|
|
||||||
sed -i 's/file_line - 1, file_line/file_line, file_line/' lua/opencode/ui/formatter.lua
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
in {
|
|
||||||
programs.nixvim = {
|
|
||||||
autoCmd = [
|
|
||||||
{
|
|
||||||
event = "FileType";
|
|
||||||
group = "Christoph";
|
|
||||||
pattern = "opencode,opencode_output";
|
|
||||||
callback.__raw = ''
|
|
||||||
function()
|
|
||||||
vim.b.ministatusline_disable = true
|
|
||||||
end
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
extraPlugins = [
|
|
||||||
opencode-nvim
|
|
||||||
];
|
|
||||||
extraConfigLua = ''
|
|
||||||
require("opencode").setup({
|
|
||||||
debug = {
|
|
||||||
show_ids = false,
|
|
||||||
},
|
|
||||||
ui = {
|
|
||||||
input = {
|
|
||||||
text = {
|
|
||||||
wrap = true,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
icons = {
|
|
||||||
preset = "nerdfonts",
|
|
||||||
overrides = {
|
|
||||||
header_user = "▌ ",
|
|
||||||
header_assistant = " ",
|
|
||||||
run = " ",
|
|
||||||
task = " ",
|
|
||||||
read = " ",
|
|
||||||
edit = " ",
|
|
||||||
write = " ",
|
|
||||||
plan = " ",
|
|
||||||
search = " ",
|
|
||||||
web = " ",
|
|
||||||
list = " ",
|
|
||||||
tool = " ",
|
|
||||||
snapshot = " ",
|
|
||||||
file = " ",
|
|
||||||
folder = " ",
|
|
||||||
attached_file = " ",
|
|
||||||
agent = " ",
|
|
||||||
reasoning = " ",
|
|
||||||
question = " ",
|
|
||||||
completed = " ",
|
|
||||||
pending = " ",
|
|
||||||
running = " ",
|
|
||||||
bash = " ",
|
|
||||||
command = " ",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
local p = require("rose-pine.palette")
|
|
||||||
local hl = vim.api.nvim_set_hl
|
|
||||||
hl(0, "OpencodeBorder", { fg = p.muted })
|
|
||||||
hl(0, "OpencodeToolBorder", { fg = p.base })
|
|
||||||
hl(0, "OpencodeDiffAdd", { bg = p.highlight_med })
|
|
||||||
hl(0, "OpencodeDiffDelete", { bg = p.overlay })
|
|
||||||
hl(0, "OpencodeAgentPlan", { bg = p.iris, fg = p.surface })
|
|
||||||
hl(0, "OpencodeAgentBuild", { bg = p.foam, fg = p.surface })
|
|
||||||
hl(0, "OpencodeAgentCustom", { bg = p.gold, fg = p.surface })
|
|
||||||
hl(0, "OpencodeContestualAction", { bg = p.highlight_med })
|
|
||||||
hl(0, "OpencodeInputLegend", { bg = p.overlay, fg = p.subtle })
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -3,7 +3,7 @@
|
|||||||
enable = true;
|
enable = true;
|
||||||
settings = {
|
settings = {
|
||||||
anti_conceal = {enabled = false;};
|
anti_conceal = {enabled = false;};
|
||||||
file_types = ["markdown" "opencode_output"];
|
file_types = ["markdown"];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,28 @@
|
|||||||
# Global AGENTS.md
|
# AGENTS.md
|
||||||
|
|
||||||
## Version Control
|
## Version Control
|
||||||
|
|
||||||
- Use `jj` for VCS, not `git`
|
- Use `jj` for version control, not `git`.
|
||||||
- `jj tug` is an alias for `jj bookmark move --from closest_bookmark(@-) --to @-`
|
- `jj tug` is an alias for `jj bookmark move --from closest_bookmark(@-) --to @-`.
|
||||||
|
- Never attempt historically destructive Git commands.
|
||||||
|
- Make small, frequent commits.
|
||||||
|
- "Commit" means `jj commit`, not `jj desc`; `desc` stays on the same working copy.
|
||||||
|
|
||||||
## Scripting
|
## Scripting
|
||||||
|
|
||||||
- Always use Nushell (`nu`) for scripting
|
- Use Nushell (`nu`) for scripting.
|
||||||
- Never use Python, Perl, Lua, awk, or any other scripting language
|
- Do not use Python, Perl, Lua, awk, or any other scripting language. You are programatically blocked from doing so.
|
||||||
|
|
||||||
|
## Workflow
|
||||||
|
|
||||||
|
- Always complete the requested work.
|
||||||
|
- If there is any ambiguity about what to do next, do NOT make a decision yourself. Stop your work and ask.
|
||||||
|
- Do not end with “If you want me to…” or “I can…”; take the next necessary step and finish the job without waiting for additional confirmation.
|
||||||
|
- Do not future-proof things. Stick to the original plan.
|
||||||
|
- Do not add fallbacks or backward compatibility unless explicitly required by the user. By default, replace the previous implementation with the new one entirely.
|
||||||
|
|
||||||
|
## Validation
|
||||||
|
|
||||||
|
- Do not ignore failing tests or checks, even if they appear unrelated to your changes.
|
||||||
|
- After completing and validating your work, the final step is to run the project's full validation and test commands and ensure they all pass.
|
||||||
|
|
||||||
|
|||||||
@@ -1,45 +0,0 @@
|
|||||||
---
|
|
||||||
description: Reviews code for quality, bugs, security, and best practices
|
|
||||||
mode: subagent
|
|
||||||
temperature: 0.1
|
|
||||||
tools:
|
|
||||||
write: false
|
|
||||||
edit: false
|
|
||||||
permission:
|
|
||||||
edit: deny
|
|
||||||
webfetch: allow
|
|
||||||
---
|
|
||||||
You are a code reviewer. Provide actionable feedback on code changes.
|
|
||||||
|
|
||||||
**Diffs alone are not enough.** Read the full file(s) being modified to understand context. Code that looks wrong in isolation may be correct given surrounding logic.
|
|
||||||
|
|
||||||
## What to Look For
|
|
||||||
|
|
||||||
**Bugs** — Primary focus.
|
|
||||||
- Logic errors, off-by-one mistakes, incorrect conditionals
|
|
||||||
- Missing guards, unreachable code paths, broken error handling
|
|
||||||
- Edge cases: null/empty inputs, race conditions
|
|
||||||
- Security: injection, auth bypass, data exposure
|
|
||||||
|
|
||||||
**Structure** — Does the code fit the codebase?
|
|
||||||
- Follows existing patterns and conventions?
|
|
||||||
- Uses established abstractions?
|
|
||||||
- Excessive nesting that could be flattened?
|
|
||||||
|
|
||||||
**Performance** — Only flag if obviously problematic.
|
|
||||||
- O(n²) on unbounded data, N+1 queries, blocking I/O on hot paths
|
|
||||||
|
|
||||||
## Before You Flag Something
|
|
||||||
|
|
||||||
- **Be certain.** Don't flag something as a bug if you're unsure — investigate first.
|
|
||||||
- **Don't invent hypothetical problems.** If an edge case matters, explain the realistic scenario.
|
|
||||||
- **Don't be a zealot about style.** Some "violations" are acceptable when they're the simplest option.
|
|
||||||
- Only review the changes — not pre-existing code that wasn't modified.
|
|
||||||
|
|
||||||
## Output
|
|
||||||
|
|
||||||
- Be direct about bugs and why they're bugs
|
|
||||||
- Communicate severity honestly — don't overstate
|
|
||||||
- Include file paths and line numbers
|
|
||||||
- Suggest fixes when appropriate
|
|
||||||
- Matter-of-fact tone, no flattery
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
description: Review changes with parallel @code-review subagents
|
|
||||||
---
|
|
||||||
Review the code changes using THREE (3) @code-review subagents and correlate results into a summary ranked by severity. Use the provided user guidance to steer the review and focus on specific code paths, changes, and/or areas of concern. Once all three @code-review subagents return their findings and you have correlated and summarized the results, consult the @oracle subagent to perform a deep review on the findings focusing on accuracy and correctness by evaluating the surrounding code, system, subsystems, abstractions, and overall architecture of each item. Apply any recommendations from the oracle. NEVER SKIP ORACLE REVIEW.
|
|
||||||
|
|
||||||
Guidance: $ARGUMENTS
|
|
||||||
|
|
||||||
First, call `skill({ name: 'vcs-detect' })` to determine whether the repo uses git or jj, then use the appropriate VCS commands throughout.
|
|
||||||
|
|
||||||
Review uncommitted changes by default. If no uncommitted changes, review the last commit. If the user provides a pull request/merge request number or link, use CLI tools (gh/glab) to fetch it and then perform your review.
|
|
||||||
@@ -10,6 +10,7 @@ Hard requirements:
|
|||||||
- Do not use pattern-matching commands or searches (`grep`, `rg`, `awk`, `sed`, `himalaya envelope list` query filters, etc.).
|
- Do not use pattern-matching commands or searches (`grep`, `rg`, `awk`, `sed`, `himalaya envelope list` query filters, etc.).
|
||||||
- Always inspect current folders first, then triage.
|
- Always inspect current folders first, then triage.
|
||||||
- Treat this as a single deterministic run over a snapshot of message IDs discovered during this run.
|
- Treat this as a single deterministic run over a snapshot of message IDs discovered during this run.
|
||||||
|
- Ingest valuable document attachments into Paperless (see Document Ingestion section below).
|
||||||
|
|
||||||
Workflow:
|
Workflow:
|
||||||
1. Run `himalaya folder list` first and use those folders as the primary taxonomy.
|
1. Run `himalaya folder list` first and use those folders as the primary taxonomy.
|
||||||
@@ -35,8 +36,9 @@ Workflow:
|
|||||||
5. For each single envelope ID, do all checks before any move/delete:
|
5. For each single envelope ID, do all checks before any move/delete:
|
||||||
- Check envelope flags from the JSON listing (seen/answered/flagged) before reading.
|
- Check envelope flags from the JSON listing (seen/answered/flagged) before reading.
|
||||||
- Read the message: `himalaya message read -f "<source>" <id>`.
|
- Read the message: `himalaya message read -f "<source>" <id>`.
|
||||||
- If needed for classification, inspect attachments: `himalaya attachment download -f "<source>" <id> --dir /tmp/himalaya-triage`.
|
- If needed for classification or ingestion, download attachments: `himalaya attachment download -f "<source>" <id> --dir /tmp/himalaya-triage`.
|
||||||
- If attachments are downloaded, inspect them and `rm` the downloaded files from `/tmp/himalaya-triage` after use.
|
- If the message qualifies for document ingestion (see Document Ingestion below), copy eligible attachments to the Paperless consume directory before cleanup.
|
||||||
|
- Always `rm` downloaded files from `/tmp/himalaya-triage` after processing (whether ingested or not).
|
||||||
- Move: `himalaya message move -f "<source>" "<destination>" <id>`.
|
- Move: `himalaya message move -f "<source>" "<destination>" <id>`.
|
||||||
- Delete: `himalaya message delete -f "<source>" <id>`.
|
- Delete: `himalaya message delete -f "<source>" <id>`.
|
||||||
6. Classification precedence (higher rule wins on conflict):
|
6. Classification precedence (higher rule wins on conflict):
|
||||||
@@ -58,6 +60,30 @@ Workflow:
|
|||||||
- Naming constraints: concise topic name, avoid duplicates, and avoid broad catch-all names.
|
- Naming constraints: concise topic name, avoid duplicates, and avoid broad catch-all names.
|
||||||
- Command: `himalaya folder add "<new-folder>"`.
|
- Command: `himalaya folder add "<new-folder>"`.
|
||||||
|
|
||||||
|
Document Ingestion (Paperless):
|
||||||
|
- **Purpose**: Automatically archive valuable document attachments into Paperless via its consumption directory.
|
||||||
|
- **Ingestion path**: `/var/lib/paperless/consume/inbox-triage/`
|
||||||
|
- **When to ingest**: Only for messages whose attachments have long-term archival value. Eligible categories:
|
||||||
|
- Invoices, receipts, and billing statements (messages going to `Orders and Invoices` or `Payments`)
|
||||||
|
- Contracts, agreements, and legal documents
|
||||||
|
- Tax documents, account statements, and financial summaries
|
||||||
|
- Insurance documents and policy papers
|
||||||
|
- Official correspondence with document attachments (government, institutions)
|
||||||
|
- **When NOT to ingest**:
|
||||||
|
- Marketing emails, newsletters, promotional material
|
||||||
|
- Shipping/tracking notifications without invoice attachments
|
||||||
|
- OTP codes, login alerts, password resets, ephemeral notifications
|
||||||
|
- Subscription renewal reminders without actual invoices
|
||||||
|
- Duplicate documents already seen in this run
|
||||||
|
- Inline images, email signatures, logos, and non-document attachments
|
||||||
|
- **Eligible file types**: PDF, PNG, JPG/JPEG, TIFF, WEBP (documents and scans only). Skip archive files (ZIP, etc.), calendar invites (ICS), and other non-document formats.
|
||||||
|
- **Procedure**:
|
||||||
|
1. After downloading attachments to `/tmp/himalaya-triage`, check if any are eligible documents.
|
||||||
|
2. Copy eligible files: `cp /tmp/himalaya-triage/<filename> /var/lib/paperless/consume/inbox-triage/`
|
||||||
|
3. If multiple messages could produce filename collisions, prefix the filename with the message ID: `<id>-<filename>`.
|
||||||
|
4. Log each ingested file in the action log at the end of the run.
|
||||||
|
- **Conservative rule**: When in doubt whether an attachment is worth archiving, skip it. Paperless storage is cheap, but noise degrades searchability. Prefer false negatives over false positives for marketing material, but prefer false positives over false negatives for anything that looks like a financial or legal document.
|
||||||
|
|
||||||
Execution rules:
|
Execution rules:
|
||||||
- Never perform bulk operations. One message ID per `read`, `move`, `delete`, and attachment command.
|
- Never perform bulk operations. One message ID per `read`, `move`, `delete`, and attachment command.
|
||||||
- Always use page size 20 for envelope listing (`-s 20`).
|
- Always use page size 20 for envelope listing (`-s 20`).
|
||||||
@@ -74,6 +100,7 @@ Execution rules:
|
|||||||
- counts by action (untouched/moved-to-folder/deleted),
|
- counts by action (untouched/moved-to-folder/deleted),
|
||||||
- per-destination-folder counts,
|
- per-destination-folder counts,
|
||||||
- created folders,
|
- created folders,
|
||||||
|
- documents ingested to Paperless (count and filenames),
|
||||||
- short rationale for non-obvious classifications.
|
- short rationale for non-obvious classifications.
|
||||||
|
|
||||||
<user-request>
|
<user-request>
|
||||||
|
|||||||
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
description: Dialogue-driven spec development through skeptical questioning
|
|
||||||
---
|
|
||||||
|
|
||||||
Develop implementation-ready specs through iterative dialogue and skeptical questioning.
|
|
||||||
|
|
||||||
First, invoke the skill tool to load the spec-planner skill:
|
|
||||||
|
|
||||||
```
|
|
||||||
skill({ name: 'spec-planner' })
|
|
||||||
```
|
|
||||||
|
|
||||||
Then follow the skill instructions to develop the spec.
|
|
||||||
|
|
||||||
<user-request>
|
|
||||||
$ARGUMENTS
|
|
||||||
</user-request>
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
---
|
|
||||||
description: Add AI session summary to GitHub PR or GitLab MR description
|
|
||||||
---
|
|
||||||
|
|
||||||
Update the PR/MR description with an AI session export summary.
|
|
||||||
|
|
||||||
First, invoke the skill tool to load the session-export skill:
|
|
||||||
|
|
||||||
```
|
|
||||||
skill({ name: 'session-export' })
|
|
||||||
```
|
|
||||||
|
|
||||||
Then follow the skill instructions to export the session summary.
|
|
||||||
|
|
||||||
<user-request>
|
|
||||||
$ARGUMENTS
|
|
||||||
</user-request>
|
|
||||||
150
modules/_opencode/command/supermemory-init.md
Normal file
150
modules/_opencode/command/supermemory-init.md
Normal file
@@ -0,0 +1,150 @@
|
|||||||
|
---
|
||||||
|
description: Initialize Supermemory with comprehensive codebase knowledge
|
||||||
|
---
|
||||||
|
|
||||||
|
# Initializing Supermemory
|
||||||
|
|
||||||
|
You are initializing persistent memory for this codebase. This is not just data collection - you're building context that will make you significantly more effective across all future sessions.
|
||||||
|
|
||||||
|
## Understanding Context
|
||||||
|
|
||||||
|
You are a **stateful** coding agent. Users expect to work with you over extended periods - potentially the entire lifecycle of a project. Your memory is how you get better over time and maintain continuity.
|
||||||
|
|
||||||
|
## What to Remember
|
||||||
|
|
||||||
|
### 1. Procedures (Rules & Workflows)
|
||||||
|
Explicit rules that should always be followed:
|
||||||
|
- "Never commit directly to main - always use feature branches"
|
||||||
|
- "Always run lint before tests"
|
||||||
|
- "Use conventional commits format"
|
||||||
|
|
||||||
|
### 2. Preferences (Style & Conventions)
|
||||||
|
Project and user coding style:
|
||||||
|
- "Prefer functional components over class components"
|
||||||
|
- "Use early returns instead of nested conditionals"
|
||||||
|
- "Always add JSDoc to exported functions"
|
||||||
|
|
||||||
|
### 3. Architecture & Context
|
||||||
|
How the codebase works and why:
|
||||||
|
- "Auth system was refactored in v2.0 - old patterns deprecated"
|
||||||
|
- "The monorepo used to have 3 modules before consolidation"
|
||||||
|
- "This pagination bug was fixed before - similar to PR #234"
|
||||||
|
|
||||||
|
## Memory Scopes
|
||||||
|
|
||||||
|
**Project-scoped** (\`scope: "project"\`):
|
||||||
|
- Build/test/lint commands
|
||||||
|
- Architecture and key directories
|
||||||
|
- Team conventions specific to this codebase
|
||||||
|
- Technology stack and framework choices
|
||||||
|
- Known issues and their solutions
|
||||||
|
|
||||||
|
**User-scoped** (\`scope: "user"\`):
|
||||||
|
- Personal coding preferences across all projects
|
||||||
|
- Communication style preferences
|
||||||
|
- General workflow habits
|
||||||
|
|
||||||
|
## Research Approach
|
||||||
|
|
||||||
|
This is a **deep research** initialization. Take your time and be thorough (~50+ tool calls). The goal is to genuinely understand the project, not just collect surface-level facts.
|
||||||
|
|
||||||
|
**What to uncover:**
|
||||||
|
- Tech stack and dependencies (explicit and implicit)
|
||||||
|
- Project structure and architecture
|
||||||
|
- Build/test/deploy commands and workflows
|
||||||
|
- Contributors & team dynamics (who works on what?)
|
||||||
|
- Commit conventions and branching strategy
|
||||||
|
- Code evolution (major refactors, architecture changes)
|
||||||
|
- Pain points (areas with lots of bug fixes)
|
||||||
|
- Implicit conventions not documented anywhere
|
||||||
|
|
||||||
|
## Research Techniques
|
||||||
|
|
||||||
|
### File-based
|
||||||
|
- README.md, CONTRIBUTING.md, AGENTS.md, CLAUDE.md
|
||||||
|
- Package manifests (package.json, Cargo.toml, pyproject.toml, go.mod)
|
||||||
|
- Config files (.eslintrc, tsconfig.json, .prettierrc)
|
||||||
|
- CI/CD configs (.github/workflows/)
|
||||||
|
|
||||||
|
### Git-based
|
||||||
|
- \`git log --oneline -20\` - Recent history
|
||||||
|
- \`git branch -a\` - Branching strategy
|
||||||
|
- \`git log --format="%s" -50\` - Commit conventions
|
||||||
|
- \`git shortlog -sn --all | head -10\` - Main contributors
|
||||||
|
|
||||||
|
### Explore Agent
|
||||||
|
Fire parallel explore queries for broad understanding:
|
||||||
|
\`\`\`
|
||||||
|
Task(explore, "What is the tech stack and key dependencies?")
|
||||||
|
Task(explore, "What is the project structure? Key directories?")
|
||||||
|
Task(explore, "How do you build, test, and run this project?")
|
||||||
|
Task(explore, "What are the main architectural patterns?")
|
||||||
|
Task(explore, "What conventions or patterns are used?")
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
## How to Do Thorough Research
|
||||||
|
|
||||||
|
**Don't just collect data - analyze and cross-reference.**
|
||||||
|
|
||||||
|
Bad (shallow):
|
||||||
|
- Run commands, copy output
|
||||||
|
- List facts without understanding
|
||||||
|
|
||||||
|
Good (thorough):
|
||||||
|
- Cross-reference findings (if inconsistent, dig deeper)
|
||||||
|
- Resolve ambiguities (don't leave questions unanswered)
|
||||||
|
- Read actual file content, not just names
|
||||||
|
- Look for patterns (what do commits tell you about workflow?)
|
||||||
|
- Think like a new team member - what would you want to know?
|
||||||
|
|
||||||
|
## Saving Memories
|
||||||
|
|
||||||
|
Use the \`supermemory\` tool for each distinct insight:
|
||||||
|
|
||||||
|
\`\`\`
|
||||||
|
supermemory(mode: "add", content: "...", type: "...", scope: "project")
|
||||||
|
\`\`\`
|
||||||
|
|
||||||
|
**Types:**
|
||||||
|
- \`project-config\` - tech stack, commands, tooling
|
||||||
|
- \`architecture\` - codebase structure, key components, data flow
|
||||||
|
- \`learned-pattern\` - conventions specific to this codebase
|
||||||
|
- \`error-solution\` - known issues and their fixes
|
||||||
|
- \`preference\` - coding style preferences (use with user scope)
|
||||||
|
|
||||||
|
**Guidelines:**
|
||||||
|
- Save each distinct insight as a separate memory
|
||||||
|
- Be concise but include enough context to be useful
|
||||||
|
- Include the "why" not just the "what" when relevant
|
||||||
|
- Update memories incrementally as you research (don't wait until the end)
|
||||||
|
|
||||||
|
**Good memories:**
|
||||||
|
- "Uses Bun runtime and package manager. Commands: bun install, bun run dev, bun test"
|
||||||
|
- "API routes in src/routes/, handlers in src/handlers/. Hono framework."
|
||||||
|
- "Auth uses Redis sessions, not JWT. Implementation in src/lib/auth.ts"
|
||||||
|
- "Never use \`any\` type - strict TypeScript. Use \`unknown\` and narrow."
|
||||||
|
- "Database migrations must be backward compatible - we do rolling deploys"
|
||||||
|
|
||||||
|
## Upfront Questions
|
||||||
|
|
||||||
|
Before diving in, ask:
|
||||||
|
1. "Any specific rules I should always follow?"
|
||||||
|
2. "Preferences for how I communicate? (terse/detailed)"
|
||||||
|
|
||||||
|
## Reflection Phase
|
||||||
|
|
||||||
|
Before finishing, reflect:
|
||||||
|
1. **Completeness**: Did you cover commands, architecture, conventions, gotchas?
|
||||||
|
2. **Quality**: Are memories concise and searchable?
|
||||||
|
3. **Scope**: Did you correctly separate project vs user knowledge?
|
||||||
|
|
||||||
|
Then ask: "I've initialized memory with X insights. Want me to continue refining, or is this good?"
|
||||||
|
|
||||||
|
## Your Task
|
||||||
|
|
||||||
|
1. Ask upfront questions (research depth, rules, preferences)
|
||||||
|
2. Check existing memories: \`supermemory(mode: "list", scope: "project")\`
|
||||||
|
3. Research based on chosen depth
|
||||||
|
4. Save memories incrementally as you discover insights
|
||||||
|
5. Reflect and verify completeness
|
||||||
|
6. Summarize what was learned and ask if user wants refinement
|
||||||
@@ -1,13 +1,44 @@
|
|||||||
import type { Plugin } from "@opencode-ai/plugin";
|
import type { Plugin } from "@opencode-ai/plugin";
|
||||||
|
|
||||||
const GIT_PATTERN = /(?:^|[;&|]\s*|&&\s*|\|\|\s*|\$\(\s*|`\s*)git\s/;
|
const COMMAND_PREFIXES = new Set([
|
||||||
|
"env",
|
||||||
|
"command",
|
||||||
|
"builtin",
|
||||||
|
"time",
|
||||||
|
"sudo",
|
||||||
|
"nohup",
|
||||||
|
"nice",
|
||||||
|
]);
|
||||||
|
|
||||||
|
function findCommandWord(words: string[]): string | undefined {
|
||||||
|
for (const word of words) {
|
||||||
|
if (COMMAND_PREFIXES.has(word)) continue;
|
||||||
|
if (/^[A-Za-z_][A-Za-z0-9_]*=/.test(word)) continue;
|
||||||
|
return word;
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
function segmentHasGit(words: string[]): boolean {
|
||||||
|
const cmd = findCommandWord(words);
|
||||||
|
return cmd === "git";
|
||||||
|
}
|
||||||
|
|
||||||
|
function containsBlockedGit(command: string): boolean {
|
||||||
|
const segments = command.split(/\s*(?:&&|\|\||[;&|]|\$\(|`)\s*/);
|
||||||
|
for (const segment of segments) {
|
||||||
|
const words = segment.trim().split(/\s+/).filter(Boolean);
|
||||||
|
if (segmentHasGit(words)) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
export const BlockGitPlugin: Plugin = async () => {
|
export const BlockGitPlugin: Plugin = async () => {
|
||||||
return {
|
return {
|
||||||
"tool.execute.before": async (input, output) => {
|
"tool.execute.before": async (input, output) => {
|
||||||
if (input.tool === "bash") {
|
if (input.tool === "bash") {
|
||||||
const command = output.args.command as string;
|
const command = output.args.command as string;
|
||||||
if (GIT_PATTERN.test(command)) {
|
if (containsBlockedGit(command)) {
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"This project uses jj, only use `jj` commands, not `git`.",
|
"This project uses jj, only use `jj` commands, not `git`.",
|
||||||
);
|
);
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
import type { Plugin } from "@opencode-ai/plugin";
|
import type { Plugin } from "@opencode-ai/plugin";
|
||||||
|
|
||||||
const SCRIPTING_PATTERN =
|
const SCRIPTING_PATTERN =
|
||||||
/(?:^|[;&|]\s*|&&\s*|\|\|\s*|\$\(\s*|`\s*)(?:python[23]?|perl|ruby|php|lua|bash\s+-c|sh\s+-c)\s/;
|
/(?:^|[;&|]\s*|&&\s*|\|\|\s*|\$\(\s*|`\s*)(?:python[23]?|perl|ruby|php|lua|node\s+-e|bash\s+-c|sh\s+-c)\s/;
|
||||||
|
|
||||||
export const BlockScriptingPlugin: Plugin = async () => {
|
export const BlockScriptingPlugin: Plugin = async () => {
|
||||||
return {
|
return {
|
||||||
|
|||||||
@@ -1,16 +0,0 @@
|
|||||||
import type { Plugin } from "@opencode-ai/plugin";
|
|
||||||
|
|
||||||
export const DirenvPlugin: Plugin = async ({ $ }) => {
|
|
||||||
return {
|
|
||||||
"shell.env": async (input, output) => {
|
|
||||||
try {
|
|
||||||
const exported = await $`direnv export json`
|
|
||||||
.cwd(input.cwd)
|
|
||||||
.quiet()
|
|
||||||
.json();
|
|
||||||
|
|
||||||
Object.assign(output.env, exported);
|
|
||||||
} catch {}
|
|
||||||
},
|
|
||||||
};
|
|
||||||
};
|
|
||||||
1434
modules/_opencode/plugin/review.ts
Normal file
1434
modules/_opencode/plugin/review.ts
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1,123 +0,0 @@
|
|||||||
---
|
|
||||||
name: librarian
|
|
||||||
description: Multi-repository codebase exploration. Research library internals, find code patterns, understand architecture, compare implementations across GitHub/npm/PyPI/crates. Use when needing deep understanding of how libraries work, finding implementations across open source, or exploring remote repository structure.
|
|
||||||
references:
|
|
||||||
- references/tool-routing.md
|
|
||||||
- references/opensrc-api.md
|
|
||||||
- references/opensrc-examples.md
|
|
||||||
- references/linking.md
|
|
||||||
- references/diagrams.md
|
|
||||||
---
|
|
||||||
|
|
||||||
# Librarian Skill
|
|
||||||
|
|
||||||
Deep codebase exploration across remote repositories.
|
|
||||||
|
|
||||||
## How to Use This Skill
|
|
||||||
|
|
||||||
### Reference Structure
|
|
||||||
|
|
||||||
| File | Purpose | When to Read |
|
|
||||||
|------|---------|--------------|
|
|
||||||
| `tool-routing.md` | Tool selection decision trees | **Always read first** |
|
|
||||||
| `opensrc-api.md` | API reference, types | Writing opensrc code |
|
|
||||||
| `opensrc-examples.md` | JavaScript patterns, workflows | Implementation examples |
|
|
||||||
| `linking.md` | GitHub URL patterns | Formatting responses |
|
|
||||||
| `diagrams.md` | Mermaid patterns | Visualizing architecture |
|
|
||||||
|
|
||||||
### Reading Order
|
|
||||||
|
|
||||||
1. **Start** with `tool-routing.md` → choose tool strategy
|
|
||||||
2. **If using opensrc:**
|
|
||||||
- Read `opensrc-api.md` for API details
|
|
||||||
- Read `opensrc-examples.md` for patterns
|
|
||||||
3. **Before responding:** `linking.md` + `diagrams.md` for output formatting
|
|
||||||
|
|
||||||
## Tool Arsenal
|
|
||||||
|
|
||||||
| Tool | Best For | Limitations |
|
|
||||||
|------|----------|-------------|
|
|
||||||
| **grep_app** | Find patterns across ALL public GitHub | Literal search only |
|
|
||||||
| **context7** | Library docs, API examples, usage | Known libraries only |
|
|
||||||
| **opensrc** | Fetch full source for deep exploration | Must fetch before read |
|
|
||||||
|
|
||||||
## Quick Decision Trees
|
|
||||||
|
|
||||||
### "How does X work?"
|
|
||||||
|
|
||||||
```
|
|
||||||
Known library?
|
|
||||||
├─ Yes → context7.resolve-library-id → context7.query-docs
|
|
||||||
│ └─ Need internals? → opensrc.fetch → read source
|
|
||||||
└─ No → grep_app search → opensrc.fetch top result
|
|
||||||
```
|
|
||||||
|
|
||||||
### "Find pattern X"
|
|
||||||
|
|
||||||
```
|
|
||||||
Specific repo?
|
|
||||||
├─ Yes → opensrc.fetch → opensrc.grep → read matches
|
|
||||||
└─ No → grep_app (broad) → opensrc.fetch interesting repos
|
|
||||||
```
|
|
||||||
|
|
||||||
### "Explore repo structure"
|
|
||||||
|
|
||||||
```
|
|
||||||
1. opensrc.fetch(target)
|
|
||||||
2. opensrc.tree(source.name) → quick overview
|
|
||||||
3. opensrc.files(source.name, "**/*.ts") → detailed listing
|
|
||||||
4. Read: README, package.json, src/index.*
|
|
||||||
5. Create architecture diagram (see diagrams.md)
|
|
||||||
```
|
|
||||||
|
|
||||||
### "Compare X vs Y"
|
|
||||||
|
|
||||||
```
|
|
||||||
1. opensrc.fetch(["X", "Y"])
|
|
||||||
2. Use source.name from results for subsequent calls
|
|
||||||
3. opensrc.grep(pattern, { sources: [nameX, nameY] })
|
|
||||||
4. Read comparable files, synthesize differences
|
|
||||||
```
|
|
||||||
|
|
||||||
## Critical: Source Naming Convention
|
|
||||||
|
|
||||||
**After fetching, always use `source.name` for subsequent calls:**
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const [{ source }] = await opensrc.fetch("vercel/ai");
|
|
||||||
const files = await opensrc.files(source.name, "**/*.ts");
|
|
||||||
```
|
|
||||||
|
|
||||||
| Type | Fetch Spec | Source Name |
|
|
||||||
|------|------------|-------------|
|
|
||||||
| npm | `"zod"` | `"zod"` |
|
|
||||||
| npm scoped | `"@tanstack/react-query"` | `"@tanstack/react-query"` |
|
|
||||||
| pypi | `"pypi:requests"` | `"requests"` |
|
|
||||||
| crates | `"crates:serde"` | `"serde"` |
|
|
||||||
| GitHub | `"vercel/ai"` | `"github.com/vercel/ai"` |
|
|
||||||
| GitLab | `"gitlab:org/repo"` | `"gitlab.com/org/repo"` |
|
|
||||||
|
|
||||||
## When NOT to Use opensrc
|
|
||||||
|
|
||||||
| Scenario | Use Instead |
|
|
||||||
|----------|-------------|
|
|
||||||
| Simple library API questions | context7 |
|
|
||||||
| Finding examples across many repos | grep_app |
|
|
||||||
| Very large monorepos (>10GB) | Clone locally |
|
|
||||||
| Private repositories | Direct access |
|
|
||||||
|
|
||||||
## Output Guidelines
|
|
||||||
|
|
||||||
1. **Comprehensive final message** - only last message returns to main agent
|
|
||||||
2. **Parallel tool calls** - maximize efficiency
|
|
||||||
3. **Link every file reference** - see `linking.md`
|
|
||||||
4. **Diagram complex relationships** - see `diagrams.md`
|
|
||||||
5. **Never mention tool names** - say "I'll search" not "I'll use opensrc"
|
|
||||||
|
|
||||||
## References
|
|
||||||
|
|
||||||
- [Tool Routing Decision Trees](references/tool-routing.md)
|
|
||||||
- [opensrc API Reference](references/opensrc-api.md)
|
|
||||||
- [opensrc Code Examples](references/opensrc-examples.md)
|
|
||||||
- [GitHub Linking Patterns](references/linking.md)
|
|
||||||
- [Mermaid Diagram Patterns](references/diagrams.md)
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
# Mermaid Diagram Patterns
|
|
||||||
|
|
||||||
Create diagrams for:
|
|
||||||
- Architecture (component relationships)
|
|
||||||
- Data flow (request → response)
|
|
||||||
- Dependencies (import graph)
|
|
||||||
- Sequences (step-by-step processes)
|
|
||||||
|
|
||||||
## Architecture
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
graph TD
|
|
||||||
A[Client] --> B[API Gateway]
|
|
||||||
B --> C[Auth Service]
|
|
||||||
B --> D[Data Service]
|
|
||||||
D --> E[(Database)]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Flow
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
flowchart LR
|
|
||||||
Input --> Parse --> Validate --> Transform --> Output
|
|
||||||
```
|
|
||||||
|
|
||||||
## Sequence
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
sequenceDiagram
|
|
||||||
Client->>+Server: Request
|
|
||||||
Server->>+DB: Query
|
|
||||||
DB-->>-Server: Result
|
|
||||||
Server-->>-Client: Response
|
|
||||||
```
|
|
||||||
|
|
||||||
## When to Use
|
|
||||||
|
|
||||||
| Type | Use For |
|
|
||||||
|------|---------|
|
|
||||||
| `graph TD` | Component hierarchy, dependencies |
|
|
||||||
| `flowchart LR` | Data transformation, pipelines |
|
|
||||||
| `sequenceDiagram` | Request/response, multi-party interaction |
|
|
||||||
| `classDiagram` | Type relationships, inheritance |
|
|
||||||
| `stateDiagram` | State machines, lifecycle |
|
|
||||||
|
|
||||||
## Tips
|
|
||||||
|
|
||||||
- Keep nodes short (3-4 words max)
|
|
||||||
- Use subgraphs for grouping related components
|
|
||||||
- Arrow labels for relationship types
|
|
||||||
- Prefer LR (left-right) for flows, TD (top-down) for hierarchies
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
# GitHub Linking Patterns
|
|
||||||
|
|
||||||
All file/dir/code refs → fluent markdown links. Never raw URLs.
|
|
||||||
|
|
||||||
## URL Formats
|
|
||||||
|
|
||||||
### File
|
|
||||||
```
|
|
||||||
https://github.com/{owner}/{repo}/blob/{ref}/{path}
|
|
||||||
```
|
|
||||||
|
|
||||||
### File + Lines
|
|
||||||
```
|
|
||||||
https://github.com/{owner}/{repo}/blob/{ref}/{path}#L{start}-L{end}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Directory
|
|
||||||
```
|
|
||||||
https://github.com/{owner}/{repo}/tree/{ref}/{path}
|
|
||||||
```
|
|
||||||
|
|
||||||
### GitLab (note `/-/blob/`)
|
|
||||||
```
|
|
||||||
https://gitlab.com/{owner}/{repo}/-/blob/{ref}/{path}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Ref Resolution
|
|
||||||
|
|
||||||
| Source | Use as ref |
|
|
||||||
|--------|------------|
|
|
||||||
| Known version | `v{version}` |
|
|
||||||
| Default branch | `main` or `master` |
|
|
||||||
| opensrc fetch | ref from result |
|
|
||||||
| Specific commit | full SHA |
|
|
||||||
|
|
||||||
## Examples
|
|
||||||
|
|
||||||
### Correct
|
|
||||||
```markdown
|
|
||||||
The [`parseAsync`](https://github.com/colinhacks/zod/blob/main/src/types.ts#L450-L480) method handles...
|
|
||||||
```
|
|
||||||
|
|
||||||
### Wrong
|
|
||||||
```markdown
|
|
||||||
See https://github.com/colinhacks/zod/blob/main/src/types.ts#L100
|
|
||||||
The parseAsync method in src/types.ts handles...
|
|
||||||
```
|
|
||||||
|
|
||||||
## Line Numbers
|
|
||||||
|
|
||||||
- Single: `#L42`
|
|
||||||
- Range: `#L42-L50`
|
|
||||||
- Prefer ranges for context (2-5 lines around key code)
|
|
||||||
|
|
||||||
## Registry → GitHub
|
|
||||||
|
|
||||||
| Registry | Find repo in |
|
|
||||||
|----------|--------------|
|
|
||||||
| npm | `package.json` → `repository` |
|
|
||||||
| PyPI | `pyproject.toml` or setup.py |
|
|
||||||
| crates | `Cargo.toml` |
|
|
||||||
@@ -1,235 +0,0 @@
|
|||||||
# opensrc API Reference
|
|
||||||
|
|
||||||
## Tool
|
|
||||||
|
|
||||||
Use the **opensrc MCP server** via single tool:
|
|
||||||
|
|
||||||
| Tool | Purpose |
|
|
||||||
|------|---------|
|
|
||||||
| `opensrc_execute` | All operations (fetch, read, grep, files, remove, etc.) |
|
|
||||||
|
|
||||||
Takes a `code` parameter: JavaScript async arrow function executed server-side. Source trees stay on server, only results return.
|
|
||||||
|
|
||||||
## API Surface
|
|
||||||
|
|
||||||
### Read Operations
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// List all fetched sources
|
|
||||||
opensrc.list(): Source[]
|
|
||||||
|
|
||||||
// Check if source exists
|
|
||||||
opensrc.has(name: string, version?: string): boolean
|
|
||||||
|
|
||||||
// Get source metadata
|
|
||||||
opensrc.get(name: string): Source | undefined
|
|
||||||
|
|
||||||
// List files with optional glob
|
|
||||||
opensrc.files(sourceName: string, glob?: string): Promise<FileEntry[]>
|
|
||||||
|
|
||||||
// Get directory tree structure (default depth: 3)
|
|
||||||
opensrc.tree(sourceName: string, options?: { depth?: number }): Promise<TreeNode>
|
|
||||||
|
|
||||||
// Regex search file contents
|
|
||||||
opensrc.grep(pattern: string, options?: GrepOptions): Promise<GrepResult[]>
|
|
||||||
|
|
||||||
// AST-based semantic code search
|
|
||||||
opensrc.astGrep(sourceName: string, pattern: string, options?: AstGrepOptions): Promise<AstGrepMatch[]>
|
|
||||||
|
|
||||||
// Read single file
|
|
||||||
opensrc.read(sourceName: string, filePath: string): Promise<string>
|
|
||||||
|
|
||||||
// Batch read multiple files (supports globs!)
|
|
||||||
opensrc.readMany(sourceName: string, paths: string[]): Promise<Record<string, string>>
|
|
||||||
|
|
||||||
// Parse fetch spec
|
|
||||||
opensrc.resolve(spec: string): Promise<ParsedSpec>
|
|
||||||
```
|
|
||||||
|
|
||||||
### Mutation Operations
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Fetch packages/repos
|
|
||||||
opensrc.fetch(specs: string | string[], options?: { modify?: boolean }): Promise<FetchedSource[]>
|
|
||||||
|
|
||||||
// Remove sources
|
|
||||||
opensrc.remove(names: string[]): Promise<RemoveResult>
|
|
||||||
|
|
||||||
// Clean by type
|
|
||||||
opensrc.clean(options?: CleanOptions): Promise<RemoveResult>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Types
|
|
||||||
|
|
||||||
### Source
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface Source {
|
|
||||||
type: "npm" | "pypi" | "crates" | "repo";
|
|
||||||
name: string; // Use this for all subsequent calls
|
|
||||||
version?: string;
|
|
||||||
ref?: string;
|
|
||||||
path: string;
|
|
||||||
fetchedAt: string;
|
|
||||||
repository: string;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### FetchedSource
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface FetchedSource {
|
|
||||||
source: Source; // IMPORTANT: use source.name for subsequent calls
|
|
||||||
alreadyExists: boolean;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### GrepOptions
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface GrepOptions {
|
|
||||||
sources?: string[]; // Filter to specific sources
|
|
||||||
include?: string; // File glob pattern (e.g., "*.ts")
|
|
||||||
maxResults?: number; // Limit results (default: 100)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### GrepResult
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface GrepResult {
|
|
||||||
source: string;
|
|
||||||
file: string;
|
|
||||||
line: number;
|
|
||||||
content: string;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### AstGrepOptions
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface AstGrepOptions {
|
|
||||||
glob?: string; // File glob pattern (e.g., "**/*.ts")
|
|
||||||
lang?: string | string[]; // Language(s): "js", "ts", "tsx", "html", "css"
|
|
||||||
limit?: number; // Max results (default: 1000)
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### AstGrepMatch
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface AstGrepMatch {
|
|
||||||
file: string;
|
|
||||||
line: number;
|
|
||||||
column: number;
|
|
||||||
endLine: number;
|
|
||||||
endColumn: number;
|
|
||||||
text: string; // Matched code text
|
|
||||||
metavars: Record<string, string>; // Captured $VAR → text
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
#### AST Pattern Syntax
|
|
||||||
|
|
||||||
| Pattern | Matches |
|
|
||||||
|---------|---------|
|
|
||||||
| `$NAME` | Single node, captures to metavars |
|
|
||||||
| `$$$ARGS` | Zero or more nodes (variadic), captures |
|
|
||||||
| `$_` | Single node, no capture |
|
|
||||||
| `$$$` | Zero or more nodes, no capture |
|
|
||||||
|
|
||||||
### FileEntry
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface FileEntry {
|
|
||||||
path: string;
|
|
||||||
size: number;
|
|
||||||
isDirectory: boolean;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### TreeNode
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface TreeNode {
|
|
||||||
name: string;
|
|
||||||
type: "file" | "dir";
|
|
||||||
children?: TreeNode[]; // only for dirs
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### CleanOptions
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface CleanOptions {
|
|
||||||
packages?: boolean;
|
|
||||||
repos?: boolean;
|
|
||||||
npm?: boolean;
|
|
||||||
pypi?: boolean;
|
|
||||||
crates?: boolean;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### RemoveResult
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
interface RemoveResult {
|
|
||||||
success: boolean;
|
|
||||||
removed: string[];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Error Handling
|
|
||||||
|
|
||||||
Operations throw on errors. Wrap in try/catch if needed:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
try {
|
|
||||||
const content = await opensrc.read("zod", "missing.ts");
|
|
||||||
return content;
|
|
||||||
} catch (e) {
|
|
||||||
return { error: e.message };
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
`readMany` returns errors as string values prefixed with `[Error:`:
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
const files = await opensrc.readMany("zod", ["exists.ts", "missing.ts"]);
|
|
||||||
// { "exists.ts": "content...", "missing.ts": "[Error: ENOENT...]" }
|
|
||||||
|
|
||||||
// Filter successful reads
|
|
||||||
const successful = Object.entries(files)
|
|
||||||
.filter(([_, content]) => !content.startsWith("[Error:"));
|
|
||||||
```
|
|
||||||
|
|
||||||
## Package Spec Formats
|
|
||||||
|
|
||||||
| Format | Example | Source Name After Fetch |
|
|
||||||
|--------|---------|------------------------|
|
|
||||||
| `<name>` | `"zod"` | `"zod"` |
|
|
||||||
| `<name>@<version>` | `"zod@3.22.0"` | `"zod"` |
|
|
||||||
| `pypi:<name>` | `"pypi:requests"` | `"requests"` |
|
|
||||||
| `crates:<name>` | `"crates:serde"` | `"serde"` |
|
|
||||||
| `owner/repo` | `"vercel/ai"` | `"github.com/vercel/ai"` |
|
|
||||||
| `owner/repo@ref` | `"vercel/ai@v1.0.0"` | `"github.com/vercel/ai"` |
|
|
||||||
| `gitlab:owner/repo` | `"gitlab:org/repo"` | `"gitlab.com/org/repo"` |
|
|
||||||
|
|
||||||
## Critical Pattern
|
|
||||||
|
|
||||||
**Always capture `source.name` from fetch results:**
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const [{ source }] = await opensrc.fetch("vercel/ai");
|
|
||||||
|
|
||||||
// GitHub repos: "vercel/ai" → "github.com/vercel/ai"
|
|
||||||
const sourceName = source.name;
|
|
||||||
|
|
||||||
// Use sourceName for ALL subsequent calls
|
|
||||||
const files = await opensrc.files(sourceName, "src/**/*.ts");
|
|
||||||
return files;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
@@ -1,336 +0,0 @@
|
|||||||
# opensrc Code Examples
|
|
||||||
|
|
||||||
## Workflow: Fetch → Explore
|
|
||||||
|
|
||||||
### Basic Fetch and Explore with tree()
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const [{ source }] = await opensrc.fetch("vercel/ai");
|
|
||||||
// Get directory structure first
|
|
||||||
const tree = await opensrc.tree(source.name, { depth: 2 });
|
|
||||||
return tree;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Fetch and Read Key Files
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const [{ source }] = await opensrc.fetch("vercel/ai");
|
|
||||||
const sourceName = source.name; // "github.com/vercel/ai"
|
|
||||||
|
|
||||||
const files = await opensrc.readMany(sourceName, [
|
|
||||||
"package.json",
|
|
||||||
"README.md",
|
|
||||||
"src/index.ts"
|
|
||||||
]);
|
|
||||||
|
|
||||||
return { sourceName, files };
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### readMany with Globs
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const [{ source }] = await opensrc.fetch("zod");
|
|
||||||
// Read all package.json files in monorepo
|
|
||||||
const files = await opensrc.readMany(source.name, [
|
|
||||||
"packages/*/package.json" // globs supported!
|
|
||||||
]);
|
|
||||||
return Object.keys(files);
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Batch Fetch Multiple Packages
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const results = await opensrc.fetch(["zod", "valibot", "yup"]);
|
|
||||||
const names = results.map(r => r.source.name);
|
|
||||||
|
|
||||||
// Compare how each handles string validation
|
|
||||||
const comparisons = {};
|
|
||||||
for (const name of names) {
|
|
||||||
const matches = await opensrc.grep("string.*validate|validateString", {
|
|
||||||
sources: [name],
|
|
||||||
include: "*.ts",
|
|
||||||
maxResults: 10
|
|
||||||
});
|
|
||||||
comparisons[name] = matches.map(m => `${m.file}:${m.line}`);
|
|
||||||
}
|
|
||||||
return comparisons;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Search Patterns
|
|
||||||
|
|
||||||
### Grep → Read Context
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const matches = await opensrc.grep("export function parse\\(", {
|
|
||||||
sources: ["zod"],
|
|
||||||
include: "*.ts"
|
|
||||||
});
|
|
||||||
|
|
||||||
if (matches.length === 0) return "No matches";
|
|
||||||
|
|
||||||
const match = matches[0];
|
|
||||||
const content = await opensrc.read(match.source, match.file);
|
|
||||||
const lines = content.split("\n");
|
|
||||||
|
|
||||||
// Return 40 lines starting from match
|
|
||||||
return {
|
|
||||||
file: match.file,
|
|
||||||
code: lines.slice(match.line - 1, match.line + 39).join("\n")
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Search Across All Fetched Sources
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const sources = opensrc.list();
|
|
||||||
const results = {};
|
|
||||||
|
|
||||||
for (const source of sources) {
|
|
||||||
const errorHandling = await opensrc.grep("throw new|catch \\(|\\.catch\\(", {
|
|
||||||
sources: [source.name],
|
|
||||||
include: "*.ts",
|
|
||||||
maxResults: 20
|
|
||||||
});
|
|
||||||
results[source.name] = {
|
|
||||||
type: source.type,
|
|
||||||
errorPatterns: errorHandling.length
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## AST-Based Search
|
|
||||||
|
|
||||||
Use `astGrep` for semantic code search with pattern matching.
|
|
||||||
|
|
||||||
### Find Function Declarations
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const [{ source }] = await opensrc.fetch("lodash");
|
|
||||||
|
|
||||||
const fns = await opensrc.astGrep(source.name, "function $NAME($$$ARGS) { $$$BODY }", {
|
|
||||||
lang: "js",
|
|
||||||
limit: 20
|
|
||||||
});
|
|
||||||
|
|
||||||
return fns.map(m => ({
|
|
||||||
file: m.file,
|
|
||||||
line: m.line,
|
|
||||||
name: m.metavars.NAME
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Find React Hooks Usage
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const [{ source }] = await opensrc.fetch("vercel/ai");
|
|
||||||
|
|
||||||
const stateHooks = await opensrc.astGrep(
|
|
||||||
source.name,
|
|
||||||
"const [$STATE, $SETTER] = useState($$$INIT)",
|
|
||||||
{ lang: ["ts", "tsx"], limit: 50 }
|
|
||||||
);
|
|
||||||
|
|
||||||
return stateHooks.map(m => ({
|
|
||||||
file: m.file,
|
|
||||||
state: m.metavars.STATE,
|
|
||||||
setter: m.metavars.SETTER
|
|
||||||
}));
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Find Class Definitions with Context
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const [{ source }] = await opensrc.fetch("zod");
|
|
||||||
|
|
||||||
const classes = await opensrc.astGrep(source.name, "class $NAME", {
|
|
||||||
glob: "**/*.ts"
|
|
||||||
});
|
|
||||||
|
|
||||||
const details = [];
|
|
||||||
for (const cls of classes.slice(0, 5)) {
|
|
||||||
const content = await opensrc.read(source.name, cls.file);
|
|
||||||
const lines = content.split("\n");
|
|
||||||
details.push({
|
|
||||||
name: cls.metavars.NAME,
|
|
||||||
file: cls.file,
|
|
||||||
preview: lines.slice(cls.line - 1, cls.line + 9).join("\n")
|
|
||||||
});
|
|
||||||
}
|
|
||||||
return details;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Compare Export Patterns Across Libraries
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const results = await opensrc.fetch(["zod", "valibot"]);
|
|
||||||
const names = results.map(r => r.source.name);
|
|
||||||
|
|
||||||
const exports = {};
|
|
||||||
for (const name of names) {
|
|
||||||
const matches = await opensrc.astGrep(name, "export const $NAME = $_", {
|
|
||||||
lang: "ts",
|
|
||||||
limit: 30
|
|
||||||
});
|
|
||||||
exports[name] = matches.map(m => m.metavars.NAME);
|
|
||||||
}
|
|
||||||
return exports;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### grep vs astGrep
|
|
||||||
|
|
||||||
| Use Case | Tool |
|
|
||||||
|----------|------|
|
|
||||||
| Text/regex pattern | `grep` |
|
|
||||||
| Function declarations | `astGrep`: `function $NAME($$$) { $$$ }` |
|
|
||||||
| Arrow functions | `astGrep`: `const $N = ($$$) => $_` |
|
|
||||||
| Class definitions | `astGrep`: `class $NAME extends $PARENT` |
|
|
||||||
| Import statements | `astGrep`: `import { $$$IMPORTS } from "$MOD"` |
|
|
||||||
| JSX components | `astGrep`: `<$COMP $$$PROPS />` |
|
|
||||||
|
|
||||||
## Repository Exploration
|
|
||||||
|
|
||||||
### Find Entry Points
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const name = "github.com/vercel/ai";
|
|
||||||
|
|
||||||
const allFiles = await opensrc.files(name, "**/*.{ts,js}");
|
|
||||||
const entryPoints = allFiles.filter(f =>
|
|
||||||
f.path.match(/^(src\/)?(index|main|mod)\.(ts|js)$/) ||
|
|
||||||
f.path.includes("/index.ts")
|
|
||||||
);
|
|
||||||
|
|
||||||
// Read all entry points
|
|
||||||
const contents = {};
|
|
||||||
for (const ep of entryPoints.slice(0, 5)) {
|
|
||||||
contents[ep.path] = await opensrc.read(name, ep.path);
|
|
||||||
}
|
|
||||||
|
|
||||||
return {
|
|
||||||
totalFiles: allFiles.length,
|
|
||||||
entryPoints: entryPoints.map(f => f.path),
|
|
||||||
contents
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Explore Package Structure
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const name = "zod";
|
|
||||||
|
|
||||||
// Get all TypeScript files
|
|
||||||
const tsFiles = await opensrc.files(name, "**/*.ts");
|
|
||||||
|
|
||||||
// Group by directory
|
|
||||||
const byDir = {};
|
|
||||||
for (const f of tsFiles) {
|
|
||||||
const dir = f.path.split("/").slice(0, -1).join("/") || ".";
|
|
||||||
byDir[dir] = (byDir[dir] || 0) + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Read key files
|
|
||||||
const pkg = await opensrc.read(name, "package.json");
|
|
||||||
const readme = await opensrc.read(name, "README.md");
|
|
||||||
|
|
||||||
return {
|
|
||||||
structure: byDir,
|
|
||||||
package: JSON.parse(pkg),
|
|
||||||
readmePreview: readme.slice(0, 500)
|
|
||||||
};
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Batch Operations
|
|
||||||
|
|
||||||
### Read Many with Error Handling
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const files = await opensrc.readMany("zod", [
|
|
||||||
"src/index.ts",
|
|
||||||
"src/types.ts",
|
|
||||||
"src/ZodError.ts",
|
|
||||||
"src/helpers/parseUtil.ts"
|
|
||||||
]);
|
|
||||||
|
|
||||||
// files is Record<string, string> - errors start with "[Error:"
|
|
||||||
const successful = Object.entries(files)
|
|
||||||
.filter(([_, content]) => !content.startsWith("[Error:"))
|
|
||||||
.map(([path, content]) => ({ path, lines: content.split("\n").length }));
|
|
||||||
|
|
||||||
return successful;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Parallel Grep Across Multiple Sources
|
|
||||||
|
|
||||||
```javascript
|
|
||||||
async () => {
|
|
||||||
const targets = ["zod", "valibot"];
|
|
||||||
const pattern = "export (type|interface)";
|
|
||||||
|
|
||||||
const results = await Promise.all(
|
|
||||||
targets.map(async (name) => {
|
|
||||||
const matches = await opensrc.grep(pattern, {
|
|
||||||
sources: [name],
|
|
||||||
include: "*.ts",
|
|
||||||
maxResults: 50
|
|
||||||
});
|
|
||||||
return { name, count: matches.length, matches };
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Workflow Checklist
|
|
||||||
|
|
||||||
### Comprehensive Repository Analysis
|
|
||||||
|
|
||||||
```
|
|
||||||
Repository Analysis Progress:
|
|
||||||
- [ ] 1. Fetch repository
|
|
||||||
- [ ] 2. Read package.json + README
|
|
||||||
- [ ] 3. Identify entry points (src/index.*)
|
|
||||||
- [ ] 4. Read main entry file
|
|
||||||
- [ ] 5. Map exports and public API
|
|
||||||
- [ ] 6. Trace key functionality
|
|
||||||
- [ ] 7. Create architecture diagram
|
|
||||||
```
|
|
||||||
|
|
||||||
### Library Comparison
|
|
||||||
|
|
||||||
```
|
|
||||||
Comparison Progress:
|
|
||||||
- [ ] 1. Fetch all libraries
|
|
||||||
- [ ] 2. Grep for target pattern in each
|
|
||||||
- [ ] 3. Read matching implementations
|
|
||||||
- [ ] 4. Create comparison table
|
|
||||||
- [ ] 5. Synthesize findings
|
|
||||||
```
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
# Tool Routing
|
|
||||||
|
|
||||||
## Decision Flowchart
|
|
||||||
|
|
||||||
```mermaid
|
|
||||||
graph TD
|
|
||||||
Q[User Query] --> T{Query Type?}
|
|
||||||
T -->|Understand/Explain| U[UNDERSTAND]
|
|
||||||
T -->|Find/Search| F[FIND]
|
|
||||||
T -->|Explore/Architecture| E[EXPLORE]
|
|
||||||
T -->|Compare| C[COMPARE]
|
|
||||||
|
|
||||||
U --> U1{Known library?}
|
|
||||||
U1 -->|Yes| U2[context7.resolve-library-id]
|
|
||||||
U2 --> U3[context7.query-docs]
|
|
||||||
U3 --> U4{Need source?}
|
|
||||||
U4 -->|Yes| U5[opensrc.fetch → read]
|
|
||||||
U1 -->|No| U6[grep_app → opensrc.fetch]
|
|
||||||
|
|
||||||
F --> F1{Specific repo?}
|
|
||||||
F1 -->|Yes| F2[opensrc.fetch → grep → read]
|
|
||||||
F1 -->|No| F3[grep_app broad search]
|
|
||||||
F3 --> F4[opensrc.fetch interesting repos]
|
|
||||||
|
|
||||||
E --> E1[opensrc.fetch]
|
|
||||||
E1 --> E2[opensrc.files]
|
|
||||||
E2 --> E3[Read entry points]
|
|
||||||
E3 --> E4[Create diagram]
|
|
||||||
|
|
||||||
C --> C1["opensrc.fetch([X, Y])"]
|
|
||||||
C1 --> C2[grep same pattern]
|
|
||||||
C2 --> C3[Read comparable files]
|
|
||||||
C3 --> C4[Synthesize comparison]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Query Type Detection
|
|
||||||
|
|
||||||
| Keywords | Query Type | Start With |
|
|
||||||
|----------|------------|------------|
|
|
||||||
| "how does", "why does", "explain", "purpose of" | UNDERSTAND | context7 |
|
|
||||||
| "find", "where is", "implementations of", "examples of" | FIND | grep_app |
|
|
||||||
| "explore", "walk through", "architecture", "structure" | EXPLORE | opensrc |
|
|
||||||
| "compare", "vs", "difference between" | COMPARE | opensrc |
|
|
||||||
|
|
||||||
## UNDERSTAND Queries
|
|
||||||
|
|
||||||
```
|
|
||||||
Known library? → context7.resolve-library-id → context7.query-docs
|
|
||||||
└─ Need source? → opensrc.fetch → read
|
|
||||||
|
|
||||||
Unknown? → grep_app search → opensrc.fetch top result → read
|
|
||||||
```
|
|
||||||
|
|
||||||
**When to transition context7 → opensrc:**
|
|
||||||
- Need implementation details (not just API docs)
|
|
||||||
- Question about internals/private methods
|
|
||||||
- Tracing code flow through library
|
|
||||||
|
|
||||||
## FIND Queries
|
|
||||||
|
|
||||||
```
|
|
||||||
Specific repo? → opensrc.fetch → opensrc.grep → read matches
|
|
||||||
|
|
||||||
Broad search? → grep_app → analyze → opensrc.fetch interesting repos
|
|
||||||
```
|
|
||||||
|
|
||||||
**grep_app query tips:**
|
|
||||||
- Use literal code patterns: `useState(` not "react hooks"
|
|
||||||
- Filter by language: `language: ["TypeScript"]`
|
|
||||||
- Narrow by repo: `repo: "vercel/"` for org
|
|
||||||
|
|
||||||
## EXPLORE Queries
|
|
||||||
|
|
||||||
```
|
|
||||||
1. opensrc.fetch(target)
|
|
||||||
2. opensrc.files → understand structure
|
|
||||||
3. Identify entry points: README, package.json, src/index.*
|
|
||||||
4. Read entry → internals
|
|
||||||
5. Create architecture diagram
|
|
||||||
```
|
|
||||||
|
|
||||||
## COMPARE Queries
|
|
||||||
|
|
||||||
```
|
|
||||||
1. opensrc.fetch([X, Y])
|
|
||||||
2. Extract source.name from each result
|
|
||||||
3. opensrc.grep same pattern in both
|
|
||||||
4. Read comparable files
|
|
||||||
5. Synthesize → comparison table
|
|
||||||
```
|
|
||||||
|
|
||||||
## Tool Capabilities
|
|
||||||
|
|
||||||
| Tool | Best For | Not For |
|
|
||||||
|------|----------|---------|
|
|
||||||
| **grep_app** | Broad search, unknown scope, finding repos | Semantic queries |
|
|
||||||
| **context7** | Library APIs, best practices, common patterns | Library internals |
|
|
||||||
| **opensrc** | Deep exploration, reading internals, tracing flow | Initial discovery |
|
|
||||||
|
|
||||||
## Anti-patterns
|
|
||||||
|
|
||||||
| Don't | Do |
|
|
||||||
|-------|-----|
|
|
||||||
| grep_app for known library docs | context7 first |
|
|
||||||
| opensrc.fetch before knowing target | grep_app to discover |
|
|
||||||
| Multiple small reads | opensrc.readMany batch |
|
|
||||||
| Describe without linking | Link every file ref |
|
|
||||||
| Text for complex relationships | Mermaid diagram |
|
|
||||||
| Use tool names in responses | "I'll search..." not "I'll use opensrc" |
|
|
||||||
@@ -1,122 +0,0 @@
|
|||||||
---
|
|
||||||
name: session-export
|
|
||||||
description: Update GitHub PR descriptions with AI session export summaries. Use when user asks to add session summary to PR/MR, document AI assistance in PR/MR, or export conversation summary to PR/MR description.
|
|
||||||
---
|
|
||||||
|
|
||||||
# Session Export
|
|
||||||
|
|
||||||
Update PR/MR descriptions with a structured summary of the AI-assisted conversation.
|
|
||||||
|
|
||||||
## Output Format
|
|
||||||
|
|
||||||
```markdown
|
|
||||||
> [!NOTE]
|
|
||||||
> This PR was written with AI assistance.
|
|
||||||
|
|
||||||
<details><summary>AI Session Export</summary>
|
|
||||||
<p>
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"info": {
|
|
||||||
"title": "<brief task description>",
|
|
||||||
"agent": "opencode",
|
|
||||||
"models": ["<model(s) used>"]
|
|
||||||
},
|
|
||||||
"summary": [
|
|
||||||
"<action 1>",
|
|
||||||
"<action 2>",
|
|
||||||
...
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
</p>
|
|
||||||
</details>
|
|
||||||
```
|
|
||||||
|
|
||||||
## Workflow
|
|
||||||
|
|
||||||
### 1. Export Session Data
|
|
||||||
|
|
||||||
Get session data using OpenCode CLI:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
opencode export [sessionID]
|
|
||||||
```
|
|
||||||
|
|
||||||
Returns JSON with session info including models used. Use current session if no sessionID provided.
|
|
||||||
|
|
||||||
### 2. Generate Summary JSON
|
|
||||||
|
|
||||||
From exported data and conversation context, create summary:
|
|
||||||
|
|
||||||
- **title**: 2-5 word task description (lowercase)
|
|
||||||
- **agent**: always "opencode"
|
|
||||||
- **models**: array from export data
|
|
||||||
- **summary**: array of terse action statements
|
|
||||||
- Use past tense ("added", "fixed", "created")
|
|
||||||
- Start with "user requested..." or "user asked..."
|
|
||||||
- Chronological order
|
|
||||||
- Attempt to keep the summary to a max of 25 turns ("user requested", "agent did")
|
|
||||||
- **NEVER include sensitive data**: API keys, credentials, secrets, tokens, passwords, env vars
|
|
||||||
|
|
||||||
### 3. Update PR/MR Description
|
|
||||||
|
|
||||||
**GitHub:**
|
|
||||||
```bash
|
|
||||||
gh pr edit <PR_NUMBER> --body "$(cat <<'EOF'
|
|
||||||
<existing description>
|
|
||||||
|
|
||||||
> [!NOTE]
|
|
||||||
> This PR was written with AI assistance.
|
|
||||||
|
|
||||||
<details><summary>AI Session Export</summary>
|
|
||||||
...
|
|
||||||
</details>
|
|
||||||
EOF
|
|
||||||
)"
|
|
||||||
```
|
|
||||||
|
|
||||||
### 4. Preserve Existing Content
|
|
||||||
|
|
||||||
Always fetch and preserve existing PR/MR description:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# GitHub
|
|
||||||
gh pr view <PR_NUMBER> --json body -q '.body'
|
|
||||||
|
|
||||||
Append session export after existing content with blank line separator.
|
|
||||||
|
|
||||||
## Example Summary
|
|
||||||
|
|
||||||
For a session where user asked to add dark mode:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"info": {
|
|
||||||
"title": "dark mode implementation",
|
|
||||||
"agent": "opencode",
|
|
||||||
"models": ["claude sonnet 4"]
|
|
||||||
},
|
|
||||||
"summary": [
|
|
||||||
"user requested dark mode toggle in settings",
|
|
||||||
"agent explored existing theme system",
|
|
||||||
"agent created ThemeContext for state management",
|
|
||||||
"agent added DarkModeToggle component",
|
|
||||||
"agent updated CSS variables for dark theme",
|
|
||||||
"agent ran tests and fixed 2 failures",
|
|
||||||
"agent committed changes"
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
## Security
|
|
||||||
|
|
||||||
**NEVER include in summary:**
|
|
||||||
- API keys, tokens, secrets
|
|
||||||
- Passwords, credentials
|
|
||||||
- Environment variable values
|
|
||||||
- Private URLs with auth tokens
|
|
||||||
- Personal identifiable information
|
|
||||||
- Internal hostnames/IPs
|
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
---
|
|
||||||
name: vcs-detect
|
|
||||||
description: Detect whether the current project uses jj (Jujutsu) or git for version control. Run this BEFORE any VCS command to use the correct tool.
|
|
||||||
---
|
|
||||||
|
|
||||||
# VCS Detection Skill
|
|
||||||
|
|
||||||
Detect the version control system in use before running any VCS commands.
|
|
||||||
|
|
||||||
## Why This Matters
|
|
||||||
|
|
||||||
- jj (Jujutsu) and git have different CLIs and workflows
|
|
||||||
- Running `git` commands in a jj repo (or vice versa) causes errors
|
|
||||||
- Some repos use jj with git colocated (both `.jj/` and `.git/` exist)
|
|
||||||
|
|
||||||
## Detection Logic
|
|
||||||
|
|
||||||
Both `jj root` and `git rev-parse --show-toplevel` walk up the filesystem to find repo root.
|
|
||||||
|
|
||||||
**Priority order:**
|
|
||||||
|
|
||||||
1. `jj root` succeeds → jj (handles colocated too)
|
|
||||||
2. `git rev-parse` succeeds → git
|
|
||||||
3. Both fail → no VCS
|
|
||||||
|
|
||||||
## Detection Command
|
|
||||||
|
|
||||||
```bash
|
|
||||||
if jj root &>/dev/null; then echo "jj"
|
|
||||||
elif git rev-parse --show-toplevel &>/dev/null; then echo "git"
|
|
||||||
else echo "none"
|
|
||||||
fi
|
|
||||||
```
|
|
||||||
|
|
||||||
## Command Mappings
|
|
||||||
|
|
||||||
| Operation | git | jj |
|
|
||||||
|-----------|-----|-----|
|
|
||||||
| Status | `git status` | `jj status` |
|
|
||||||
| Log | `git log` | `jj log` |
|
|
||||||
| Diff | `git diff` | `jj diff` |
|
|
||||||
| Commit | `git commit` | `jj commit` / `jj describe` |
|
|
||||||
| Branch list | `git branch` | `jj branch list` |
|
|
||||||
| New branch | `git checkout -b <name>` | `jj branch create <name>` |
|
|
||||||
| Push | `git push` | `jj git push` |
|
|
||||||
| Pull/Fetch | `git pull` / `git fetch` | `jj git fetch` |
|
|
||||||
| Rebase | `git rebase` | `jj rebase` |
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
Before any VCS operation:
|
|
||||||
|
|
||||||
1. Run detection command
|
|
||||||
2. Use appropriate CLI based on result
|
|
||||||
3. If `none`, warn user directory is not version controlled
|
|
||||||
|
|
||||||
## Example Integration
|
|
||||||
|
|
||||||
```
|
|
||||||
User: Show me the git log
|
|
||||||
Agent: [Runs detection] -> Result: jj
|
|
||||||
Agent: [Runs `jj log` instead of `git log`]
|
|
||||||
```
|
|
||||||
|
|
||||||
## Colocated Repos
|
|
||||||
|
|
||||||
When both `.jj/` and `.git/` exist, the repo is "colocated":
|
|
||||||
- jj manages the working copy
|
|
||||||
- git is available for compatibility (GitHub, etc.)
|
|
||||||
- **Always prefer jj commands** in colocated repos
|
|
||||||
6
modules/_overlays/ast-grep.nix
Normal file
6
modules/_overlays/ast-grep.nix
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{inputs, ...}: final: prev: {
|
||||||
|
ast-grep =
|
||||||
|
prev.ast-grep.overrideAttrs (old: {
|
||||||
|
doCheck = false;
|
||||||
|
});
|
||||||
|
}
|
||||||
44
modules/_overlays/cog-cli.nix
Normal file
44
modules/_overlays/cog-cli.nix
Normal file
@@ -0,0 +1,44 @@
|
|||||||
|
{inputs, ...}: final: prev: let
|
||||||
|
version = "0.24.1";
|
||||||
|
srcs = {
|
||||||
|
x86_64-linux =
|
||||||
|
prev.fetchurl {
|
||||||
|
url = "https://github.com/trycog/cog-cli/releases/download/v${version}/cog-linux-x86_64.tar.gz";
|
||||||
|
hash = "sha256-/ioEuM58F3ppO0wlc5nw7ZNHunoweOXL/Gda65r0Ig4=";
|
||||||
|
};
|
||||||
|
aarch64-darwin =
|
||||||
|
prev.fetchurl {
|
||||||
|
url = "https://github.com/trycog/cog-cli/releases/download/v${version}/cog-darwin-arm64.tar.gz";
|
||||||
|
hash = "sha256-o/A2hVU3Jzmlzx5RbGLFCpfGAghcLGTD8Bm+bVR5OkQ=";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
cog-cli =
|
||||||
|
prev.stdenvNoCC.mkDerivation {
|
||||||
|
pname = "cog-cli";
|
||||||
|
inherit version;
|
||||||
|
src =
|
||||||
|
srcs.${prev.stdenv.hostPlatform.system}
|
||||||
|
or (throw "Unsupported system for cog-cli: ${prev.stdenv.hostPlatform.system}");
|
||||||
|
|
||||||
|
dontUnpack = true;
|
||||||
|
dontConfigure = true;
|
||||||
|
dontBuild = true;
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
runHook preInstall
|
||||||
|
tar -xzf "$src"
|
||||||
|
install -Dm755 cog "$out/bin/cog"
|
||||||
|
runHook postInstall
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = with prev.lib; {
|
||||||
|
description = "Memory, code intelligence, and debugging for AI agents";
|
||||||
|
homepage = "https://github.com/trycog/cog-cli";
|
||||||
|
license = licenses.mit;
|
||||||
|
mainProgram = "cog";
|
||||||
|
platforms = builtins.attrNames srcs;
|
||||||
|
sourceProvenance = [sourceTypes.binaryNativeCode];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
3
modules/_overlays/himalaya.nix
Normal file
3
modules/_overlays/himalaya.nix
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{inputs, ...}: final: prev: {
|
||||||
|
himalaya = inputs.himalaya.packages.${prev.stdenv.hostPlatform.system}.default;
|
||||||
|
}
|
||||||
15
modules/_overlays/jj-ryu.nix
Normal file
15
modules/_overlays/jj-ryu.nix
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
{inputs, ...}: final: prev: let
|
||||||
|
naersk-lib = prev.callPackage inputs.naersk {};
|
||||||
|
manifest = (prev.lib.importTOML "${inputs.jj-ryu}/Cargo.toml").package;
|
||||||
|
in {
|
||||||
|
jj-ryu =
|
||||||
|
naersk-lib.buildPackage {
|
||||||
|
pname = manifest.name;
|
||||||
|
version = manifest.version;
|
||||||
|
src = inputs.jj-ryu;
|
||||||
|
nativeBuildInputs = [prev.pkg-config];
|
||||||
|
buildInputs = [prev.openssl];
|
||||||
|
OPENSSL_NO_VENDOR = 1;
|
||||||
|
doCheck = false;
|
||||||
|
};
|
||||||
|
}
|
||||||
1
modules/_overlays/jj-starship.nix
Normal file
1
modules/_overlays/jj-starship.nix
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{inputs, ...}: inputs.jj-starship.overlays.default
|
||||||
3
modules/_overlays/zjstatus.nix
Normal file
3
modules/_overlays/zjstatus.nix
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{inputs, ...}: final: prev: {
|
||||||
|
zjstatus = inputs.zjstatus.packages.${prev.stdenv.hostPlatform.system}.default;
|
||||||
|
}
|
||||||
26
modules/_paperless/tag_prompt.tmpl
Normal file
26
modules/_paperless/tag_prompt.tmpl
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
I will provide you with the content and title of a document. Your task is to select appropriate tags for the document from the available list.
|
||||||
|
Only select tags from the provided list.
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
1. Focus on WHAT the document IS (document type) and what TOPIC it relates to — not on incidental details mentioned in the content.
|
||||||
|
- GOOD tags for a server hosting invoice: "Invoice", "Hosting"
|
||||||
|
- BAD tags for a server hosting invoice: "IBAN", "VAT", "Bank account" — these are just details that appear on any invoice.
|
||||||
|
2. Pick 1-4 tags maximum. Fewer is better. Every tag must add distinct, meaningful categorisation value.
|
||||||
|
3. All tags must be in English.
|
||||||
|
4. Never tag based on formatting details, payment methods, reference numbers, or boilerplate text.
|
||||||
|
|
||||||
|
The content is likely in {{.Language}}, but tags must always be in English.
|
||||||
|
|
||||||
|
<available_tags>
|
||||||
|
{{.AvailableTags | join ", "}}
|
||||||
|
</available_tags>
|
||||||
|
|
||||||
|
<title>
|
||||||
|
{{.Title}}
|
||||||
|
</title>
|
||||||
|
|
||||||
|
<content>
|
||||||
|
{{.Content}}
|
||||||
|
</content>
|
||||||
|
|
||||||
|
Respond only with the selected tags as a comma-separated list, without any additional information.
|
||||||
26
modules/_paperless/title_prompt.tmpl
Normal file
26
modules/_paperless/title_prompt.tmpl
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
I will provide you with the content of a document that has been partially read by OCR (so it may contain errors).
|
||||||
|
Your task is to generate a clear, consistent document title for use in paperless-ngx.
|
||||||
|
|
||||||
|
Title format: "YYYY-MM-DD - Sender - Description"
|
||||||
|
- YYYY-MM-DD: The document date (issue date, statement date, etc.). Use the most specific date available. If no date is found, omit the date prefix.
|
||||||
|
- Sender: The company, organisation, or person who sent/issued the document. Use their common short name (e.g. "Hetzner" not "Hetzner Online GmbH").
|
||||||
|
- Description: A brief description of what the document is (e.g. "Server hosting invoice", "Payslip January", "Employment contract", "Tax assessment 2024"). Keep it concise but specific enough to distinguish from similar documents.
|
||||||
|
|
||||||
|
Examples:
|
||||||
|
- "2025-03-01 - Hetzner - Server hosting invoice"
|
||||||
|
- "2024-12-15 - Techniker Krankenkasse - Health insurance statement"
|
||||||
|
- "2024-06-30 - Acme Corp - Payslip June"
|
||||||
|
- "2024-01-10 - Finanzamt Berlin - Tax assessment 2023"
|
||||||
|
|
||||||
|
Rules:
|
||||||
|
1. Always write the title in English, regardless of the document language.
|
||||||
|
2. Keep the description part under 6 words.
|
||||||
|
3. If the original title contains useful information, use it to inform your suggestion.
|
||||||
|
4. Respond only with the title, without any additional information.
|
||||||
|
|
||||||
|
The content is likely in {{.Language}}.
|
||||||
|
|
||||||
|
<original_title>{{.Title}}</original_title>
|
||||||
|
<content>
|
||||||
|
{{.Content}}
|
||||||
|
</content>
|
||||||
181
modules/_terminal/rose-pine-dawn-glow.json
Normal file
181
modules/_terminal/rose-pine-dawn-glow.json
Normal file
@@ -0,0 +1,181 @@
|
|||||||
|
{
|
||||||
|
"document": {
|
||||||
|
"block_prefix": "\n",
|
||||||
|
"block_suffix": "\n",
|
||||||
|
"color": "#575279",
|
||||||
|
"margin": 2
|
||||||
|
},
|
||||||
|
"block_quote": {
|
||||||
|
"color": "#797593",
|
||||||
|
"italic": true,
|
||||||
|
"indent": 1,
|
||||||
|
"indent_token": "│ "
|
||||||
|
},
|
||||||
|
"list": {
|
||||||
|
"color": "#575279",
|
||||||
|
"level_indent": 2
|
||||||
|
},
|
||||||
|
"heading": {
|
||||||
|
"block_suffix": "\n",
|
||||||
|
"color": "#907aa9",
|
||||||
|
"bold": true
|
||||||
|
},
|
||||||
|
"h1": {
|
||||||
|
"prefix": "# ",
|
||||||
|
"bold": true
|
||||||
|
},
|
||||||
|
"h2": {
|
||||||
|
"prefix": "## "
|
||||||
|
},
|
||||||
|
"h3": {
|
||||||
|
"prefix": "### "
|
||||||
|
},
|
||||||
|
"h4": {
|
||||||
|
"prefix": "#### "
|
||||||
|
},
|
||||||
|
"h5": {
|
||||||
|
"prefix": "##### "
|
||||||
|
},
|
||||||
|
"h6": {
|
||||||
|
"prefix": "###### "
|
||||||
|
},
|
||||||
|
"strikethrough": {
|
||||||
|
"crossed_out": true
|
||||||
|
},
|
||||||
|
"emph": {
|
||||||
|
"italic": true,
|
||||||
|
"color": "#d7827e"
|
||||||
|
},
|
||||||
|
"strong": {
|
||||||
|
"bold": true,
|
||||||
|
"color": "#286983"
|
||||||
|
},
|
||||||
|
"hr": {
|
||||||
|
"color": "#dfdad9",
|
||||||
|
"format": "\n--------\n"
|
||||||
|
},
|
||||||
|
"item": {
|
||||||
|
"block_prefix": "• "
|
||||||
|
},
|
||||||
|
"enumeration": {
|
||||||
|
"block_prefix": ". ",
|
||||||
|
"color": "#286983"
|
||||||
|
},
|
||||||
|
"task": {
|
||||||
|
"ticked": "[✓] ",
|
||||||
|
"unticked": "[ ] "
|
||||||
|
},
|
||||||
|
"link": {
|
||||||
|
"color": "#286983",
|
||||||
|
"underline": true
|
||||||
|
},
|
||||||
|
"link_text": {
|
||||||
|
"color": "#56949f"
|
||||||
|
},
|
||||||
|
"image": {
|
||||||
|
"color": "#286983",
|
||||||
|
"underline": true
|
||||||
|
},
|
||||||
|
"image_text": {
|
||||||
|
"color": "#56949f",
|
||||||
|
"format": "Image: {{.text}} →"
|
||||||
|
},
|
||||||
|
"code": {
|
||||||
|
"color": "#ea9d34",
|
||||||
|
"background_color": "#f2e9e1",
|
||||||
|
"prefix": " ",
|
||||||
|
"suffix": " "
|
||||||
|
},
|
||||||
|
"code_block": {
|
||||||
|
"color": "#ea9d34",
|
||||||
|
"margin": 2,
|
||||||
|
"chroma": {
|
||||||
|
"text": {
|
||||||
|
"color": "#575279"
|
||||||
|
},
|
||||||
|
"error": {
|
||||||
|
"color": "#faf4ed",
|
||||||
|
"background_color": "#b4637a"
|
||||||
|
},
|
||||||
|
"comment": {
|
||||||
|
"color": "#9893a5"
|
||||||
|
},
|
||||||
|
"comment_preproc": {
|
||||||
|
"color": "#56949f"
|
||||||
|
},
|
||||||
|
"keyword": {
|
||||||
|
"color": "#b4637a"
|
||||||
|
},
|
||||||
|
"keyword_reserved": {
|
||||||
|
"color": "#b4637a"
|
||||||
|
},
|
||||||
|
"keyword_namespace": {
|
||||||
|
"color": "#b4637a"
|
||||||
|
},
|
||||||
|
"keyword_type": {
|
||||||
|
"color": "#907aa9"
|
||||||
|
},
|
||||||
|
"operator": {
|
||||||
|
"color": "#56949f"
|
||||||
|
},
|
||||||
|
"punctuation": {
|
||||||
|
"color": "#797593"
|
||||||
|
},
|
||||||
|
"name": {
|
||||||
|
"color": "#286983"
|
||||||
|
},
|
||||||
|
"name_constant": {
|
||||||
|
"color": "#907aa9"
|
||||||
|
},
|
||||||
|
"name_builtin": {
|
||||||
|
"color": "#d7827e"
|
||||||
|
},
|
||||||
|
"name_tag": {
|
||||||
|
"color": "#b4637a"
|
||||||
|
},
|
||||||
|
"name_attribute": {
|
||||||
|
"color": "#d7827e"
|
||||||
|
},
|
||||||
|
"name_class": {
|
||||||
|
"color": "#907aa9"
|
||||||
|
},
|
||||||
|
"name_decorator": {
|
||||||
|
"color": "#56949f"
|
||||||
|
},
|
||||||
|
"name_function": {
|
||||||
|
"color": "#286983"
|
||||||
|
},
|
||||||
|
"literal_number": {
|
||||||
|
"color": "#ea9d34"
|
||||||
|
},
|
||||||
|
"literal_string": {
|
||||||
|
"color": "#ea9d34"
|
||||||
|
},
|
||||||
|
"literal_string_escape": {
|
||||||
|
"color": "#d7827e"
|
||||||
|
},
|
||||||
|
"generic_deleted": {
|
||||||
|
"color": "#b4637a"
|
||||||
|
},
|
||||||
|
"generic_emph": {
|
||||||
|
"italic": true
|
||||||
|
},
|
||||||
|
"generic_inserted": {
|
||||||
|
"color": "#286983"
|
||||||
|
},
|
||||||
|
"generic_strong": {
|
||||||
|
"bold": true
|
||||||
|
},
|
||||||
|
"generic_subheading": {
|
||||||
|
"color": "#907aa9"
|
||||||
|
},
|
||||||
|
"background": {
|
||||||
|
"background_color": "#f2e9e1"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"table": {},
|
||||||
|
"definition_description": {
|
||||||
|
"block_prefix": "\n🠶 "
|
||||||
|
}
|
||||||
|
}
|
||||||
62
modules/adguardhome.nix
Normal file
62
modules/adguardhome.nix
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
{...}: let
|
||||||
|
caddyLib = import ./_lib/caddy.nix;
|
||||||
|
in {
|
||||||
|
den.aspects.adguardhome.nixos = {config, ...}: {
|
||||||
|
services.adguardhome = {
|
||||||
|
enable = true;
|
||||||
|
host = "127.0.0.1";
|
||||||
|
port = 10000;
|
||||||
|
settings = {
|
||||||
|
dhcp.enabled = false;
|
||||||
|
dns.upstream_dns = [
|
||||||
|
"1.1.1.1"
|
||||||
|
"1.0.0.1"
|
||||||
|
];
|
||||||
|
filtering = {
|
||||||
|
protection_enabled = true;
|
||||||
|
filtering_enabled = true;
|
||||||
|
safe_search.enabled = false;
|
||||||
|
safebrowsing_enabled = true;
|
||||||
|
blocked_response_ttl = 10;
|
||||||
|
filters_update_interval = 24;
|
||||||
|
blocked_services.ids = [
|
||||||
|
"reddit"
|
||||||
|
"twitter"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
filters = [
|
||||||
|
{
|
||||||
|
enabled = true;
|
||||||
|
url = "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/adblock/pro.txt";
|
||||||
|
name = "HaGeZi Multi PRO";
|
||||||
|
id = 1;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
enabled = true;
|
||||||
|
url = "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/adblock/tif.txt";
|
||||||
|
name = "HaGeZi Threat Intelligence Feeds";
|
||||||
|
id = 2;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
enabled = true;
|
||||||
|
url = "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/adblock/gambling.txt";
|
||||||
|
name = "HaGeZi Gambling";
|
||||||
|
id = 3;
|
||||||
|
}
|
||||||
|
{
|
||||||
|
enabled = true;
|
||||||
|
url = "https://cdn.jsdelivr.net/gh/hagezi/dns-blocklists@latest/adblock/nsfw.txt";
|
||||||
|
name = "HaGeZi NSFW";
|
||||||
|
id = 4;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.caddy.virtualHosts =
|
||||||
|
caddyLib.mkTailscaleVHost {
|
||||||
|
name = "adguard";
|
||||||
|
configText = "reverse_proxy localhost:${toString config.services.adguardhome.port}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,42 +1,129 @@
|
|||||||
{
|
{inputs, ...}: let
|
||||||
|
local = import ./_lib/local.nix;
|
||||||
|
inherit (local) secretPath;
|
||||||
|
opencodeSecretPath = secretPath "opencode-api-key";
|
||||||
|
in {
|
||||||
den.aspects.ai-tools.homeManager = {
|
den.aspects.ai-tools.homeManager = {
|
||||||
config,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
inputs',
|
inputs',
|
||||||
...
|
...
|
||||||
}: {
|
}: {
|
||||||
home.packages = [
|
home.packages = [
|
||||||
inputs'.llm-agents.packages.claude-code
|
inputs'.llm-agents.packages.claude-code
|
||||||
pkgs.nono
|
pkgs.cog-cli
|
||||||
];
|
];
|
||||||
|
|
||||||
home.shellAliases = {
|
programs.nushell.extraEnv =
|
||||||
noc = "nono run -s --allow-cwd --profile opencode --allow ~/.bun --allow ~/.local/share/opensrc --allow ~/.config/jj --network-profile developer --proxy-allow models.dev --proxy-allow chatgpt.com --proxy-allow mcp.grep.app --proxy-allow mcp.context7.com --proxy-allow mcp.exa.ai -- opencode";
|
lib.mkAfter ''
|
||||||
};
|
if ("${opencodeSecretPath}" | path exists) {
|
||||||
|
$env.OPENCODE_API_KEY = (open --raw "${opencodeSecretPath}" | str trim)
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
programs.opencode = {
|
programs.opencode = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package = inputs'.llm-agents.packages.opencode;
|
package = inputs'.llm-agents.packages.opencode;
|
||||||
settings = {
|
tui = {
|
||||||
model = "anthropic/claude-opus-4-6";
|
|
||||||
small_model = "anthropic/claude-haiku-4-5";
|
|
||||||
theme = "rosepine";
|
theme = "rosepine";
|
||||||
plugin = ["opencode-anthropic-auth@latest"];
|
plugin = ["./plugin/review.ts"];
|
||||||
|
};
|
||||||
|
settings = {
|
||||||
|
model = "openai/gpt-5.4";
|
||||||
|
small_model = "openai/gpt-5.1-codex-mini";
|
||||||
|
plugin = [
|
||||||
|
"opencode-claude-auth"
|
||||||
|
"opencode-supermemory"
|
||||||
|
];
|
||||||
permission = {
|
permission = {
|
||||||
|
external_directory = {
|
||||||
|
"*" = "allow";
|
||||||
|
"**/.gnupg/**" = "deny";
|
||||||
|
"**/.ssh/**" = "deny";
|
||||||
|
"~/.config/gh/hosts.yml" = "deny";
|
||||||
|
"~/.config/sops/age/keys.txt" = "deny";
|
||||||
|
"~/.local/share/opencode/mcp-auth.json" = "deny";
|
||||||
|
"/etc/ssh/ssh_host_*" = "deny";
|
||||||
|
"/run/secrets/*" = "deny";
|
||||||
|
};
|
||||||
|
bash = {
|
||||||
|
"*" = "allow";
|
||||||
|
env = "deny";
|
||||||
|
"env *" = "deny";
|
||||||
|
printenv = "deny";
|
||||||
|
"printenv *" = "deny";
|
||||||
|
"export *" = "deny";
|
||||||
|
"gh auth *" = "deny";
|
||||||
|
ssh = "ask";
|
||||||
|
"ssh *" = "ask";
|
||||||
|
mosh = "ask";
|
||||||
|
"mosh *" = "ask";
|
||||||
|
"cat *.env" = "deny";
|
||||||
|
"cat *.env.*" = "deny";
|
||||||
|
"cat **/.env" = "deny";
|
||||||
|
"cat **/.env.*" = "deny";
|
||||||
|
"cat *.envrc" = "deny";
|
||||||
|
"cat **/.envrc" = "deny";
|
||||||
|
"cat .dev.vars" = "deny";
|
||||||
|
"cat **/.dev.vars" = "deny";
|
||||||
|
"cat *.pem" = "deny";
|
||||||
|
"cat *.key" = "deny";
|
||||||
|
"cat **/.gnupg/**" = "deny";
|
||||||
|
"cat **/.ssh/**" = "deny";
|
||||||
|
"cat ~/.config/gh/hosts.yml" = "deny";
|
||||||
|
"cat ~/.config/sops/age/keys.txt" = "deny";
|
||||||
|
"cat ~/.local/share/opencode/mcp-auth.json" = "deny";
|
||||||
|
"cat /etc/ssh/ssh_host_*" = "deny";
|
||||||
|
"cat /run/secrets/*" = "deny";
|
||||||
|
};
|
||||||
|
edit = {
|
||||||
|
"*" = "allow";
|
||||||
|
"**/.gnupg/**" = "deny";
|
||||||
|
"**/.ssh/**" = "deny";
|
||||||
|
"**/secrets/**" = "deny";
|
||||||
|
"secrets/*" = "deny";
|
||||||
|
"~/.config/gh/hosts.yml" = "deny";
|
||||||
|
"~/.config/sops/age/keys.txt" = "deny";
|
||||||
|
"~/.local/share/opencode/mcp-auth.json" = "deny";
|
||||||
|
"/etc/ssh/ssh_host_*" = "deny";
|
||||||
|
"/run/secrets/*" = "deny";
|
||||||
|
};
|
||||||
|
glob = "allow";
|
||||||
|
grep = "allow";
|
||||||
|
list = "allow";
|
||||||
|
lsp = "allow";
|
||||||
|
question = "allow";
|
||||||
read = {
|
read = {
|
||||||
"*" = "allow";
|
"*" = "allow";
|
||||||
"*.env" = "deny";
|
"*.env" = "deny";
|
||||||
"*.env.*" = "deny";
|
"*.env.*" = "deny";
|
||||||
"*.envrc" = "deny";
|
"*.envrc" = "deny";
|
||||||
|
"**/.env" = "deny";
|
||||||
|
"**/.env.*" = "deny";
|
||||||
|
"**/.envrc" = "deny";
|
||||||
|
".dev.vars" = "deny";
|
||||||
|
"**/.dev.vars" = "deny";
|
||||||
|
"**/.gnupg/**" = "deny";
|
||||||
|
"**/.ssh/**" = "deny";
|
||||||
|
"*.key" = "deny";
|
||||||
|
"*.pem" = "deny";
|
||||||
|
"**/secrets/**" = "deny";
|
||||||
"secrets/*" = "deny";
|
"secrets/*" = "deny";
|
||||||
|
"~/.config/gh/hosts.yml" = "deny";
|
||||||
|
"~/.config/sops/age/keys.txt" = "deny";
|
||||||
|
"~/.local/share/opencode/mcp-auth.json" = "deny";
|
||||||
|
"/etc/ssh/ssh_host_*" = "deny";
|
||||||
|
"/run/secrets/*" = "deny";
|
||||||
};
|
};
|
||||||
|
skill = "allow";
|
||||||
|
task = "allow";
|
||||||
|
webfetch = "allow";
|
||||||
|
websearch = "allow";
|
||||||
|
codesearch = "allow";
|
||||||
};
|
};
|
||||||
agent = {
|
agent = {
|
||||||
plan = {
|
|
||||||
model = "anthropic/claude-opus-4-6";
|
|
||||||
};
|
|
||||||
explore = {
|
explore = {
|
||||||
model = "anthropic/claude-haiku-4-5";
|
model = "openai/gpt-5.1-codex-mini";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
instructions = [
|
instructions = [
|
||||||
@@ -70,27 +157,11 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
systemd.user.services.opencode-server = {
|
|
||||||
Unit = {
|
|
||||||
Description = "OpenCode AI server";
|
|
||||||
After = ["default.target"];
|
|
||||||
};
|
|
||||||
Service = {
|
|
||||||
ExecStart = "${inputs'.llm-agents.packages.opencode}/bin/opencode serve --port 18822 --host 100.64.0.1";
|
|
||||||
Restart = "on-failure";
|
|
||||||
RestartSec = 5;
|
|
||||||
Environment = "PATH=${config.home.profileDirectory}/bin:/run/current-system/sw/bin";
|
|
||||||
};
|
|
||||||
Install = {
|
|
||||||
WantedBy = ["default.target"];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
xdg.configFile = {
|
xdg.configFile = {
|
||||||
"opencode/agent" = {
|
# "opencode/agent" = {
|
||||||
source = ./_opencode/agent;
|
# source = ./_opencode/agent;
|
||||||
recursive = true;
|
# recursive = true;
|
||||||
};
|
# };
|
||||||
"opencode/command" = {
|
"opencode/command" = {
|
||||||
source = ./_opencode/command;
|
source = ./_opencode/command;
|
||||||
recursive = true;
|
recursive = true;
|
||||||
@@ -99,10 +170,6 @@
|
|||||||
source = ./_opencode/skill;
|
source = ./_opencode/skill;
|
||||||
recursive = true;
|
recursive = true;
|
||||||
};
|
};
|
||||||
"opencode/tool" = {
|
|
||||||
source = ./_opencode/tool;
|
|
||||||
recursive = true;
|
|
||||||
};
|
|
||||||
"opencode/plugin" = {
|
"opencode/plugin" = {
|
||||||
source = ./_opencode/plugin;
|
source = ./_opencode/plugin;
|
||||||
recursive = true;
|
recursive = true;
|
||||||
|
|||||||
@@ -26,8 +26,8 @@
|
|||||||
'')}/bin/${name}";
|
'')}/bin/${name}";
|
||||||
meta.description = descriptions.${name};
|
meta.description = descriptions.${name};
|
||||||
};
|
};
|
||||||
platformAppNames = ["build" "rollback" "update"];
|
platformAppNames = ["build" "rollback"];
|
||||||
sharedAppNames = ["apply"];
|
sharedAppNames = ["apply" "update"];
|
||||||
in {
|
in {
|
||||||
apps =
|
apps =
|
||||||
pkgs.lib.genAttrs platformAppNames mkPlatformApp
|
pkgs.lib.genAttrs platformAppNames mkPlatformApp
|
||||||
|
|||||||
11
modules/cache.nix
Normal file
11
modules/cache.nix
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{...}: let
|
||||||
|
caddyLib = import ./_lib/caddy.nix;
|
||||||
|
in {
|
||||||
|
den.aspects.cache.nixos = {
|
||||||
|
services.caddy.virtualHosts =
|
||||||
|
caddyLib.mkTailscaleVHost {
|
||||||
|
name = "cache";
|
||||||
|
configText = "reverse_proxy localhost:32843";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
{den, ...}: {
|
|
||||||
den.aspects.chidi.includes = [
|
|
||||||
den.aspects.darwin-system
|
|
||||||
den.aspects.core
|
|
||||||
den.aspects.tailscale
|
|
||||||
den.aspects.desktop
|
|
||||||
den.aspects.terminal
|
|
||||||
den.aspects.atuin
|
|
||||||
den.aspects.dev-tools
|
|
||||||
den.aspects.neovim
|
|
||||||
den.aspects.ai-tools
|
|
||||||
den.aspects.zellij
|
|
||||||
den.aspects.zk
|
|
||||||
];
|
|
||||||
|
|
||||||
den.aspects.chidi.darwin = {pkgs, ...}: {
|
|
||||||
networking.hostName = "chidi";
|
|
||||||
networking.computerName = "chidi";
|
|
||||||
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
slack
|
|
||||||
];
|
|
||||||
};
|
|
||||||
|
|
||||||
den.aspects.chidi.homeManager = {...}: {
|
|
||||||
fonts.fontconfig.enable = true;
|
|
||||||
programs.git.settings.user.email = "christoph@tuist.dev";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -22,6 +22,7 @@
|
|||||||
nix = {
|
nix = {
|
||||||
package = pkgs.nix;
|
package = pkgs.nix;
|
||||||
settings = {
|
settings = {
|
||||||
|
cores = 4;
|
||||||
substituters = [
|
substituters = [
|
||||||
"https://nix-community.cachix.org"
|
"https://nix-community.cachix.org"
|
||||||
"https://cache.nixos.org"
|
"https://cache.nixos.org"
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
{inputs, ...}: {
|
{inputs, ...}: let
|
||||||
|
local = import ./_lib/local.nix;
|
||||||
|
userHome = "/Users/${local.user.name}";
|
||||||
|
in {
|
||||||
den.aspects.darwin-system.darwin = {pkgs, ...}: {
|
den.aspects.darwin-system.darwin = {pkgs, ...}: {
|
||||||
imports = [
|
imports = [
|
||||||
inputs.nix-homebrew.darwinModules.nix-homebrew
|
inputs.nix-homebrew.darwinModules.nix-homebrew
|
||||||
@@ -6,11 +9,10 @@
|
|||||||
./_darwin/dock.nix
|
./_darwin/dock.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
system.primaryUser = "cschmatzler";
|
system.primaryUser = local.user.name;
|
||||||
|
|
||||||
# Darwin system utilities
|
# Darwin system utilities
|
||||||
environment.systemPackages = with pkgs; [
|
environment.systemPackages = with pkgs; [
|
||||||
alcove
|
|
||||||
dockutil
|
dockutil
|
||||||
mas
|
mas
|
||||||
];
|
];
|
||||||
@@ -42,7 +44,7 @@
|
|||||||
autohide = true;
|
autohide = true;
|
||||||
show-recents = false;
|
show-recents = false;
|
||||||
launchanim = true;
|
launchanim = true;
|
||||||
orientation = "bottom";
|
orientation = "left";
|
||||||
tilesize = 60;
|
tilesize = 60;
|
||||||
minimize-to-application = true;
|
minimize-to-application = true;
|
||||||
mru-spaces = false;
|
mru-spaces = false;
|
||||||
@@ -112,7 +114,7 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
nix = {
|
nix = {
|
||||||
settings.trusted-users = ["cschmatzler"];
|
settings.trusted-users = [local.user.name];
|
||||||
gc.interval = {
|
gc.interval = {
|
||||||
Weekday = 0;
|
Weekday = 0;
|
||||||
Hour = 2;
|
Hour = 2;
|
||||||
@@ -120,18 +122,16 @@
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
users.users.cschmatzler = {
|
users.users.${local.user.name} = {
|
||||||
name = "cschmatzler";
|
name = local.user.name;
|
||||||
home = "/Users/cschmatzler";
|
home = userHome;
|
||||||
isHidden = false;
|
isHidden = false;
|
||||||
shell = pkgs.nushell;
|
shell = pkgs.nushell;
|
||||||
};
|
};
|
||||||
|
|
||||||
home-manager.useGlobalPkgs = true;
|
|
||||||
|
|
||||||
nix-homebrew = {
|
nix-homebrew = {
|
||||||
enable = true;
|
enable = true;
|
||||||
user = "cschmatzler";
|
user = local.user.name;
|
||||||
mutableTaps = true;
|
mutableTaps = true;
|
||||||
taps = {
|
taps = {
|
||||||
"homebrew/homebrew-core" = inputs.homebrew-core;
|
"homebrew/homebrew-core" = inputs.homebrew-core;
|
||||||
@@ -141,10 +141,24 @@
|
|||||||
|
|
||||||
homebrew = {
|
homebrew = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
onActivation = {
|
||||||
|
autoUpdate = true;
|
||||||
|
cleanup = "uninstall";
|
||||||
|
upgrade = true;
|
||||||
|
};
|
||||||
|
taps = [
|
||||||
|
"homebrew/cask"
|
||||||
|
];
|
||||||
casks = [
|
casks = [
|
||||||
|
"1password"
|
||||||
|
"alcove"
|
||||||
|
"aqua-voice"
|
||||||
|
"chatgpt"
|
||||||
"ghostty@tip"
|
"ghostty@tip"
|
||||||
"helium-browser"
|
"raycast"
|
||||||
"tidal"
|
"spotify"
|
||||||
|
"tailscale"
|
||||||
|
"whatsapp"
|
||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -25,8 +25,18 @@
|
|||||||
flake.flakeModules = {
|
flake.flakeModules = {
|
||||||
# Shared system foundations
|
# Shared system foundations
|
||||||
core = ./core.nix;
|
core = ./core.nix;
|
||||||
|
darwin = ./darwin.nix;
|
||||||
network = ./network.nix;
|
network = ./network.nix;
|
||||||
nixos-system = ./nixos-system.nix;
|
nixos-system = ./nixos-system.nix;
|
||||||
|
overlays = ./overlays.nix;
|
||||||
|
secrets = ./secrets.nix;
|
||||||
|
|
||||||
|
# Shared host features
|
||||||
|
adguardhome = ./adguardhome.nix;
|
||||||
|
cache = ./cache.nix;
|
||||||
|
gitea = ./gitea.nix;
|
||||||
|
opencode = ./opencode.nix;
|
||||||
|
paperless = ./paperless.nix;
|
||||||
|
|
||||||
# User environment
|
# User environment
|
||||||
ai-tools = ./ai-tools.nix;
|
ai-tools = ./ai-tools.nix;
|
||||||
@@ -34,18 +44,21 @@
|
|||||||
desktop = ./desktop.nix;
|
desktop = ./desktop.nix;
|
||||||
dev-tools = ./dev-tools.nix;
|
dev-tools = ./dev-tools.nix;
|
||||||
email = ./email.nix;
|
email = ./email.nix;
|
||||||
finance = ./finance.nix;
|
|
||||||
neovim = ./neovim.nix;
|
neovim = ./neovim.nix;
|
||||||
shell = ./shell.nix;
|
shell = ./shell.nix;
|
||||||
ssh-client = ./ssh-client.nix;
|
ssh-client = ./ssh-client.nix;
|
||||||
terminal = ./terminal.nix;
|
terminal = ./terminal.nix;
|
||||||
user = ./user.nix;
|
|
||||||
zellij = ./zellij.nix;
|
zellij = ./zellij.nix;
|
||||||
zk = ./zk.nix;
|
zk = ./zk.nix;
|
||||||
};
|
};
|
||||||
den.default.nixos.system.stateVersion = "25.11";
|
den.default.nixos.system.stateVersion = "25.11";
|
||||||
den.default.darwin.system.stateVersion = 6;
|
den.default.darwin.system.stateVersion = 6;
|
||||||
den.default.homeManager.home.stateVersion = "25.11";
|
den.default.homeManager = {
|
||||||
|
home.stateVersion = "25.11";
|
||||||
|
programs.home-manager.enable = true;
|
||||||
|
};
|
||||||
|
den.default.nixos.home-manager.useGlobalPkgs = true;
|
||||||
|
den.default.darwin.home-manager.useGlobalPkgs = true;
|
||||||
|
|
||||||
den.default.includes = [
|
den.default.includes = [
|
||||||
den.provides.define-user
|
den.provides.define-user
|
||||||
|
|||||||
@@ -62,7 +62,6 @@
|
|||||||
};
|
};
|
||||||
jj-starship.url = "github:dmmulroy/jj-starship";
|
jj-starship.url = "github:dmmulroy/jj-starship";
|
||||||
zjstatus.url = "github:dj95/zjstatus";
|
zjstatus.url = "github:dj95/zjstatus";
|
||||||
tuicr.url = "github:agavra/tuicr";
|
|
||||||
fenix = {
|
fenix = {
|
||||||
url = "github:nix-community/fenix";
|
url = "github:nix-community/fenix";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
@@ -71,12 +70,17 @@
|
|||||||
url = "github:nix-community/naersk/master";
|
url = "github:nix-community/naersk/master";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
nono = {
|
# Neovim plugin inputs
|
||||||
url = "github:always-further/nono";
|
code-review-nvim = {
|
||||||
|
url = "github:choplin/code-review.nvim";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
vite-plus = {
|
jj-nvim = {
|
||||||
url = "github:voidzero-dev/vite-plus/v0.1.11";
|
url = "github:NicolasGB/jj.nvim";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
|
jj-diffconflicts = {
|
||||||
|
url = "github:rafikdraoui/jj-diffconflicts";
|
||||||
flake = false;
|
flake = false;
|
||||||
};
|
};
|
||||||
# Secrets inputs
|
# Secrets inputs
|
||||||
|
|||||||
@@ -2,24 +2,36 @@
|
|||||||
inputs,
|
inputs,
|
||||||
config,
|
config,
|
||||||
...
|
...
|
||||||
}: {
|
}: let
|
||||||
|
local = import ./_lib/local.nix;
|
||||||
|
acceptNewHostKeys = [
|
||||||
|
"-o"
|
||||||
|
"StrictHostKeyChecking=accept-new"
|
||||||
|
];
|
||||||
|
mkSystemNode = {
|
||||||
|
hostname,
|
||||||
|
host,
|
||||||
|
}: {
|
||||||
|
inherit hostname;
|
||||||
|
sshUser = local.user.name;
|
||||||
|
sshOpts = acceptNewHostKeys;
|
||||||
|
profiles.system = {
|
||||||
|
user = "root";
|
||||||
|
path = inputs.deploy-rs.lib.x86_64-linux.activate.nixos config.flake.nixosConfigurations.${host};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in {
|
||||||
flake.deploy.nodes = {
|
flake.deploy.nodes = {
|
||||||
michael = {
|
michael =
|
||||||
hostname = "michael";
|
mkSystemNode {
|
||||||
sshUser = "cschmatzler";
|
hostname = "git.schmatzler.com";
|
||||||
profiles.system = {
|
host = "michael";
|
||||||
user = "root";
|
|
||||||
path = inputs.deploy-rs.lib.x86_64-linux.activate.nixos config.flake.nixosConfigurations.michael;
|
|
||||||
};
|
};
|
||||||
};
|
tahani =
|
||||||
tahani = {
|
mkSystemNode {
|
||||||
hostname = "tahani";
|
hostname = "127.0.0.1";
|
||||||
sshUser = "cschmatzler";
|
host = "tahani";
|
||||||
profiles.system = {
|
|
||||||
user = "root";
|
|
||||||
path = inputs.deploy-rs.lib.x86_64-linux.activate.nixos config.flake.nixosConfigurations.tahani;
|
|
||||||
};
|
};
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
flake.checks.x86_64-linux = inputs.deploy-rs.lib.x86_64-linux.deployChecks config.flake.deploy;
|
flake.checks.x86_64-linux = inputs.deploy-rs.lib.x86_64-linux.deployChecks config.flake.deploy;
|
||||||
|
|||||||
@@ -1,15 +1,9 @@
|
|||||||
{...}: {
|
{...}: {
|
||||||
den.aspects.desktop.homeManager = {
|
den.aspects.desktop.homeManager = {
|
||||||
pkgs,
|
|
||||||
lib,
|
lib,
|
||||||
|
pkgs,
|
||||||
...
|
...
|
||||||
}: {
|
}: {
|
||||||
home.packages = with pkgs;
|
|
||||||
lib.optionals stdenv.isDarwin [
|
|
||||||
_1password-gui
|
|
||||||
raycast
|
|
||||||
];
|
|
||||||
|
|
||||||
programs.aerospace = {
|
programs.aerospace = {
|
||||||
enable = true;
|
enable = true;
|
||||||
launchd.enable = true;
|
launchd.enable = true;
|
||||||
@@ -62,7 +56,7 @@
|
|||||||
}
|
}
|
||||||
{
|
{
|
||||||
"if" = {
|
"if" = {
|
||||||
"app-id" = "net.imput.helium";
|
"app-id" = "com.apple.Safari";
|
||||||
};
|
};
|
||||||
run = "move-node-to-workspace 2";
|
run = "move-node-to-workspace 2";
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,10 +1,13 @@
|
|||||||
{...}: {
|
{...}: let
|
||||||
|
local = import ./_lib/local.nix;
|
||||||
|
palette = (import ./_lib/theme.nix).rosePineDawn.hex;
|
||||||
|
in {
|
||||||
den.aspects.dev-tools.homeManager = {
|
den.aspects.dev-tools.homeManager = {
|
||||||
pkgs,
|
pkgs,
|
||||||
lib,
|
lib,
|
||||||
...
|
...
|
||||||
}: let
|
}: let
|
||||||
name = "Christoph Schmatzler";
|
name = local.user.fullName;
|
||||||
in {
|
in {
|
||||||
home.packages = with pkgs;
|
home.packages = with pkgs;
|
||||||
[
|
[
|
||||||
@@ -16,6 +19,7 @@
|
|||||||
devenv
|
devenv
|
||||||
docker
|
docker
|
||||||
docker-compose
|
docker-compose
|
||||||
|
lazydocker
|
||||||
gh
|
gh
|
||||||
gnumake
|
gnumake
|
||||||
hyperfine
|
hyperfine
|
||||||
@@ -32,6 +36,13 @@
|
|||||||
tea
|
tea
|
||||||
tokei
|
tokei
|
||||||
tree-sitter
|
tree-sitter
|
||||||
|
(pkgs.writeShellApplication {
|
||||||
|
name = "tuist-pr";
|
||||||
|
runtimeInputs = with pkgs; [coreutils fzf gh git nushell];
|
||||||
|
text = ''
|
||||||
|
exec nu ${./_dev-tools/tuist-pr.nu} "$@"
|
||||||
|
'';
|
||||||
|
})
|
||||||
]
|
]
|
||||||
++ lib.optionals stdenv.isDarwin [
|
++ lib.optionals stdenv.isDarwin [
|
||||||
xcodes
|
xcodes
|
||||||
@@ -84,7 +95,7 @@
|
|||||||
settings = {
|
settings = {
|
||||||
user = {
|
user = {
|
||||||
name = name;
|
name = name;
|
||||||
email = "christoph@schmatzler.com";
|
email = local.user.emails.personal;
|
||||||
};
|
};
|
||||||
git = {
|
git = {
|
||||||
sign-on-push = true;
|
sign-on-push = true;
|
||||||
@@ -116,7 +127,7 @@
|
|||||||
revset-aliases = {
|
revset-aliases = {
|
||||||
"closest_bookmark(to)" = "heads(::to & bookmarks())";
|
"closest_bookmark(to)" = "heads(::to & bookmarks())";
|
||||||
"closest_pushable(to)" = "heads(::to & mutable() & ~description(exact:\"\") & (~empty() | merges()))";
|
"closest_pushable(to)" = "heads(::to & mutable() & ~description(exact:\"\") & (~empty() | merges()))";
|
||||||
"mine()" = "author(\"christoph@schmatzler.com\")";
|
"mine()" = "author(\"${local.user.emails.personal}\")";
|
||||||
"wip()" = "mine() ~ immutable()";
|
"wip()" = "mine() ~ immutable()";
|
||||||
"open()" = "mine() ~ ::trunk()";
|
"open()" = "mine() ~ ::trunk()";
|
||||||
"current()" = "@:: & mutable()";
|
"current()" = "@:: & mutable()";
|
||||||
@@ -142,76 +153,76 @@
|
|||||||
programs.jjui = {
|
programs.jjui = {
|
||||||
enable = true;
|
enable = true;
|
||||||
settings.ui.colors = {
|
settings.ui.colors = {
|
||||||
text = {fg = "#575279";};
|
text = {fg = palette.text;};
|
||||||
dimmed = {fg = "#9893a5";};
|
dimmed = {fg = palette.muted;};
|
||||||
selected = {
|
selected = {
|
||||||
bg = "#f2e9e1";
|
bg = palette.overlay;
|
||||||
fg = "#575279";
|
fg = palette.text;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
border = {fg = "#9893a5";};
|
border = {fg = palette.muted;};
|
||||||
title = {
|
title = {
|
||||||
fg = "#907aa9";
|
fg = palette.iris;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
shortcut = {
|
shortcut = {
|
||||||
fg = "#286983";
|
fg = palette.pine;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
matched = {
|
matched = {
|
||||||
fg = "#ea9d34";
|
fg = palette.gold;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
"revisions selected" = {
|
"revisions selected" = {
|
||||||
bg = "#f2e9e1";
|
bg = palette.overlay;
|
||||||
fg = "#575279";
|
fg = palette.text;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
"status" = {bg = "#f2e9e1";};
|
"status" = {bg = palette.overlay;};
|
||||||
"status title" = {
|
"status title" = {
|
||||||
bg = "#907aa9";
|
bg = palette.iris;
|
||||||
fg = "#faf4ed";
|
fg = palette.base;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
"status shortcut" = {fg = "#286983";};
|
"status shortcut" = {fg = palette.pine;};
|
||||||
"status dimmed" = {fg = "#9893a5";};
|
"status dimmed" = {fg = palette.muted;};
|
||||||
"menu" = {bg = "#faf4ed";};
|
"menu" = {bg = palette.base;};
|
||||||
"menu selected" = {
|
"menu selected" = {
|
||||||
bg = "#f2e9e1";
|
bg = palette.overlay;
|
||||||
fg = "#575279";
|
fg = palette.text;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
"menu border" = {fg = "#9893a5";};
|
"menu border" = {fg = palette.muted;};
|
||||||
"menu title" = {
|
"menu title" = {
|
||||||
fg = "#907aa9";
|
fg = palette.iris;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
"menu shortcut" = {fg = "#286983";};
|
"menu shortcut" = {fg = palette.pine;};
|
||||||
"menu matched" = {
|
"menu matched" = {
|
||||||
fg = "#ea9d34";
|
fg = palette.gold;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
"preview border" = {fg = "#9893a5";};
|
"preview border" = {fg = palette.muted;};
|
||||||
"help" = {bg = "#faf4ed";};
|
"help" = {bg = palette.base;};
|
||||||
"help border" = {fg = "#9893a5";};
|
"help border" = {fg = palette.muted;};
|
||||||
"help title" = {
|
"help title" = {
|
||||||
fg = "#907aa9";
|
fg = palette.iris;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
"confirmation" = {bg = "#faf4ed";};
|
"confirmation" = {bg = palette.base;};
|
||||||
"confirmation border" = {fg = "#9893a5";};
|
"confirmation border" = {fg = palette.muted;};
|
||||||
"confirmation selected" = {
|
"confirmation selected" = {
|
||||||
bg = "#f2e9e1";
|
bg = palette.overlay;
|
||||||
fg = "#575279";
|
fg = palette.text;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
"confirmation dimmed" = {fg = "#9893a5";};
|
"confirmation dimmed" = {fg = palette.muted;};
|
||||||
source_marker = {
|
source_marker = {
|
||||||
fg = "#56949f";
|
fg = palette.foam;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
target_marker = {
|
target_marker = {
|
||||||
fg = "#d7827e";
|
fg = palette.rose;
|
||||||
bold = true;
|
bold = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +1,12 @@
|
|||||||
{...}: {
|
{...}: let
|
||||||
|
local = import ./_lib/local.nix;
|
||||||
|
in {
|
||||||
den.aspects.email.homeManager = {pkgs, ...}: {
|
den.aspects.email.homeManager = {pkgs, ...}: {
|
||||||
|
programs.aerc = {
|
||||||
|
enable = true;
|
||||||
|
extraConfig.general.unsafe-accounts-conf = true;
|
||||||
|
};
|
||||||
|
|
||||||
programs.himalaya = {
|
programs.himalaya = {
|
||||||
enable = true;
|
enable = true;
|
||||||
package =
|
package =
|
||||||
@@ -19,13 +26,13 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
accounts.email = {
|
accounts.email = {
|
||||||
accounts."christoph@schmatzler.com" = {
|
accounts.${local.user.emails.personal} = {
|
||||||
primary = true;
|
primary = true;
|
||||||
maildir.path = "christoph@schmatzler.com";
|
maildir.path = local.user.emails.personal;
|
||||||
address = "christoph@schmatzler.com";
|
address = local.user.emails.personal;
|
||||||
userName = "christoph.schmatzler@icloud.com";
|
userName = local.user.emails.icloud;
|
||||||
realName = "Christoph Schmatzler";
|
realName = local.user.fullName;
|
||||||
passwordCommand = ["${pkgs.coreutils}/bin/cat" "/run/secrets/tahani-email-password"];
|
passwordCommand = ["${pkgs.coreutils}/bin/cat" (local.secretPath "tahani-email-password")];
|
||||||
folders = {
|
folders = {
|
||||||
inbox = "INBOX";
|
inbox = "INBOX";
|
||||||
drafts = "Drafts";
|
drafts = "Drafts";
|
||||||
@@ -48,6 +55,7 @@
|
|||||||
port = 993;
|
port = 993;
|
||||||
tls.enable = true;
|
tls.enable = true;
|
||||||
};
|
};
|
||||||
|
aerc.enable = true;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -1,5 +0,0 @@
|
|||||||
{...}: {
|
|
||||||
den.aspects.finance.homeManager = {pkgs, ...}: {
|
|
||||||
home.packages = [pkgs.hledger];
|
|
||||||
};
|
|
||||||
}
|
|
||||||
166
modules/gitea.nix
Normal file
166
modules/gitea.nix
Normal file
@@ -0,0 +1,166 @@
|
|||||||
|
{lib, ...}: let
|
||||||
|
secretLib = import ./_lib/secrets.nix {inherit lib;};
|
||||||
|
in {
|
||||||
|
den.aspects.gitea.nixos = {
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
sops.secrets = {
|
||||||
|
michael-gitea-litestream =
|
||||||
|
secretLib.mkServiceBinarySecret {
|
||||||
|
name = "michael-gitea-litestream";
|
||||||
|
serviceUser = "gitea";
|
||||||
|
sopsFile = ../secrets/michael-gitea-litestream;
|
||||||
|
};
|
||||||
|
michael-gitea-restic-password =
|
||||||
|
secretLib.mkServiceBinarySecret {
|
||||||
|
name = "michael-gitea-restic-password";
|
||||||
|
serviceUser = "gitea";
|
||||||
|
sopsFile = ../secrets/michael-gitea-restic-password;
|
||||||
|
};
|
||||||
|
michael-gitea-restic-env =
|
||||||
|
secretLib.mkServiceBinarySecret {
|
||||||
|
name = "michael-gitea-restic-env";
|
||||||
|
serviceUser = "gitea";
|
||||||
|
sopsFile = ../secrets/michael-gitea-restic-env;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
networking.firewall.allowedTCPPorts = [80 443];
|
||||||
|
|
||||||
|
services.redis.servers.gitea = {
|
||||||
|
enable = true;
|
||||||
|
port = 6380;
|
||||||
|
bind = "127.0.0.1";
|
||||||
|
settings = {
|
||||||
|
maxmemory = "64mb";
|
||||||
|
maxmemory-policy = "allkeys-lru";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.gitea = {
|
||||||
|
enable = true;
|
||||||
|
database = {
|
||||||
|
type = "sqlite3";
|
||||||
|
path = "/var/lib/gitea/data/gitea.db";
|
||||||
|
};
|
||||||
|
settings = {
|
||||||
|
server = {
|
||||||
|
ROOT_URL = "https://git.schmatzler.com/";
|
||||||
|
DOMAIN = "git.schmatzler.com";
|
||||||
|
HTTP_ADDR = "127.0.0.1";
|
||||||
|
HTTP_PORT = 3000;
|
||||||
|
LANDING_PAGE = "explore";
|
||||||
|
};
|
||||||
|
service.DISABLE_REGISTRATION = true;
|
||||||
|
security.INSTALL_LOCK = true;
|
||||||
|
cache = {
|
||||||
|
ADAPTER = "redis";
|
||||||
|
HOST = "redis://127.0.0.1:6380/0?pool_size=100&idle_timeout=180s";
|
||||||
|
ITEM_TTL = "16h";
|
||||||
|
};
|
||||||
|
"cache.last_commit" = {
|
||||||
|
ITEM_TTL = "8760h";
|
||||||
|
COMMITS_COUNT = 100;
|
||||||
|
};
|
||||||
|
session = {
|
||||||
|
PROVIDER = "redis";
|
||||||
|
PROVIDER_CONFIG = "redis://127.0.0.1:6380/1?pool_size=100&idle_timeout=180s";
|
||||||
|
COOKIE_SECURE = true;
|
||||||
|
SAME_SITE = "strict";
|
||||||
|
};
|
||||||
|
api.ENABLE_SWAGGER = false;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.litestream = {
|
||||||
|
enable = true;
|
||||||
|
environmentFile = config.sops.secrets.michael-gitea-litestream.path;
|
||||||
|
settings.dbs = [
|
||||||
|
{
|
||||||
|
path = "/var/lib/gitea/data/gitea.db";
|
||||||
|
replicas = [
|
||||||
|
{
|
||||||
|
type = "s3";
|
||||||
|
bucket = "michael-gitea-litestream";
|
||||||
|
path = "gitea";
|
||||||
|
endpoint = "s3.eu-central-003.backblazeb2.com";
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.litestream.serviceConfig = {
|
||||||
|
User = lib.mkForce "gitea";
|
||||||
|
Group = lib.mkForce "gitea";
|
||||||
|
};
|
||||||
|
|
||||||
|
services.caddy = {
|
||||||
|
enable = true;
|
||||||
|
virtualHosts."git.schmatzler.com".extraConfig = ''
|
||||||
|
header {
|
||||||
|
Strict-Transport-Security "max-age=31536000; includeSubDomains"
|
||||||
|
X-Content-Type-Options "nosniff"
|
||||||
|
X-Frame-Options "DENY"
|
||||||
|
Referrer-Policy "strict-origin-when-cross-origin"
|
||||||
|
}
|
||||||
|
reverse_proxy localhost:3000
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
services.restic.backups.gitea = {
|
||||||
|
repository = "s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories";
|
||||||
|
paths = ["/var/lib/gitea"];
|
||||||
|
exclude = [
|
||||||
|
"/var/lib/gitea/log"
|
||||||
|
"/var/lib/gitea/data/gitea.db"
|
||||||
|
"/var/lib/gitea/data/gitea.db-shm"
|
||||||
|
"/var/lib/gitea/data/gitea.db-wal"
|
||||||
|
];
|
||||||
|
passwordFile = config.sops.secrets.michael-gitea-restic-password.path;
|
||||||
|
environmentFile = config.sops.secrets.michael-gitea-restic-env.path;
|
||||||
|
pruneOpts = [
|
||||||
|
"--keep-daily 7"
|
||||||
|
"--keep-weekly 4"
|
||||||
|
"--keep-monthly 6"
|
||||||
|
];
|
||||||
|
timerConfig = {
|
||||||
|
OnCalendar = "daily";
|
||||||
|
Persistent = true;
|
||||||
|
RandomizedDelaySec = "1h";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.restic-backups-gitea = {
|
||||||
|
wants = ["restic-init-gitea.service"];
|
||||||
|
after = ["restic-init-gitea.service"];
|
||||||
|
serviceConfig = {
|
||||||
|
User = lib.mkForce "gitea";
|
||||||
|
Group = lib.mkForce "gitea";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.services.restic-init-gitea = {
|
||||||
|
description = "Initialize Restic repository for Gitea backups";
|
||||||
|
wantedBy = ["multi-user.target"];
|
||||||
|
after = ["network-online.target"];
|
||||||
|
wants = ["network-online.target"];
|
||||||
|
path = [pkgs.restic];
|
||||||
|
serviceConfig = {
|
||||||
|
Type = "oneshot";
|
||||||
|
User = "gitea";
|
||||||
|
Group = "gitea";
|
||||||
|
RemainAfterExit = true;
|
||||||
|
EnvironmentFile = config.sops.secrets.michael-gitea-restic-env.path;
|
||||||
|
};
|
||||||
|
script = ''
|
||||||
|
export RESTIC_PASSWORD=$(cat ${config.sops.secrets.michael-gitea-restic-password.path})
|
||||||
|
restic -r s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories snapshots &>/dev/null || \
|
||||||
|
restic -r s3:s3.eu-central-003.backblazeb2.com/michael-gitea-repositories init
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
{...}: {
|
|
||||||
den.hosts.aarch64-darwin.chidi.users.cschmatzler = {};
|
|
||||||
den.hosts.aarch64-darwin.jason.users.cschmatzler = {};
|
|
||||||
den.hosts.x86_64-linux.michael.users.cschmatzler = {};
|
|
||||||
den.hosts.x86_64-linux.tahani.users.cschmatzler = {};
|
|
||||||
}
|
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
nameservers = ["1.1.1.1"];
|
nameservers = ["1.1.1.1"];
|
||||||
firewall = {
|
firewall = {
|
||||||
enable = true;
|
enable = true;
|
||||||
trustedInterfaces = ["eno1" "tailscale0"];
|
trustedInterfaces = ["eno1" "tailscale0" "docker0"];
|
||||||
allowedUDPPorts = [
|
allowedUDPPorts = [
|
||||||
53
|
53
|
||||||
config.services.tailscale.port
|
config.services.tailscale.port
|
||||||
35
modules/hosts/chidi.nix
Normal file
35
modules/hosts/chidi.nix
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
den,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
hostLib = import ../_lib/hosts.nix {inherit den lib;};
|
||||||
|
local = import ../_lib/local.nix;
|
||||||
|
host = "chidi";
|
||||||
|
hostMeta = local.hosts.chidi;
|
||||||
|
in
|
||||||
|
lib.recursiveUpdate
|
||||||
|
(hostLib.mkUserHost {
|
||||||
|
system = hostMeta.system;
|
||||||
|
inherit host;
|
||||||
|
user = local.user.name;
|
||||||
|
includes = [den.aspects.user-darwin-laptop];
|
||||||
|
homeManager = {...}: {
|
||||||
|
programs.git.settings.user.email = local.user.emails.work;
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(hostLib.mkPerHostAspect {
|
||||||
|
inherit host;
|
||||||
|
includes = [
|
||||||
|
den.aspects.host-darwin-base
|
||||||
|
den.aspects.opencode-api-key
|
||||||
|
];
|
||||||
|
darwin = {...}: {
|
||||||
|
networking.hostName = host;
|
||||||
|
networking.computerName = host;
|
||||||
|
|
||||||
|
homebrew.casks = [
|
||||||
|
"slack"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
})
|
||||||
31
modules/hosts/janet.nix
Normal file
31
modules/hosts/janet.nix
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
{
|
||||||
|
den,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
hostLib = import ../_lib/hosts.nix {inherit den lib;};
|
||||||
|
local = import ../_lib/local.nix;
|
||||||
|
host = "janet";
|
||||||
|
hostMeta = local.hosts.janet;
|
||||||
|
in
|
||||||
|
lib.recursiveUpdate
|
||||||
|
(hostLib.mkUserHost {
|
||||||
|
system = hostMeta.system;
|
||||||
|
inherit host;
|
||||||
|
user = local.user.name;
|
||||||
|
includes = [
|
||||||
|
den.aspects.user-darwin-laptop
|
||||||
|
den.aspects.user-personal
|
||||||
|
];
|
||||||
|
})
|
||||||
|
(hostLib.mkPerHostAspect {
|
||||||
|
inherit host;
|
||||||
|
includes = [
|
||||||
|
den.aspects.host-darwin-base
|
||||||
|
den.aspects.opencode-api-key
|
||||||
|
];
|
||||||
|
darwin = {...}: {
|
||||||
|
networking.hostName = host;
|
||||||
|
networking.computerName = host;
|
||||||
|
};
|
||||||
|
})
|
||||||
35
modules/hosts/michael.nix
Normal file
35
modules/hosts/michael.nix
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
den,
|
||||||
|
inputs,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
hostLib = import ../_lib/hosts.nix {inherit den lib;};
|
||||||
|
local = import ../_lib/local.nix;
|
||||||
|
host = "michael";
|
||||||
|
hostMeta = local.hosts.michael;
|
||||||
|
in
|
||||||
|
lib.recursiveUpdate
|
||||||
|
(hostLib.mkUserHost {
|
||||||
|
system = hostMeta.system;
|
||||||
|
inherit host;
|
||||||
|
user = local.user.name;
|
||||||
|
includes = [den.aspects.user-minimal];
|
||||||
|
})
|
||||||
|
(hostLib.mkPerHostAspect {
|
||||||
|
inherit host;
|
||||||
|
includes = [
|
||||||
|
den.aspects.host-public-server
|
||||||
|
den.aspects.gitea
|
||||||
|
];
|
||||||
|
nixos = {modulesPath, ...}: {
|
||||||
|
imports = [
|
||||||
|
(modulesPath + "/installer/scan/not-detected.nix")
|
||||||
|
./_parts/michael/disk-config.nix
|
||||||
|
./_parts/michael/hardware-configuration.nix
|
||||||
|
inputs.disko.nixosModules.default
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.hostName = host;
|
||||||
|
};
|
||||||
|
})
|
||||||
109
modules/hosts/tahani.nix
Normal file
109
modules/hosts/tahani.nix
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
{
|
||||||
|
den,
|
||||||
|
lib,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
hostLib = import ../_lib/hosts.nix {inherit den lib;};
|
||||||
|
local = import ../_lib/local.nix;
|
||||||
|
secretLib = import ../_lib/secrets.nix {inherit lib;};
|
||||||
|
host = "tahani";
|
||||||
|
hostMeta = local.hosts.tahani;
|
||||||
|
in
|
||||||
|
lib.recursiveUpdate
|
||||||
|
(hostLib.mkUserHost {
|
||||||
|
system = hostMeta.system;
|
||||||
|
inherit host;
|
||||||
|
user = local.user.name;
|
||||||
|
includes = [
|
||||||
|
den.aspects.user-workstation
|
||||||
|
den.aspects.user-personal
|
||||||
|
den.aspects.email
|
||||||
|
];
|
||||||
|
homeManager = {
|
||||||
|
config,
|
||||||
|
inputs',
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
opencode = inputs'.llm-agents.packages.opencode;
|
||||||
|
in {
|
||||||
|
programs.opencode.settings.permission.external_directory = {
|
||||||
|
"/tmp/himalaya-triage/*" = "allow";
|
||||||
|
"/var/lib/paperless/consume/inbox-triage/*" = "allow";
|
||||||
|
};
|
||||||
|
|
||||||
|
programs.nushell.extraConfig = ''
|
||||||
|
if $nu.is-interactive and ('SSH_CONNECTION' in ($env | columns)) and ('ZELLIJ' not-in ($env | columns)) {
|
||||||
|
try {
|
||||||
|
zellij attach -c main
|
||||||
|
exit
|
||||||
|
} catch {
|
||||||
|
print "zellij auto-start failed; staying in shell"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
|
||||||
|
systemd.user.services.opencode-inbox-triage = {
|
||||||
|
Unit = {
|
||||||
|
Description = "OpenCode inbox triage";
|
||||||
|
};
|
||||||
|
Service = {
|
||||||
|
Type = "oneshot";
|
||||||
|
ExecStart = "${opencode}/bin/opencode run --command inbox-triage --model opencode-go/glm-5";
|
||||||
|
Environment = "PATH=${config.home.profileDirectory}/bin:/run/current-system/sw/bin";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
systemd.user.timers.opencode-inbox-triage = {
|
||||||
|
Unit = {
|
||||||
|
Description = "Run OpenCode inbox triage every 12 hours";
|
||||||
|
};
|
||||||
|
Timer = {
|
||||||
|
OnCalendar = "*-*-* 0/12:00:00";
|
||||||
|
Persistent = true;
|
||||||
|
};
|
||||||
|
Install = {
|
||||||
|
WantedBy = ["timers.target"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
})
|
||||||
|
(hostLib.mkPerHostAspect {
|
||||||
|
inherit host;
|
||||||
|
includes = [
|
||||||
|
den.aspects.host-nixos-base
|
||||||
|
den.aspects.opencode-api-key
|
||||||
|
den.aspects.adguardhome
|
||||||
|
den.aspects.cache
|
||||||
|
den.aspects.paperless
|
||||||
|
];
|
||||||
|
nixos = {...}: {
|
||||||
|
imports = [
|
||||||
|
./_parts/tahani/networking.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
networking.hostName = host;
|
||||||
|
|
||||||
|
sops.secrets.tahani-email-password =
|
||||||
|
secretLib.mkUserBinarySecret {
|
||||||
|
name = "tahani-email-password";
|
||||||
|
sopsFile = ../../secrets/tahani-email-password;
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualisation.docker.enable = true;
|
||||||
|
users.users.${local.user.name}.extraGroups = [
|
||||||
|
"docker"
|
||||||
|
"paperless"
|
||||||
|
];
|
||||||
|
|
||||||
|
systemd.tmpfiles.rules = [
|
||||||
|
"d /var/lib/paperless/consume 2775 paperless paperless -"
|
||||||
|
"d /var/lib/paperless/consume/inbox-triage 2775 paperless paperless -"
|
||||||
|
];
|
||||||
|
swapDevices = [
|
||||||
|
{
|
||||||
|
device = "/swapfile";
|
||||||
|
size = 16 * 1024;
|
||||||
|
}
|
||||||
|
];
|
||||||
|
};
|
||||||
|
})
|
||||||
10
modules/inventory.nix
Normal file
10
modules/inventory.nix
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
{lib, ...}: let
|
||||||
|
local = import ./_lib/local.nix;
|
||||||
|
in
|
||||||
|
lib.foldl' lib.recursiveUpdate {} (
|
||||||
|
lib.mapAttrsToList (
|
||||||
|
host: hostMeta:
|
||||||
|
lib.setAttrByPath ["den" "hosts" hostMeta.system host "users" local.user.name] {}
|
||||||
|
)
|
||||||
|
local.hosts
|
||||||
|
)
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
{den, ...}: {
|
|
||||||
den.aspects.jason.includes = [
|
|
||||||
den.aspects.darwin-system
|
|
||||||
den.aspects.core
|
|
||||||
den.aspects.tailscale
|
|
||||||
den.aspects.desktop
|
|
||||||
den.aspects.terminal
|
|
||||||
den.aspects.atuin
|
|
||||||
den.aspects.dev-tools
|
|
||||||
den.aspects.neovim
|
|
||||||
den.aspects.ai-tools
|
|
||||||
den.aspects.zellij
|
|
||||||
den.aspects.zk
|
|
||||||
];
|
|
||||||
|
|
||||||
den.aspects.jason.darwin = {...}: {
|
|
||||||
networking.hostName = "jason";
|
|
||||||
networking.computerName = "jason";
|
|
||||||
};
|
|
||||||
|
|
||||||
den.aspects.jason.homeManager = {...}: {
|
|
||||||
fonts.fontconfig.enable = true;
|
|
||||||
programs.git.settings.user.email = "christoph@schmatzler.com";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
{
|
|
||||||
inputs,
|
|
||||||
den,
|
|
||||||
...
|
|
||||||
}: {
|
|
||||||
den.aspects.michael.includes = [
|
|
||||||
den.aspects.nixos-system
|
|
||||||
den.aspects.core
|
|
||||||
den.aspects.openssh
|
|
||||||
den.aspects.fail2ban
|
|
||||||
den.aspects.tailscale
|
|
||||||
];
|
|
||||||
|
|
||||||
den.aspects.michael.nixos = {modulesPath, ...}: {
|
|
||||||
imports = [
|
|
||||||
(modulesPath + "/installer/scan/not-detected.nix")
|
|
||||||
./_hosts/michael/backups.nix
|
|
||||||
./_hosts/michael/disk-config.nix
|
|
||||||
./_hosts/michael/gitea.nix
|
|
||||||
./_hosts/michael/hardware-configuration.nix
|
|
||||||
inputs.disko.nixosModules.default
|
|
||||||
];
|
|
||||||
|
|
||||||
networking.hostName = "michael";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -4,5 +4,11 @@
|
|||||||
inputs.nixvim.homeModules.nixvim
|
inputs.nixvim.homeModules.nixvim
|
||||||
./_neovim/default.nix
|
./_neovim/default.nix
|
||||||
];
|
];
|
||||||
|
|
||||||
|
_module.args.nvim-plugin-sources = {
|
||||||
|
code-review-nvim = inputs.code-review-nvim;
|
||||||
|
jj-nvim = inputs.jj-nvim;
|
||||||
|
jj-diffconflicts = inputs.jj-diffconflicts;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -21,24 +21,20 @@
|
|||||||
overalljails = true;
|
overalljails = true;
|
||||||
};
|
};
|
||||||
jails = {
|
jails = {
|
||||||
sshd = {
|
sshd.settings = {
|
||||||
settings = {
|
enabled = true;
|
||||||
enabled = true;
|
port = "ssh";
|
||||||
port = "ssh";
|
filter = "sshd";
|
||||||
filter = "sshd";
|
maxretry = 3;
|
||||||
maxretry = 3;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
gitea = {
|
gitea.settings = {
|
||||||
settings = {
|
enabled = true;
|
||||||
enabled = true;
|
filter = "gitea";
|
||||||
filter = "gitea";
|
logpath = "/var/lib/gitea/log/gitea.log";
|
||||||
logpath = "/var/lib/gitea/log/gitea.log";
|
maxretry = 10;
|
||||||
maxretry = 10;
|
findtime = 3600;
|
||||||
findtime = 3600;
|
bantime = 900;
|
||||||
bantime = 900;
|
action = "iptables-allports";
|
||||||
action = "iptables-allports";
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
@@ -53,30 +49,28 @@
|
|||||||
den.aspects.tailscale.nixos = {
|
den.aspects.tailscale.nixos = {
|
||||||
services.tailscale = {
|
services.tailscale = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
extraSetFlags = ["--ssh"];
|
||||||
openFirewall = true;
|
openFirewall = true;
|
||||||
permitCertUid = "caddy";
|
permitCertUid = "caddy";
|
||||||
useRoutingFeatures = "server";
|
useRoutingFeatures = "server";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
den.aspects.tailscale.darwin = {
|
den.aspects.mosh.nixos = {
|
||||||
services.tailscale = {
|
programs.mosh = {
|
||||||
enable = true;
|
enable = true;
|
||||||
|
openFirewall = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
networking.firewall.interfaces.tailscale0.allowedUDPPortRanges = [
|
||||||
|
{
|
||||||
|
from = 60000;
|
||||||
|
to = 61000;
|
||||||
|
}
|
||||||
|
];
|
||||||
};
|
};
|
||||||
|
|
||||||
# Network tools
|
den.aspects.tailscale.darwin = {
|
||||||
den.aspects.network.homeManager = {
|
services.tailscale.enable = true;
|
||||||
pkgs,
|
|
||||||
lib,
|
|
||||||
...
|
|
||||||
}: {
|
|
||||||
home.packages = with pkgs;
|
|
||||||
[
|
|
||||||
dig
|
|
||||||
]
|
|
||||||
++ lib.optionals stdenv.isDarwin [
|
|
||||||
tailscale
|
|
||||||
];
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
{inputs, ...}: {
|
{inputs, ...}: let
|
||||||
|
local = import ./_lib/local.nix;
|
||||||
|
userHome = "/home/${local.user.name}";
|
||||||
|
in {
|
||||||
den.aspects.nixos-system.nixos = {pkgs, ...}: {
|
den.aspects.nixos-system.nixos = {pkgs, ...}: {
|
||||||
imports = [inputs.home-manager.nixosModules.home-manager];
|
imports = [inputs.home-manager.nixosModules.home-manager];
|
||||||
|
|
||||||
security.sudo.enable = true;
|
security.sudo.enable = true;
|
||||||
security.sudo.extraRules = [
|
security.sudo.extraRules = [
|
||||||
{
|
{
|
||||||
users = ["cschmatzler"];
|
users = [local.user.name];
|
||||||
commands = [
|
commands = [
|
||||||
{
|
{
|
||||||
command = "/run/current-system/sw/bin/nix-env";
|
command = "/run/current-system/sw/bin/nix-env";
|
||||||
@@ -46,9 +49,9 @@
|
|||||||
time.timeZone = "UTC";
|
time.timeZone = "UTC";
|
||||||
|
|
||||||
nix = {
|
nix = {
|
||||||
settings.trusted-users = ["cschmatzler"];
|
settings.trusted-users = [local.user.name];
|
||||||
gc.dates = "weekly";
|
gc.dates = "weekly";
|
||||||
nixPath = ["nixos-config=/home/cschmatzler/.local/share/src/nixos-config:/etc/nixos"];
|
nixPath = ["nixos-config=${userHome}/.local/share/src/nixos-config:/etc/nixos"];
|
||||||
};
|
};
|
||||||
|
|
||||||
boot = {
|
boot = {
|
||||||
@@ -71,9 +74,9 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
users.users = {
|
users.users = {
|
||||||
cschmatzler = {
|
${local.user.name} = {
|
||||||
isNormalUser = true;
|
isNormalUser = true;
|
||||||
home = "/home/cschmatzler";
|
home = userHome;
|
||||||
extraGroups = [
|
extraGroups = [
|
||||||
"wheel"
|
"wheel"
|
||||||
"sudo"
|
"sudo"
|
||||||
@@ -93,7 +96,5 @@
|
|||||||
];
|
];
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
home-manager.useGlobalPkgs = true;
|
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
11
modules/opencode.nix
Normal file
11
modules/opencode.nix
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{lib, ...}: let
|
||||||
|
secretLib = import ./_lib/secrets.nix {inherit lib;};
|
||||||
|
in {
|
||||||
|
den.aspects.opencode-api-key.os = {
|
||||||
|
sops.secrets.opencode-api-key =
|
||||||
|
secretLib.mkUserBinarySecret {
|
||||||
|
name = "opencode-api-key";
|
||||||
|
sopsFile = ../secrets/opencode-api-key;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,58 +1,29 @@
|
|||||||
{inputs, ...}: let
|
{inputs, ...}: let
|
||||||
overlays = [
|
overlays = [
|
||||||
# himalaya
|
# himalaya
|
||||||
(final: prev: {
|
(import ./_overlays/himalaya.nix {inherit inputs;})
|
||||||
himalaya = inputs.himalaya.packages.${prev.stdenv.hostPlatform.system}.default;
|
# direnv (Go 1.26 on darwin disables cgo, but direnv forces external linking)
|
||||||
})
|
(final: prev:
|
||||||
|
prev.lib.optionalAttrs prev.stdenv.hostPlatform.isDarwin {
|
||||||
|
direnv =
|
||||||
|
prev.direnv.overrideAttrs (old: {
|
||||||
|
env =
|
||||||
|
(old.env or {})
|
||||||
|
// {
|
||||||
|
CGO_ENABLED = 1;
|
||||||
|
};
|
||||||
|
});
|
||||||
|
})
|
||||||
# ast-grep (test_scan_invalid_rule_id fails on darwin in sandbox)
|
# ast-grep (test_scan_invalid_rule_id fails on darwin in sandbox)
|
||||||
(final: prev: {
|
(import ./_overlays/ast-grep.nix {inherit inputs;})
|
||||||
ast-grep =
|
|
||||||
prev.ast-grep.overrideAttrs (old: {
|
|
||||||
doCheck = false;
|
|
||||||
});
|
|
||||||
})
|
|
||||||
# jj-ryu
|
# jj-ryu
|
||||||
(final: prev: let
|
(import ./_overlays/jj-ryu.nix {inherit inputs;})
|
||||||
naersk-lib = prev.callPackage inputs.naersk {};
|
# cog-cli
|
||||||
manifest = (prev.lib.importTOML "${inputs.jj-ryu}/Cargo.toml").package;
|
(import ./_overlays/cog-cli.nix {inherit inputs;})
|
||||||
in {
|
|
||||||
jj-ryu =
|
|
||||||
naersk-lib.buildPackage {
|
|
||||||
pname = manifest.name;
|
|
||||||
version = manifest.version;
|
|
||||||
src = inputs.jj-ryu;
|
|
||||||
nativeBuildInputs = [prev.pkg-config];
|
|
||||||
buildInputs = [prev.openssl];
|
|
||||||
OPENSSL_NO_VENDOR = 1;
|
|
||||||
doCheck = false;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
# nono (AI agent sandbox CLI — Cargo workspace)
|
|
||||||
(final: prev: let
|
|
||||||
naersk-lib = prev.callPackage inputs.naersk {};
|
|
||||||
manifest = (prev.lib.importTOML "${inputs.nono}/crates/nono-cli/Cargo.toml").package;
|
|
||||||
in {
|
|
||||||
nono =
|
|
||||||
naersk-lib.buildPackage {
|
|
||||||
pname = manifest.name;
|
|
||||||
version = manifest.version;
|
|
||||||
src = inputs.nono;
|
|
||||||
nativeBuildInputs = [prev.pkg-config prev.cmake prev.perl];
|
|
||||||
buildInputs = [prev.openssl] ++ prev.lib.optionals prev.stdenv.isLinux [prev.dbus];
|
|
||||||
OPENSSL_NO_VENDOR = 1;
|
|
||||||
doCheck = false;
|
|
||||||
};
|
|
||||||
})
|
|
||||||
# jj-starship (passes through upstream overlay)
|
# jj-starship (passes through upstream overlay)
|
||||||
inputs.jj-starship.overlays.default
|
(import ./_overlays/jj-starship.nix {inherit inputs;})
|
||||||
# zjstatus
|
# zjstatus
|
||||||
(final: prev: {
|
(import ./_overlays/zjstatus.nix {inherit inputs;})
|
||||||
zjstatus = inputs.zjstatus.packages.${prev.stdenv.hostPlatform.system}.default;
|
|
||||||
})
|
|
||||||
# tuicr
|
|
||||||
(final: prev: {
|
|
||||||
tuicr = inputs.tuicr.defaultPackage.${prev.stdenv.hostPlatform.system};
|
|
||||||
})
|
|
||||||
];
|
];
|
||||||
in {
|
in {
|
||||||
den.default.nixos.nixpkgs.overlays = overlays;
|
den.default.nixos.nixpkgs.overlays = overlays;
|
||||||
|
|||||||
100
modules/paperless.nix
Normal file
100
modules/paperless.nix
Normal file
@@ -0,0 +1,100 @@
|
|||||||
|
{lib, ...}: let
|
||||||
|
caddyLib = import ./_lib/caddy.nix;
|
||||||
|
local = import ./_lib/local.nix;
|
||||||
|
secretLib = import ./_lib/secrets.nix {inherit lib;};
|
||||||
|
paperlessPrompts = ./_paperless;
|
||||||
|
in {
|
||||||
|
den.aspects.paperless.nixos = {config, ...}: {
|
||||||
|
sops.secrets = {
|
||||||
|
tahani-paperless-password =
|
||||||
|
secretLib.mkBinarySecret {
|
||||||
|
name = "tahani-paperless-password";
|
||||||
|
sopsFile = ../secrets/tahani-paperless-password;
|
||||||
|
};
|
||||||
|
tahani-paperless-gpt-env =
|
||||||
|
secretLib.mkBinarySecret {
|
||||||
|
name = "tahani-paperless-gpt-env";
|
||||||
|
sopsFile = ../secrets/tahani-paperless-gpt-env;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.caddy = {
|
||||||
|
enable = true;
|
||||||
|
enableReload = false;
|
||||||
|
globalConfig = ''
|
||||||
|
admin off
|
||||||
|
'';
|
||||||
|
virtualHosts =
|
||||||
|
caddyLib.mkTailscaleVHost {
|
||||||
|
name = "docs";
|
||||||
|
configText = "reverse_proxy localhost:${toString config.services.paperless.port}";
|
||||||
|
}
|
||||||
|
// caddyLib.mkTailscaleVHost {
|
||||||
|
name = "docs-ai";
|
||||||
|
configText = "reverse_proxy localhost:8081";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
virtualisation.oci-containers = {
|
||||||
|
backend = "docker";
|
||||||
|
containers.paperless-gpt = {
|
||||||
|
image = "icereed/paperless-gpt:latest";
|
||||||
|
autoStart = true;
|
||||||
|
ports = [
|
||||||
|
"127.0.0.1:8081:8080"
|
||||||
|
];
|
||||||
|
volumes = [
|
||||||
|
"paperless-gpt-data:/app/data"
|
||||||
|
"paperless-gpt-prompts:/app/prompts"
|
||||||
|
"${paperlessPrompts}/tag_prompt.tmpl:/app/prompts/tag_prompt.tmpl:ro"
|
||||||
|
"${paperlessPrompts}/title_prompt.tmpl:/app/prompts/title_prompt.tmpl:ro"
|
||||||
|
];
|
||||||
|
environment = {
|
||||||
|
PAPERLESS_BASE_URL = "http://host.docker.internal:${toString config.services.paperless.port}";
|
||||||
|
LLM_PROVIDER = "openai";
|
||||||
|
LLM_MODEL = "gpt-5.4";
|
||||||
|
LLM_LANGUAGE = "German";
|
||||||
|
VISION_LLM_PROVIDER = "openai";
|
||||||
|
VISION_LLM_MODEL = "gpt-5.4";
|
||||||
|
LOG_LEVEL = "info";
|
||||||
|
};
|
||||||
|
environmentFiles = [
|
||||||
|
config.sops.secrets.tahani-paperless-gpt-env.path
|
||||||
|
];
|
||||||
|
extraOptions = [
|
||||||
|
"--add-host=host.docker.internal:host-gateway"
|
||||||
|
];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.redis.servers.paperless = {
|
||||||
|
enable = true;
|
||||||
|
port = 6379;
|
||||||
|
bind = "127.0.0.1";
|
||||||
|
settings = {
|
||||||
|
maxmemory = "256mb";
|
||||||
|
maxmemory-policy = "allkeys-lru";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
services.paperless = {
|
||||||
|
enable = true;
|
||||||
|
address = "0.0.0.0";
|
||||||
|
consumptionDir = "/var/lib/paperless/consume";
|
||||||
|
passwordFile = config.sops.secrets.tahani-paperless-password.path;
|
||||||
|
settings = {
|
||||||
|
PAPERLESS_DBENGINE = "sqlite";
|
||||||
|
PAPERLESS_REDIS = "redis://127.0.0.1:6379";
|
||||||
|
PAPERLESS_CONSUMER_IGNORE_PATTERN = [
|
||||||
|
".DS_STORE/*"
|
||||||
|
"desktop.ini"
|
||||||
|
];
|
||||||
|
PAPERLESS_CONSUMER_POLLING = 30;
|
||||||
|
PAPERLESS_CONSUMER_RECURSIVE = true;
|
||||||
|
PAPERLESS_CONSUMER_SUBDIRS_AS_TAGS = true;
|
||||||
|
PAPERLESS_OCR_LANGUAGE = "deu+eng";
|
||||||
|
PAPERLESS_CSRF_TRUSTED_ORIGINS = "https://${local.tailscaleHost "docs"}";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
7
modules/profiles/host/darwin-base.nix
Normal file
7
modules/profiles/host/darwin-base.nix
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{den, ...}: {
|
||||||
|
den.aspects.host-darwin-base.includes = [
|
||||||
|
den.aspects.darwin-system
|
||||||
|
den.aspects.core
|
||||||
|
den.aspects.tailscale
|
||||||
|
];
|
||||||
|
}
|
||||||
9
modules/profiles/host/nixos-base.nix
Normal file
9
modules/profiles/host/nixos-base.nix
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{den, ...}: {
|
||||||
|
den.aspects.host-nixos-base.includes = [
|
||||||
|
den.aspects.nixos-system
|
||||||
|
den.aspects.core
|
||||||
|
den.aspects.mosh
|
||||||
|
den.aspects.openssh
|
||||||
|
den.aspects.tailscale
|
||||||
|
];
|
||||||
|
}
|
||||||
6
modules/profiles/host/public-server.nix
Normal file
6
modules/profiles/host/public-server.nix
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{den, ...}: {
|
||||||
|
den.aspects.host-public-server.includes = [
|
||||||
|
den.aspects.host-nixos-base
|
||||||
|
den.aspects.fail2ban
|
||||||
|
];
|
||||||
|
}
|
||||||
11
modules/profiles/user/base.nix
Normal file
11
modules/profiles/user/base.nix
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
{den, ...}: {
|
||||||
|
den.aspects.user-base.includes = [
|
||||||
|
den.aspects.shell
|
||||||
|
den.aspects.ssh-client
|
||||||
|
den.aspects.terminal
|
||||||
|
den.aspects.atuin
|
||||||
|
den.aspects.secrets
|
||||||
|
den.aspects.zellij
|
||||||
|
den.aspects.zk
|
||||||
|
];
|
||||||
|
}
|
||||||
12
modules/profiles/user/darwin-laptop.nix
Normal file
12
modules/profiles/user/darwin-laptop.nix
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
{den, ...}: {
|
||||||
|
den.aspects.user-darwin-laptop = {
|
||||||
|
includes = [
|
||||||
|
den.aspects.user-workstation
|
||||||
|
den.aspects.desktop
|
||||||
|
];
|
||||||
|
|
||||||
|
homeManager = {
|
||||||
|
fonts.fontconfig.enable = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
5
modules/profiles/user/minimal.nix
Normal file
5
modules/profiles/user/minimal.nix
Normal file
@@ -0,0 +1,5 @@
|
|||||||
|
{den, ...}: {
|
||||||
|
den.aspects.user-minimal.includes = [
|
||||||
|
den.aspects.shell
|
||||||
|
];
|
||||||
|
}
|
||||||
7
modules/profiles/user/personal.nix
Normal file
7
modules/profiles/user/personal.nix
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{...}: let
|
||||||
|
local = import ../../_lib/local.nix;
|
||||||
|
in {
|
||||||
|
den.aspects.user-personal.homeManager = {
|
||||||
|
programs.git.settings.user.email = local.user.emails.personal;
|
||||||
|
};
|
||||||
|
}
|
||||||
8
modules/profiles/user/workstation.nix
Normal file
8
modules/profiles/user/workstation.nix
Normal file
@@ -0,0 +1,8 @@
|
|||||||
|
{den, ...}: {
|
||||||
|
den.aspects.user-workstation.includes = [
|
||||||
|
den.aspects.user-base
|
||||||
|
den.aspects.dev-tools
|
||||||
|
den.aspects.neovim
|
||||||
|
den.aspects.ai-tools
|
||||||
|
];
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user