Compare commits
94 Commits
25968af711
...
master
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c6c3ffb548 | ||
|
|
9b423315b3 | ||
|
|
14d906ef93 | ||
|
|
e7393d6fa4 | ||
| 1da8c96447 | |||
| 6a8cb62903 | |||
| a3e247e5af | |||
| f7f0c4072e | |||
|
|
e601fde026 | ||
|
|
642e764b81 | ||
|
|
0a224db2fc | ||
|
|
69e8fb93be | ||
|
|
c63ecc899c | ||
|
|
5d2bfbd27c | ||
|
|
66c398d196 | ||
|
|
729ea971c5 | ||
|
|
ce16dfff2e | ||
|
|
31464e245e | ||
| 251a6892a3 | |||
| 95aeff28ad | |||
|
|
fa339ae8cc | ||
|
|
cec0c31d91 | ||
|
|
6a8360305d | ||
|
|
5edd0929d0 | ||
|
|
60aeec7cfe | ||
|
|
161be34111 | ||
|
|
7b6bcfeb1c | ||
|
|
03ad7451fc | ||
|
|
69b736e302 | ||
| 300ef0c28f | |||
|
|
57ebad1358 | ||
|
|
35f4821bc5 | ||
| bc41c9a428 | |||
|
|
edae9ba3c9 | ||
|
|
54fa93574b | ||
|
|
c9ecc0809f | ||
|
|
44c7e0d19a | ||
|
|
a0f4d401df | ||
|
|
d04d405297 | ||
|
|
2494da1054 | ||
|
|
b2c8c935af | ||
|
|
c6d8376dda | ||
|
|
0331316755 | ||
|
|
a4e540630d | ||
|
|
6c985c640d | ||
|
|
5f90f16d99 | ||
| 613e9223e4 | |||
|
|
03ffb69526 | ||
| 7d231f0a05 | |||
|
|
b6f13f06d0 | ||
| 72f4784445 | |||
|
|
3d8f9e3003 | ||
|
|
6426490fe7 | ||
|
|
8dfe6a8356 | ||
|
|
1a9a60b0db | ||
|
|
77833a7eca | ||
|
|
715d06e83b | ||
|
|
19332ebb74 | ||
|
|
b65bbcacbe | ||
|
|
feb5dcd673 | ||
|
|
c6740493b4 | ||
|
|
53220ae007 | ||
|
|
ccd2e7df08 | ||
|
|
ec6b3b9683 | ||
|
|
85fd377ab1 | ||
|
|
cd3c5ed9ad | ||
|
|
df3c1f6e70 | ||
|
|
561b174ddc | ||
|
|
e915e5ea19 | ||
|
|
7e09e3b5ed | ||
|
|
a63f91d25b | ||
|
|
53671a4717 | ||
|
|
5c7156a93c | ||
|
|
c72bd4fdc8 | ||
|
|
564d209402 | ||
|
|
0f2693a812 | ||
|
|
b85be2cec0 | ||
|
|
0aa01e3738 | ||
|
|
6d8bcbb27b | ||
|
|
27eb412218 | ||
|
|
038afafd33 | ||
|
|
39931b3eab | ||
|
|
151dde22f6 | ||
|
|
db57ddae1f | ||
|
|
66480706fa | ||
|
|
418c3b1331 | ||
|
|
99d281fd4f | ||
|
|
e98be80e55 | ||
|
|
f084f3a4f5 | ||
|
|
d1b7cd99e2 | ||
|
|
6b04156750 | ||
|
|
10ef69b99f | ||
|
|
ef786fd0b1 | ||
|
|
15fff9fb99 |
73
.beads/.gitignore
vendored
Normal file
73
.beads/.gitignore
vendored
Normal file
@@ -0,0 +1,73 @@
|
|||||||
|
# Dolt database (managed by Dolt, not git)
|
||||||
|
dolt/
|
||||||
|
embeddeddolt/
|
||||||
|
|
||||||
|
# Runtime files
|
||||||
|
bd.sock
|
||||||
|
bd.sock.startlock
|
||||||
|
sync-state.json
|
||||||
|
last-touched
|
||||||
|
.exclusive-lock
|
||||||
|
|
||||||
|
# Daemon runtime (lock, log, pid)
|
||||||
|
daemon.*
|
||||||
|
|
||||||
|
# Interactions log (runtime, not versioned)
|
||||||
|
interactions.jsonl
|
||||||
|
|
||||||
|
# Push state (runtime, per-machine)
|
||||||
|
push-state.json
|
||||||
|
|
||||||
|
# Lock files (various runtime locks)
|
||||||
|
*.lock
|
||||||
|
|
||||||
|
# Credential key (encryption key for federation peer auth — never commit)
|
||||||
|
.beads-credential-key
|
||||||
|
|
||||||
|
# Local version tracking (prevents upgrade notification spam after git ops)
|
||||||
|
.local_version
|
||||||
|
|
||||||
|
# Worktree redirect file (contains relative path to main repo's .beads/)
|
||||||
|
# Must not be committed as paths would be wrong in other clones
|
||||||
|
redirect
|
||||||
|
|
||||||
|
# Sync state (local-only, per-machine)
|
||||||
|
# These files are machine-specific and should not be shared across clones
|
||||||
|
.sync.lock
|
||||||
|
export-state/
|
||||||
|
export-state.json
|
||||||
|
|
||||||
|
# Ephemeral store (SQLite - wisps/molecules, intentionally not versioned)
|
||||||
|
ephemeral.sqlite3
|
||||||
|
ephemeral.sqlite3-journal
|
||||||
|
ephemeral.sqlite3-wal
|
||||||
|
ephemeral.sqlite3-shm
|
||||||
|
|
||||||
|
# Dolt server management (auto-started by bd)
|
||||||
|
dolt-server.pid
|
||||||
|
dolt-server.log
|
||||||
|
dolt-server.lock
|
||||||
|
dolt-server.port
|
||||||
|
dolt-server.activity
|
||||||
|
|
||||||
|
# Corrupt backup directories (created by bd doctor --fix recovery)
|
||||||
|
*.corrupt.backup/
|
||||||
|
|
||||||
|
# Backup data (auto-exported JSONL, local-only)
|
||||||
|
backup/
|
||||||
|
|
||||||
|
# Per-project environment file (Dolt connection config, GH#2520)
|
||||||
|
.env
|
||||||
|
|
||||||
|
# Legacy files (from pre-Dolt versions)
|
||||||
|
*.db
|
||||||
|
*.db?*
|
||||||
|
*.db-journal
|
||||||
|
*.db-wal
|
||||||
|
*.db-shm
|
||||||
|
db.sqlite
|
||||||
|
bd.db
|
||||||
|
# NOTE: Do NOT add negation patterns here.
|
||||||
|
# They would override fork protection in .git/info/exclude.
|
||||||
|
# Config files (metadata.json, config.yaml) are tracked by git by default
|
||||||
|
# since no pattern above ignores them.
|
||||||
81
.beads/README.md
Normal file
81
.beads/README.md
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
# Beads - AI-Native Issue Tracking
|
||||||
|
|
||||||
|
Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.
|
||||||
|
|
||||||
|
## What is Beads?
|
||||||
|
|
||||||
|
Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.
|
||||||
|
|
||||||
|
**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads)
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
### Essential Commands
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Create new issues
|
||||||
|
bd create "Add user authentication"
|
||||||
|
|
||||||
|
# View all issues
|
||||||
|
bd list
|
||||||
|
|
||||||
|
# View issue details
|
||||||
|
bd show <issue-id>
|
||||||
|
|
||||||
|
# Update issue status
|
||||||
|
bd update <issue-id> --claim
|
||||||
|
bd update <issue-id> --status done
|
||||||
|
|
||||||
|
# Sync with Dolt remote
|
||||||
|
bd dolt push
|
||||||
|
```
|
||||||
|
|
||||||
|
### Working with Issues
|
||||||
|
|
||||||
|
Issues in Beads are:
|
||||||
|
- **Git-native**: Stored in Dolt database with version control and branching
|
||||||
|
- **AI-friendly**: CLI-first design works perfectly with AI coding agents
|
||||||
|
- **Branch-aware**: Issues can follow your branch workflow
|
||||||
|
- **Always in sync**: Auto-syncs with your commits
|
||||||
|
|
||||||
|
## Why Beads?
|
||||||
|
|
||||||
|
✨ **AI-Native Design**
|
||||||
|
- Built specifically for AI-assisted development workflows
|
||||||
|
- CLI-first interface works seamlessly with AI coding agents
|
||||||
|
- No context switching to web UIs
|
||||||
|
|
||||||
|
🚀 **Developer Focused**
|
||||||
|
- Issues live in your repo, right next to your code
|
||||||
|
- Works offline, syncs when you push
|
||||||
|
- Fast, lightweight, and stays out of your way
|
||||||
|
|
||||||
|
🔧 **Git Integration**
|
||||||
|
- Automatic sync with git commits
|
||||||
|
- Branch-aware issue tracking
|
||||||
|
- Dolt-native three-way merge resolution
|
||||||
|
|
||||||
|
## Get Started with Beads
|
||||||
|
|
||||||
|
Try Beads in your own projects:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Install Beads
|
||||||
|
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
|
||||||
|
|
||||||
|
# Initialize in your repo
|
||||||
|
bd init
|
||||||
|
|
||||||
|
# Create your first issue
|
||||||
|
bd create "Try out Beads"
|
||||||
|
```
|
||||||
|
|
||||||
|
## Learn More
|
||||||
|
|
||||||
|
- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs)
|
||||||
|
- **Quick Start Guide**: Run `bd quickstart`
|
||||||
|
- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Beads: Issue tracking that moves at the speed of thought* ⚡
|
||||||
56
.beads/config.yaml
Normal file
56
.beads/config.yaml
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
# Beads Configuration File
|
||||||
|
# This file configures default behavior for all bd commands in this repository
|
||||||
|
# All settings can also be set via environment variables (BD_* prefix)
|
||||||
|
# or overridden with command-line flags
|
||||||
|
|
||||||
|
# Issue prefix for this repository (used by bd init)
|
||||||
|
# If not set, bd init will auto-detect from directory name
|
||||||
|
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
|
||||||
|
# issue-prefix: ""
|
||||||
|
|
||||||
|
# Use no-db mode: JSONL-only, no Dolt database
|
||||||
|
# When true, bd will use .beads/issues.jsonl as the source of truth
|
||||||
|
# no-db: false
|
||||||
|
|
||||||
|
# Enable JSON output by default
|
||||||
|
# json: false
|
||||||
|
|
||||||
|
# Feedback title formatting for mutating commands (create/update/close/dep/edit)
|
||||||
|
# 0 = hide titles, N > 0 = truncate to N characters
|
||||||
|
# output:
|
||||||
|
# title-length: 255
|
||||||
|
|
||||||
|
# Default actor for audit trails (overridden by BEADS_ACTOR or --actor)
|
||||||
|
# actor: ""
|
||||||
|
|
||||||
|
# Export events (audit trail) to .beads/events.jsonl on each flush/sync
|
||||||
|
# When enabled, new events are appended incrementally using a high-water mark.
|
||||||
|
# Use 'bd export --events' to trigger manually regardless of this setting.
|
||||||
|
# events-export: false
|
||||||
|
|
||||||
|
# Multi-repo configuration (experimental - bd-307)
|
||||||
|
# Allows hydrating from multiple repositories and routing writes to the correct database
|
||||||
|
# repos:
|
||||||
|
# primary: "." # Primary repo (where this database lives)
|
||||||
|
# additional: # Additional repos to hydrate from (read-only)
|
||||||
|
# - ~/beads-planning # Personal planning repo
|
||||||
|
# - ~/work-planning # Work planning repo
|
||||||
|
|
||||||
|
# JSONL backup (periodic export for off-machine recovery)
|
||||||
|
# Auto-enabled when a git remote exists. Override explicitly:
|
||||||
|
# backup:
|
||||||
|
# enabled: false # Disable auto-backup entirely
|
||||||
|
# interval: 15m # Minimum time between auto-exports
|
||||||
|
# git-push: false # Disable git push (export locally only)
|
||||||
|
# git-repo: "" # Separate git repo for backups (default: project repo)
|
||||||
|
|
||||||
|
# Integration settings (access with 'bd config get/set')
|
||||||
|
# These are stored in the database, not in this file:
|
||||||
|
# - jira.url
|
||||||
|
# - jira.project
|
||||||
|
# - linear.url
|
||||||
|
# - linear.api-key
|
||||||
|
# - github.org
|
||||||
|
# - github.repo
|
||||||
|
|
||||||
|
sync.remote: "git+ssh://gitea@code.m3ta.dev/m3tam3re/nixpkgs.git"
|
||||||
24
.beads/hooks/post-checkout
Executable file
24
.beads/hooks/post-checkout
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
|
||||||
|
# This section is managed by beads. Do not remove these markers.
|
||||||
|
if command -v bd >/dev/null 2>&1; then
|
||||||
|
export BD_GIT_HOOK=1
|
||||||
|
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
|
||||||
|
if command -v timeout >/dev/null 2>&1; then
|
||||||
|
timeout "$_bd_timeout" bd hooks run post-checkout "$@"
|
||||||
|
_bd_exit=$?
|
||||||
|
if [ $_bd_exit -eq 124 ]; then
|
||||||
|
echo >&2 "beads: hook 'post-checkout' timed out after ${_bd_timeout}s — continuing without beads"
|
||||||
|
_bd_exit=0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
bd hooks run post-checkout "$@"
|
||||||
|
_bd_exit=$?
|
||||||
|
fi
|
||||||
|
if [ $_bd_exit -eq 3 ]; then
|
||||||
|
echo >&2 "beads: database not initialized — skipping hook 'post-checkout'"
|
||||||
|
_bd_exit=0
|
||||||
|
fi
|
||||||
|
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
|
||||||
|
fi
|
||||||
|
# --- END BEADS INTEGRATION v1.0.2 ---
|
||||||
24
.beads/hooks/post-merge
Executable file
24
.beads/hooks/post-merge
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
|
||||||
|
# This section is managed by beads. Do not remove these markers.
|
||||||
|
if command -v bd >/dev/null 2>&1; then
|
||||||
|
export BD_GIT_HOOK=1
|
||||||
|
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
|
||||||
|
if command -v timeout >/dev/null 2>&1; then
|
||||||
|
timeout "$_bd_timeout" bd hooks run post-merge "$@"
|
||||||
|
_bd_exit=$?
|
||||||
|
if [ $_bd_exit -eq 124 ]; then
|
||||||
|
echo >&2 "beads: hook 'post-merge' timed out after ${_bd_timeout}s — continuing without beads"
|
||||||
|
_bd_exit=0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
bd hooks run post-merge "$@"
|
||||||
|
_bd_exit=$?
|
||||||
|
fi
|
||||||
|
if [ $_bd_exit -eq 3 ]; then
|
||||||
|
echo >&2 "beads: database not initialized — skipping hook 'post-merge'"
|
||||||
|
_bd_exit=0
|
||||||
|
fi
|
||||||
|
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
|
||||||
|
fi
|
||||||
|
# --- END BEADS INTEGRATION v1.0.2 ---
|
||||||
24
.beads/hooks/pre-commit
Executable file
24
.beads/hooks/pre-commit
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
|
||||||
|
# This section is managed by beads. Do not remove these markers.
|
||||||
|
if command -v bd >/dev/null 2>&1; then
|
||||||
|
export BD_GIT_HOOK=1
|
||||||
|
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
|
||||||
|
if command -v timeout >/dev/null 2>&1; then
|
||||||
|
timeout "$_bd_timeout" bd hooks run pre-commit "$@"
|
||||||
|
_bd_exit=$?
|
||||||
|
if [ $_bd_exit -eq 124 ]; then
|
||||||
|
echo >&2 "beads: hook 'pre-commit' timed out after ${_bd_timeout}s — continuing without beads"
|
||||||
|
_bd_exit=0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
bd hooks run pre-commit "$@"
|
||||||
|
_bd_exit=$?
|
||||||
|
fi
|
||||||
|
if [ $_bd_exit -eq 3 ]; then
|
||||||
|
echo >&2 "beads: database not initialized — skipping hook 'pre-commit'"
|
||||||
|
_bd_exit=0
|
||||||
|
fi
|
||||||
|
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
|
||||||
|
fi
|
||||||
|
# --- END BEADS INTEGRATION v1.0.2 ---
|
||||||
24
.beads/hooks/pre-push
Executable file
24
.beads/hooks/pre-push
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
|
||||||
|
# This section is managed by beads. Do not remove these markers.
|
||||||
|
if command -v bd >/dev/null 2>&1; then
|
||||||
|
export BD_GIT_HOOK=1
|
||||||
|
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
|
||||||
|
if command -v timeout >/dev/null 2>&1; then
|
||||||
|
timeout "$_bd_timeout" bd hooks run pre-push "$@"
|
||||||
|
_bd_exit=$?
|
||||||
|
if [ $_bd_exit -eq 124 ]; then
|
||||||
|
echo >&2 "beads: hook 'pre-push' timed out after ${_bd_timeout}s — continuing without beads"
|
||||||
|
_bd_exit=0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
bd hooks run pre-push "$@"
|
||||||
|
_bd_exit=$?
|
||||||
|
fi
|
||||||
|
if [ $_bd_exit -eq 3 ]; then
|
||||||
|
echo >&2 "beads: database not initialized — skipping hook 'pre-push'"
|
||||||
|
_bd_exit=0
|
||||||
|
fi
|
||||||
|
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
|
||||||
|
fi
|
||||||
|
# --- END BEADS INTEGRATION v1.0.2 ---
|
||||||
24
.beads/hooks/prepare-commit-msg
Executable file
24
.beads/hooks/prepare-commit-msg
Executable file
@@ -0,0 +1,24 @@
|
|||||||
|
#!/usr/bin/env sh
|
||||||
|
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
|
||||||
|
# This section is managed by beads. Do not remove these markers.
|
||||||
|
if command -v bd >/dev/null 2>&1; then
|
||||||
|
export BD_GIT_HOOK=1
|
||||||
|
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
|
||||||
|
if command -v timeout >/dev/null 2>&1; then
|
||||||
|
timeout "$_bd_timeout" bd hooks run prepare-commit-msg "$@"
|
||||||
|
_bd_exit=$?
|
||||||
|
if [ $_bd_exit -eq 124 ]; then
|
||||||
|
echo >&2 "beads: hook 'prepare-commit-msg' timed out after ${_bd_timeout}s — continuing without beads"
|
||||||
|
_bd_exit=0
|
||||||
|
fi
|
||||||
|
else
|
||||||
|
bd hooks run prepare-commit-msg "$@"
|
||||||
|
_bd_exit=$?
|
||||||
|
fi
|
||||||
|
if [ $_bd_exit -eq 3 ]; then
|
||||||
|
echo >&2 "beads: database not initialized — skipping hook 'prepare-commit-msg'"
|
||||||
|
_bd_exit=0
|
||||||
|
fi
|
||||||
|
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
|
||||||
|
fi
|
||||||
|
# --- END BEADS INTEGRATION v1.0.2 ---
|
||||||
1
.beads/issues.jsonl
Normal file
1
.beads/issues.jsonl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"id":"nixpkgs-ng1","title":"Configure agent git identity in nixpkgs repo","description":"Git commits are using p@m3ta.dev instead of m3ta-chiron@agentmail.to. The GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL environment variables are not set in this environment. Need to configure the agent git identity for this repository following the pattern in AGENTS.md","status":"open","priority":2,"issue_type":"task","owner":"p@m3ta.dev","created_at":"2026-04-27T18:16:17Z","created_by":"m3tm3re","updated_at":"2026-04-27T18:16:17Z","dependency_count":0,"dependent_count":0,"comment_count":0}
|
||||||
7
.beads/metadata.json
Normal file
7
.beads/metadata.json
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"database": "dolt",
|
||||||
|
"backend": "dolt",
|
||||||
|
"dolt_mode": "embedded",
|
||||||
|
"dolt_database": "nixpkgs",
|
||||||
|
"project_id": "b57a167a-6526-4211-a6c1-51686e431912"
|
||||||
|
}
|
||||||
@@ -179,94 +179,79 @@ jobs:
|
|||||||
is_custom_update_script() {
|
is_custom_update_script() {
|
||||||
local pkg=$1
|
local pkg=$1
|
||||||
local result
|
local result
|
||||||
# nix-update-script returns a list like [ "/nix/store/...-nix-update/bin/nix-update" ]
|
# Custom scripts (./update.sh) become store paths ending in .sh
|
||||||
# Custom scripts return a path like "/nix/store/.../update.sh"
|
# nix-update-script produces a list with nix-update binary path
|
||||||
result=$(nix eval --impure --raw --expr "
|
result=$(nix eval --impure --raw --expr "
|
||||||
let
|
let
|
||||||
flake = builtins.getFlake (toString ./.);
|
flake = builtins.getFlake (toString ./.);
|
||||||
pkg = flake.packages.\${builtins.currentSystem}.${pkg};
|
pkg = flake.packages.\${builtins.currentSystem}.${pkg};
|
||||||
script = pkg.passthru.updateScript or [];
|
script = pkg.passthru.updateScript or null;
|
||||||
in
|
in
|
||||||
if builtins.isPath script then
|
if script == null then \"none\"
|
||||||
\"custom\"
|
else if builtins.isPath script then \"custom\"
|
||||||
else if builtins.isList script && builtins.length script > 0 then
|
else if builtins.isString script then
|
||||||
|
(if builtins.match \".*\\.sh$\" script != null then \"custom\" else \"other\")
|
||||||
|
else if builtins.isList script then
|
||||||
let first = builtins.head script;
|
let first = builtins.head script;
|
||||||
in if builtins.isString first && builtins.match \".*/nix-update$\" first != null then
|
in if builtins.isString first && builtins.match \".*/nix-update$\" first != null
|
||||||
\"nix-update-script\"
|
then \"nix-update-script\"
|
||||||
else if builtins.isPath first then
|
else \"custom\"
|
||||||
\"custom\"
|
else if builtins.isAttrs script && script ? command then \"custom\"
|
||||||
else
|
else \"other\"
|
||||||
\"other\"
|
|
||||||
else if builtins.isAttrs script && script ? command then
|
|
||||||
if builtins.isPath script.command then \"custom\"
|
|
||||||
else if builtins.isList script.command && builtins.isPath (builtins.head script.command) then \"custom\"
|
|
||||||
else \"other\"
|
|
||||||
else
|
|
||||||
\"other\"
|
|
||||||
" 2>/dev/null || echo "other")
|
" 2>/dev/null || echo "other")
|
||||||
[[ "$result" == "custom" ]]
|
[[ "$result" == "custom" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
# Run a custom update script directly (for packages like n8n)
|
# Run a custom update script directly
|
||||||
|
# Scripts must use nix-shell shebang for their own dependencies
|
||||||
run_custom_update_script() {
|
run_custom_update_script() {
|
||||||
local pkg=$1
|
local pkg=$1
|
||||||
local before_hash=$(git rev-parse HEAD)
|
local before_hash=$(git rev-parse HEAD)
|
||||||
|
|
||||||
echo " 🔧 Detected custom update script for $pkg"
|
echo " 🔧 Detected custom update script for $pkg"
|
||||||
|
|
||||||
# Get package metadata for environment variables
|
# Resolve the store path of the update script
|
||||||
local name pname version
|
local script_path
|
||||||
name=$(nix eval --raw .#${pkg}.name 2>/dev/null || echo "$pkg")
|
script_path=$(nix eval --impure --raw --expr "
|
||||||
pname=$(nix eval --raw .#${pkg}.pname 2>/dev/null || echo "$pkg")
|
let
|
||||||
version=$(nix eval --raw .#${pkg}.version 2>/dev/null || echo "unknown")
|
flake = builtins.getFlake (toString ./.);
|
||||||
|
pkg = flake.packages.\${builtins.currentSystem}.${pkg};
|
||||||
# Run the custom script using nix develop
|
script = pkg.passthru.updateScript;
|
||||||
if nix develop --impure --expr "
|
cmd = if builtins.isAttrs script then script.command
|
||||||
with builtins;
|
else if builtins.isList script then builtins.head script
|
||||||
let
|
else script;
|
||||||
flake = getFlake (toString ./.);
|
in toString cmd
|
||||||
pkgs = flake.inputs.nixpkgs.legacyPackages.\${currentSystem};
|
" 2>/dev/null)
|
||||||
pkg' = flake.packages.\${currentSystem}.${pkg};
|
|
||||||
script = pkg'.passthru.updateScript;
|
if [ -z "$script_path" ]; then
|
||||||
cmd = if isAttrs script then script.command else script;
|
echo "❌ Could not resolve update script path for $pkg"
|
||||||
scriptPath = if isList cmd then head cmd else cmd;
|
return 1
|
||||||
in pkgs.mkShell {
|
fi
|
||||||
inputsFrom = [pkg'];
|
|
||||||
packages = with pkgs; [ curl jq git ];
|
# Set environment variables that nix-update would normally provide
|
||||||
}
|
export UPDATE_NIX_NAME=$(nix eval --raw .#${pkg}.name 2>/dev/null || echo "$pkg")
|
||||||
" --command bash -c "
|
export UPDATE_NIX_PNAME=$(nix eval --raw .#${pkg}.pname 2>/dev/null || echo "$pkg")
|
||||||
export UPDATE_NIX_NAME='${name}'
|
export UPDATE_NIX_OLD_VERSION=$(nix eval --raw .#${pkg}.version 2>/dev/null || echo "unknown")
|
||||||
export UPDATE_NIX_PNAME='${pname}'
|
export UPDATE_NIX_ATTR_PATH="$pkg"
|
||||||
export UPDATE_NIX_OLD_VERSION='${version}'
|
|
||||||
export UPDATE_NIX_ATTR_PATH='${pkg}'
|
echo " Running: $script_path"
|
||||||
|
if bash "$script_path" 2>&1 | tee /tmp/update-${pkg}.log; then
|
||||||
# Get the script path and execute it
|
|
||||||
script_path=\$(nix eval --impure --raw --expr '
|
|
||||||
let
|
|
||||||
flake = builtins.getFlake (toString ./.);
|
|
||||||
pkg = flake.packages.\${builtins.currentSystem}.${pkg};
|
|
||||||
script = pkg.passthru.updateScript;
|
|
||||||
cmd = if builtins.isAttrs script then script.command else script;
|
|
||||||
in if builtins.isList cmd then toString (builtins.head cmd)
|
|
||||||
else toString cmd
|
|
||||||
' 2>/dev/null)
|
|
||||||
|
|
||||||
if [ -n \"\$script_path\" ]; then
|
|
||||||
echo \"Running: \$script_path\"
|
|
||||||
bash \"\$script_path\"
|
|
||||||
fi
|
|
||||||
" 2>&1 | tee /tmp/update-${pkg}.log; then
|
|
||||||
if [ "$(check_commit "$before_hash")" = "true" ]; then
|
if [ "$(check_commit "$before_hash")" = "true" ]; then
|
||||||
echo "✅ Updated $pkg (via custom script)"
|
echo "✅ Updated $pkg (via custom script)"
|
||||||
return 0
|
return 0
|
||||||
fi
|
fi
|
||||||
|
# Script succeeded but no commit — may already be up to date
|
||||||
|
if grep -q "already at latest\|nothing to do" /tmp/update-${pkg}.log; then
|
||||||
|
echo "✓ $pkg already up to date"
|
||||||
|
return 1
|
||||||
|
fi
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Clean up on failure
|
# Clean up on failure
|
||||||
git checkout -- . 2>/dev/null || true
|
git checkout -- . 2>/dev/null || true
|
||||||
git clean -fd 2>/dev/null || true
|
git clean -fd 2>/dev/null || true
|
||||||
|
|
||||||
if ! grep -q "already up to date\|No new version found" /tmp/update-${pkg}.log; then
|
if ! grep -q "already at latest\|nothing to do\|No new version found" /tmp/update-${pkg}.log; then
|
||||||
echo "⚠️ Custom update script failed for $pkg"
|
echo "⚠️ Custom update script failed for $pkg"
|
||||||
fi
|
fi
|
||||||
return 1
|
return 1
|
||||||
|
|||||||
13
.gitignore
vendored
13
.gitignore
vendored
@@ -37,3 +37,16 @@ flake.lock.bak
|
|||||||
.sidecar/
|
.sidecar/
|
||||||
.sidecar-*
|
.sidecar-*
|
||||||
.sisyphus/
|
.sisyphus/
|
||||||
|
.sidecar-agent
|
||||||
|
.sidecar-task
|
||||||
|
.sidecar-pr
|
||||||
|
.sidecar-start.sh
|
||||||
|
.sidecar-base
|
||||||
|
.td-root
|
||||||
|
.cache
|
||||||
|
.pi*
|
||||||
|
|
||||||
|
# Beads / Dolt files (added by bd init)
|
||||||
|
.dolt/
|
||||||
|
*.db
|
||||||
|
.beads-credential-key
|
||||||
|
|||||||
218
AGENTS.md
218
AGENTS.md
@@ -1,170 +1,84 @@
|
|||||||
# m3ta-nixpkgs Knowledge Base
|
# Agent Instructions
|
||||||
|
|
||||||
## MANDATORY: Use td for Task Management
|
This project uses **bd** (beads) for issue tracking. Run `bd prime` for full workflow context.
|
||||||
|
|
||||||
You must run td usage --new-session at conversation start (or after /clear) to see current work.
|
## Quick Reference
|
||||||
Use td usage -q for subsequent reads.
|
|
||||||
|
|
||||||
**Generated:** 2026-02-14
|
|
||||||
**Commit:** dc2f3b6
|
|
||||||
**Branch:** master
|
|
||||||
|
|
||||||
## OVERVIEW
|
|
||||||
|
|
||||||
Personal Nix flake: custom packages, overlays, NixOS/Home Manager modules, dev shells. Flakes-only (no channels).
|
|
||||||
|
|
||||||
## STRUCTURE
|
|
||||||
|
|
||||||
```
|
|
||||||
.
|
|
||||||
├── flake.nix # Entry: packages, overlays, modules, shells, lib
|
|
||||||
├── pkgs/ # Custom packages (one dir each, callPackage registry)
|
|
||||||
├── modules/
|
|
||||||
│ ├── nixos/ # System modules (ports.nix)
|
|
||||||
│ └── home-manager/ # User modules by category (cli/, coding/, ports.nix)
|
|
||||||
├── lib/ # Shared utilities (ports.nix)
|
|
||||||
├── shells/ # Dev environments (default, python, devops)
|
|
||||||
├── overlays/mods/ # Package modifications (n8n version bump)
|
|
||||||
├── templates/ # Boilerplate for new packages/modules
|
|
||||||
├── examples/ # Usage examples
|
|
||||||
└── .gitea/workflows/ # CI/CD workflows (nix-update automation)
|
|
||||||
```
|
|
||||||
|
|
||||||
## WHERE TO LOOK
|
|
||||||
|
|
||||||
| Task | Location | Notes |
|
|
||||||
| -------------------- | ---------------------------------- | ------------------------------------- |
|
|
||||||
| Add package | `pkgs/<name>/default.nix` | Register in `pkgs/default.nix` |
|
|
||||||
| Add NixOS module | `modules/nixos/<name>.nix` | Import in `modules/nixos/default.nix` |
|
|
||||||
| Add HM module | `modules/home-manager/<category>/` | Category: cli, coding, or root |
|
|
||||||
| Override nixpkgs pkg | `overlays/mods/<name>.nix` | Import in `overlays/mods/default.nix` |
|
|
||||||
| Add dev shell | `shells/<name>.nix` | Register in `shells/default.nix` |
|
|
||||||
| Use port management | `config.m3ta.ports.get "service"` | Host-specific via `hostOverrides` |
|
|
||||||
| CI/CD workflows | `.gitea/workflows/<name>.yml` | Automated package updates (nix-update) |
|
|
||||||
|
|
||||||
## CONVENTIONS
|
|
||||||
|
|
||||||
**Formatter**: `nix fmt` before commit (alejandra)
|
|
||||||
|
|
||||||
**Naming**:
|
|
||||||
|
|
||||||
- Packages: `lowercase-hyphen` (e.g., `hyprpaper-random`)
|
|
||||||
- Variables: `camelCase` (e.g., `portHelpers`)
|
|
||||||
- Module options: `m3ta.*` namespace
|
|
||||||
|
|
||||||
**Imports**: Multi-line, trailing commas:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{
|
|
||||||
lib,
|
|
||||||
stdenv,
|
|
||||||
fetchFromGitHub,
|
|
||||||
}:
|
|
||||||
```
|
|
||||||
|
|
||||||
**Modules**: Standard pattern:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{ config, lib, pkgs, ... }:
|
|
||||||
with lib; let
|
|
||||||
cfg = config.m3ta.myModule;
|
|
||||||
in {
|
|
||||||
options.m3ta.myModule = {
|
|
||||||
enable = mkEnableOption "description";
|
|
||||||
};
|
|
||||||
config = mkIf cfg.enable { ... };
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
**Meta**: Always include all fields:
|
|
||||||
|
|
||||||
```nix
|
|
||||||
meta = with lib; {
|
|
||||||
description = "...";
|
|
||||||
homepage = "...";
|
|
||||||
license = licenses.mit;
|
|
||||||
platforms = platforms.linux;
|
|
||||||
mainProgram = "...";
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## PACKAGE PATTERNS
|
|
||||||
|
|
||||||
**Rust**: `rustPlatform.buildRustPackage rec { cargoLock.lockFile = src + "/Cargo.lock"; }`
|
|
||||||
|
|
||||||
**Shell**: `writeShellScriptBin "name" ''script''` or `mkDerivation` with custom `installPhase`
|
|
||||||
|
|
||||||
**AppImage**: `appimageTools.wrapType2 { ... }`
|
|
||||||
|
|
||||||
**Custom fetcher**: `fetchFromGitea { domain = "code.m3ta.dev"; owner = "m3tam3re"; ... }`
|
|
||||||
|
|
||||||
## MODULE PATTERNS
|
|
||||||
|
|
||||||
**Simple**: `options.cli.name = { enable = mkEnableOption "..."; }; config = mkIf cfg.enable { ... };`
|
|
||||||
|
|
||||||
**Multiple**: `config = mkMerge [ (mkIf cfg.x.enable { ... }) (mkIf cfg.y.enable { ... }) ];`
|
|
||||||
|
|
||||||
**Shared lib**: `portsLib = import ../../lib/ports.nix { inherit lib; }; portHelpers = portsLib.mkPortHelpers { ... };`
|
|
||||||
|
|
||||||
## PORT MANAGEMENT
|
|
||||||
|
|
||||||
Central port management: `config.m3ta.ports.get "service"` with host-specific via `hostOverrides`
|
|
||||||
|
|
||||||
Generated: `/etc/m3ta/ports.json` (NixOS), `~/.config/m3ta/ports.json` (HM)
|
|
||||||
|
|
||||||
## COMMANDS
|
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
nix flake check # Validate flake
|
bd ready # Find available work
|
||||||
nix fmt # Format (alejandra)
|
bd show <id> # View issue details
|
||||||
nix build .#<pkg> # Build package
|
bd update <id> --claim # Claim work atomically
|
||||||
nix flake show # List outputs
|
bd close <id> # Complete work
|
||||||
nix develop # Enter dev shell
|
bd dolt push # Push beads data to remote
|
||||||
nix develop .#python # Python shell
|
|
||||||
nix develop .#devops # DevOps shell
|
|
||||||
|
|
||||||
# In dev shell only:
|
|
||||||
statix check . # Lint
|
|
||||||
deadnix . # Find dead code
|
|
||||||
```
|
```
|
||||||
|
|
||||||
## ANTI-PATTERNS
|
## Non-Interactive Shell Commands
|
||||||
|
|
||||||
| Don't | Do Instead |
|
**ALWAYS use non-interactive flags** with file operations to avoid hanging on confirmation prompts.
|
||||||
| ------------------------- | ------------------------------------------------------------------- |
|
|
||||||
| `lib.fakeHash` in commits | Get real hash: `nix build`, copy from error |
|
|
||||||
| Flat module files | Organize by category (`cli/`, `coding/`) |
|
|
||||||
| Hardcode ports | Use `m3ta.ports` module |
|
|
||||||
| Skip meta fields | Include all: description, homepage, license, platforms, mainProgram |
|
|
||||||
| `with pkgs;` in modules | Explicit `pkgs.package` or `with pkgs; [ ... ]` in lists only |
|
|
||||||
|
|
||||||
## COMMIT FORMAT
|
Shell commands like `cp`, `mv`, and `rm` may be aliased to include `-i` (interactive) mode on some systems, causing the agent to hang indefinitely waiting for y/n input.
|
||||||
|
|
||||||
```
|
**Use these forms instead:**
|
||||||
type: brief description
|
```bash
|
||||||
|
# Force overwrite without prompting
|
||||||
|
cp -f source dest # NOT: cp source dest
|
||||||
|
mv -f source dest # NOT: mv source dest
|
||||||
|
rm -f file # NOT: rm file
|
||||||
|
|
||||||
|
# For recursive operations
|
||||||
|
rm -rf directory # NOT: rm -r directory
|
||||||
|
cp -rf source dest # NOT: cp -r source dest
|
||||||
```
|
```
|
||||||
|
|
||||||
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `chore`
|
**Other commands that may prompt:**
|
||||||
|
- `scp` - use `-o BatchMode=yes` for non-interactive
|
||||||
|
- `ssh` - use `-o BatchMode=yes` to fail instead of prompting
|
||||||
|
- `apt-get` - use `-y` flag
|
||||||
|
- `brew` - use `HOMEBREW_NO_AUTO_UPDATE=1` env var
|
||||||
|
|
||||||
## NOTES
|
<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:ca08a54f -->
|
||||||
|
## Beads Issue Tracker
|
||||||
|
|
||||||
- **Hash fetching**: Use `lib.fakeHash` initially, build to get real hash
|
This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands.
|
||||||
- **HM modules**: Category subdirs (`cli/`, `coding/`) have own `default.nix` aggregators
|
|
||||||
- **Ports module**: Different for NixOS vs HM (HM adds `generateEnvVars` option)
|
|
||||||
- **Overlays**: `modifications` overlay uses `{prev}:` pattern, not `{final, prev}:`
|
|
||||||
- **Dev shell tools**: `statix`, `deadnix` only available inside `nix develop`
|
|
||||||
- **Automated package updates**: Packages are automatically updated weekly via Gitea Actions using `nix-update`. Review PRs from the automation before merging. For urgent updates, manually run the workflow or update manually.
|
|
||||||
|
|
||||||
## Task Management
|
### Quick Reference
|
||||||
|
|
||||||
This project uses **td** for tracking tasks across AI coding sessions.
|
```bash
|
||||||
Run `td usage --new-session` at conversation start to see current work.
|
bd ready # Find available work
|
||||||
Use `td usage -q` for subsequent reads.
|
bd show <id> # View issue details
|
||||||
|
bd update <id> --claim # Claim work
|
||||||
|
bd close <id> # Complete work
|
||||||
|
```
|
||||||
|
|
||||||
**Quick reference:**
|
### Rules
|
||||||
|
|
||||||
- `td usage --new-session` - Start new session and view tasks
|
- Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
|
||||||
- `td usage -q` - Quick view of current tasks (subsequent reads)
|
- Run `bd prime` for detailed command reference and session close protocol
|
||||||
- `td version` - Check version
|
- Use `bd remember` for persistent knowledge — do NOT use MEMORY.md files
|
||||||
|
|
||||||
For full workflow details, see the [td documentation](./docs/packages/td.md).
|
## Session Completion
|
||||||
|
|
||||||
|
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
|
||||||
|
|
||||||
|
**MANDATORY WORKFLOW:**
|
||||||
|
|
||||||
|
1. **File issues for remaining work** - Create issues for anything that needs follow-up
|
||||||
|
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
||||||
|
3. **Update issue status** - Close finished work, update in-progress items
|
||||||
|
4. **PUSH TO REMOTE** - This is MANDATORY:
|
||||||
|
```bash
|
||||||
|
git pull --rebase
|
||||||
|
bd dolt push
|
||||||
|
git push
|
||||||
|
git status # MUST show "up to date with origin"
|
||||||
|
```
|
||||||
|
5. **Clean up** - Clear stashes, prune remote branches
|
||||||
|
6. **Verify** - All changes committed AND pushed
|
||||||
|
7. **Hand off** - Provide context for next session
|
||||||
|
|
||||||
|
**CRITICAL RULES:**
|
||||||
|
- Work is NOT complete until `git push` succeeds
|
||||||
|
- NEVER stop before pushing - that leaves work stranded locally
|
||||||
|
- NEVER say "ready to push when you are" - YOU must push
|
||||||
|
- If push fails, resolve and retry until it succeeds
|
||||||
|
<!-- END BEADS INTEGRATION -->
|
||||||
|
|||||||
108
CHANGELOG.md
Normal file
108
CHANGELOG.md
Normal file
@@ -0,0 +1,108 @@
|
|||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Remove duplicate opencode-rules.nix (backward-compat alias preserved)
|
||||||
|
- Tool-agnostic naming in coding-rules lib internals
|
||||||
|
- Remove redundant overlay entries for non-existent flake inputs
|
||||||
|
- Remove redundant 'additions' overlay (identical to 'default')
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
- Dead overlay entries for non-existent flake inputs
|
||||||
|
- Legacy `mkOpencodeRules` alias and `lib.opencode-rules` backward-compat entry (use `mkCodingRules` / `lib.coding-rules`)
|
||||||
|
|
||||||
|
## [0.4.0] - 2026-04-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Pi-agent wrapper with systemd sandbox and per-host-user policy
|
||||||
|
- Containerized Pi agent
|
||||||
|
- `lib.agents.nix` with loadCanonical, renderers (OpenCode, Claude Code, Pi), and shellHook
|
||||||
|
- `lib.coding-rules` helper for per-project rule injection (renamed from opencode-rules)
|
||||||
|
- Home Manager modules for coding agents: `claude-code`, `opencode`, `pi`
|
||||||
|
- Agents rework with canonical TOML format and harness-agnostic renderers
|
||||||
|
- `vibetyper` and `eigent` packages
|
||||||
|
- `openspec` package
|
||||||
|
- `basecamp-cli` package
|
||||||
|
- `openshell` package (0.0.14 through 0.0.23)
|
||||||
|
- `openwork` package
|
||||||
|
- Opencode config moved into m3ta-nixpkgs
|
||||||
|
- Opencode dev shell with mkCodingRules demo
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- OpenCode flake input updated through v1.1.65 to v1.3.6
|
||||||
|
- Switched from local opencode package to upstream flake input
|
||||||
|
- Removed opencode-desktop (awaiting upstream fix), later re-enabled
|
||||||
|
- Nix eval warnings resolved
|
||||||
|
- Flake inputs updated throughout
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Pi settings sync
|
||||||
|
- Remove openwork sidecars in preFixup to prevent .opencode-wrapped conflict
|
||||||
|
- Remove sidecar binaries from openwork $out/bin to fix buildEnv conflict
|
||||||
|
- Vibetyper .desktop entry
|
||||||
|
- Opencode module formatting
|
||||||
|
- Formatting opencode module
|
||||||
|
|
||||||
|
## [0.3.0] - 2026-02-20
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- `notesmd-cli` package with flake checks
|
||||||
|
- `sidecar` and `td` packages
|
||||||
|
- `opencode-desktop` package with Wayland support
|
||||||
|
- `mem0` package (1.0.2 through 1.0.9)
|
||||||
|
- `kestracli` / `kestractl` package (1.0.0 to 1.2.2)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Nix-update CI workflow optimized with caching and parallel processing
|
||||||
|
- Restructured n8n version handling for nix-update compatibility
|
||||||
|
- Switched formatter from nixpkgs-fmt to alejandra
|
||||||
|
- Replace local opencode with upstream flake input v1.1.27
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- n8n build error
|
||||||
|
- n8n pnpm hash
|
||||||
|
- n8n update script
|
||||||
|
- Gitea runner opencode.url flake input
|
||||||
|
- nix-update workflow: YAML syntax, jobs indentation, PR body formatting
|
||||||
|
- Arithmetic increment failing with set -e in nix-update workflow
|
||||||
|
- Removed magic-nix-cache-action causing platform mapping error
|
||||||
|
- Opencode bun version requirement patched to match upstream lockfile
|
||||||
|
- Deprecated opencode update logic removed
|
||||||
|
- nix fmt without arg in workflow
|
||||||
|
- Extra Lua config renamed initLua
|
||||||
|
- Stt-ptt use pkill for better process management
|
||||||
|
|
||||||
|
## [0.2.0] - 2026-01-13
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Gitea Actions workflow for automated package updates with nix-update
|
||||||
|
- `n8n`, `beads`, and `opencode` packages
|
||||||
|
- `stt-ptt` package with auto-language detection
|
||||||
|
- `rofi-project-opener` for rofi-based project launching
|
||||||
|
- Hierarchical AGENTS.md knowledge base
|
||||||
|
- Dev shell structure with python and devops shells
|
||||||
|
- Port management modules (NixOS + Home Manager)
|
||||||
|
- Port helper library (`lib/ports.nix`)
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Beads updated through v0.49.1
|
||||||
|
- N8n updated through v2.8.1
|
||||||
|
- Opencode updated through v1.1.18
|
||||||
|
- Documentation expanded with comprehensive patterns and HM module docs
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Python env version fix for marimo
|
||||||
|
|
||||||
|
## [0.1.0] - 2025-10-04
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Initial flake setup with packages, overlays, modules, and shells
|
||||||
|
- NixOS and Home Manager module infrastructure
|
||||||
|
- `lib/` shared utilities
|
||||||
|
- `overlays/mods/` for package modifications
|
||||||
|
- `templates/` for new packages/modules
|
||||||
|
- `examples/` for usage documentation
|
||||||
28
README.md
28
README.md
@@ -38,24 +38,16 @@ nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#zellij-ps
|
|||||||
|
|
||||||
## Available Packages
|
## Available Packages
|
||||||
|
|
||||||
| Package | Description |
|
See [📦 Packages](./docs/packages/) for the full index with descriptions.
|
||||||
| ------------------ | ------------------------------------- |
|
|
||||||
| `code2prompt` | Convert code to prompts |
|
Quick reference — build any package directly:
|
||||||
| `hyprpaper-random` | Random wallpaper setter for Hyprpaper |
|
|
||||||
| `kestractl` | CLI for the Kestra workflow orchestration platform |
|
```bash
|
||||||
| `launch-webapp` | Launch web applications |
|
nix build git+https://code.m3ta.dev/m3tam3re/nixpkgs#<package-name>
|
||||||
| `mem0` | AI memory assistant with vector storage |
|
nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#<package-name>
|
||||||
| `msty-studio` | Msty Studio application |
|
```
|
||||||
| `n8n` | Free and source-available fair-code licensed workflow automation tool |
|
|
||||||
| `notesmd-cli` | Obsidian CLI (Community) - Interact with Obsidian in the terminal |
|
Notable packages: `sidecar`, `td`, `code2prompt`, `mem0`, `n8n`, `zellij-ps`.
|
||||||
| `opencode-desktop` | OpenCode Desktop App with Wayland support (includes workaround for upstream issue #11755) |
|
|
||||||
| `pomodoro-timer` | Pomodoro timer utility |
|
|
||||||
| `rofi-project-opener` | Rofi-based project launcher |
|
|
||||||
| `sidecar` | Companion tool for CLI agents with diffs, file trees, and task management |
|
|
||||||
| `stt-ptt` | Push to Talk Speech to Text |
|
|
||||||
| `td` | Minimalist CLI for tracking tasks across AI coding sessions |
|
|
||||||
| `tuxedo-backlight` | Backlight control for Tuxedo laptops |
|
|
||||||
| `zellij-ps` | Project switcher for Zellij |
|
|
||||||
|
|
||||||
## Automated Package Updates
|
## Automated Package Updates
|
||||||
|
|
||||||
|
|||||||
@@ -20,29 +20,16 @@ Step-by-step guides for common tasks:
|
|||||||
|
|
||||||
- [Getting Started](./guides/getting-started.md) - Initial setup and basic usage
|
- [Getting Started](./guides/getting-started.md) - Initial setup and basic usage
|
||||||
- [Adding Packages](./guides/adding-packages.md) - How to add new packages
|
- [Adding Packages](./guides/adding-packages.md) - How to add new packages
|
||||||
|
- [Adding Modules](./guides/adding-modules.md) - How to add new NixOS or Home Manager modules
|
||||||
- [Port Management](./guides/port-management.md) - Managing service ports across hosts
|
- [Port Management](./guides/port-management.md) - Managing service ports across hosts
|
||||||
- [Using Modules](./guides/using-modules.md) - Using NixOS and Home Manager modules
|
- [Using Modules](./guides/using-modules.md) - Using NixOS and Home Manager modules
|
||||||
- [Development Workflow](./guides/development-workflow.md) - Development and testing workflow
|
- [Development Workflow](./guides/development-workflow.md) - Development and testing workflow
|
||||||
|
|
||||||
### 📦 Packages
|
### 📦 Packages
|
||||||
|
|
||||||
Documentation for all custom packages:
|
- [Packages Index](./packages/) - All packages with descriptions
|
||||||
|
- [Adding Packages](../guides/adding-packages.md) - How to add new packages
|
||||||
- [code2prompt](./packages/code2prompt.md) - Convert code to prompts
|
- [Templates](../templates.md) - Boilerplate templates
|
||||||
- [hyprpaper-random](./packages/hyprpaper-random.md) - Random wallpaper setter for Hyprpaper
|
|
||||||
- [kestractl](./packages/kestractl.md) - CLI for the Kestra workflow orchestration platform
|
|
||||||
- [launch-webapp](./packages/launch-webapp.md) - Launch web applications
|
|
||||||
- [mem0](./packages/mem0.md) - AI memory assistant with vector storage
|
|
||||||
- [msty-studio](./packages/msty-studio.md) - Msty Studio application
|
|
||||||
- [n8n](./packages/n8n.md) - Free and source-available fair-code licensed workflow automation tool
|
|
||||||
- [notesmd-cli](./packages/notesmd-cli.md) - Obsidian CLI (Community) - Interact with Obsidian in the terminal
|
|
||||||
- [pomodoro-timer](./packages/pomodoro-timer.md) - Pomodoro timer utility
|
|
||||||
- [rofi-project-opener](./packages/rofi-project-opener.md) - Rofi-based project launcher with custom args
|
|
||||||
- [sidecar](./packages/sidecar.md) - Companion tool for CLI agents with diffs, file trees, and task management
|
|
||||||
- [stt-ptt](./packages/stt-ptt.md) - Push to Talk Speech to Text using Whisper
|
|
||||||
- [td](./packages/td.md) - Minimalist CLI for tracking tasks across AI coding sessions
|
|
||||||
- [tuxedo-backlight](./packages/tuxedo-backlight.md) - Backlight control for Tuxedo laptops
|
|
||||||
- [zellij-ps](./packages/zellij-ps.md) - Project switcher for Zellij
|
|
||||||
|
|
||||||
### ⚙️ Modules
|
### ⚙️ Modules
|
||||||
|
|
||||||
@@ -68,6 +55,7 @@ Technical references and APIs:
|
|||||||
|
|
||||||
- [Functions](./reference/functions.md) - Library functions documentation
|
- [Functions](./reference/functions.md) - Library functions documentation
|
||||||
- [Patterns](./reference/patterns.md) - Code patterns and anti-patterns
|
- [Patterns](./reference/patterns.md) - Code patterns and anti-patterns
|
||||||
|
- [Templates](../templates.md) - Boilerplate for packages and modules
|
||||||
|
|
||||||
## Repository Structure
|
## Repository Structure
|
||||||
|
|
||||||
|
|||||||
261
docs/guides/adding-modules.md
Normal file
261
docs/guides/adding-modules.md
Normal file
@@ -0,0 +1,261 @@
|
|||||||
|
# Adding Modules Guide
|
||||||
|
|
||||||
|
How to add new NixOS and Home Manager modules to m3ta-nixpkgs.
|
||||||
|
|
||||||
|
## Overview
|
||||||
|
|
||||||
|
Modules extend your system or user configuration with reusable, declarative options. m3ta-nixpkgs uses the standard NixOS module system with a `m3ta.*` namespace.
|
||||||
|
|
||||||
|
## Quick Start
|
||||||
|
|
||||||
|
Use a template for quick setup:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# NixOS module
|
||||||
|
nix flake init -t .#nixos-module my-module
|
||||||
|
|
||||||
|
# Home Manager module
|
||||||
|
nix flake init -t .#home-manager-module my-module
|
||||||
|
```
|
||||||
|
|
||||||
|
This copies the template into `templates/` — move it to the appropriate location and customize.
|
||||||
|
|
||||||
|
## Adding a NixOS Module
|
||||||
|
|
||||||
|
### 1. Create the Module File
|
||||||
|
|
||||||
|
Create `modules/nixos/<my-module>.nix`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{config, lib, pkgs, ...}:
|
||||||
|
with lib; let
|
||||||
|
cfg = config.m3ta.myModule;
|
||||||
|
in {
|
||||||
|
options.m3ta.myModule = {
|
||||||
|
enable = mkEnableOption "my module description";
|
||||||
|
# Add custom options here
|
||||||
|
someOption = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "default-value";
|
||||||
|
description = "Description of this option";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# System configuration goes here
|
||||||
|
environment.systemPackages = [pkgs.some-package];
|
||||||
|
|
||||||
|
# Or systemd services
|
||||||
|
systemd.services.my-service = {
|
||||||
|
enable = true;
|
||||||
|
description = "My service";
|
||||||
|
wantedBy = ["multi-user.target"];
|
||||||
|
serviceConfig = {
|
||||||
|
ExecStart = "${pkgs.some-package}/bin/some-daemon";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Register in the Aggregator
|
||||||
|
|
||||||
|
Add to `modules/nixos/default.nix`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./ports.nix
|
||||||
|
./mem0.nix
|
||||||
|
./<my-module>.nix # ← add your module
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Export from flake.nix
|
||||||
|
|
||||||
|
Add to the `nixosModules` output in `flake.nix` (optional, for direct import):
|
||||||
|
|
||||||
|
```nix
|
||||||
|
nixosModules = {
|
||||||
|
default = ./modules/nixos;
|
||||||
|
ports = ./modules/nixos/ports.nix;
|
||||||
|
mem0 = ./modules/nixos/mem0.nix;
|
||||||
|
my-module = ./modules/nixos/<my-module>.nix; # ← add this
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Adding a Home Manager Module
|
||||||
|
|
||||||
|
Home Manager modules are organized by category under `modules/home-manager/`.
|
||||||
|
|
||||||
|
### Categories
|
||||||
|
|
||||||
|
| Category | Purpose | Location |
|
||||||
|
|----------|---------|----------|
|
||||||
|
| `cli/` | Command-line tools and utilities | `modules/home-manager/cli/` |
|
||||||
|
| `coding/` | Development tools, editors, agents | `modules/home-manager/coding/` |
|
||||||
|
| Root | Cross-cutting concerns (e.g., ports) | `modules/home-manager/` |
|
||||||
|
|
||||||
|
### 1. Choose a Category
|
||||||
|
|
||||||
|
- **CLI tools** (zsh plugins, tmux config, etc.) → `cli/`
|
||||||
|
- **Development tools** (editor config, linters, etc.) → `coding/`
|
||||||
|
- **System-wide settings** (ports, environment) → root level
|
||||||
|
|
||||||
|
### 2. Create the Module File
|
||||||
|
|
||||||
|
Create `modules/home-manager/<category>/<my-module>.nix`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{config, lib, pkgs, ...}:
|
||||||
|
with lib; let
|
||||||
|
cfg = config.m3ta.myModule;
|
||||||
|
in {
|
||||||
|
options.m3ta.myModule = {
|
||||||
|
enable = mkEnableOption "my user module description";
|
||||||
|
someOption = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "value";
|
||||||
|
description = "An option for this module";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
home.packages = [pkgs.some-package];
|
||||||
|
|
||||||
|
# Or Home Manager-specific options
|
||||||
|
programs.zsh.enable = true;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 3. Register in the Category Aggregator
|
||||||
|
|
||||||
|
For `cli/` modules, add to `modules/home-manager/cli/default.nix`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./rofi-project-opener.nix
|
||||||
|
./stt-ptt.nix
|
||||||
|
./zellij-ps.nix
|
||||||
|
./<my-module>.nix # ← add your module
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
For `coding/` modules, add to `modules/home-manager/coding/default.nix`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./editors.nix
|
||||||
|
./opencode.nix
|
||||||
|
./agents
|
||||||
|
./<my-module>.nix # ← add your module
|
||||||
|
];
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
### 4. Export from flake.nix
|
||||||
|
|
||||||
|
Add to `homeManagerModules` in `flake.nix`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
homeManagerModules = {
|
||||||
|
default = import ./modules/home-manager;
|
||||||
|
my-module = import ./modules/home-manager/<category>/<my-module>.nix; # ← add this
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Module Patterns
|
||||||
|
|
||||||
|
### Standard Enable Option
|
||||||
|
|
||||||
|
Always start with `mkEnableOption`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
options.m3ta.myModule = {
|
||||||
|
enable = mkEnableOption "my module";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Conditional Configuration
|
||||||
|
|
||||||
|
Use `mkIf` for conditional config:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
# Only applied when enabled
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### Multiple Conditions
|
||||||
|
|
||||||
|
Use `mkMerge` when combining multiple conditional blocks:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
config = mkMerge [
|
||||||
|
(mkIf cfg.feature1.enable { ... })
|
||||||
|
(mkIf cfg.feature2.enable { ... })
|
||||||
|
];
|
||||||
|
```
|
||||||
|
|
||||||
|
### Nested Namespaces
|
||||||
|
|
||||||
|
For logically grouped options, use nested namespaces:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
options.m3ta.coding = {
|
||||||
|
myTool = {
|
||||||
|
enable = mkEnableOption "my coding tool";
|
||||||
|
# ...
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
Usage: `m3ta.coding.myTool.enable = true;`
|
||||||
|
|
||||||
|
### Shared Library Functions
|
||||||
|
|
||||||
|
For shared utilities (port helpers, etc.), import from `lib/`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
let
|
||||||
|
portsLib = import ../../lib/ports.nix {inherit lib;};
|
||||||
|
portHelpers = portsLib.mkPortHelpers { /* ... */ };
|
||||||
|
in {
|
||||||
|
# use portHelpers
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Documentation
|
||||||
|
|
||||||
|
Add documentation for your module:
|
||||||
|
|
||||||
|
1. Create `docs/modules/nixos/<my-module>.md` (NixOS) or `docs/modules/home-manager/<category>/<my-module>.md` (HM)
|
||||||
|
2. Follow the existing format in `docs/modules/`
|
||||||
|
3. Add it to the appropriate overview page's "Available Modules" list
|
||||||
|
4. Link it from `docs/guides/using-modules.md`
|
||||||
|
|
||||||
|
## Testing
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Validate the module loads correctly
|
||||||
|
nix flake check
|
||||||
|
|
||||||
|
# Test with a minimal configuration (NixOS)
|
||||||
|
nixos-rebuild dry-build -I nixpkgs=. --option experimental-features flakes
|
||||||
|
|
||||||
|
# Format before commit
|
||||||
|
nix fmt
|
||||||
|
```
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Using Modules](./using-modules.md) - How to use existing modules
|
||||||
|
- [Port Management](./port-management.md) - Centralized port management
|
||||||
|
- [Development Workflow](./development-workflow.md) - Local development
|
||||||
|
- [Adding Packages](./adding-packages.md) - Adding packages (not modules)
|
||||||
|
- [Architecture](../ARCHITECTURE.md) - Repository structure
|
||||||
@@ -32,7 +32,13 @@ modules/home-manager/
|
|||||||
│ └── zellij-ps.nix
|
│ └── zellij-ps.nix
|
||||||
└── coding/ # Development tools
|
└── coding/ # Development tools
|
||||||
├── default.nix # Aggregates coding modules
|
├── default.nix # Aggregates coding modules
|
||||||
└── editors.nix
|
├── editors.nix
|
||||||
|
├── opencode.nix # OpenCode non-agent config
|
||||||
|
└── agents/ # Per-tool agent deployment
|
||||||
|
├── default.nix
|
||||||
|
├── opencode.nix
|
||||||
|
├── claude-code.nix
|
||||||
|
└── pi.nix
|
||||||
```
|
```
|
||||||
|
|
||||||
## Importing Modules
|
## Importing Modules
|
||||||
@@ -197,6 +203,61 @@ m3ta.coding.editors = {
|
|||||||
|
|
||||||
**Documentation**: [Editors Module](../modules/home-manager/coding/editors.md)
|
**Documentation**: [Editors Module](../modules/home-manager/coding/editors.md)
|
||||||
|
|
||||||
|
### `coding.opencode`
|
||||||
|
|
||||||
|
OpenCode AI coding assistant (non-agent config: theme, formatter, plugins).
|
||||||
|
|
||||||
|
```nix
|
||||||
|
coding.opencode = {
|
||||||
|
enable = true;
|
||||||
|
ohMyOpencodeSettings = {
|
||||||
|
agents.sisyphus.model = "anthropic/claude-opus-4-5";
|
||||||
|
};
|
||||||
|
extraSettings = {
|
||||||
|
provider.anthropic.name = "Anthropic";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### `coding.agents.opencode`
|
||||||
|
|
||||||
|
OpenCode agent deployment from canonical TOML definitions.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
coding.agents.opencode = {
|
||||||
|
enable = true;
|
||||||
|
agentsInput = inputs.agents;
|
||||||
|
modelOverrides = {
|
||||||
|
chiron = "anthropic/claude-sonnet-4";
|
||||||
|
};
|
||||||
|
externalSkills = [
|
||||||
|
{ src = inputs.skills-anthropic; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### `coding.agents.claude-code`
|
||||||
|
|
||||||
|
Claude Code agent deployment from canonical TOML definitions.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
coding.agents.claude-code = {
|
||||||
|
enable = true;
|
||||||
|
agentsInput = inputs.agents;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
### `coding.agents.pi`
|
||||||
|
|
||||||
|
Pi agent deployment from canonical TOML definitions.
|
||||||
|
|
||||||
|
```nix
|
||||||
|
coding.agents.pi = {
|
||||||
|
enable = true;
|
||||||
|
agentsInput = inputs.agents;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## Common Patterns
|
## Common Patterns
|
||||||
|
|
||||||
### Module Configuration
|
### Module Configuration
|
||||||
@@ -601,5 +662,7 @@ nix eval .#nixosConfigurations.hostname.config.m3ta --apply builtins.attrNames
|
|||||||
|
|
||||||
- [Port Management](./port-management.md) - Detailed port management guide
|
- [Port Management](./port-management.md) - Detailed port management guide
|
||||||
- [Adding Packages](./adding-packages.md) - How to add new packages
|
- [Adding Packages](./adding-packages.md) - How to add new packages
|
||||||
|
- [Adding Modules](./adding-modules.md) - How to add new NixOS or Home Manager modules
|
||||||
|
- [Templates](../templates.md) - Boilerplate for new packages and modules
|
||||||
- [Architecture](../ARCHITECTURE.md) - Understanding module structure
|
- [Architecture](../ARCHITECTURE.md) - Understanding module structure
|
||||||
- [Contributing](../CONTRIBUTING.md) - Code style and guidelines
|
- [Contributing](../CONTRIBUTING.md) - Code style and guidelines
|
||||||
|
|||||||
53
docs/packages/README.md
Normal file
53
docs/packages/README.md
Normal file
@@ -0,0 +1,53 @@
|
|||||||
|
# Packages
|
||||||
|
|
||||||
|
Documentation for packages in m3ta-nixpkgs. Each package directory may contain a `README.md` with detailed documentation.
|
||||||
|
|
||||||
|
## Index
|
||||||
|
|
||||||
|
Packages are organized in `pkgs/<name>/`. Add a `README.md` inside a package directory to document it here.
|
||||||
|
|
||||||
|
### Local Packages
|
||||||
|
|
||||||
|
These packages are built from source in `pkgs/<name>/`:
|
||||||
|
|
||||||
|
| Package | Description | Type | Location |
|
||||||
|
|---------|-------------|------|----------|
|
||||||
|
| `sidecar` | Companion tool for CLI agents with diffs, file trees, and task management | Go | `pkgs/sidecar/` |
|
||||||
|
| `td` | Minimalist CLI for tracking tasks across AI coding sessions | Go | `pkgs/td/` |
|
||||||
|
| `code2prompt` | Convert code to prompts | Go | `pkgs/code2prompt/` |
|
||||||
|
| `eigent` | Eigenvalue tool | Python | `pkgs/eigent/` |
|
||||||
|
| `hyprpaper-random` | Random wallpaper setter for Hyprpaper | Shell | `pkgs/hyprpaper-random/` |
|
||||||
|
| `kestractl` | CLI for Kestra workflow orchestration | Go | `pkgs/kestractl/` |
|
||||||
|
| `launch-webapp` | Launch web applications | Shell | `pkgs/launch-webapp/` |
|
||||||
|
| `mem0` | AI memory assistant with vector storage | Python | `pkgs/mem0/` |
|
||||||
|
| `msty-studio` | Msty Studio application | Python | `pkgs/msty-studio/` |
|
||||||
|
| `n8n` | Workflow automation tool | Node.js | `pkgs/n8n/` |
|
||||||
|
| `openshell` | AI shell assistant | Go | `pkgs/openshell/` |
|
||||||
|
| `pomodoro-timer` | Pomodoro timer utility | Shell | `pkgs/pomodoro-timer/` |
|
||||||
|
| `rofi-project-opener` | Rofi-based project launcher | Shell | `pkgs/rofi-project-opener/` |
|
||||||
|
| `stt-ptt` | Push to Talk Speech to Text | Python | `pkgs/stt-ptt/` |
|
||||||
|
| `tuxedo-backlight` | Backlight control for Tuxedo laptops | C | `pkgs/tuxedo-backlight/` |
|
||||||
|
| `vibetyper` | Typing practice tool | Python | `pkgs/vibetyper/` |
|
||||||
|
| `zellij-ps` | Project switcher for Zellij | Rust | `pkgs/zellij-ps/` |
|
||||||
|
|
||||||
|
### Pass-Through Packages
|
||||||
|
|
||||||
|
These packages are imported directly from flake inputs with minor modifications:
|
||||||
|
|
||||||
|
| Package | Source | Modification | Location |
|
||||||
|
|---------|--------|-------------|----------|
|
||||||
|
| `opencode-desktop` | `inputs.opencode` | Tauri desktop wrapper + Wayland fix | `pkgs/opencode-desktop/` |
|
||||||
|
|
||||||
|
## Adding Package Documentation
|
||||||
|
|
||||||
|
To document a package in detail, add a `README.md` inside the package directory (e.g., `pkgs/sidecar/README.md`). This guide indexes all packages and provides a quick overview.
|
||||||
|
|
||||||
|
## Automated Updates
|
||||||
|
|
||||||
|
Packages are automatically updated weekly by the Gitea Actions `nix-update` workflow. See the main README for details.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Adding Packages](../guides/adding-packages.md) - How to add new packages
|
||||||
|
- [Architecture](../ARCHITECTURE.md) - Repository structure
|
||||||
|
- [Quick Start](../QUICKSTART.md) - Getting started
|
||||||
@@ -1,117 +0,0 @@
|
|||||||
# notesmd-cli
|
|
||||||
|
|
||||||
Obsidian CLI (Community) - Interact with Obsidian in the terminal.
|
|
||||||
|
|
||||||
## Description
|
|
||||||
|
|
||||||
notesmd-cli is a command-line interface for interacting with Obsidian, the popular knowledge management and note-taking application. It allows you to create, search, and manipulate notes directly from the terminal.
|
|
||||||
|
|
||||||
## Features
|
|
||||||
|
|
||||||
- 📝 **Note Creation**: Create new notes from the command line
|
|
||||||
- 🔍 **Search**: Search through your Obsidian vault
|
|
||||||
- 📂 **Vault Management**: Interact with your vault structure
|
|
||||||
- 🔗 **WikiLink Support**: Work with Obsidian's WikiLink format
|
|
||||||
- 🏷️ **Tag Support**: Manage and search by tags
|
|
||||||
- ⚡ **Fast**: Lightweight Go binary with no external dependencies
|
|
||||||
|
|
||||||
## Installation
|
|
||||||
|
|
||||||
### Via Overlay
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{pkgs, ...}: {
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
notesmd-cli
|
|
||||||
];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Direct Reference
|
|
||||||
|
|
||||||
```nix
|
|
||||||
{pkgs, ...}: {
|
|
||||||
environment.systemPackages = with pkgs; [
|
|
||||||
inputs.m3ta-nixpkgs.packages.${pkgs.system}.notesmd-cli
|
|
||||||
];
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Run Directly
|
|
||||||
|
|
||||||
```bash
|
|
||||||
nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#notesmd-cli
|
|
||||||
```
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Basic Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Show help
|
|
||||||
notesmd-cli --help
|
|
||||||
|
|
||||||
# Create a new note
|
|
||||||
notesmd-cli new "My Note Title"
|
|
||||||
|
|
||||||
# Search notes
|
|
||||||
notesmd-cli search "search term"
|
|
||||||
|
|
||||||
# List notes
|
|
||||||
notesmd-cli list
|
|
||||||
```
|
|
||||||
|
|
||||||
### Working with Vaults
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Specify vault path
|
|
||||||
notesmd-cli --vault /path/to/vault new "Note Title"
|
|
||||||
|
|
||||||
# Open a note in Obsidian
|
|
||||||
notesmd-cli open "Note Name"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Advanced Usage
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Search with tags
|
|
||||||
notesmd-cli search --tag "project"
|
|
||||||
|
|
||||||
# Append to existing note
|
|
||||||
notesmd-cli append "Note Name" "Additional content"
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
- `OBSIDIAN_VAULT`: Default vault path
|
|
||||||
|
|
||||||
### Command Line Options
|
|
||||||
|
|
||||||
Run `notesmd-cli --help` for a complete list of options.
|
|
||||||
|
|
||||||
## Build Information
|
|
||||||
|
|
||||||
- **Version**: 0.3.0
|
|
||||||
- **Language**: Go
|
|
||||||
- **License**: MIT
|
|
||||||
- **Source**: [GitHub](https://github.com/Yakitrak/notesmd-cli)
|
|
||||||
- **Vendor Hash**: null (no external dependencies)
|
|
||||||
|
|
||||||
## Platform Support
|
|
||||||
|
|
||||||
- Linux
|
|
||||||
- macOS (Unix systems)
|
|
||||||
|
|
||||||
## Notes
|
|
||||||
|
|
||||||
- No vendor dependencies (pure Go stdlib)
|
|
||||||
- The binary is named `notesmd-cli` (not `notesmd`)
|
|
||||||
- This is the community CLI, not the official Obsidian CLI
|
|
||||||
|
|
||||||
## Related
|
|
||||||
|
|
||||||
- [Obsidian](https://obsidian.md) - The Obsidian application
|
|
||||||
- [Adding Packages](../guides/adding-packages.md) - How to add new packages
|
|
||||||
- [Quick Start](../QUICKSTART.md) - Getting started guide
|
|
||||||
637
docs/plans/2026-04-15-nixpkgs-cleanup-and-improvements.md
Normal file
637
docs/plans/2026-04-15-nixpkgs-cleanup-and-improvements.md
Normal file
@@ -0,0 +1,637 @@
|
|||||||
|
# m3ta-nixpkgs: Cleanup & Improvements Plan
|
||||||
|
|
||||||
|
> **For Hermes:** Use subagent-driven-development skill to implement this plan task-by-task.
|
||||||
|
|
||||||
|
**Goal:** Address 10 issues identified in codebase review — reduce duplication, improve naming consistency, extract inline scripts, add testing, and update documentation.
|
||||||
|
|
||||||
|
**Architecture:** Incremental improvements across lib/, modules/, overlays/, docs/, and CI. Each change is self-contained and can be merged independently. No breaking changes to public API (backward-compat aliases preserved where needed).
|
||||||
|
|
||||||
|
**Repo:** `gitea@code.m3ta.dev:m3tam3re/nixpkgs.git` (master branch)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 1: Deduplication & Naming (Low Risk)
|
||||||
|
|
||||||
|
### Task 1: Remove duplicate opencode-rules.nix file
|
||||||
|
|
||||||
|
**Objective:** Eliminate the duplicate file import. The `coding-rules.nix` is the canonical source; `opencode-rules.nix` is an identical copy. Make the alias a one-liner in `lib/default.nix`.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Delete: `lib/opencode-rules.nix`
|
||||||
|
- Modify: `lib/default.nix`
|
||||||
|
|
||||||
|
**Step 1: Update lib/default.nix to alias directly**
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{lib}: {
|
||||||
|
ports = import ./ports.nix {inherit lib;};
|
||||||
|
|
||||||
|
coding-rules = import ./coding-rules.nix {inherit lib;};
|
||||||
|
|
||||||
|
# Backward-compat alias: opencode-rules → coding-rules
|
||||||
|
opencode-rules = import ./coding-rules.nix {inherit lib;};
|
||||||
|
opencode = import ./coding-rules.nix {inherit lib;};
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2: Delete the duplicate file**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git rm lib/opencode-rules.nix
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3: Verify nothing breaks**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "refactor: remove duplicate opencode-rules.nix, use alias in default.nix"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 2: Tool-agnostic naming in coding-rules.nix internals
|
||||||
|
|
||||||
|
**Objective:** Rename internal variables and output artifacts in `coding-rules.nix` from opencode-specific names to generic names, while keeping the backward-compat alias `mkOpencodeRules`.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `lib/coding-rules.nix`
|
||||||
|
|
||||||
|
**Step 1: Rename internal symbols**
|
||||||
|
|
||||||
|
In `lib/coding-rules.nix`, rename:
|
||||||
|
- `rulesDir` stays `.opencode-rules` (this is a filesystem path used by existing projects, changing it would break)
|
||||||
|
- `opencodeConfig` → `rulesConfig`
|
||||||
|
- `opencode.json` output → `coding-rules.json` (add a comment noting it was renamed)
|
||||||
|
- Add `rulesDir` option to function signature with default `.opencode-rules`
|
||||||
|
|
||||||
|
Updated function:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{lib}: let
|
||||||
|
mkCodingRules = {
|
||||||
|
agents,
|
||||||
|
languages ? [],
|
||||||
|
concerns ? [
|
||||||
|
"coding-style"
|
||||||
|
"naming"
|
||||||
|
"documentation"
|
||||||
|
"testing"
|
||||||
|
"git-workflow"
|
||||||
|
"project-structure"
|
||||||
|
],
|
||||||
|
frameworks ? [],
|
||||||
|
extraInstructions ? [],
|
||||||
|
rulesDir ? ".opencode-rules",
|
||||||
|
}: let
|
||||||
|
instructions =
|
||||||
|
(map (c: "${rulesDir}/concerns/${c}.md") concerns)
|
||||||
|
++ (map (l: "${rulesDir}/languages/${l}.md") languages)
|
||||||
|
++ (map (f: "${rulesDir}/frameworks/${f}.md") frameworks)
|
||||||
|
++ extraInstructions;
|
||||||
|
|
||||||
|
rulesConfig = {
|
||||||
|
"$schema" = "https://opencode.ai/config.json";
|
||||||
|
inherit instructions;
|
||||||
|
};
|
||||||
|
in {
|
||||||
|
inherit instructions;
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
# Create/update symlink to AGENTS rules directory
|
||||||
|
ln -sfn ${agents}/rules ${rulesDir}
|
||||||
|
|
||||||
|
# Generate coding-rules configuration file
|
||||||
|
cat > coding-rules.json <<'RULES_EOF'
|
||||||
|
${builtins.toJSON rulesConfig}
|
||||||
|
RULES_EOF
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# Backward-compat alias
|
||||||
|
mkOpencodeRules = mkCodingRules;
|
||||||
|
in {
|
||||||
|
inherit mkCodingRules mkOpencodeRules;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2: Update shellHook comment in AGENTS.md**
|
||||||
|
|
||||||
|
In `AGENTS.md`, update the coding-rules section to mention the new `rulesDir` parameter and the `coding-rules.json` output file.
|
||||||
|
|
||||||
|
**Step 3: Verify**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "refactor: tool-agnostic naming in coding-rules.nix internals"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 3: Remove redundant overlays entry in flake.nix
|
||||||
|
|
||||||
|
**Objective:** The `default` and `additions` overlays in `flake.nix` produce identical output. Remove `additions` if not referenced elsewhere, or document why both exist.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `flake.nix`
|
||||||
|
- Check: all consumer repos for references to `overlays.additions`
|
||||||
|
|
||||||
|
**Step 1: Search for consumers of overlays.additions**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Check nixos-config and other repos
|
||||||
|
grep -r "overlays.additions" /data/.hermes/repos/nixos-config/
|
||||||
|
grep -r "additions" /data/.hermes/repos/nixos-config/ --include="*.nix" | grep overlay
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2: If no consumers found, remove additions**
|
||||||
|
|
||||||
|
In `flake.nix`, simplify overlays to:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
overlays = {
|
||||||
|
default = final: prev:
|
||||||
|
import ./pkgs {
|
||||||
|
pkgs = final;
|
||||||
|
inputs = inputs;
|
||||||
|
};
|
||||||
|
|
||||||
|
modifications = final: prev: import ./overlays/mods {inherit prev;};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3: Verify**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "refactor: remove redundant 'additions' overlay (identical to 'default')"
|
||||||
|
```
|
||||||
|
|
||||||
|
**Note:** If `additions` IS used elsewhere, add a comment explaining the convention and skip this task.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 2: Extract Inline Scripts (Medium Risk)
|
||||||
|
|
||||||
|
### Task 4: Extract pi-agent runner script to standalone file
|
||||||
|
|
||||||
|
**Objective:** Move the ~200-line inline bash script in `modules/nixos/pi-agent.nix` (the `runner` variable) to a separate file `modules/nixos/pi-agent-runner.sh` that gets imported via `builtins.readFile` + `pkgs.writeShellApplication`.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `modules/nixos/pi-agent-runner.sh`
|
||||||
|
- Modify: `modules/nixos/pi-agent.nix`
|
||||||
|
|
||||||
|
**Step 1: Create the runner script file**
|
||||||
|
|
||||||
|
Extract the body of the `runner` script (everything inside the `pkgs.writeShellScriptBin cfg.wrapper.runnerName '' ... ''`) into `modules/nixos/pi-agent-runner.sh`.
|
||||||
|
|
||||||
|
The script uses Nix-style variable interpolation (`${...}`). We need to keep Nix template variables as `${...}` and convert runtime bash variables to use `$` prefix. Since the script already uses Nix `escapeShellArg` and `escapeShellArg` calls, the cleanest approach is:
|
||||||
|
|
||||||
|
Create `modules/nixos/pi-agent-runner.sh` as a template that `pkgs.substituteAll` or `builtins.readFile` + string replacement can process. However, given the heavy Nix interpolation, the pragmatic approach is to use `pkgs.writeShellApplication` with the script body inline but extracted to a `let` binding:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# In pi-agent.nix, replace the inline runner with:
|
||||||
|
let
|
||||||
|
runnerScript = builtins.readFile ./pi-agent-runner.sh;
|
||||||
|
# ... or keep as let binding but move the body to a separate derivation
|
||||||
|
```
|
||||||
|
|
||||||
|
**Important caveat:** The script has ~30 Nix variable interpolations (`${cfg.user}`, `${escapeShellArg ...}`, etc.). Full extraction to a .sh file would require either:
|
||||||
|
- (a) `substituteAll` with `--replace` for each variable — unwieldy at 30+ substitutions
|
||||||
|
- (b) Converting to env vars passed at runtime — cleaner but changes security posture
|
||||||
|
- (c) Keeping the Nix interpolation but extracting to a `let` block in a separate `.nix` file
|
||||||
|
|
||||||
|
**Recommended approach: Option (c)** — Create `modules/nixos/pi-agent-runner.nix` as a function that takes `cfg` and returns the script:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# modules/nixos/pi-agent-runner.nix
|
||||||
|
{cfg, pkgs, lib, ...}:
|
||||||
|
with lib; let
|
||||||
|
# ... all the helper variables from pi-agent.nix ...
|
||||||
|
in
|
||||||
|
pkgs.writeShellScriptBin cfg.wrapper.runnerName ''
|
||||||
|
# ... the script body ...
|
||||||
|
'';
|
||||||
|
```
|
||||||
|
|
||||||
|
Then in `pi-agent.nix`:
|
||||||
|
```nix
|
||||||
|
runner = import ./pi-agent-runner.nix {inherit cfg pkgs lib;};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2: Similarly extract the wrapper script**
|
||||||
|
|
||||||
|
Create `modules/nixos/pi-agent-wrapper.nix` for the `wrapper` variable.
|
||||||
|
|
||||||
|
**Step 3: Verify**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix flake check
|
||||||
|
# Also test in a nixos-rebuild if possible
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add modules/nixos/pi-agent-runner.nix modules/nixos/pi-agent-wrapper.nix
|
||||||
|
git commit -m "refactor: extract pi-agent runner and wrapper to separate files"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 3: Testing (Higher Value)
|
||||||
|
|
||||||
|
### Task 5: Add basic lib function tests
|
||||||
|
|
||||||
|
**Objective:** Add `nix eval`-based tests for `lib/agents.nix` parseRule logic and `lib/coding-rules.nix` instruction generation.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `tests/lib/agents-test.nix`
|
||||||
|
- Create: `tests/lib/coding-rules-test.nix`
|
||||||
|
- Modify: `flake.nix` (add checks)
|
||||||
|
|
||||||
|
**Step 1: Create test infrastructure**
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# tests/lib/default.nix
|
||||||
|
{
|
||||||
|
agents = import ./agents-test.nix;
|
||||||
|
coding-rules = import ./coding-rules-test.nix;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2: Write agents.nix parseRule test**
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# tests/lib/agents-test.nix
|
||||||
|
let
|
||||||
|
lib = import <nixpkgs/lib>;
|
||||||
|
agentsLib = (import ../../lib {inherit lib;}).agents;
|
||||||
|
|
||||||
|
# Test parseRule helper
|
||||||
|
test1 = let
|
||||||
|
result = builtins.tryEval (
|
||||||
|
let
|
||||||
|
# We can't directly test parseRule since it's internal.
|
||||||
|
# Instead, test the renderer with minimal input.
|
||||||
|
canonical = {
|
||||||
|
test-agent = {
|
||||||
|
description = "Test agent";
|
||||||
|
mode = "primary";
|
||||||
|
systemPrompt = "You are a test.";
|
||||||
|
permissions = {
|
||||||
|
bash = { intent = "allow"; };
|
||||||
|
edit = { intent = "ask"; rules = ["rm -rf *:deny"]; };
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
pkgs = import <nixpkgs> { system = "x86_64-linux"; };
|
||||||
|
rendered = agentsLib.renderForOpencode {
|
||||||
|
inherit pkgs canonical;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
# Verify the derivation builds
|
||||||
|
builtins.pathExists "${rendered}/test-agent.md"
|
||||||
|
);
|
||||||
|
in assert result.value == true; true;
|
||||||
|
|
||||||
|
in {
|
||||||
|
parseRule-basic = test1;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3: Write coding-rules test**
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# tests/lib/coding-rules-test.nix
|
||||||
|
let
|
||||||
|
lib = import <nixpkgs/lib>;
|
||||||
|
codingRulesLib = (import ../../lib {inherit lib;}).coding-rules;
|
||||||
|
|
||||||
|
rules = codingRulesLib.mkCodingRules {
|
||||||
|
agents = "/tmp/fake-agents";
|
||||||
|
languages = ["python"];
|
||||||
|
concerns = ["naming"];
|
||||||
|
rulesDir = ".coding-rules";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Verify instructions are generated correctly
|
||||||
|
test1 = assert rules.instructions == [
|
||||||
|
".coding-rules/concerns/naming.md"
|
||||||
|
".coding-rules/languages/python.md"
|
||||||
|
]; true;
|
||||||
|
|
||||||
|
# Verify backward-compat alias exists
|
||||||
|
test2 = assert codingRulesLib.mkOpencodeRules == codingRulesLib.mkCodingRules; true;
|
||||||
|
|
||||||
|
in {
|
||||||
|
instructions-correct = test1;
|
||||||
|
backward-compat = test2;
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 4: Add to flake.nix checks**
|
||||||
|
|
||||||
|
In `flake.nix`, extend the `checks` attribute:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
checks = forAllSystems (system: let
|
||||||
|
pkgs = pkgsFor system;
|
||||||
|
packages = import ./pkgs {inherit pkgs inputs;};
|
||||||
|
in
|
||||||
|
builtins.mapAttrs (name: pkg: pkgs.lib.hydraJob pkg) packages
|
||||||
|
// {
|
||||||
|
formatting = pkgs.runCommand "check-formatting" {} ''
|
||||||
|
${pkgs.alejandra}/bin/alejandra --check ${./.}
|
||||||
|
touch $out
|
||||||
|
'';
|
||||||
|
lib-tests = pkgs.runCommand "lib-tests" {} ''
|
||||||
|
${pkgs.nix}/bin/nix-instantiate --eval ${./tests/lib/default.nix}
|
||||||
|
touch $out
|
||||||
|
'';
|
||||||
|
});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 5: Verify**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 6: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add tests/
|
||||||
|
git commit -m "test: add basic lib function tests for agents and coding-rules"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 6: Add NixOS VM test for pi-agent module
|
||||||
|
|
||||||
|
**Objective:** Add a basic NixOS VM test that verifies the pi-agent module can be evaluated and the wrapper/runner scripts exist.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `tests/nixos/pi-agent-test.nix`
|
||||||
|
- Modify: `flake.nix` (add to checks)
|
||||||
|
|
||||||
|
**Step 1: Write the VM test**
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# tests/nixos/pi-agent-test.nix
|
||||||
|
{pkgs, ...}: {
|
||||||
|
name = "pi-agent";
|
||||||
|
|
||||||
|
nodes.machine = {config, ...}: {
|
||||||
|
imports = [
|
||||||
|
${(pkgs.path + "/nixos/modules/module-list.nix")}
|
||||||
|
];
|
||||||
|
|
||||||
|
# Minimal pi-agent config
|
||||||
|
m3ta.pi-agent = {
|
||||||
|
enable = true;
|
||||||
|
package = pkgs.writeScriptBin "pi-agent" ''
|
||||||
|
#!/bin/sh
|
||||||
|
echo "pi-agent mock"
|
||||||
|
'';
|
||||||
|
createUser = true;
|
||||||
|
hostUsers = {
|
||||||
|
testuser = {
|
||||||
|
projectRoots = ["/tmp/test-project"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
users.users.testuser = {
|
||||||
|
isNormalUser = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
testScript = ''
|
||||||
|
machine.start()
|
||||||
|
machine.wait_for_unit("multi-user.target")
|
||||||
|
|
||||||
|
# Verify user was created
|
||||||
|
machine.succeed("id pi-agent")
|
||||||
|
|
||||||
|
# Verify wrapper exists
|
||||||
|
machine.succeed("which pi")
|
||||||
|
|
||||||
|
# Verify state directory
|
||||||
|
machine.succeed("test -d /var/lib/pi-agent")
|
||||||
|
machine.succeed("test -d /var/lib/pi-agent/.pi")
|
||||||
|
'';
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2: Add to flake.nix checks**
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# In the checks attrset:
|
||||||
|
pi-agent-vm-test = pkgs.nixosTest (import ./tests/nixos/pi-agent-test.nix {inherit pkgs;});
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3: Verify**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix build .#checks.x86_64-linux.pi-agent-vm-test
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 4: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add tests/nixos/
|
||||||
|
git commit -m "test: add NixOS VM test for pi-agent module"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 4: Documentation (Low Risk, High Value)
|
||||||
|
|
||||||
|
### Task 7: Update AGENTS.md to reflect current state
|
||||||
|
|
||||||
|
**Objective:** Remove outdated migration sections, update function signatures, and align with current code.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `AGENTS.md`
|
||||||
|
|
||||||
|
**Step 1: Update the AGENTS REWORK migration section**
|
||||||
|
|
||||||
|
The section starting with `## MIGRATION: Agent System (OpenCode → Canonical TOML)` describes a completed migration. Convert it to a brief "Architecture" section that describes the current state, not the migration path.
|
||||||
|
|
||||||
|
**Step 2: Update lib.agents function table**
|
||||||
|
|
||||||
|
Verify that the function signatures and descriptions in the AGENTS.md table match the actual functions in `lib/agents.nix`. Specifically:
|
||||||
|
- `loadCanonical` takes `{agentsInput}` — confirm docs match
|
||||||
|
- `renderForPi` now has `primaryAgent` parameter — confirm documented
|
||||||
|
- `shellHookForTool` exists — confirm documented
|
||||||
|
|
||||||
|
**Step 3: Update coding-rules documentation**
|
||||||
|
|
||||||
|
Replace references to `mkOpencodeRules` with `mkCodingRules` as primary, `mkOpencodeRules` as backward-compat alias. Document the new `rulesDir` parameter.
|
||||||
|
|
||||||
|
**Step 4: Update overlay documentation**
|
||||||
|
|
||||||
|
Remove or annotate the `additions` overlay depending on Task 3 outcome.
|
||||||
|
|
||||||
|
**Step 5: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "docs: update AGENTS.md to reflect current codebase state"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 8: Add CHANGELOG.md
|
||||||
|
|
||||||
|
**Objective:** Create a changelog that captures recent work (from git log) so consumers can track changes.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Create: `CHANGELOG.md`
|
||||||
|
|
||||||
|
**Step 1: Generate changelog from git history**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /data/.hermes/repos/nixpkgs-review
|
||||||
|
git log --oneline --no-merges master | head -30
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2: Write CHANGELOG.md**
|
||||||
|
|
||||||
|
Structure as Keep a Changelog format:
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
# Changelog
|
||||||
|
|
||||||
|
All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
|
The format is based on [Keep a Changelog](https://keepachangelog.com/).
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
## [0.4.0] - 2026-04-15
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Pi agent wrapper with per-host-user policy enforcement (`m3ta.pi-agent` NixOS module)
|
||||||
|
- `coding.agents.pi` Home Manager module with settings, MCP, and skills support
|
||||||
|
- `coding.agents.claude-code` Home Manager module with MCP integration
|
||||||
|
- Automated package updates via Gitea Actions (`nix-update` workflow)
|
||||||
|
- `lib.agents.renderForPi` with primaryAgent selection and pi-subagents format
|
||||||
|
- `pkgs/td` - Task management CLI for AI coding sessions
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- Renamed `lib.opencode-rules` → `lib.coding-rules` (backward-compat alias preserved)
|
||||||
|
- Agent system migrated to harness-agnostic canonical format
|
||||||
|
- Pi settings sync now merges host and Nix-managed values via deep_merge
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
- Pi settings sync race condition on first run
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "docs: add CHANGELOG.md"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Phase 5: Minor Cleanups (Low Risk)
|
||||||
|
|
||||||
|
### Task 9: Clean up pkgs/default.nix unused `system` binding
|
||||||
|
|
||||||
|
**Objective:** The `system = pkgs.stdenv.hostPlatform.system;` binding in `pkgs/default.nix` is only used for the two input-pass-throughs. If those are the only consumers, it's fine, but add a clarifying comment.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `pkgs/default.nix`
|
||||||
|
|
||||||
|
**Step 1: Add clarifying comment**
|
||||||
|
|
||||||
|
```nix
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
inputs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
# Only used for flake input pass-throughs below
|
||||||
|
system = pkgs.stdenv.hostPlatform.system;
|
||||||
|
in {
|
||||||
|
...
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 2: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "docs: clarify system binding in pkgs/default.nix"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
### Task 10: Remove commented-out overlay entries in overlays/default.nix
|
||||||
|
|
||||||
|
**Objective:** Clean up the large block of commented-out code in `overlays/default.nix` (nodejs_24, paperless-ngx, anytype-heart, hyprpanel, etc.). These belong in git history, not in active code.
|
||||||
|
|
||||||
|
**Files:**
|
||||||
|
- Modify: `overlays/default.nix`
|
||||||
|
|
||||||
|
**Step 1: Remove commented-out blocks**
|
||||||
|
|
||||||
|
Remove:
|
||||||
|
- The `rose-pine-hyprcursor` addition from `additions` (if it's unused — check with grep)
|
||||||
|
- The commented-out `nodejs_24`, `paperless-ngx`, `anytype-heart`, `trezord`, `mesa`, `hyprpanel` blocks from `modifications`
|
||||||
|
- The commented-out overlay inputs (`temp-packages`, `stable-packages`, `pinned-packages`, `locked-packages`, `master-packages`) if they reference inputs not in `flake.nix`
|
||||||
|
|
||||||
|
Actually, `nixpkgs-stable`, `nixpkgs-9e9486b`, `nixpkgs-9472de4`, `nixpkgs-locked`, `nixpkgs-master` are NOT in the current `flake.nix` inputs. These overlays will fail if referenced. They should either be removed or the inputs should be added.
|
||||||
|
|
||||||
|
**Action:**
|
||||||
|
- Keep `master-packages` IF `nixpkgs-master` is in flake.nix inputs (it IS — good)
|
||||||
|
- Remove `temp-packages`, `pinned-packages`, `locked-packages` (inputs don't exist)
|
||||||
|
- Keep `stable-packages` IF `nixpkgs-stable` exists in inputs (check — it does NOT currently exist)
|
||||||
|
- Keep `additions` with `rose-pine-hyprcursor` IF `rose-pine-hyprcursor` input exists (check)
|
||||||
|
|
||||||
|
**Step 2: Verify**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix flake check
|
||||||
|
```
|
||||||
|
|
||||||
|
**Step 3: Commit**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git commit -m "chore: remove dead overlay entries for non-existent flake inputs"
|
||||||
|
```
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Execution Order & Priority
|
||||||
|
|
||||||
|
| Task | Risk | Effort | Impact | Dependencies |
|
||||||
|
|------|------|--------|--------|-------------|
|
||||||
|
| T1: Remove opencode-rules.nix | Low | 5min | Clean | None |
|
||||||
|
| T2: Tool-agnostic naming | Low | 15min | Consistency | None |
|
||||||
|
| T3: Remove redundant overlay | Low | 10min | Clean | Check consumers |
|
||||||
|
| T9: Clarify system binding | Low | 2min | Docs | None |
|
||||||
|
| T10: Remove dead overlays | Low | 10min | Clean | None |
|
||||||
|
| T7: Update AGENTS.md | Low | 20min | Docs | After T1, T2 |
|
||||||
|
| T8: Add CHANGELOG.md | Low | 15min | Docs | None |
|
||||||
|
| T4: Extract pi-agent scripts | Medium | 45min | Maintainability | None |
|
||||||
|
| T5: Lib function tests | Medium | 30min | Quality | None |
|
||||||
|
| T6: NixOS VM test | Medium | 45min | Quality | None |
|
||||||
|
|
||||||
|
**Recommended order:** T1 → T9 → T10 → T3 → T2 → T7 → T8 → T5 → T4 → T6
|
||||||
|
|
||||||
|
**Branching strategy:** Create a feature branch `chore/cleanup-review` from master, implement all tasks, open PR for review before merging.
|
||||||
@@ -153,33 +153,6 @@ allServices = portHelpers.listServices;
|
|||||||
# Returns: ["nginx" "grafana" "prometheus" "homepage"]
|
# Returns: ["nginx" "grafana" "prometheus" "homepage"]
|
||||||
```
|
```
|
||||||
|
|
||||||
### `getDefaultPort`
|
|
||||||
|
|
||||||
Simple helper to get a port without host override.
|
|
||||||
|
|
||||||
#### Signature
|
|
||||||
|
|
||||||
```nix
|
|
||||||
getDefaultPort :: portsConfig -> string -> int-or-null
|
|
||||||
```
|
|
||||||
|
|
||||||
#### Arguments
|
|
||||||
|
|
||||||
1. `portsConfig` - Same structure as `mkPortHelpers`
|
|
||||||
2. `service` - The service name (string)
|
|
||||||
|
|
||||||
#### Returns
|
|
||||||
|
|
||||||
Port number (int) or `null` if service not found.
|
|
||||||
|
|
||||||
#### Usage
|
|
||||||
|
|
||||||
```nix
|
|
||||||
services.my-service = {
|
|
||||||
port = m3taLib.ports.getDefaultPort myPorts "my-service";
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Using Library Functions
|
## Using Library Functions
|
||||||
|
|
||||||
### Importing
|
### Importing
|
||||||
@@ -262,7 +235,7 @@ in {
|
|||||||
| `getPort` | Get port with optional host override | `int or null` |
|
| `getPort` | Get port with optional host override | `int or null` |
|
||||||
| `getHostPorts` | Get all ports for host | `attrs` |
|
| `getHostPorts` | Get all ports for host | `attrs` |
|
||||||
| `listServices` | List all service names | `[string]` |
|
| `listServices` | List all service names | `[string]` |
|
||||||
| `getDefaultPort` | Get default port only | `int or null` |
|
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
|||||||
162
docs/templates.md
Normal file
162
docs/templates.md
Normal file
@@ -0,0 +1,162 @@
|
|||||||
|
# Templates
|
||||||
|
|
||||||
|
Boilerplate templates for quickly adding new packages or modules to m3ta-nixpkgs.
|
||||||
|
|
||||||
|
## Available Templates
|
||||||
|
|
||||||
|
| Template | Command | Creates |
|
||||||
|
|---------|---------|---------|
|
||||||
|
| Package | `nix flake init -t .#package` | `templates/package/` |
|
||||||
|
| NixOS Module | `nix flake init -t .#nixos-module` | `templates/nixos-module/` |
|
||||||
|
| Home Manager Module | `nix flake init -t .#home-manager-module` | `templates/home-manager-module/` |
|
||||||
|
|
||||||
|
## Using Templates
|
||||||
|
|
||||||
|
### 1. List Available Templates
|
||||||
|
|
||||||
|
```bash
|
||||||
|
nix flake show --templates .
|
||||||
|
```
|
||||||
|
|
||||||
|
### 2. Initialize from a Template
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# Package
|
||||||
|
nix flake init -t .#package
|
||||||
|
|
||||||
|
# NixOS Module
|
||||||
|
nix flake init -t .#nixos-module
|
||||||
|
|
||||||
|
# Home Manager Module
|
||||||
|
nix flake init -t .#home-manager-module
|
||||||
|
```
|
||||||
|
|
||||||
|
Note: `nix flake init` copies the template contents into the current directory. Use a subdirectory name:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
mkdir new-package && cd new-package
|
||||||
|
nix flake init -t ..#package
|
||||||
|
```
|
||||||
|
|
||||||
|
## Package Template
|
||||||
|
|
||||||
|
Creates a complete package structure:
|
||||||
|
|
||||||
|
```
|
||||||
|
templates/package/
|
||||||
|
├── default.nix # Package definition with comments
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fields to Fill In
|
||||||
|
|
||||||
|
| Field | Location | Notes |
|
||||||
|
|-------|----------|-------|
|
||||||
|
| `pname` | `default.nix` | Package name (kebab-case) |
|
||||||
|
| `version` | `default.nix` | Semantic version |
|
||||||
|
| `src` | `default.nix` | Fetcher (GitHub, URL, Git, etc.) |
|
||||||
|
| `hash` | `default.nix` | Use `lib.fakeHash`, build to get real hash |
|
||||||
|
| `meta.description` | `default.nix` | Short one-line description |
|
||||||
|
| `meta.homepage` | `default.nix` | Project URL |
|
||||||
|
| `meta.license` | `default.nix` | Use `lib.licenses.*` |
|
||||||
|
| `meta.platforms` | `default.nix` | Usually `platforms.linux` |
|
||||||
|
| `meta.mainProgram` | `default.nix` | Main binary name |
|
||||||
|
|
||||||
|
### Common Build Systems
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# Rust (recommended)
|
||||||
|
rustPlatform.buildRustPackage rec { ... }
|
||||||
|
|
||||||
|
# Python
|
||||||
|
python3.pkgs.buildPythonPackage rec { ... }
|
||||||
|
|
||||||
|
# Node.js
|
||||||
|
pkg-config, nodejs, npm2nix, or pnpm + prisma
|
||||||
|
|
||||||
|
# Shell script
|
||||||
|
writeShellScriptBin "name" ''echo hello''
|
||||||
|
|
||||||
|
# Go
|
||||||
|
go mdbook build
|
||||||
|
|
||||||
|
# Generic C/Make
|
||||||
|
stdenv.mkDerivation { ... }
|
||||||
|
```
|
||||||
|
|
||||||
|
See [Adding Packages](./guides/adding-packages.md) for detailed instructions.
|
||||||
|
|
||||||
|
## NixOS Module Template
|
||||||
|
|
||||||
|
Creates a complete NixOS module:
|
||||||
|
|
||||||
|
```
|
||||||
|
templates/nixos-module/
|
||||||
|
├── default.nix # Module with options
|
||||||
|
└── README.md # Module documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fields to Fill In
|
||||||
|
|
||||||
|
| Field | Location | Notes |
|
||||||
|
|-------|----------|-------|
|
||||||
|
| Module name | `default.nix` | File name matches `m3ta.<name>` |
|
||||||
|
| Options | `default.nix` | Add under `options.m3ta.<name>` |
|
||||||
|
| Config | `default.nix` | Add under `config.m3ta.<name>` |
|
||||||
|
| Description | `README.md` | What the module does |
|
||||||
|
|
||||||
|
### After Creating
|
||||||
|
|
||||||
|
1. Add to `modules/nixos/default.nix` imports
|
||||||
|
2. Optionally export from `flake.nix` `nixosModules`
|
||||||
|
3. Add documentation to `docs/modules/nixos/`
|
||||||
|
4. Run `nix flake check`
|
||||||
|
|
||||||
|
## Home Manager Module Template
|
||||||
|
|
||||||
|
Creates a complete Home Manager module:
|
||||||
|
|
||||||
|
```
|
||||||
|
templates/home-manager-module/
|
||||||
|
├── default.nix # Module with options
|
||||||
|
└── README.md # Module documentation
|
||||||
|
```
|
||||||
|
|
||||||
|
### Fields to Fill In
|
||||||
|
|
||||||
|
| Field | Location | Notes |
|
||||||
|
|-------|----------|-------|
|
||||||
|
| Category | Directory | Choose `cli/` or `coding/` |
|
||||||
|
| Options | `default.nix` | Add under `options.m3ta.<name>` |
|
||||||
|
| Config | `default.nix` | Add under `config.m3ta.<name>` |
|
||||||
|
| Description | `README.md` | What the module does |
|
||||||
|
|
||||||
|
### After Creating
|
||||||
|
|
||||||
|
1. Add to the appropriate category aggregator (`cli/default.nix` or `coding/default.nix`)
|
||||||
|
2. Optionally export from `flake.nix` `homeManagerModules`
|
||||||
|
3. Add documentation to `docs/modules/home-manager/`
|
||||||
|
4. Run `nix flake check`
|
||||||
|
|
||||||
|
## Template Variables
|
||||||
|
|
||||||
|
Templates use Nix attribute references. After copying, search for these placeholders:
|
||||||
|
|
||||||
|
| Placeholder | Replace With |
|
||||||
|
|-------------|--------------|
|
||||||
|
| `package-name` | Your package name (kebab-case) |
|
||||||
|
| `owner-name` / `repo-name` | GitHub owner and repo |
|
||||||
|
| `0.1.0` | Initial version |
|
||||||
|
| `lib.fakeHash` | Real hash after first build |
|
||||||
|
| `lib.licenses.mit` | Appropriate license |
|
||||||
|
| `A short description` | One-line description |
|
||||||
|
|
||||||
|
## Automated Updates
|
||||||
|
|
||||||
|
Packages created from templates are automatically updated weekly by the Gitea Actions workflow. See the main README for details on the `nix-update` automation.
|
||||||
|
|
||||||
|
## Related
|
||||||
|
|
||||||
|
- [Adding Packages](./guides/adding-packages.md) - Detailed package guide
|
||||||
|
- [Adding Modules](./guides/adding-modules.md) - Detailed module guide
|
||||||
|
- [Development Workflow](./guides/development-workflow.md) - Local development
|
||||||
|
- [Architecture](./ARCHITECTURE.md) - Repository structure
|
||||||
79
flake.lock
generated
79
flake.lock
generated
@@ -1,12 +1,49 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
|
"agents": {
|
||||||
|
"flake": false,
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1777399938,
|
||||||
|
"narHash": "sha256-xXPqUQezDdDtF8MbpZnwD1HkybOYwF92evx8rJ6OXCU=",
|
||||||
|
"ref": "refs/heads/master",
|
||||||
|
"rev": "9a91f1ee0cf011a7eaf1f16a9e17610b0457e055",
|
||||||
|
"revCount": 85,
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://code.m3ta.dev/m3tam3re/AGENTS"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://code.m3ta.dev/m3tam3re/AGENTS"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"basecamp": {
|
||||||
|
"inputs": {
|
||||||
|
"nixpkgs": [
|
||||||
|
"nixpkgs"
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"locked": {
|
||||||
|
"lastModified": 1774505501,
|
||||||
|
"narHash": "sha256-7UiRrDptj7yuEFwToOfdunUMz/i3jRLR7CmMoYQjq6k=",
|
||||||
|
"owner": "basecamp",
|
||||||
|
"repo": "basecamp-cli",
|
||||||
|
"rev": "f087e6ef84002503d0dbc75ea1c8c928a8928d9e",
|
||||||
|
"type": "github"
|
||||||
|
},
|
||||||
|
"original": {
|
||||||
|
"owner": "basecamp",
|
||||||
|
"ref": "v0.7.2",
|
||||||
|
"repo": "basecamp-cli",
|
||||||
|
"type": "github"
|
||||||
|
}
|
||||||
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1773821835,
|
"lastModified": 1777268161,
|
||||||
"narHash": "sha256-TJ3lSQtW0E2JrznGVm8hOQGVpXjJyXY2guAxku2O9A4=",
|
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "b40629efe5d6ec48dd1efba650c797ddbd39ace0",
|
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -18,11 +55,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-master": {
|
"nixpkgs-master": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1774053289,
|
"lastModified": 1777643636,
|
||||||
"narHash": "sha256-U+wnCEgsGdRqXfUlt1yZffhLMGbUnkb4MYmisBI+KcU=",
|
"narHash": "sha256-7vvm5Ia8o3g7YNErFcDsbCx+Pk8HbnA+ZYuA5Zga7hY=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "28afc93f3c091ae6cacfba64d8a486fab9c9af07",
|
"rev": "da2366fac507ce7bd31852e7351e55b951656999",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -32,27 +69,6 @@
|
|||||||
"type": "github"
|
"type": "github"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"opencode": {
|
|
||||||
"inputs": {
|
|
||||||
"nixpkgs": [
|
|
||||||
"nixpkgs-master"
|
|
||||||
]
|
|
||||||
},
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1773628428,
|
|
||||||
"narHash": "sha256-JUlFfILzcUCME3mOxdxDbcCXphNVEfVGIKhwAwtJPl8=",
|
|
||||||
"owner": "anomalyco",
|
|
||||||
"repo": "opencode",
|
|
||||||
"rev": "4ee426ba549131c4903a71dfb6259200467aca81",
|
|
||||||
"type": "github"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"owner": "anomalyco",
|
|
||||||
"ref": "v1.2.27",
|
|
||||||
"repo": "opencode",
|
|
||||||
"type": "github"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"openspec": {
|
"openspec": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -60,11 +76,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1772182342,
|
"lastModified": 1777556999,
|
||||||
"narHash": "sha256-9Q0iUyZGcDPLdgvnrBN3GumV8g9akV8TFb8bFkD1yYs=",
|
"narHash": "sha256-HfFlRwR8IMjudRttN4T8L3DJKnNlpWfeNzQPly/HaRY=",
|
||||||
"owner": "Fission-AI",
|
"owner": "Fission-AI",
|
||||||
"repo": "OpenSpec",
|
"repo": "OpenSpec",
|
||||||
"rev": "afdca0d5dab1aa109cfd8848b2512333ccad60c3",
|
"rev": "347f0277e3be3549cd85cdea364fbd7710f1922b",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -75,9 +91,10 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"agents": "agents",
|
||||||
|
"basecamp": "basecamp",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"nixpkgs-master": "nixpkgs-master",
|
"nixpkgs-master": "nixpkgs-master",
|
||||||
"opencode": "opencode",
|
|
||||||
"openspec": "openspec"
|
"openspec": "openspec"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
43
flake.nix
43
flake.nix
@@ -5,10 +5,9 @@
|
|||||||
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
|
||||||
nixpkgs-master.url = "github:NixOS/nixpkgs/master";
|
nixpkgs-master.url = "github:NixOS/nixpkgs/master";
|
||||||
|
|
||||||
# opencode needs newer bun from master
|
basecamp = {
|
||||||
opencode = {
|
url = "github:basecamp/basecamp-cli/v0.7.2";
|
||||||
url = "github:anomalyco/opencode/v1.2.27";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
inputs.nixpkgs.follows = "nixpkgs-master";
|
|
||||||
};
|
};
|
||||||
|
|
||||||
# openspec - spec-driven development for AI coding assistants
|
# openspec - spec-driven development for AI coding assistants
|
||||||
@@ -16,6 +15,12 @@
|
|||||||
url = "github:Fission-AI/OpenSpec";
|
url = "github:Fission-AI/OpenSpec";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Agent definitions and coding rules
|
||||||
|
agents = {
|
||||||
|
url = "git+https://code.m3ta.dev/m3tam3re/AGENTS";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
@@ -51,13 +56,6 @@
|
|||||||
inputs = inputs;
|
inputs = inputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Individual overlays for more granular control
|
|
||||||
additions = final: prev:
|
|
||||||
import ./pkgs {
|
|
||||||
pkgs = final;
|
|
||||||
inputs = inputs;
|
|
||||||
};
|
|
||||||
|
|
||||||
modifications = final: prev: import ./overlays/mods {inherit prev;};
|
modifications = final: prev: import ./overlays/mods {inherit prev;};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -73,22 +71,24 @@
|
|||||||
homeManagerModules = {
|
homeManagerModules = {
|
||||||
default = import ./modules/home-manager;
|
default = import ./modules/home-manager;
|
||||||
ports = import ./modules/home-manager/ports.nix;
|
ports = import ./modules/home-manager/ports.nix;
|
||||||
|
opencode = import ./modules/home-manager/coding/opencode.nix;
|
||||||
|
agents = import ./modules/home-manager/coding/agents;
|
||||||
zellij-ps = import ./modules/home-manager/zellij-ps.nix;
|
zellij-ps = import ./modules/home-manager/zellij-ps.nix;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Library functions - helper utilities for your configuration
|
# Library functions - helper utilities for your configuration
|
||||||
lib = forAllSystems (system: let
|
lib = forAllSystems (system: import ./lib {lib = nixpkgs.lib;});
|
||||||
pkgs = pkgsFor system;
|
|
||||||
in
|
|
||||||
import ./lib {lib = pkgs.lib;});
|
|
||||||
|
|
||||||
# Development shells for various programming environments
|
# Development shells for various programming environments
|
||||||
# Usage: nix develop .#<shell-name>
|
# Usage: nix develop .#<shell-name>
|
||||||
# Available shells: default, python, devops, opencode
|
# Available shells: default, python, devops, coding
|
||||||
devShells = forAllSystems (system: let
|
devShells = forAllSystems (system: let
|
||||||
pkgs = pkgsFor system;
|
pkgs = pkgsFor system;
|
||||||
in
|
in
|
||||||
import ./shells {inherit pkgs inputs;});
|
import ./shells {
|
||||||
|
inherit pkgs inputs;
|
||||||
|
agents = inputs.agents;
|
||||||
|
});
|
||||||
|
|
||||||
# Formatter for 'nix fmt'
|
# Formatter for 'nix fmt'
|
||||||
formatter = forAllSystems (system: (pkgsFor system).alejandra);
|
formatter = forAllSystems (system: (pkgsFor system).alejandra);
|
||||||
@@ -104,6 +104,15 @@
|
|||||||
${pkgs.alejandra}/bin/alejandra --check ${./.}
|
${pkgs.alejandra}/bin/alejandra --check ${./.}
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
|
# Lib unit tests
|
||||||
|
lib-agents = import ./tests/lib/agents-test.nix {
|
||||||
|
inherit pkgs;
|
||||||
|
lib = pkgs.lib;
|
||||||
|
};
|
||||||
|
lib-coding-rules = import ./tests/lib/coding-rules-test.nix {
|
||||||
|
inherit pkgs;
|
||||||
|
lib = pkgs.lib;
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
# Templates for creating new packages/modules
|
# Templates for creating new packages/modules
|
||||||
|
|||||||
433
lib/agents.nix
Normal file
433
lib/agents.nix
Normal file
@@ -0,0 +1,433 @@
|
|||||||
|
# Harness-agnostic agent management utilities
|
||||||
|
#
|
||||||
|
# This module provides functions to load canonical agent definitions and
|
||||||
|
# render them for different AI coding tools (OpenCode, Claude Code, Pi).
|
||||||
|
#
|
||||||
|
# Usage in your configuration:
|
||||||
|
#
|
||||||
|
# let
|
||||||
|
# m3taLib = inputs.m3ta-nixpkgs.lib.${system};
|
||||||
|
# canonical = m3taLib.agents.loadCanonical { agentsInput = inputs.agents; };
|
||||||
|
#
|
||||||
|
# # Render for a specific tool
|
||||||
|
# rendered = m3taLib.agents.renderForOpencode {
|
||||||
|
# inherit pkgs canonical;
|
||||||
|
# modelOverrides = { chiron = "anthropic/claude-sonnet-4"; };
|
||||||
|
# };
|
||||||
|
# in { ... }
|
||||||
|
{lib}: let
|
||||||
|
# ── Shared helpers ─────────────────────────────────────────────
|
||||||
|
# Split a rule string on the LAST colon to get { pattern, action }.
|
||||||
|
# e.g. "rm -rf *:ask" → pattern="rm -rf *", action="ask"
|
||||||
|
# e.g. "/run/agenix/**:deny" → pattern="/run/agenix/**", action="deny"
|
||||||
|
parseRule = ruleStr: let
|
||||||
|
parts = lib.strings.splitString ":" ruleStr;
|
||||||
|
action = lib.last parts;
|
||||||
|
pattern = lib.concatStringsSep ":" (lib.init parts);
|
||||||
|
in {inherit pattern action;};
|
||||||
|
|
||||||
|
# ── Shared renderer primitives ──────────────────────────────────
|
||||||
|
# Render agent files from canonical definitions into a directory.
|
||||||
|
# Each agent gets a "<name>.md" file containing mkContent name agent.
|
||||||
|
#
|
||||||
|
# Args:
|
||||||
|
# pkgs — Nixpkgs package set with linkFarm
|
||||||
|
# canonical — Attribute set of agent definitions (keyed by slug)
|
||||||
|
# mkContent — Function: name: agent → string (file content)
|
||||||
|
# name — Derivation name (e.g. "opencode-agents")
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# A store path containing all agent *.md files.
|
||||||
|
renderAgentFiles = pkgs: canonical: mkContent: name:
|
||||||
|
pkgs.linkFarm name (
|
||||||
|
lib.mapAttrsToList (n: a: {
|
||||||
|
name = "${n}.md";
|
||||||
|
path = pkgs.writeText "${n}.md" (mkContent n a);
|
||||||
|
})
|
||||||
|
canonical
|
||||||
|
);
|
||||||
|
|
||||||
|
agentsLib = {
|
||||||
|
# ── loadCanonical ─────────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# Load canonical agent definitions from the AGENTS flake input.
|
||||||
|
# Returns the canonical attrset from lib.loadAgents (keyed by slug).
|
||||||
|
|
||||||
|
loadCanonical = {agentsInput}: agentsInput.lib.loadAgents;
|
||||||
|
|
||||||
|
# ── OpenCode renderer ─────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# Produces a directory of agent *.md files suitable for
|
||||||
|
# ~/.config/opencode/agents/ (system-level)
|
||||||
|
# .opencode/agents/ (project-level)
|
||||||
|
#
|
||||||
|
# Each file has YAML frontmatter (description, mode, optional model,
|
||||||
|
# optional permission) followed by the agent's systemPrompt content.
|
||||||
|
# The filename (without .md) becomes the agent name in OpenCode.
|
||||||
|
|
||||||
|
renderForOpencode = {
|
||||||
|
pkgs,
|
||||||
|
canonical,
|
||||||
|
modelOverrides ? {},
|
||||||
|
}: let
|
||||||
|
# Render one permission section to YAML lines.
|
||||||
|
# intent-only → single line: " <tool>: <intent>"
|
||||||
|
# intent+rules → nested block
|
||||||
|
renderPermSection = tool: section:
|
||||||
|
if !(section ? rules) || section.rules == []
|
||||||
|
then [" ${tool}: ${section.intent}"]
|
||||||
|
else let
|
||||||
|
parsedRules = map parseRule section.rules;
|
||||||
|
wildcardLine = " \"*\": ${section.intent}";
|
||||||
|
ruleLines = map (r: " \"${r.pattern}\": ${r.action}") parsedRules;
|
||||||
|
in
|
||||||
|
[" ${tool}:"] ++ [wildcardLine] ++ ruleLines;
|
||||||
|
|
||||||
|
renderPermBlock = permissions:
|
||||||
|
if permissions == {} || permissions == null
|
||||||
|
then []
|
||||||
|
else
|
||||||
|
["permission:"]
|
||||||
|
++ lib.concatLists (
|
||||||
|
lib.mapAttrsToList renderPermSection permissions
|
||||||
|
);
|
||||||
|
|
||||||
|
mkFrontmatter = name: agent: let
|
||||||
|
descLine = "description: \"${agent.description}.\"";
|
||||||
|
modeLine = "mode: ${agent.mode}";
|
||||||
|
modelLine =
|
||||||
|
lib.optionalString
|
||||||
|
(modelOverrides ? ${name})
|
||||||
|
"model: ${modelOverrides.${name}}\n";
|
||||||
|
permBlock = renderPermBlock (agent.permissions or {});
|
||||||
|
permLines =
|
||||||
|
if permBlock == []
|
||||||
|
then ""
|
||||||
|
else lib.concatStringsSep "\n" permBlock + "\n";
|
||||||
|
in "---\n${descLine}\n${modeLine}\n${modelLine}${permLines}---\n";
|
||||||
|
|
||||||
|
mkAgentContent = name: agent:
|
||||||
|
(mkFrontmatter name agent) + agent.systemPrompt;
|
||||||
|
in
|
||||||
|
renderAgentFiles pkgs canonical mkAgentContent "opencode-agents";
|
||||||
|
|
||||||
|
# ── Claude Code renderer ──────────────────────────────────────
|
||||||
|
#
|
||||||
|
# Produces a directory containing:
|
||||||
|
# .claude/agents/<name>.md — one per agent with YAML frontmatter
|
||||||
|
# .claude/settings.json — permission rules in Claude Code DSL
|
||||||
|
#
|
||||||
|
# Claude Code requires:
|
||||||
|
# - name field: [a-z0-9-]+ (kebab-case)
|
||||||
|
# - description field: required
|
||||||
|
# - All agents are subagents (no primary/subagent distinction)
|
||||||
|
|
||||||
|
renderForClaudeCode = {
|
||||||
|
pkgs,
|
||||||
|
canonical,
|
||||||
|
modelOverrides ? {},
|
||||||
|
}: let
|
||||||
|
# Claude Code permission DSL format: "Tool(pattern)" or just "Tool"
|
||||||
|
# Canonical bash rules → "Bash(pattern)" entries
|
||||||
|
# Canonical edit rules → "Edit(pattern)" entries
|
||||||
|
renderPermAllow = permissions: let
|
||||||
|
bashRules =
|
||||||
|
if !(permissions ? bash)
|
||||||
|
then []
|
||||||
|
else if permissions.bash.intent == "allow"
|
||||||
|
then ["Bash"]
|
||||||
|
else
|
||||||
|
map
|
||||||
|
(r: let parsed = parseRule r; in "Bash(${parsed.pattern})")
|
||||||
|
(lib.filter (r: (parseRule r).action == "allow") (permissions.bash.rules or []));
|
||||||
|
editRules =
|
||||||
|
if !(permissions ? edit)
|
||||||
|
then []
|
||||||
|
else if permissions.edit.intent == "allow"
|
||||||
|
then ["Edit"]
|
||||||
|
else
|
||||||
|
map
|
||||||
|
(r: let parsed = parseRule r; in "Edit(${parsed.pattern})")
|
||||||
|
(lib.filter (r: (parseRule r).action == "allow") (permissions.edit.rules or []));
|
||||||
|
webRules =
|
||||||
|
lib.optional (permissions.webfetch.intent or "" == "allow") "WebFetch";
|
||||||
|
in
|
||||||
|
bashRules ++ editRules ++ webRules;
|
||||||
|
|
||||||
|
renderPermDeny = permissions: let
|
||||||
|
bashRules =
|
||||||
|
if !(permissions ? bash)
|
||||||
|
then []
|
||||||
|
else
|
||||||
|
map
|
||||||
|
(r: let parsed = parseRule r; in "Bash(${parsed.pattern})")
|
||||||
|
(lib.filter (r: (parseRule r).action == "deny") (permissions.bash.rules or []));
|
||||||
|
editRules =
|
||||||
|
if !(permissions ? edit)
|
||||||
|
then []
|
||||||
|
else
|
||||||
|
map
|
||||||
|
(r: let parsed = parseRule r; in "Edit(${parsed.pattern})")
|
||||||
|
(lib.filter (r: (parseRule r).action == "deny") (permissions.edit.rules or []));
|
||||||
|
in
|
||||||
|
bashRules ++ editRules;
|
||||||
|
|
||||||
|
# Build YAML frontmatter for one Claude Code agent .md file.
|
||||||
|
mkClaudeFrontmatter = name: agent: let
|
||||||
|
descLine = "description: \"${agent.description}\"";
|
||||||
|
modelLine =
|
||||||
|
lib.optionalString
|
||||||
|
(modelOverrides ? ${name})
|
||||||
|
"model: ${modelOverrides.${name}}\n";
|
||||||
|
skillsLine =
|
||||||
|
if (agent ? skills) && agent.skills != []
|
||||||
|
then "skills:\n" + lib.concatStringsSep "\n" (map (s: " - ${s}") agent.skills) + "\n"
|
||||||
|
else "";
|
||||||
|
in "---\n${descLine}\n${modelLine}${skillsLine}---\n";
|
||||||
|
|
||||||
|
mkClaudeAgentContent = name: agent:
|
||||||
|
(mkClaudeFrontmatter name agent) + agent.systemPrompt;
|
||||||
|
|
||||||
|
agentFiles = renderAgentFiles pkgs canonical mkClaudeAgentContent "claude-code-agent-files";
|
||||||
|
|
||||||
|
# Build settings.json with permission rules aggregated from all agents.
|
||||||
|
allAllows = lib.flatten (lib.mapAttrsToList (_: agent: renderPermAllow (agent.permissions or {})) canonical);
|
||||||
|
allDenies = lib.flatten (lib.mapAttrsToList (_: agent: renderPermDeny (agent.permissions or {})) canonical);
|
||||||
|
|
||||||
|
settingsJson = builtins.toJSON {
|
||||||
|
permissions = {
|
||||||
|
allow = lib.unique (lib.sort (a: b: a < b) allAllows);
|
||||||
|
deny = lib.unique (lib.sort (a: b: a < b) allDenies);
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
settingsFile = pkgs.writeText "claude-settings.json" settingsJson;
|
||||||
|
in
|
||||||
|
pkgs.runCommand "claude-code-agents" {} ''
|
||||||
|
mkdir -p $out/.claude/agents
|
||||||
|
cp -r ${agentFiles}/* $out/.claude/agents/
|
||||||
|
cp ${settingsFile} $out/.claude/settings.json
|
||||||
|
'';
|
||||||
|
|
||||||
|
# ── Pi renderer ───────────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# This renderer produces:
|
||||||
|
# AGENTS.md — concatenated agent descriptions + specialist listing
|
||||||
|
# SYSTEM.md — primary agent's system prompt (replaces Pi default)
|
||||||
|
# agents/{name}.md — one per agent for pi-subagents (YAML frontmatter + prompt)
|
||||||
|
#
|
||||||
|
# The agents/ files use pi-subagents frontmatter format:
|
||||||
|
# name, description, tools, extensions, model, thinking, skill,
|
||||||
|
# output, defaultReads, defaultProgress, interactive, maxSubagentDepth
|
||||||
|
|
||||||
|
renderForPi = {
|
||||||
|
pkgs,
|
||||||
|
canonical,
|
||||||
|
modelOverrides ? {},
|
||||||
|
primaryAgent ? null,
|
||||||
|
codingRules ? null,
|
||||||
|
}: let
|
||||||
|
# Import coding-rules lib for concatRulesMd when codingRules is provided
|
||||||
|
codingRulesLib = import ./coding-rules.nix {inherit lib;};
|
||||||
|
# Find the primary agent (there should be exactly one).
|
||||||
|
primaryAgents = lib.filterAttrs (_: a: a.mode == "primary") canonical;
|
||||||
|
primaryNames = lib.attrNames primaryAgents;
|
||||||
|
primaryName =
|
||||||
|
if primaryAgent != null
|
||||||
|
then primaryAgent
|
||||||
|
else if primaryNames == []
|
||||||
|
then throw "lib.agents.renderForPi: no primary agent found"
|
||||||
|
else builtins.head primaryNames;
|
||||||
|
primary = builtins.getAttr primaryName primaryAgents;
|
||||||
|
|
||||||
|
# Subagents for the specialist listing.
|
||||||
|
subagents = lib.filterAttrs (_: a: a.mode != "primary") canonical;
|
||||||
|
|
||||||
|
# ── Permission → Pi tool mapping ──────────────────────────────
|
||||||
|
#
|
||||||
|
# Pi built-in tools: read, bash, edit, write, grep, find, ls,
|
||||||
|
# mcp, subagent, web_search, fetch_content, etc.
|
||||||
|
# Canonical tools: bash, edit, webfetch, websearch, question, external_directory
|
||||||
|
#
|
||||||
|
# We map canonical permissions to Pi's tool list.
|
||||||
|
# intent=allow → include tool; intent=deny → exclude; intent=ask → include (Pi has no ask granularity)
|
||||||
|
# When specific allow rules exist, the tool is always included (Pi can't restrict by pattern).
|
||||||
|
|
||||||
|
piToolsForAgent = agent: let
|
||||||
|
perms = agent.permissions or {};
|
||||||
|
tools = [];
|
||||||
|
# Always available: read (no permission concept in Pi)
|
||||||
|
addIf = tool: section:
|
||||||
|
if section.intent == "allow" || section.intent == "ask"
|
||||||
|
then [tool]
|
||||||
|
else [];
|
||||||
|
# bash → bash
|
||||||
|
withBash = tools ++ (addIf "bash" (perms.bash or {intent = "ask";}));
|
||||||
|
# edit → edit
|
||||||
|
withEdit = withBash ++ (addIf "edit" (perms.edit or {intent = "deny";}));
|
||||||
|
# webfetch → fetch_content
|
||||||
|
withFetch = withEdit ++ (addIf "fetch_content" (perms.webfetch or {intent = "deny";}));
|
||||||
|
# websearch → web_search
|
||||||
|
withSearch = withFetch ++ (addIf "web_search" (perms.websearch or {intent = "deny";}));
|
||||||
|
in
|
||||||
|
lib.unique (withSearch ++ ["read" "grep" "find" "ls"]);
|
||||||
|
|
||||||
|
# ── Build YAML frontmatter for pi-subagents .md files ──────────
|
||||||
|
mkPiFrontmatter = name: agent: let
|
||||||
|
tools = piToolsForAgent agent;
|
||||||
|
descLine = "description: \"${agent.description}\"";
|
||||||
|
toolsLine = "tools: ${lib.concatStringsSep ", " tools}";
|
||||||
|
model =
|
||||||
|
if modelOverrides ? ${name}
|
||||||
|
then "model: ${modelOverrides.${name}}"
|
||||||
|
else "";
|
||||||
|
skillsLine =
|
||||||
|
if (agent ? skills) && agent.skills != []
|
||||||
|
then "skill: ${lib.concatStringsSep ", " agent.skills}"
|
||||||
|
else "";
|
||||||
|
in
|
||||||
|
"---\n"
|
||||||
|
+ "name: ${name}\n"
|
||||||
|
+ "${descLine}\n"
|
||||||
|
+ "${toolsLine}\n"
|
||||||
|
+ (lib.optionalString (model != "") "${model}\n")
|
||||||
|
+ (lib.optionalString (skillsLine != "") "${skillsLine}\n")
|
||||||
|
+ "---\n";
|
||||||
|
|
||||||
|
mkPiAgentContent = name: agent:
|
||||||
|
(mkPiFrontmatter name agent) + agent.systemPrompt;
|
||||||
|
|
||||||
|
piAgentFiles = renderAgentFiles pkgs canonical mkPiAgentContent "pi-agent-files";
|
||||||
|
|
||||||
|
# ── Build AGENTS.md content ───────────────────────────────────
|
||||||
|
primaryDn = primary.display_name or primaryName;
|
||||||
|
specialistEntries = let
|
||||||
|
mkEntry = name: agent: let
|
||||||
|
dn = agent.display_name or name;
|
||||||
|
in
|
||||||
|
"- **" + dn + "**: " + agent.description;
|
||||||
|
in
|
||||||
|
lib.mapAttrsToList mkEntry subagents;
|
||||||
|
# ── Coding rules section (optional) ────────────────────────
|
||||||
|
# When codingRules is provided, append selected rules to AGENTS.md.
|
||||||
|
# codingRules attrset: { agents, languages, concerns, frameworks }
|
||||||
|
codingRulesSection =
|
||||||
|
if codingRules != null
|
||||||
|
then let
|
||||||
|
section = codingRulesLib.mkRulesMdSection codingRules;
|
||||||
|
in
|
||||||
|
if section != ""
|
||||||
|
then "\n" + section
|
||||||
|
else ""
|
||||||
|
else "";
|
||||||
|
|
||||||
|
agentsMd =
|
||||||
|
"# Agent Instructions\n"
|
||||||
|
+ "\n"
|
||||||
|
+ "## "
|
||||||
|
+ primaryDn
|
||||||
|
+ "\n"
|
||||||
|
+ "\n"
|
||||||
|
+ primary.description
|
||||||
|
+ "\n"
|
||||||
|
+ "\n"
|
||||||
|
+ (
|
||||||
|
if subagents == {}
|
||||||
|
then ""
|
||||||
|
else "## Available Specialists\n\n" + lib.concatStringsSep "\n" specialistEntries + "\n"
|
||||||
|
)
|
||||||
|
+ codingRulesSection;
|
||||||
|
|
||||||
|
agentsMdFile = pkgs.writeText "AGENTS.md" agentsMd;
|
||||||
|
systemMdFile = pkgs.writeText "SYSTEM.md" primary.systemPrompt;
|
||||||
|
in
|
||||||
|
pkgs.runCommand "pi-agents" {} ''
|
||||||
|
mkdir -p $out/agents
|
||||||
|
cp ${agentsMdFile} $out/AGENTS.md
|
||||||
|
cp ${systemMdFile} $out/SYSTEM.md
|
||||||
|
cp -r ${piAgentFiles}/* $out/agents/
|
||||||
|
'';
|
||||||
|
|
||||||
|
# ── renderForTool dispatcher ──────────────────────────────────
|
||||||
|
#
|
||||||
|
# Dispatches to the correct renderer by tool name.
|
||||||
|
# tool: "opencode" | "claude-code" | "pi"
|
||||||
|
|
||||||
|
renderForTool = {
|
||||||
|
pkgs,
|
||||||
|
agentsInput,
|
||||||
|
tool,
|
||||||
|
modelOverrides ? {},
|
||||||
|
codingRules ? null,
|
||||||
|
}: let
|
||||||
|
canonical = agentsInput.lib.loadAgents;
|
||||||
|
in
|
||||||
|
if tool == "opencode"
|
||||||
|
then
|
||||||
|
agentsLib.renderForOpencode {
|
||||||
|
inherit pkgs canonical modelOverrides;
|
||||||
|
}
|
||||||
|
else if tool == "claude-code"
|
||||||
|
then
|
||||||
|
agentsLib.renderForClaudeCode {
|
||||||
|
inherit pkgs canonical modelOverrides;
|
||||||
|
}
|
||||||
|
else if tool == "pi"
|
||||||
|
then
|
||||||
|
agentsLib.renderForPi {
|
||||||
|
inherit pkgs canonical modelOverrides codingRules;
|
||||||
|
}
|
||||||
|
else throw "lib.agents.renderForTool: unknown tool '${tool}'. Must be opencode, claude-code, or pi.";
|
||||||
|
|
||||||
|
# ── shellHookForTool ──────────────────────────────────────────
|
||||||
|
#
|
||||||
|
# Generates a shellHook string for use in devShells that symlinks
|
||||||
|
# rendered agent files into the project directory.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# devShells.default = pkgs.mkShell {
|
||||||
|
# shellHook = m3taLib.agents.shellHookForTool {
|
||||||
|
# inherit pkgs;
|
||||||
|
# agentsInput = inputs.agents;
|
||||||
|
# tool = "opencode";
|
||||||
|
# modelOverrides = { chiron = "anthropic/claude-sonnet-4"; };
|
||||||
|
# };
|
||||||
|
# };
|
||||||
|
|
||||||
|
shellHookForTool = {
|
||||||
|
pkgs,
|
||||||
|
agentsInput,
|
||||||
|
tool,
|
||||||
|
modelOverrides ? {},
|
||||||
|
codingRules ? null,
|
||||||
|
}: let
|
||||||
|
rendered = agentsLib.renderForTool {
|
||||||
|
inherit pkgs agentsInput tool modelOverrides codingRules;
|
||||||
|
};
|
||||||
|
in
|
||||||
|
if tool == "opencode"
|
||||||
|
then ''
|
||||||
|
# Agent files for OpenCode
|
||||||
|
mkdir -p .opencode/agents
|
||||||
|
ln -sfn ${rendered}/* .opencode/agents/
|
||||||
|
''
|
||||||
|
else if tool == "claude-code"
|
||||||
|
then ''
|
||||||
|
# Agent files for Claude Code
|
||||||
|
mkdir -p .claude/agents
|
||||||
|
ln -sfn ${rendered}/.claude/agents/* .claude/agents/
|
||||||
|
ln -sfn ${rendered}/.claude/settings.json .claude/settings.json
|
||||||
|
''
|
||||||
|
else if tool == "pi"
|
||||||
|
then ''
|
||||||
|
# Agent files for Pi
|
||||||
|
ln -sfn ${rendered}/AGENTS.md AGENTS.md
|
||||||
|
mkdir -p .pi
|
||||||
|
ln -sfn ${rendered}/SYSTEM.md .pi/SYSTEM.md
|
||||||
|
mkdir -p .pi/agents
|
||||||
|
ln -sfn ${rendered}/agents/* .pi/agents/
|
||||||
|
''
|
||||||
|
else throw "lib.agents.shellHookForTool: unknown tool '${tool}'";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
agentsLib
|
||||||
233
lib/coding-rules.nix
Normal file
233
lib/coding-rules.nix
Normal file
@@ -0,0 +1,233 @@
|
|||||||
|
# Coding rules management utilities
|
||||||
|
#
|
||||||
|
# This module provides functions to configure Opencode agent rules across
|
||||||
|
# multiple projects. Rules are defined in the AGENTS repository and can be
|
||||||
|
# selectively included based on language, framework, and concerns.
|
||||||
|
#
|
||||||
|
# Usage in your configuration:
|
||||||
|
#
|
||||||
|
# # In your flake or configuration:
|
||||||
|
# let
|
||||||
|
# m3taLib = inputs.m3ta-nixpkgs.lib.${system};
|
||||||
|
#
|
||||||
|
# rules = m3taLib.coding-rules.mkCodingRules {
|
||||||
|
# agents = inputs.agents;
|
||||||
|
# languages = [ "python" "typescript" ];
|
||||||
|
# concerns = [ "coding-style" "naming" "documentation" ];
|
||||||
|
# frameworks = [ "react" "fastapi" ];
|
||||||
|
# };
|
||||||
|
# in {
|
||||||
|
# # Use in your devShell:
|
||||||
|
# devShells.default = pkgs.mkShell {
|
||||||
|
# shellHook = rules.shellHook;
|
||||||
|
# inherit (rules) instructions;
|
||||||
|
# };
|
||||||
|
# }
|
||||||
|
#
|
||||||
|
# The shellHook creates:
|
||||||
|
# - A `.opencode-rules/` symlink pointing to the AGENTS repository rules directory
|
||||||
|
# - A `coding-rules.json` file with a $schema reference and instructions list
|
||||||
|
# - (Optional) Appends coding rules to `AGENTS.md` for Pi agent discovery
|
||||||
|
#
|
||||||
|
# The instructions list contains paths relative to the project root, all prefixed
|
||||||
|
# with `.opencode-rules/`, making them portable across different project locations.
|
||||||
|
{lib}: let
|
||||||
|
# Create Opencode rules configuration from AGENTS repository
|
||||||
|
#
|
||||||
|
# Args:
|
||||||
|
# agents: Path to the AGENTS repository (non-flake input)
|
||||||
|
# languages: Optional list of language-specific rules to include
|
||||||
|
# (e.g., [ "python" "typescript" "rust" ])
|
||||||
|
# concerns: Optional list of concern rules to include
|
||||||
|
# Default: [ "coding-style" "naming" "documentation" "testing" "git-workflow" "project-structure" ]
|
||||||
|
# frameworks: Optional list of framework-specific rules to include
|
||||||
|
# (e.g., [ "react" "fastapi" "django" ])
|
||||||
|
# extraInstructions: Optional list of additional instruction paths
|
||||||
|
# (for custom rules outside standard locations)
|
||||||
|
# forPi: Whether to also append rules to AGENTS.md for Pi agent (default: true)
|
||||||
|
# Pi discovers AGENTS.md files by walking parent dirs + cwd and concatenates them.
|
||||||
|
# When enabled, a delimited block is appended to (or created in) AGENTS.md.
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# An attribute set containing:
|
||||||
|
# - shellHook: Bash code to create symlink and coding-rules.json
|
||||||
|
# - instructions: List of rule file paths (relative to project root)
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# mkCodingRules {
|
||||||
|
# agents = inputs.agents;
|
||||||
|
# languages = [ "python" ];
|
||||||
|
# frameworks = [ "fastapi" ];
|
||||||
|
# }
|
||||||
|
# # Returns:
|
||||||
|
# # {
|
||||||
|
# # shellHook = "...";
|
||||||
|
# # instructions = [
|
||||||
|
# # ".opencode-rules/concerns/coding-style.md"
|
||||||
|
# # ".opencode-rules/concerns/naming.md"
|
||||||
|
# # ".opencode-rules/concerns/documentation.md"
|
||||||
|
# # ".opencode-rules/concerns/testing.md"
|
||||||
|
# # ".opencode-rules/concerns/git-workflow.md"
|
||||||
|
# # ".opencode-rules/concerns/project-structure.md"
|
||||||
|
# # ".opencode-rules/languages/python.md"
|
||||||
|
# # ".opencode-rules/frameworks/fastapi.md"
|
||||||
|
# # ];
|
||||||
|
# # }
|
||||||
|
mkCodingRules = {
|
||||||
|
agents,
|
||||||
|
languages ? [],
|
||||||
|
concerns ? [
|
||||||
|
"coding-style"
|
||||||
|
"naming"
|
||||||
|
"documentation"
|
||||||
|
"testing"
|
||||||
|
"git-workflow"
|
||||||
|
"project-structure"
|
||||||
|
],
|
||||||
|
frameworks ? [],
|
||||||
|
extraInstructions ? [],
|
||||||
|
rulesDir ? ".opencode-rules",
|
||||||
|
forPi ? false,
|
||||||
|
}: let
|
||||||
|
# Build instructions list by mapping concerns, languages, frameworks to their file paths
|
||||||
|
# All paths are relative to project root via the rulesDir symlink
|
||||||
|
instructions =
|
||||||
|
(map (c: "${rulesDir}/concerns/${c}.md") concerns)
|
||||||
|
++ (map (l: "${rulesDir}/languages/${l}.md") languages)
|
||||||
|
++ (map (f: "${rulesDir}/frameworks/${f}.md") frameworks)
|
||||||
|
++ extraInstructions;
|
||||||
|
|
||||||
|
# Generate JSON configuration for coding rules
|
||||||
|
rulesConfig = {
|
||||||
|
"$schema" = "https://opencode.ai/config.json";
|
||||||
|
inherit instructions;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Pi rules content (concatenated markdown) — only computed when forPi is true
|
||||||
|
piRulesSection =
|
||||||
|
if forPi
|
||||||
|
then mkRulesMdSection {inherit agents languages concerns frameworks;}
|
||||||
|
else "";
|
||||||
|
|
||||||
|
# Bash snippet to append rules to AGENTS.md for Pi discovery.
|
||||||
|
# Uses HTML comment markers for idempotent updates:
|
||||||
|
# - Removes any existing CODING-RULES block
|
||||||
|
# - Appends the new block
|
||||||
|
# - Creates AGENTS.md if it doesn't exist
|
||||||
|
# Note: Uses plain if-then-else instead of lib.optionalString to avoid
|
||||||
|
# forcing the `lib` argument (which may come from import <nixpkgs/lib>)
|
||||||
|
# when forPi is false.
|
||||||
|
piShellHook =
|
||||||
|
if forPi && piRulesSection != ""
|
||||||
|
then ''
|
||||||
|
# Pi agent: append coding rules to AGENTS.md
|
||||||
|
if [ -f AGENTS.md ]; then
|
||||||
|
# Remove existing coding-rules block (if any)
|
||||||
|
sed -i '/<!-- CODING-RULES:START -->/,/<!-- CODING-RULES:END -->/d' AGENTS.md
|
||||||
|
# Append new coding-rules block
|
||||||
|
cat >> AGENTS.md <<'PIRULES_EOF'
|
||||||
|
${piRulesSection}
|
||||||
|
PIRULES_EOF
|
||||||
|
else
|
||||||
|
# Create AGENTS.md with just the coding rules
|
||||||
|
cat > AGENTS.md <<'PIRULES_EOF'
|
||||||
|
${piRulesSection}
|
||||||
|
PIRULES_EOF
|
||||||
|
fi
|
||||||
|
''
|
||||||
|
else "";
|
||||||
|
in {
|
||||||
|
inherit instructions;
|
||||||
|
|
||||||
|
# Shell hook to set up rules in the project
|
||||||
|
# Creates a symlink to the AGENTS rules directory and generates coding-rules.json
|
||||||
|
# Optionally appends rules to AGENTS.md for Pi agent discovery
|
||||||
|
shellHook = ''
|
||||||
|
# Create/update symlink to AGENTS rules directory
|
||||||
|
ln -sfn ${agents}/rules ${rulesDir}
|
||||||
|
|
||||||
|
# Generate coding-rules.json configuration file
|
||||||
|
cat > coding-rules.json <<'RULES_EOF'
|
||||||
|
${builtins.toJSON rulesConfig}
|
||||||
|
RULES_EOF
|
||||||
|
|
||||||
|
${piShellHook}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
# Concatenate selected rule files from the AGENTS repository into a single
|
||||||
|
# markdown string. Used by Pi (append to AGENTS.md) and could be used by
|
||||||
|
# other tools that don't support an instructions list.
|
||||||
|
#
|
||||||
|
# Args:
|
||||||
|
# agents: Path to the AGENTS repository (non-flake input)
|
||||||
|
# languages: Optional list of language-specific rules to include
|
||||||
|
# concerns: Optional list of concern rules to include
|
||||||
|
# Default: [ "coding-style" "naming" "documentation" "testing" "git-workflow" "project-structure" ]
|
||||||
|
# frameworks: Optional list of framework-specific rules to include
|
||||||
|
#
|
||||||
|
# Returns: A single concatenated markdown string with all selected rules.
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# concatRulesMd {
|
||||||
|
# agents = inputs.agents;
|
||||||
|
# languages = [ "python" ];
|
||||||
|
# concerns = [ "coding-style" ];
|
||||||
|
# }
|
||||||
|
# # Returns: "\n# Coding Style\n\n...python rules...\n"
|
||||||
|
concatRulesMd = {
|
||||||
|
agents,
|
||||||
|
languages ? [],
|
||||||
|
concerns ? [
|
||||||
|
"coding-style"
|
||||||
|
"naming"
|
||||||
|
"documentation"
|
||||||
|
"testing"
|
||||||
|
"git-workflow"
|
||||||
|
"project-structure"
|
||||||
|
],
|
||||||
|
frameworks ? [],
|
||||||
|
}: let
|
||||||
|
rulePaths =
|
||||||
|
(map (c: {
|
||||||
|
kind = "concerns";
|
||||||
|
name = c;
|
||||||
|
})
|
||||||
|
concerns)
|
||||||
|
++ (map (l: {
|
||||||
|
kind = "languages";
|
||||||
|
name = l;
|
||||||
|
})
|
||||||
|
languages)
|
||||||
|
++ (map (f: {
|
||||||
|
kind = "frameworks";
|
||||||
|
name = f;
|
||||||
|
})
|
||||||
|
frameworks);
|
||||||
|
|
||||||
|
readRule = rule: builtins.readFile "${agents}/rules/${rule.kind}/${rule.name}.md";
|
||||||
|
ruleContents = map readRule rulePaths;
|
||||||
|
in
|
||||||
|
lib.concatStringsSep "\n\n" ruleContents;
|
||||||
|
|
||||||
|
# Build a coding rules section suitable for appending to AGENTS.md.
|
||||||
|
# Wraps concatRulesMd output with a header and HTML comment markers
|
||||||
|
# for idempotent updates in project-level shellHooks.
|
||||||
|
#
|
||||||
|
# Args: Same as concatRulesMd
|
||||||
|
#
|
||||||
|
# Returns: A markdown string with start/end markers and a header.
|
||||||
|
mkRulesMdSection = args: let
|
||||||
|
content = concatRulesMd args;
|
||||||
|
in
|
||||||
|
if builtins.stringLength content == 0
|
||||||
|
then ""
|
||||||
|
else ''
|
||||||
|
<!-- CODING-RULES:START -->
|
||||||
|
# Coding Rules
|
||||||
|
|
||||||
|
${content}
|
||||||
|
<!-- CODING-RULES:END -->
|
||||||
|
'';
|
||||||
|
in {
|
||||||
|
inherit mkCodingRules concatRulesMd mkRulesMdSection;
|
||||||
|
}
|
||||||
@@ -7,6 +7,9 @@
|
|||||||
# Port management utilities
|
# Port management utilities
|
||||||
ports = import ./ports.nix {inherit lib;};
|
ports = import ./ports.nix {inherit lib;};
|
||||||
|
|
||||||
# OpenCode rules injection utilities
|
# Coding rules injection utilities
|
||||||
opencode-rules = import ./opencode-rules.nix {inherit lib;};
|
coding-rules = import ./coding-rules.nix {inherit lib;};
|
||||||
|
|
||||||
|
# Agent configuration management utilities
|
||||||
|
agents = import ./agents.nix {inherit lib;};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,116 +0,0 @@
|
|||||||
# Opencode rules management utilities
|
|
||||||
#
|
|
||||||
# This module provides functions to configure Opencode agent rules across
|
|
||||||
# multiple projects. Rules are defined in the AGENTS repository and can be
|
|
||||||
# selectively included based on language, framework, and concerns.
|
|
||||||
#
|
|
||||||
# Usage in your configuration:
|
|
||||||
#
|
|
||||||
# # In your flake or configuration:
|
|
||||||
# let
|
|
||||||
# m3taLib = inputs.m3ta-nixpkgs.lib.${system};
|
|
||||||
#
|
|
||||||
# rules = m3taLib.opencode-rules.mkOpencodeRules {
|
|
||||||
# agents = inputs.agents;
|
|
||||||
# languages = [ "python" "typescript" ];
|
|
||||||
# concerns = [ "coding-style" "naming" "documentation" ];
|
|
||||||
# frameworks = [ "react" "fastapi" ];
|
|
||||||
# };
|
|
||||||
# in {
|
|
||||||
# # Use in your devShell:
|
|
||||||
# devShells.default = pkgs.mkShell {
|
|
||||||
# shellHook = rules.shellHook;
|
|
||||||
# inherit (rules) instructions;
|
|
||||||
# };
|
|
||||||
# }
|
|
||||||
#
|
|
||||||
# The shellHook creates:
|
|
||||||
# - A `.opencode-rules/` symlink pointing to the AGENTS repository rules directory
|
|
||||||
# - An `opencode.json` file with a $schema reference and instructions list
|
|
||||||
#
|
|
||||||
# The instructions list contains paths relative to the project root, all prefixed
|
|
||||||
# with `.opencode-rules/`, making them portable across different project locations.
|
|
||||||
{lib}: {
|
|
||||||
# Create Opencode rules configuration from AGENTS repository
|
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
# agents: Path to the AGENTS repository (non-flake input)
|
|
||||||
# languages: Optional list of language-specific rules to include
|
|
||||||
# (e.g., [ "python" "typescript" "rust" ])
|
|
||||||
# concerns: Optional list of concern rules to include
|
|
||||||
# Default: [ "coding-style" "naming" "documentation" "testing" "git-workflow" "project-structure" ]
|
|
||||||
# frameworks: Optional list of framework-specific rules to include
|
|
||||||
# (e.g., [ "react" "fastapi" "django" ])
|
|
||||||
# extraInstructions: Optional list of additional instruction paths
|
|
||||||
# (for custom rules outside standard locations)
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# An attribute set containing:
|
|
||||||
# - shellHook: Bash code to create symlink and opencode.json
|
|
||||||
# - instructions: List of rule file paths (relative to project root)
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# mkOpencodeRules {
|
|
||||||
# agents = inputs.agents;
|
|
||||||
# languages = [ "python" ];
|
|
||||||
# frameworks = [ "fastapi" ];
|
|
||||||
# }
|
|
||||||
# # Returns:
|
|
||||||
# # {
|
|
||||||
# # shellHook = "...";
|
|
||||||
# # instructions = [
|
|
||||||
# # ".opencode-rules/concerns/coding-style.md"
|
|
||||||
# # ".opencode-rules/concerns/naming.md"
|
|
||||||
# # ".opencode-rules/concerns/documentation.md"
|
|
||||||
# # ".opencode-rules/concerns/testing.md"
|
|
||||||
# # ".opencode-rules/concerns/git-workflow.md"
|
|
||||||
# # ".opencode-rules/concerns/project-structure.md"
|
|
||||||
# # ".opencode-rules/languages/python.md"
|
|
||||||
# # ".opencode-rules/frameworks/fastapi.md"
|
|
||||||
# # ];
|
|
||||||
# # }
|
|
||||||
mkOpencodeRules = {
|
|
||||||
agents,
|
|
||||||
languages ? [],
|
|
||||||
concerns ? [
|
|
||||||
"coding-style"
|
|
||||||
"naming"
|
|
||||||
"documentation"
|
|
||||||
"testing"
|
|
||||||
"git-workflow"
|
|
||||||
"project-structure"
|
|
||||||
],
|
|
||||||
frameworks ? [],
|
|
||||||
extraInstructions ? [],
|
|
||||||
}: let
|
|
||||||
rulesDir = ".opencode-rules";
|
|
||||||
|
|
||||||
# Build instructions list by mapping concerns, languages, frameworks to their file paths
|
|
||||||
# All paths are relative to project root via the rulesDir symlink
|
|
||||||
instructions =
|
|
||||||
(map (c: "${rulesDir}/concerns/${c}.md") concerns)
|
|
||||||
++ (map (l: "${rulesDir}/languages/${l}.md") languages)
|
|
||||||
++ (map (f: "${rulesDir}/frameworks/${f}.md") frameworks)
|
|
||||||
++ extraInstructions;
|
|
||||||
|
|
||||||
# Generate JSON configuration for Opencode
|
|
||||||
opencodeConfig = {
|
|
||||||
"$schema" = "https://opencode.ai/config.json";
|
|
||||||
inherit instructions;
|
|
||||||
};
|
|
||||||
in {
|
|
||||||
inherit instructions;
|
|
||||||
|
|
||||||
# Shell hook to set up rules in the project
|
|
||||||
# Creates a symlink to the AGENTS rules directory and generates opencode.json
|
|
||||||
shellHook = ''
|
|
||||||
# Create/update symlink to AGENTS rules directory
|
|
||||||
ln -sfn ${agents}/rules ${rulesDir}
|
|
||||||
|
|
||||||
# Generate opencode.json configuration file
|
|
||||||
cat > opencode.json <<'OPENCODE_EOF'
|
|
||||||
${builtins.toJSON opencodeConfig}
|
|
||||||
OPENCODE_EOF
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -95,19 +95,4 @@
|
|||||||
# List of service names (strings)
|
# List of service names (strings)
|
||||||
listServices = lib.attrNames ports;
|
listServices = lib.attrNames ports;
|
||||||
};
|
};
|
||||||
|
|
||||||
# Simple helper to get a port without host override
|
|
||||||
# Useful when you don't need host-specific ports
|
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
# portsConfig: Same structure as mkPortHelpers
|
|
||||||
# service: The service name (string)
|
|
||||||
#
|
|
||||||
# Returns:
|
|
||||||
# Port number (int) or null if service not found
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# getDefaultPort myPorts "nginx" # Returns default port only
|
|
||||||
getDefaultPort = portsConfig: service:
|
|
||||||
portsConfig.ports.${service} or null;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,13 @@ home-manager/
|
|||||||
│ └── zellij-ps.nix
|
│ └── zellij-ps.nix
|
||||||
└── coding/ # Development tools
|
└── coding/ # Development tools
|
||||||
├── default.nix # Category aggregator
|
├── default.nix # Category aggregator
|
||||||
└── editors.nix # Neovim + Zed configs
|
├── editors.nix # Neovim + Zed configs
|
||||||
|
├── opencode.nix # OpenCode non-agent config (theme, plugins, formatter)
|
||||||
|
└── agents/ # Per-tool agent deployment (canonical TOML → rendered)
|
||||||
|
├── default.nix
|
||||||
|
├── opencode.nix # File-based agents + skills + context
|
||||||
|
├── claude-code.nix # Claude Code agents + settings.json
|
||||||
|
└── pi.nix # Pi AGENTS.md + SYSTEM.md
|
||||||
```
|
```
|
||||||
|
|
||||||
## Where to Look
|
## Where to Look
|
||||||
@@ -24,11 +30,16 @@ home-manager/
|
|||||||
| Add coding module | `coding/<name>.nix`, import in `coding/default.nix` |
|
| Add coding module | `coding/<name>.nix`, import in `coding/default.nix` |
|
||||||
| Add new category | Create `<category>/default.nix`, import in root `default.nix` |
|
| Add new category | Create `<category>/default.nix`, import in root `default.nix` |
|
||||||
| Module with host ports | Import `../../lib/ports.nix`, use `mkPortHelpers` |
|
| Module with host ports | Import `../../lib/ports.nix`, use `mkPortHelpers` |
|
||||||
|
| Add agent renderer | `coding/agents/<tool>.nix`, import in `coding/agents/default.nix` |
|
||||||
|
|
||||||
## Option Namespaces
|
## Option Namespaces
|
||||||
|
|
||||||
- `cli.*` - CLI tools (e.g., `cli.zellij-ps.enable`)
|
- `cli.*` - CLI tools (e.g., `cli.zellij-ps.enable`)
|
||||||
- `coding.editors.*` - Editor configs (e.g., `coding.editors.neovim.enable`)
|
- `coding.editors.*` - Editor configs (e.g., `coding.editors.neovim.enable`)
|
||||||
|
- `coding.opencode.*` - OpenCode non-agent config (theme, plugins, formatter)
|
||||||
|
- `coding.agents.opencode.*` - OpenCode agent deployment (file-based agents)
|
||||||
|
- `coding.agents.claude-code.*` - Claude Code agent deployment
|
||||||
|
- `coding.agents.pi.*` - Pi agent deployment
|
||||||
- `m3ta.ports.*` - Port management (shared with NixOS)
|
- `m3ta.ports.*` - Port management (shared with NixOS)
|
||||||
|
|
||||||
## Patterns
|
## Patterns
|
||||||
@@ -72,3 +83,153 @@ config = mkMerge [
|
|||||||
| `generateEnvVars` | Available | Not available |
|
| `generateEnvVars` | Available | Not available |
|
||||||
| Output file | `~/.config/m3ta/ports.json` | `/etc/m3ta/ports.json` |
|
| Output file | `~/.config/m3ta/ports.json` | `/etc/m3ta/ports.json` |
|
||||||
| Package access | `pkgs.*` via overlay | `pkgs.*` via overlay |
|
| Package access | `pkgs.*` via overlay | `pkgs.*` via overlay |
|
||||||
|
|
||||||
|
## Agent Modules
|
||||||
|
|
||||||
|
Agent definitions are stored as canonical `agent.toml` + `system-prompt.md` in the
|
||||||
|
[AGENTS repo](https://code.m3ta.dev/m3tam3re/AGENTS). Renderers in `lib/agents.nix`
|
||||||
|
transform these into tool-specific configs. Each tool has its own HM sub-module
|
||||||
|
under `coding/agents/`.
|
||||||
|
|
||||||
|
### OpenCode (`coding.agents.opencode`)
|
||||||
|
|
||||||
|
Renders file-based agents to `~/.config/opencode/agents/*.md`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
coding.agents.opencode = {
|
||||||
|
enable = true;
|
||||||
|
agentsInput = inputs.agents;
|
||||||
|
modelOverrides = {
|
||||||
|
chiron = "anthropic/claude-sonnet-4";
|
||||||
|
};
|
||||||
|
externalSkills = [
|
||||||
|
{ src = inputs.skills-anthropic; }
|
||||||
|
];
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:** `enable`, `agentsInput`, `modelOverrides`, `externalSkills`
|
||||||
|
|
||||||
|
### Claude Code (`coding.agents.claude-code`)
|
||||||
|
|
||||||
|
Renders agents to `~/.claude/agents/*.md` + `~/.claude/settings.json`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
coding.agents.claude-code = {
|
||||||
|
enable = true;
|
||||||
|
agentsInput = inputs.agents;
|
||||||
|
modelOverrides = {};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:** `enable`, `agentsInput`, `modelOverrides`
|
||||||
|
|
||||||
|
### Pi (`coding.agents.pi`)
|
||||||
|
|
||||||
|
Renders `AGENTS.md` + `SYSTEM.md` to `~/.pi/agent/`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
coding.agents.pi = {
|
||||||
|
enable = true;
|
||||||
|
agentsInput = inputs.agents;
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**Options:** `enable`, `agentsInput`
|
||||||
|
|
||||||
|
### Project-level usage
|
||||||
|
|
||||||
|
For per-project agent setup via `flake.nix` + `direnv`:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
m3taLib.agents.shellHookForTool {
|
||||||
|
inherit pkgs;
|
||||||
|
agentsInput = inputs.agents;
|
||||||
|
tool = "opencode";
|
||||||
|
modelOverrides = { chiron = "anthropic/claude-sonnet-4"; };
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
## Migration Guide (OpenCode agents)
|
||||||
|
|
||||||
|
The agent system was migrated from embedded `agents.json` to file-based canonical
|
||||||
|
`agent.toml` definitions. Here is how to migrate your home-manager config.
|
||||||
|
|
||||||
|
### What changed
|
||||||
|
|
||||||
|
| Before | After |
|
||||||
|
|--------|-------|
|
||||||
|
| `coding.opencode.agentsInput` | `coding.agents.opencode.agentsInput` |
|
||||||
|
| `coding.opencode.externalSkills` | `coding.agents.opencode.externalSkills` |
|
||||||
|
| Agents embedded in `config.json` | File-based `~/.config/opencode/agents/*.md` |
|
||||||
|
| Model hardcoded in `agents.json` | Per-machine `modelOverrides` |
|
||||||
|
| `mkOpencodeRules` | `mkCodingRules` |
|
||||||
|
|
||||||
|
### Migration steps
|
||||||
|
|
||||||
|
**1. Update home-manager config:**
|
||||||
|
|
||||||
|
Move `agentsInput` and `externalSkills` from `coding.opencode` to `coding.agents.opencode`.
|
||||||
|
Add `modelOverrides` with the models previously hardcoded in agents.json:
|
||||||
|
|
||||||
|
```nix
|
||||||
|
# BEFORE (legacy):
|
||||||
|
coding.opencode = {
|
||||||
|
enable = true;
|
||||||
|
agentsInput = inputs.agents;
|
||||||
|
externalSkills = [{ src = inputs.skills-anthropic; }];
|
||||||
|
ohMyOpencodeSettings = { ... };
|
||||||
|
};
|
||||||
|
|
||||||
|
# AFTER (new):
|
||||||
|
coding.opencode = {
|
||||||
|
enable = true;
|
||||||
|
ohMyOpencodeSettings = { ... };
|
||||||
|
};
|
||||||
|
|
||||||
|
coding.agents.opencode = {
|
||||||
|
enable = true;
|
||||||
|
agentsInput = inputs.agents;
|
||||||
|
externalSkills = [{ src = inputs.skills-anthropic; }];
|
||||||
|
modelOverrides = {
|
||||||
|
chiron = "zai-coding-plan/glm-5";
|
||||||
|
"chiron-forge" = "zai-coding-plan/glm-5";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
|
**2. Run `home-manager switch`:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
home-manager switch --flake .
|
||||||
|
```
|
||||||
|
|
||||||
|
**3. Verify agents are deployed:**
|
||||||
|
|
||||||
|
```bash
|
||||||
|
ls ~/.config/opencode/agents/
|
||||||
|
# Should show: chiron.md chiron-forge.md hermes.md athena.md apollo.md calliope.md
|
||||||
|
```
|
||||||
|
|
||||||
|
**4. Remove legacy files from AGENTS repo** (after confirming everything works):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd /home/m3tam3re/p/AI/AGENTS
|
||||||
|
rm agents/agents.json
|
||||||
|
rm prompts/chiron.txt prompts/chiron-forge.txt prompts/hermes.txt \
|
||||||
|
prompts/athena.txt prompts/apollo.txt prompts/calliope.txt
|
||||||
|
rmdir prompts/ # if empty
|
||||||
|
# Also remove lib.agentsJson from flake.nix
|
||||||
|
```
|
||||||
|
|
||||||
|
**5. Final cleanup:** After legacy files are removed from AGENTS repo,
|
||||||
|
remove `lib.agentsJson` from the AGENTS `flake.nix` (it's only needed for
|
||||||
|
backward compatibility during the transition).
|
||||||
|
|
||||||
|
### Key advantage of the new system
|
||||||
|
|
||||||
|
Prompt changes no longer require `home-manager switch`. Since agents are
|
||||||
|
deployed as file-based `~/.config/opencode/agents/*.md` (symlinks to Nix store),
|
||||||
|
you only need to edit the `system-prompt.md` in the AGENTS repo, commit, update
|
||||||
|
the flake lock, and run `home-manager switch`. Or for local development, edit
|
||||||
|
the file directly and restart the tool.
|
||||||
|
|||||||
94
modules/home-manager/coding/agents/claude-code.nix
Normal file
94
modules/home-manager/coding/agents/claude-code.nix
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
imports = [
|
||||||
|
./shared/default.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
options.coding.agents.claude-code = let
|
||||||
|
shared = import ./shared/shared-options.nix {inherit lib;};
|
||||||
|
mcpCfg = config.programs.mcp or null;
|
||||||
|
in
|
||||||
|
with lib; {
|
||||||
|
enable = mkEnableOption "Claude Code agent management via canonical agent.toml definitions";
|
||||||
|
|
||||||
|
agentsInput = shared.mkAgentsInputOption ''
|
||||||
|
The `agents` flake input (your personal AGENTS repo).
|
||||||
|
When set, agents are rendered from canonical agent.toml files
|
||||||
|
and symlinked to ~/.claude/agents/.
|
||||||
|
'';
|
||||||
|
|
||||||
|
modelOverrides = shared.mkModelOverridesOption;
|
||||||
|
|
||||||
|
externalSkills = shared.externalSkillsOption;
|
||||||
|
|
||||||
|
mcpServers = mkOption {
|
||||||
|
type = types.attrsOf types.anything;
|
||||||
|
default =
|
||||||
|
if mcpCfg != null
|
||||||
|
then mcpCfg.servers
|
||||||
|
else {};
|
||||||
|
defaultText = literalExpression "config.programs.mcp.servers";
|
||||||
|
description = ''
|
||||||
|
MCP server configurations for Claude Code.
|
||||||
|
Merged into ~/.claude/settings.json alongside permissions.
|
||||||
|
Automatically inherits from config.programs.mcp.servers.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = with lib; let
|
||||||
|
shared = import ./shared/shared-options.nix {inherit lib;};
|
||||||
|
cfg = config.coding.agents.claude-code;
|
||||||
|
agentsLib = (import ../../../../lib {inherit lib;}).agents;
|
||||||
|
in
|
||||||
|
mkIf cfg.enable (let
|
||||||
|
# Rendered agents + permissions (only if agentsInput is set)
|
||||||
|
rendered = mkIf (cfg.agentsInput != null) (
|
||||||
|
agentsLib.renderForClaudeCode {
|
||||||
|
inherit pkgs;
|
||||||
|
canonical = cfg.agentsInput.lib.loadAgents;
|
||||||
|
modelOverrides = cfg.modelOverrides;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
# Merge MCP servers into the rendered settings.json.
|
||||||
|
# The renderer produces { permissions: { allow, deny } }.
|
||||||
|
# We add mcpServers on top.
|
||||||
|
settingsJson =
|
||||||
|
if cfg.agentsInput != null
|
||||||
|
then let
|
||||||
|
renderedSettings = builtins.fromJSON (builtins.readFile "${rendered}/.claude/settings.json");
|
||||||
|
withMcp =
|
||||||
|
if cfg.mcpServers != {}
|
||||||
|
then renderedSettings // {mcpServers = cfg.mcpServers;}
|
||||||
|
else renderedSettings;
|
||||||
|
in
|
||||||
|
pkgs.writeText "claude-settings.json" (builtins.toJSON withMcp)
|
||||||
|
else if cfg.mcpServers != {}
|
||||||
|
then pkgs.writeText "claude-settings.json" (builtins.toJSON {mcpServers = cfg.mcpServers;})
|
||||||
|
else null;
|
||||||
|
in {
|
||||||
|
# Rendered agent files symlinked to ~/.claude/agents/
|
||||||
|
home.file.".claude/agents" = mkIf (cfg.agentsInput != null) {
|
||||||
|
source = "${rendered}/.claude/agents";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Skills (merged from personal AGENTS repo + optional external skills)
|
||||||
|
home.file.".claude/skills" = mkIf (cfg.agentsInput != null) {
|
||||||
|
source = cfg.agentsInput.lib.mkOpencodeSkills {
|
||||||
|
inherit pkgs;
|
||||||
|
customSkills = "${cfg.agentsInput}/skills";
|
||||||
|
externalSkills = shared.mapExternalSkills cfg.externalSkills;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Rendered settings.json with permissions + MCP servers
|
||||||
|
home.file.".claude/settings.json" = mkIf (settingsJson != null) {
|
||||||
|
source = "${settingsJson}";
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}
|
||||||
39
modules/home-manager/coding/agents/default.nix
Normal file
39
modules/home-manager/coding/agents/default.nix
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
# Per-tool agent sub-modules
|
||||||
|
# Each module handles rendering canonical agent.toml definitions
|
||||||
|
# for a specific AI coding tool.
|
||||||
|
#
|
||||||
|
# Also provides the shared coding.agents.skills submodule that writes
|
||||||
|
# ~/.agents/skills — the central skills directory used by Pi, OpenCode, etc.
|
||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
shared = import ./shared/shared-options.nix {inherit lib;};
|
||||||
|
cfg = config.coding.agents.skills;
|
||||||
|
mkIf = lib.mkIf;
|
||||||
|
in {
|
||||||
|
imports = [
|
||||||
|
./opencode.nix
|
||||||
|
./claude-code.nix
|
||||||
|
./pi.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
options.coding.agents.skills = {
|
||||||
|
agentsInput = shared.mkAgentsInputOption ''
|
||||||
|
The `agents` flake input (your personal AGENTS repo).
|
||||||
|
When set, skills are symlinked to ~/.agents/skills.
|
||||||
|
'';
|
||||||
|
|
||||||
|
externalSkills = shared.externalSkillsOption;
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf (cfg.agentsInput != null) {
|
||||||
|
home.file.".agents/skills".source = cfg.agentsInput.lib.mkOpencodeSkills {
|
||||||
|
inherit pkgs;
|
||||||
|
customSkills = "${cfg.agentsInput}/skills";
|
||||||
|
externalSkills = shared.mapExternalSkills cfg.externalSkills;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
54
modules/home-manager/coding/agents/opencode.nix
Normal file
54
modules/home-manager/coding/agents/opencode.nix
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
imports = [
|
||||||
|
./shared/default.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
options.coding.agents.opencode = let
|
||||||
|
shared = import ./shared/shared-options.nix {inherit lib;};
|
||||||
|
in
|
||||||
|
with lib; {
|
||||||
|
enable = mkEnableOption "OpenCode agent management via canonical agent.toml definitions";
|
||||||
|
|
||||||
|
agentsInput = shared.mkAgentsInputOption ''
|
||||||
|
The `agents` flake input (your personal AGENTS repo).
|
||||||
|
When set, agents are rendered from canonical agent.toml files
|
||||||
|
and symlinked to ~/.config/opencode/agents/.
|
||||||
|
'';
|
||||||
|
|
||||||
|
modelOverrides = shared.mkModelOverridesOption;
|
||||||
|
};
|
||||||
|
|
||||||
|
config = with lib; let
|
||||||
|
shared = import ./shared/shared-options.nix {inherit lib;};
|
||||||
|
cfg = config.coding.agents.opencode;
|
||||||
|
in
|
||||||
|
mkIf cfg.enable {
|
||||||
|
# Rendered agent files symlinked to ~/.config/opencode/agents/
|
||||||
|
xdg.configFile."opencode/agents" = let
|
||||||
|
agentsLib = (import ../../../../lib {inherit lib;}).agents;
|
||||||
|
in
|
||||||
|
mkIf (cfg.agentsInput != null) {
|
||||||
|
source = agentsLib.renderForOpencode {
|
||||||
|
inherit pkgs;
|
||||||
|
canonical = cfg.agentsInput.lib.loadAgents;
|
||||||
|
modelOverrides = cfg.modelOverrides;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
# Static config dirs from AGENTS repo
|
||||||
|
xdg.configFile."opencode/context" = mkIf (cfg.agentsInput != null) {
|
||||||
|
source = "${cfg.agentsInput}/context";
|
||||||
|
};
|
||||||
|
xdg.configFile."opencode/commands" = mkIf (cfg.agentsInput != null) {
|
||||||
|
source = "${cfg.agentsInput}/commands";
|
||||||
|
};
|
||||||
|
xdg.configFile."opencode/prompts" = mkIf (cfg.agentsInput != null) {
|
||||||
|
source = "${cfg.agentsInput}/prompts";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
357
modules/home-manager/coding/agents/pi.nix
Normal file
357
modules/home-manager/coding/agents/pi.nix
Normal file
@@ -0,0 +1,357 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}: {
|
||||||
|
imports = [
|
||||||
|
./shared/default.nix
|
||||||
|
];
|
||||||
|
|
||||||
|
options.coding.agents.pi = let
|
||||||
|
shared = import ./shared/shared-options.nix {inherit lib;};
|
||||||
|
mcpCfg = config.programs.mcp or null;
|
||||||
|
in
|
||||||
|
with lib; {
|
||||||
|
enable = mkEnableOption "Pi agent management via canonical agent.toml definitions";
|
||||||
|
|
||||||
|
mcpServers = mkOption {
|
||||||
|
type = types.attrsOf types.anything;
|
||||||
|
default =
|
||||||
|
if mcpCfg != null
|
||||||
|
then mcpCfg.servers
|
||||||
|
else {};
|
||||||
|
defaultText = literalExpression "config.programs.mcp.servers";
|
||||||
|
description = ''
|
||||||
|
MCP server configurations for Pi (pi-mcp-adapter).
|
||||||
|
Written to ~/.pi/agent/mcp.json.
|
||||||
|
Automatically inherits from config.programs.mcp.servers.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
agentsInput = shared.mkAgentsInputOption ''
|
||||||
|
The `agents` flake input (your personal AGENTS repo).
|
||||||
|
When set, the primary agent's system prompt is rendered as SYSTEM.md,
|
||||||
|
all agents are listed in AGENTS.md, and subagent .md files are deployed.
|
||||||
|
'';
|
||||||
|
|
||||||
|
modelOverrides = shared.mkModelOverridesOption;
|
||||||
|
|
||||||
|
primaryAgent = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Override which canonical agent is used as primary for SYSTEM.md.
|
||||||
|
When null, the first agent with mode="primary" is used.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
codingRules = mkOption {
|
||||||
|
type = types.nullOr (types.submodule {
|
||||||
|
options = {
|
||||||
|
languages = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
Language-specific coding rules to include
|
||||||
|
(e.g. [ "python" "typescript" "nix" ]).
|
||||||
|
Rule files are read from the AGENTS repo's rules/languages/ directory.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
concerns = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [
|
||||||
|
"coding-style"
|
||||||
|
"naming"
|
||||||
|
"documentation"
|
||||||
|
"testing"
|
||||||
|
"git-workflow"
|
||||||
|
"project-structure"
|
||||||
|
];
|
||||||
|
description = ''
|
||||||
|
Concern rules to include from the AGENTS repo's rules/concerns/ directory.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
frameworks = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
Framework-specific coding rules to include
|
||||||
|
(e.g. [ "react" "fastapi" ]).
|
||||||
|
Rule files are read from the AGENTS repo's rules/frameworks/ directory.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Coding rules to inject into ~/.pi/agent/AGENTS.md.
|
||||||
|
Rules are read from the AGENTS repository and appended as markdown sections.
|
||||||
|
Requires agentsInput to be set.
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
languages = [ "python" "typescript" ];
|
||||||
|
concerns = [ "coding-style" "testing" ];
|
||||||
|
frameworks = [ "fastapi" ];
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
settings = mkOption {
|
||||||
|
type = types.submodule {
|
||||||
|
freeformType = types.attrsOf types.anything;
|
||||||
|
options = {
|
||||||
|
packages = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
Pi packages to install (npm:, git:, or local paths).
|
||||||
|
These are written to ~/.pi/agent/settings.json.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultProvider = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = "Default LLM provider (e.g. 'anthropic', 'openai', 'zai').";
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultModel = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = "Default model ID.";
|
||||||
|
};
|
||||||
|
|
||||||
|
defaultThinkingLevel = mkOption {
|
||||||
|
type = types.nullOr (types.enum ["off" "minimal" "low" "medium" "high" "xhigh"]);
|
||||||
|
default = null;
|
||||||
|
description = "Default extended thinking level.";
|
||||||
|
};
|
||||||
|
|
||||||
|
theme = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = "Pi theme name.";
|
||||||
|
};
|
||||||
|
|
||||||
|
hideThinkingBlock = mkOption {
|
||||||
|
type = types.nullOr types.bool;
|
||||||
|
default = null;
|
||||||
|
description = "Hide thinking blocks in output.";
|
||||||
|
};
|
||||||
|
|
||||||
|
quietStartup = mkOption {
|
||||||
|
type = types.nullOr types.bool;
|
||||||
|
default = null;
|
||||||
|
description = "Hide startup header.";
|
||||||
|
};
|
||||||
|
|
||||||
|
compaction = mkOption {
|
||||||
|
type = types.nullOr (types.submodule {
|
||||||
|
options = {
|
||||||
|
enabled = mkOption {
|
||||||
|
type = types.nullOr types.bool;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
reserveTokens = mkOption {
|
||||||
|
type = types.nullOr types.int;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
keepRecentTokens = mkOption {
|
||||||
|
type = types.nullOr types.int;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = null;
|
||||||
|
description = "Auto-compaction settings.";
|
||||||
|
};
|
||||||
|
|
||||||
|
enabledModels = mkOption {
|
||||||
|
type = types.nullOr (types.listOf types.str);
|
||||||
|
default = null;
|
||||||
|
description = "Model patterns for Ctrl+P cycling.";
|
||||||
|
};
|
||||||
|
|
||||||
|
sessionDir = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = "Directory where session files are stored.";
|
||||||
|
};
|
||||||
|
|
||||||
|
extensions = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = "Local extension file paths or directories.";
|
||||||
|
};
|
||||||
|
|
||||||
|
skills = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = "Local skill file paths or directories.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Pi settings written to ~/.pi/agent/settings.json.
|
||||||
|
Only non-null values are included in the generated JSON.
|
||||||
|
See pi docs/settings.md for all options.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# ── Pi Guardrails ─────────────────────────────────────────────
|
||||||
|
guardrails = mkOption {
|
||||||
|
type = types.nullOr (types.submodule {
|
||||||
|
options = {
|
||||||
|
enable =
|
||||||
|
mkEnableOption
|
||||||
|
("Generate ~/.pi/agent/extensions/guardrails.json for pi-guardrails. "
|
||||||
|
+ "Adds @aliou/pi-guardrails to packages automatically.");
|
||||||
|
|
||||||
|
config = mkOption {
|
||||||
|
type = types.attrsOf types.anything;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Guardrails configuration written to ~/.pi/agent/extensions/guardrails.json.
|
||||||
|
See https://github.com/aliou/pi-guardrails for config schema.
|
||||||
|
|
||||||
|
IMPORTANT: Path access checks are lexical (not symlink-safe).
|
||||||
|
Local project .pi/extensions/guardrails.json can override same rule IDs
|
||||||
|
(memory > local > global > defaults). For immutable global policies,
|
||||||
|
consider a wrapper or upstream patch.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Pi Guardrails security configuration.
|
||||||
|
Generates ~/.pi/agent/extensions/guardrails.json when enabled.
|
||||||
|
The @aliou/pi-guardrails package is added to settings.packages automatically.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = with lib; let
|
||||||
|
shared = import ./shared/shared-options.nix {inherit lib;};
|
||||||
|
cfg = config.coding.agents.pi;
|
||||||
|
in
|
||||||
|
mkIf cfg.enable (let
|
||||||
|
# Build settings.json by filtering out null values recursively
|
||||||
|
filterNulls = attrs:
|
||||||
|
lib.filterAttrs (_: v: v != null) (
|
||||||
|
builtins.mapAttrs (_: v:
|
||||||
|
if builtins.isAttrs v
|
||||||
|
then let
|
||||||
|
filtered = filterNulls v;
|
||||||
|
in
|
||||||
|
if filtered == {}
|
||||||
|
then null
|
||||||
|
else filtered
|
||||||
|
else v)
|
||||||
|
attrs
|
||||||
|
);
|
||||||
|
|
||||||
|
# Base settings (already filtered)
|
||||||
|
piSettings = filterNulls cfg.settings;
|
||||||
|
|
||||||
|
# Guardrails package to inject when guardrails is enabled
|
||||||
|
guardrailsPackage = "npm:@aliou/pi-guardrails@0.11.1";
|
||||||
|
|
||||||
|
# Guardrails config (only when guardrails is enabled)
|
||||||
|
guardrailsJson =
|
||||||
|
if (cfg.guardrails != null && cfg.guardrails.enable)
|
||||||
|
then builtins.toJSON cfg.guardrails.config
|
||||||
|
else null;
|
||||||
|
|
||||||
|
# Merge guardrails package into settings.packages when guardrails is enabled
|
||||||
|
piSettingsWithGuardrails = let
|
||||||
|
baseSettings = cfg.settings;
|
||||||
|
basePackages = baseSettings.packages or [];
|
||||||
|
hasGuardrailsPackage =
|
||||||
|
lib.any
|
||||||
|
(p:
|
||||||
|
lib.hasPrefix "npm:@aliou/pi-guardrails" p
|
||||||
|
|| (lib.hasPrefix "git:" p && lib.hasSuffix "/pi-guardrails" p))
|
||||||
|
basePackages;
|
||||||
|
packagesWithGuardrails =
|
||||||
|
if (cfg.guardrails != null && cfg.guardrails.enable && !hasGuardrailsPackage)
|
||||||
|
then basePackages ++ [guardrailsPackage]
|
||||||
|
else basePackages;
|
||||||
|
in
|
||||||
|
if packagesWithGuardrails != basePackages
|
||||||
|
then filterNulls (baseSettings // {packages = packagesWithGuardrails;})
|
||||||
|
else piSettings;
|
||||||
|
|
||||||
|
# Coding rules config for renderForPi (only when both agentsInput and codingRules are set)
|
||||||
|
piCodingRules =
|
||||||
|
if cfg.agentsInput != null && cfg.codingRules != null
|
||||||
|
then cfg.codingRules // {agents = cfg.agentsInput;}
|
||||||
|
else null;
|
||||||
|
|
||||||
|
# Rendered agents (only computed when agentsInput is set)
|
||||||
|
rendered =
|
||||||
|
if cfg.agentsInput != null
|
||||||
|
then
|
||||||
|
(import ../../../../lib {inherit lib;}).agents.renderForPi {
|
||||||
|
inherit pkgs;
|
||||||
|
canonical = cfg.agentsInput.lib.loadAgents;
|
||||||
|
modelOverrides = cfg.modelOverrides;
|
||||||
|
primaryAgent = cfg.primaryAgent;
|
||||||
|
codingRules = piCodingRules;
|
||||||
|
}
|
||||||
|
else null;
|
||||||
|
|
||||||
|
# Dynamic home.file entries for agent .md files
|
||||||
|
agentFiles =
|
||||||
|
if cfg.agentsInput != null
|
||||||
|
then let
|
||||||
|
agentNames = builtins.attrNames cfg.agentsInput.lib.loadAgents;
|
||||||
|
in
|
||||||
|
builtins.listToAttrs (
|
||||||
|
map (name: {
|
||||||
|
name = ".pi/agent/agents/${name}.md";
|
||||||
|
value = {source = "${rendered}/agents/${name}.md";};
|
||||||
|
})
|
||||||
|
agentNames
|
||||||
|
)
|
||||||
|
else {};
|
||||||
|
in {
|
||||||
|
home.file = mkMerge [
|
||||||
|
# ── MCP servers from programs.mcp → ~/.pi/agent/mcp.json ───────
|
||||||
|
(mkIf (cfg.mcpServers != {}) {
|
||||||
|
".pi/agent/mcp.json".text = builtins.toJSON {mcpServers = cfg.mcpServers;};
|
||||||
|
".pi/agent/mcp.json".force = true;
|
||||||
|
})
|
||||||
|
|
||||||
|
# ── ~/.pi/agent/settings.json ──────────────────────────────────
|
||||||
|
{
|
||||||
|
".pi/agent/settings.json".text = builtins.toJSON piSettingsWithGuardrails;
|
||||||
|
".pi/agent/settings.json".force = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
# ── pi-guardrails config ─────────────────────────────────────
|
||||||
|
(mkIf (guardrailsJson != null) {
|
||||||
|
".pi/agent/extensions/guardrails.json".text = guardrailsJson;
|
||||||
|
".pi/agent/extensions/guardrails.json".force = true;
|
||||||
|
})
|
||||||
|
|
||||||
|
# ── AGENTS.md — agent descriptions and specialist listing ──────
|
||||||
|
(mkIf (cfg.agentsInput != null) {
|
||||||
|
".pi/agent/AGENTS.md".source = "${rendered}/AGENTS.md";
|
||||||
|
})
|
||||||
|
|
||||||
|
# ── SYSTEM.md — primary agent's system prompt ──────────────────
|
||||||
|
(mkIf (cfg.agentsInput != null) {
|
||||||
|
".pi/agent/SYSTEM.md".source = "${rendered}/SYSTEM.md";
|
||||||
|
})
|
||||||
|
|
||||||
|
# ── Agents — pi-subagents .md files ────────────────────────────
|
||||||
|
agentFiles
|
||||||
|
];
|
||||||
|
});
|
||||||
|
}
|
||||||
7
modules/home-manager/coding/agents/shared/default.nix
Normal file
7
modules/home-manager/coding/agents/shared/default.nix
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
# Shared agent module exports
|
||||||
|
# Imports all shared modules for the coding.agents namespace.
|
||||||
|
{
|
||||||
|
imports = [
|
||||||
|
./git-identity.nix
|
||||||
|
];
|
||||||
|
}
|
||||||
64
modules/home-manager/coding/agents/shared/git-identity.nix
Normal file
64
modules/home-manager/coding/agents/shared/git-identity.nix
Normal file
@@ -0,0 +1,64 @@
|
|||||||
|
# Git identity module for agent commits.
|
||||||
|
# Sets GIT_AUTHOR_*, GIT_COMMITTER_*, and GIT_SSH_COMMAND environment variables.
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib,
|
||||||
|
config,
|
||||||
|
...
|
||||||
|
}: let
|
||||||
|
cfg = config.coding.agents.gitIdentity;
|
||||||
|
in {
|
||||||
|
options.coding.agents.gitIdentity = {
|
||||||
|
enable = lib.mkEnableOption ''
|
||||||
|
Agent Git identity for commits. When enabled, sets GIT_AUTHOR_* and
|
||||||
|
GIT_COMMITTER_* environment variables for consistent bot identity.
|
||||||
|
'';
|
||||||
|
|
||||||
|
name = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "m3ta-chiron";
|
||||||
|
description = "Git user name for agent commits.";
|
||||||
|
example = "m3ta-chiron";
|
||||||
|
};
|
||||||
|
|
||||||
|
email = lib.mkOption {
|
||||||
|
type = lib.types.str;
|
||||||
|
default = "m3ta-chiron@agentmail.to";
|
||||||
|
description = "Git email for agent commits.";
|
||||||
|
example = "m3ta-chiron@agentmail.to";
|
||||||
|
};
|
||||||
|
|
||||||
|
signingKey = lib.mkOption {
|
||||||
|
type = lib.types.nullOr lib.types.path;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Optional GPG signing key for verified commits.
|
||||||
|
Set to null to disable signing.
|
||||||
|
'';
|
||||||
|
example = "/home/user/.gnupg/sign_key.gpg";
|
||||||
|
};
|
||||||
|
|
||||||
|
sshKey = lib.mkOption {
|
||||||
|
type = lib.types.path;
|
||||||
|
description = ''
|
||||||
|
Path to SSH private key for git push authentication.
|
||||||
|
Use agenix-managed paths like /run/agenix/m3ta-chiron-ssh-key
|
||||||
|
for secure secret management.
|
||||||
|
'';
|
||||||
|
example = "/run/agenix/m3ta-chiron-ssh-key";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = lib.mkIf cfg.enable {
|
||||||
|
home.sessionVariables = {
|
||||||
|
# Git author/committer identity
|
||||||
|
GIT_AUTHOR_NAME = cfg.name;
|
||||||
|
GIT_AUTHOR_EMAIL = cfg.email;
|
||||||
|
GIT_COMMITTER_NAME = cfg.name;
|
||||||
|
GIT_COMMITTER_EMAIL = cfg.email;
|
||||||
|
|
||||||
|
# SSH command for git push
|
||||||
|
GIT_SSH_COMMAND = "ssh -i ${cfg.sshKey} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
77
modules/home-manager/coding/agents/shared/shared-options.nix
Normal file
77
modules/home-manager/coding/agents/shared/shared-options.nix
Normal file
@@ -0,0 +1,77 @@
|
|||||||
|
# Shared option definitions for agent modules.
|
||||||
|
# Prevents copy-pasting the externalSkills submodule across opencode/claude-code/pi.
|
||||||
|
{lib}: let
|
||||||
|
inherit (lib) mkOption mkEnableOption types literalExpression;
|
||||||
|
in {
|
||||||
|
# Common agentsInput option used by all agent modules.
|
||||||
|
mkAgentsInputOption = description:
|
||||||
|
mkOption {
|
||||||
|
type = types.nullOr types.anything;
|
||||||
|
default = null;
|
||||||
|
inherit description;
|
||||||
|
};
|
||||||
|
|
||||||
|
# Common modelOverrides option.
|
||||||
|
mkModelOverridesOption = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Per-agent model overrides. Maps agent slug to model string.
|
||||||
|
Example: { chiron = "anthropic/claude-sonnet-4"; }
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
chiron = "anthropic/claude-sonnet-4";
|
||||||
|
"chiron-forge" = "anthropic/claude-sonnet-4";
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# External skills submodule — used by opencode, claude-code, and pi modules.
|
||||||
|
externalSkillsOption = mkOption {
|
||||||
|
type = types.listOf (types.submodule {
|
||||||
|
options = {
|
||||||
|
src = mkOption {
|
||||||
|
type = types.anything;
|
||||||
|
description = "Flake input pointing to a skills repository root.";
|
||||||
|
};
|
||||||
|
skillsDir = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "skills";
|
||||||
|
description = ''
|
||||||
|
Subdirectory inside src that contains skill folders.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
selectSkills = mkOption {
|
||||||
|
type = types.nullOr (types.listOf types.str);
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
List of skill names to cherry-pick from this source.
|
||||||
|
null means include every skill found in skillsDir.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
External skill sources passed to mkOpencodeSkills.
|
||||||
|
Each entry maps directly to an element of the externalSkills
|
||||||
|
list accepted by the AGENTS flake's lib.mkOpencodeSkills.
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
[
|
||||||
|
{ src = inputs.skills-anthropic; selectSkills = [ "claude-api" ]; }
|
||||||
|
{ src = inputs.basecamp; }
|
||||||
|
]
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
# Helper to map externalSkills from module config to mkOpencodeSkills format.
|
||||||
|
mapExternalSkills = cfgEntries:
|
||||||
|
map (
|
||||||
|
entry:
|
||||||
|
{inherit (entry) src skillsDir;}
|
||||||
|
// lib.optionalAttrs (entry.selectSkills != null) {inherit (entry) selectSkills;}
|
||||||
|
)
|
||||||
|
cfgEntries;
|
||||||
|
}
|
||||||
@@ -2,5 +2,7 @@
|
|||||||
{
|
{
|
||||||
imports = [
|
imports = [
|
||||||
./editors.nix
|
./editors.nix
|
||||||
|
./opencode.nix
|
||||||
|
./agents
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,80 +1,86 @@
|
|||||||
{
|
{
|
||||||
config,
|
config,
|
||||||
lib,
|
lib,
|
||||||
|
options,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}:
|
}:
|
||||||
with lib; let
|
with lib; let
|
||||||
cfg = config.coding.editors;
|
cfg = config.coding.editors;
|
||||||
|
# home-manager 26.05+ renamed extraLuaConfig → initLua.
|
||||||
|
# On stable 25.11 initLua does not exist; fall back to extraLuaConfig.
|
||||||
|
hasInitLua = options.programs.neovim ? initLua;
|
||||||
|
lazyVimConfig = ''
|
||||||
|
-- Bootstrap lazy.nvim
|
||||||
|
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
|
||||||
|
if not vim.loop.fs_stat(lazypath) then
|
||||||
|
vim.fn.system({
|
||||||
|
"git",
|
||||||
|
"clone",
|
||||||
|
"--filter=blob:none",
|
||||||
|
"https://github.com/folke/lazy.nvim.git",
|
||||||
|
"--branch=stable",
|
||||||
|
lazypath,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
vim.opt.rtp:prepend(lazypath)
|
||||||
|
-- Bootstrap LazyVim via lazy.nvim
|
||||||
|
-- Docs: https://github.com/folke/lazy.nvim and https://www.lazyvim.org/
|
||||||
|
require("lazy").setup({
|
||||||
|
spec = {
|
||||||
|
{ "LazyVim/LazyVim", import = "lazyvim.plugins" },
|
||||||
|
{ import = "lazyvim.plugins.extras.lang.typescript" },
|
||||||
|
{ import = "lazyvim.plugins.extras.lang.python" },
|
||||||
|
{ import = "lazyvim.plugins.extras.lang.go" },
|
||||||
|
{ import = "lazyvim.plugins.extras.lang.nix" },
|
||||||
|
{ import = "lazyvim.plugins.extras.lang.rust" },
|
||||||
|
{ import = "lazyvim.plugins.extras.lang.nushell" },
|
||||||
|
{ "Mofiqul/dracula.nvim" },
|
||||||
|
},
|
||||||
|
defaults = { lazy = false, version = false },
|
||||||
|
install = { colorscheme = { "dracula", "tokyonight", "habamax" } },
|
||||||
|
checker = { enabled = false },
|
||||||
|
performance = {
|
||||||
|
rtp = {
|
||||||
|
disabled_plugins = {
|
||||||
|
"gzip", "tarPlugin", "tohtml", "tutor", "zipPlugin",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
vim.o.termguicolors = true
|
||||||
|
vim.cmd.colorscheme("dracula")
|
||||||
|
'';
|
||||||
in {
|
in {
|
||||||
options.coding.editors = {
|
options.coding.editors = {
|
||||||
neovim = {
|
neovim = {
|
||||||
enable = mkEnableOption "neovim with LazyVim configuration";
|
enable = mkEnableOption "neovim with LazyVim configuration";
|
||||||
};
|
};
|
||||||
|
|
||||||
zed = {
|
zed = {
|
||||||
enable = mkEnableOption "zed editor with custom configuration";
|
enable = mkEnableOption "zed editor with custom configuration";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
config = mkMerge [
|
config = mkMerge [
|
||||||
# Neovim configuration
|
# Neovim configuration
|
||||||
(mkIf cfg.neovim.enable {
|
(mkIf cfg.neovim.enable (mkMerge [
|
||||||
programs.neovim = {
|
{
|
||||||
enable = true;
|
programs.neovim = {
|
||||||
defaultEditor = true;
|
enable = true;
|
||||||
viAlias = true;
|
defaultEditor = true;
|
||||||
vimAlias = true;
|
viAlias = true;
|
||||||
vimdiffAlias = true;
|
vimAlias = true;
|
||||||
withNodeJs = true;
|
vimdiffAlias = true;
|
||||||
withPython3 = true;
|
withNodeJs = true;
|
||||||
|
withPython3 = true;
|
||||||
# This is your init.lua content (extraLuaConfig for compatibility with older home-manager)
|
};
|
||||||
extraLuaConfig = ''
|
}
|
||||||
-- Bootstrap lazy.nvim
|
# Use initLua on HM 26.05+ (unstable), extraLuaConfig on HM ≤ 25.11 (stable)
|
||||||
local lazypath = vim.fn.stdpath("data") .. "/lazy/lazy.nvim"
|
(
|
||||||
if not vim.loop.fs_stat(lazypath) then
|
if hasInitLua
|
||||||
vim.fn.system({
|
then {programs.neovim.initLua = lazyVimConfig;}
|
||||||
"git",
|
else {programs.neovim.extraLuaConfig = lazyVimConfig;}
|
||||||
"clone",
|
)
|
||||||
"--filter=blob:none",
|
]))
|
||||||
"https://github.com/folke/lazy.nvim.git",
|
|
||||||
"--branch=stable",
|
|
||||||
lazypath,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
vim.opt.rtp:prepend(lazypath)
|
|
||||||
|
|
||||||
-- Bootstrap LazyVim via lazy.nvim
|
|
||||||
-- Docs: https://github.com/folke/lazy.nvim and https://www.lazyvim.org/
|
|
||||||
require("lazy").setup({
|
|
||||||
spec = {
|
|
||||||
{ "LazyVim/LazyVim", import = "lazyvim.plugins" },
|
|
||||||
{ import = "lazyvim.plugins.extras.lang.typescript" },
|
|
||||||
{ import = "lazyvim.plugins.extras.lang.python" },
|
|
||||||
{ import = "lazyvim.plugins.extras.lang.go" },
|
|
||||||
{ import = "lazyvim.plugins.extras.lang.nix" },
|
|
||||||
{ import = "lazyvim.plugins.extras.lang.rust" },
|
|
||||||
{ import = "lazyvim.plugins.extras.lang.nushell" },
|
|
||||||
{ "Mofiqul/dracula.nvim" },
|
|
||||||
},
|
|
||||||
defaults = { lazy = false, version = false },
|
|
||||||
install = { colorscheme = { "dracula", "tokyonight", "habamax" } },
|
|
||||||
checker = { enabled = false },
|
|
||||||
performance = {
|
|
||||||
rtp = {
|
|
||||||
disabled_plugins = {
|
|
||||||
"gzip", "tarPlugin", "tohtml", "tutor", "zipPlugin",
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
vim.o.termguicolors = true
|
|
||||||
vim.cmd.colorscheme("dracula")
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
})
|
|
||||||
|
|
||||||
# Zed editor configuration
|
# Zed editor configuration
|
||||||
(mkIf cfg.zed.enable {
|
(mkIf cfg.zed.enable {
|
||||||
programs.zed-editor = {
|
programs.zed-editor = {
|
||||||
@@ -85,13 +91,11 @@ in {
|
|||||||
ui_font_size = 16;
|
ui_font_size = 16;
|
||||||
buffer_font_size = 16;
|
buffer_font_size = 16;
|
||||||
buffer_font_family = "FiraCode Nerd Font";
|
buffer_font_family = "FiraCode Nerd Font";
|
||||||
|
|
||||||
# Editor Behavior
|
# Editor Behavior
|
||||||
vim_mode = true;
|
vim_mode = true;
|
||||||
auto_update = false;
|
auto_update = false;
|
||||||
format_on_save = "on";
|
format_on_save = "on";
|
||||||
load_direnv = "shell_hook";
|
load_direnv = "shell_hook";
|
||||||
|
|
||||||
# AI Features
|
# AI Features
|
||||||
features = {
|
features = {
|
||||||
edit_prediction_provider = "zed";
|
edit_prediction_provider = "zed";
|
||||||
@@ -100,14 +104,12 @@ in {
|
|||||||
mode = "subtle";
|
mode = "subtle";
|
||||||
};
|
};
|
||||||
show_edit_predictions = true;
|
show_edit_predictions = true;
|
||||||
|
|
||||||
agent = {
|
agent = {
|
||||||
default_model = {
|
default_model = {
|
||||||
provider = "zed.dev";
|
provider = "zed.dev";
|
||||||
model = "claude-sonnet-4";
|
model = "claude-sonnet-4";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
assistant = {
|
assistant = {
|
||||||
version = "2";
|
version = "2";
|
||||||
default_model = {
|
default_model = {
|
||||||
@@ -115,7 +117,6 @@ in {
|
|||||||
model = "claude-4";
|
model = "claude-4";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Language Models
|
# Language Models
|
||||||
language_models = {
|
language_models = {
|
||||||
anthropic = {
|
anthropic = {
|
||||||
@@ -128,7 +129,6 @@ in {
|
|||||||
api_url = "http://localhost:11434";
|
api_url = "http://localhost:11434";
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Languages Configuration
|
# Languages Configuration
|
||||||
languages = {
|
languages = {
|
||||||
Nix = {
|
Nix = {
|
||||||
@@ -153,7 +153,6 @@ in {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# LSP Configuration
|
# LSP Configuration
|
||||||
lsp = {
|
lsp = {
|
||||||
rust-analyzer = {
|
rust-analyzer = {
|
||||||
@@ -169,7 +168,6 @@ in {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Context Servers
|
# Context Servers
|
||||||
context_servers = {
|
context_servers = {
|
||||||
some-context-server = {
|
some-context-server = {
|
||||||
@@ -182,7 +180,6 @@ in {
|
|||||||
env = {};
|
env = {};
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
# Privacy
|
# Privacy
|
||||||
telemetry = {
|
telemetry = {
|
||||||
metrics = false;
|
metrics = false;
|
||||||
@@ -190,7 +187,6 @@ in {
|
|||||||
};
|
};
|
||||||
};
|
};
|
||||||
})
|
})
|
||||||
|
|
||||||
# Common packages (always installed if either editor is enabled)
|
# Common packages (always installed if either editor is enabled)
|
||||||
(mkIf (cfg.neovim.enable || cfg.zed.enable) {
|
(mkIf (cfg.neovim.enable || cfg.zed.enable) {
|
||||||
home.packages = with pkgs; [zig];
|
home.packages = with pkgs; [zig];
|
||||||
|
|||||||
86
modules/home-manager/coding/opencode.nix
Normal file
86
modules/home-manager/coding/opencode.nix
Normal file
@@ -0,0 +1,86 @@
|
|||||||
|
{
|
||||||
|
config,
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
...
|
||||||
|
}:
|
||||||
|
with lib; let
|
||||||
|
cfg = config.coding.opencode;
|
||||||
|
in {
|
||||||
|
options.coding.opencode = {
|
||||||
|
enable = mkEnableOption "opencode AI coding assistant";
|
||||||
|
|
||||||
|
ohMyOpencodeSettings = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Attributes merged (via recursiveUpdate) on top of the default
|
||||||
|
oh-my-opencode.json. Use this to set provider-specific model
|
||||||
|
assignments per machine.
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
agents.sisyphus.model = "anthropic/claude-opus-4-5";
|
||||||
|
categories.ultrabrain.model = "anthropic/claude-opus-4-5";
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraSettings = mkOption {
|
||||||
|
type = types.attrs;
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Extra opencode settings merged (via mkMerge) into
|
||||||
|
programs.opencode.settings. Use this to add provider
|
||||||
|
configuration that is specific to a machine or organisation.
|
||||||
|
'';
|
||||||
|
example = literalExpression ''
|
||||||
|
{
|
||||||
|
provider.anthropic = {
|
||||||
|
name = "Anthropic";
|
||||||
|
models."claude-opus-4-5" = { limit.context = 200000; };
|
||||||
|
};
|
||||||
|
}
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraPlugins = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
Additional opencode plugins to add to the plugin list.
|
||||||
|
Each entry is a path or package name passed to opencode's plugin array.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable {
|
||||||
|
programs.opencode = {
|
||||||
|
enable = true;
|
||||||
|
enableMcpIntegration = true;
|
||||||
|
settings = mkMerge [
|
||||||
|
{
|
||||||
|
theme = "opencode";
|
||||||
|
plugin = ["oh-my-openagent"] ++ cfg.extraPlugins;
|
||||||
|
formatter = {
|
||||||
|
alejandra = {
|
||||||
|
command = ["alejandra" "-q" "-"];
|
||||||
|
extensions = [".nix"];
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
|
cfg.extraSettings
|
||||||
|
];
|
||||||
|
};
|
||||||
|
|
||||||
|
home.file.".config/opencode/oh-my-opencode.json".text = builtins.toJSON (
|
||||||
|
recursiveUpdate
|
||||||
|
{
|
||||||
|
"$schema" = "https://raw.githubusercontent.com/code-yeongyu/oh-my-opencode/master/assets/oh-my-opencode.schema.json";
|
||||||
|
google_auth = false;
|
||||||
|
disabled_mcps = ["context7" "websearch"];
|
||||||
|
}
|
||||||
|
cfg.ohMyOpencodeSettings
|
||||||
|
);
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,70 +0,0 @@
|
|||||||
{inputs, ...}: {
|
|
||||||
# This one brings our custom packages from the 'pkgs' directory
|
|
||||||
additions = final: prev:
|
|
||||||
(import ../pkgs {pkgs = final;})
|
|
||||||
# // (inputs.hyprpanel.overlay final prev)
|
|
||||||
// {rose-pine-hyprcursor = inputs.rose-pine-hyprcursor.packages.${prev.system}.default;};
|
|
||||||
|
|
||||||
# This one contains whatever you want to overlay
|
|
||||||
# You can change versions, add patches, set compilation flags, anything really.
|
|
||||||
# https://nixos.wiki/wiki/Overlays
|
|
||||||
modifications = final: prev:
|
|
||||||
# Import all package modifications from mods directory
|
|
||||||
(import ./mods/default.nix {inherit prev;})
|
|
||||||
// {
|
|
||||||
# Direct configuration overrides
|
|
||||||
brave = prev.brave.override {
|
|
||||||
commandLineArgs = "--password-store=gnome-libsecret";
|
|
||||||
};
|
|
||||||
|
|
||||||
# nodejs_24 = inputs.nixpkgs-stable.legacyPackages.${prev.system}.nodejs_24;
|
|
||||||
# paperless-ngx = inputs.nixpkgs-45570c2.legacyPackages.${prev.system}.paperless-ngx;
|
|
||||||
# anytype-heart = inputs.nixpkgs-9e58ed7.legacyPackages.${prev.system}.anytype-heart;
|
|
||||||
# trezord = inputs.nixpkgs-2744d98.legacyPackages.${prev.system}.trezord;
|
|
||||||
# mesa = inputs.nixpkgs-master.legacyPackages.${prev.system}.mesa;
|
|
||||||
# hyprpanel = inputs.hyprpanel.packages.${prev.system}.default.overrideAttrs (prev: {
|
|
||||||
# version = "latest"; # or whatever version you want
|
|
||||||
# src = final.fetchFromGitHub {
|
|
||||||
# owner = "Jas-SinghFSU";
|
|
||||||
# repo = "HyprPanel";
|
|
||||||
# rev = "master"; # or a specific commit hash
|
|
||||||
# hash = "sha256-l623fIVhVCU/ylbBmohAtQNbK0YrWlEny0sC/vBJ+dU=";
|
|
||||||
# };
|
|
||||||
# });
|
|
||||||
};
|
|
||||||
|
|
||||||
temp-packages = final: _prev: {
|
|
||||||
temp = import inputs.nixpkgs-9e9486b {
|
|
||||||
system = final.system;
|
|
||||||
config.allowUnfree = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
stable-packages = final: _prev: {
|
|
||||||
stable = import inputs.nixpkgs-stable {
|
|
||||||
system = final.system;
|
|
||||||
config.allowUnfree = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
pinned-packages = final: _prev: {
|
|
||||||
pinned = import inputs.nixpkgs-9472de4 {
|
|
||||||
system = final.system;
|
|
||||||
config.allowUnfree = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
locked-packages = final: _prev: {
|
|
||||||
locked = import inputs.nixpkgs-locked {
|
|
||||||
system = final.system;
|
|
||||||
config.allowUnfree = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
master-packages = final: _prev: {
|
|
||||||
master = import inputs.nixpkgs-master {
|
|
||||||
system = final.system;
|
|
||||||
config.allowUnfree = true;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
{prev}:
|
|
||||||
prev.beads.overrideAttrs (oldAttrs: rec {
|
|
||||||
version = "0.47.1";
|
|
||||||
|
|
||||||
src = prev.fetchFromGitHub {
|
|
||||||
owner = "steveyegge";
|
|
||||||
repo = "beads";
|
|
||||||
tag = "v${version}";
|
|
||||||
hash = "sha256-DwIR/r1TJnpVd/CT1E2OTkAjU7k9/KHbcVwg5zziFVg=";
|
|
||||||
};
|
|
||||||
|
|
||||||
vendorHash = "sha256-pY5m5ODRgqghyELRwwxOr+xlW41gtJWLXaW53GlLaFw=";
|
|
||||||
|
|
||||||
# Tests require git worktree operations that fail in Nix sandbox
|
|
||||||
doCheck = false;
|
|
||||||
})
|
|
||||||
@@ -2,9 +2,6 @@
|
|||||||
# Package modifications
|
# Package modifications
|
||||||
# This overlay contains package overrides and modifications
|
# This overlay contains package overrides and modifications
|
||||||
|
|
||||||
# n8n = import ./n8n.nix {inherit prev;};
|
|
||||||
# beads = import ./beads.nix {inherit prev;};
|
|
||||||
|
|
||||||
# Add more modifications here as needed
|
# Add more modifications here as needed
|
||||||
# example-package = prev.example-package.override { ... };
|
# example-package = prev.example-package.override { ... };
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,18 +0,0 @@
|
|||||||
{prev}:
|
|
||||||
prev.n8n.overrideAttrs (oldAttrs: rec {
|
|
||||||
version = "2.4.1";
|
|
||||||
|
|
||||||
src = prev.fetchFromGitHub {
|
|
||||||
owner = "n8n-io";
|
|
||||||
repo = "n8n";
|
|
||||||
rev = "n8n@${version}";
|
|
||||||
hash = "sha256-EQP9ZI8kt30SUYE1+/UUpxQXpavzKqDu8qE24zsNifg=";
|
|
||||||
};
|
|
||||||
|
|
||||||
pnpmDeps = prev.pnpm_10.fetchDeps {
|
|
||||||
pname = oldAttrs.pname;
|
|
||||||
inherit version src;
|
|
||||||
fetcherVersion = 1;
|
|
||||||
hash = "sha256-Q30IuFEQD3896Hg0HCLd38YE2i8fJn74JY0o95LKJis=";
|
|
||||||
};
|
|
||||||
})
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
{
|
|
||||||
lib,
|
|
||||||
fetchFromGitHub,
|
|
||||||
nix-update-script,
|
|
||||||
rustPlatform,
|
|
||||||
pkg-config,
|
|
||||||
perl,
|
|
||||||
openssl,
|
|
||||||
}:
|
|
||||||
rustPlatform.buildRustPackage rec {
|
|
||||||
pname = "code2prompt";
|
|
||||||
version = "4.2.0";
|
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
|
||||||
owner = "mufeedvh";
|
|
||||||
repo = "code2prompt";
|
|
||||||
rev = "v${version}";
|
|
||||||
hash = "sha256-Gh8SsSTZW7QlyyC3SWJ5pOK2x85/GT7+LPJn2Jeczpc=";
|
|
||||||
};
|
|
||||||
|
|
||||||
cargoLock = {
|
|
||||||
lockFile = src + "/Cargo.lock";
|
|
||||||
};
|
|
||||||
|
|
||||||
buildAndTestSubdir = "crates/code2prompt";
|
|
||||||
|
|
||||||
nativeBuildInputs = [pkg-config perl];
|
|
||||||
|
|
||||||
buildInputs = [openssl];
|
|
||||||
|
|
||||||
passthru.updateScript = nix-update-script {};
|
|
||||||
|
|
||||||
meta = with lib; {
|
|
||||||
description = "A CLI tool that converts your codebase into a single LLM prompt with a source tree, prompt templating, and token counting";
|
|
||||||
homepage = "https://github.com/mufeedvh/code2prompt";
|
|
||||||
license = licenses.mit;
|
|
||||||
platforms = platforms.linux;
|
|
||||||
mainProgram = "code2prompt";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,13 +1,24 @@
|
|||||||
|
# m3ta-nixpkgs package registry
|
||||||
|
#
|
||||||
|
# Flake inputs used:
|
||||||
|
# inputs.basecamp → basecamp (pass-through)
|
||||||
|
# inputs.openspec → openspec (pass-through)
|
||||||
|
# inputs.opencode → opencode-desktop (build inputs + patches)
|
||||||
|
# inputs.agents → not used directly here (used by lib/)
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
inputs,
|
inputs,
|
||||||
...
|
...
|
||||||
}: {
|
}: let
|
||||||
# Custom packages registry
|
system = pkgs.stdenv.hostPlatform.system;
|
||||||
# Each package is defined in its own directory under pkgs/
|
in {
|
||||||
|
# ── Local packages ────────────────────────────────────────────────
|
||||||
|
# Standard packages built from source in ./<name>/default.nix.
|
||||||
|
# No flake inputs required.
|
||||||
|
|
||||||
sidecar = pkgs.callPackage ./sidecar {};
|
sidecar = pkgs.callPackage ./sidecar {};
|
||||||
td = pkgs.callPackage ./td {};
|
td = pkgs.callPackage ./td {};
|
||||||
code2prompt = pkgs.callPackage ./code2prompt {};
|
eigent = pkgs.callPackage ./eigent {};
|
||||||
hyprpaper-random = pkgs.callPackage ./hyprpaper-random {};
|
hyprpaper-random = pkgs.callPackage ./hyprpaper-random {};
|
||||||
launch-webapp = pkgs.callPackage ./launch-webapp {};
|
launch-webapp = pkgs.callPackage ./launch-webapp {};
|
||||||
mem0 = pkgs.callPackage ./mem0 {};
|
mem0 = pkgs.callPackage ./mem0 {};
|
||||||
@@ -18,8 +29,13 @@
|
|||||||
stt-ptt = pkgs.callPackage ./stt-ptt {};
|
stt-ptt = pkgs.callPackage ./stt-ptt {};
|
||||||
tuxedo-backlight = pkgs.callPackage ./tuxedo-backlight {};
|
tuxedo-backlight = pkgs.callPackage ./tuxedo-backlight {};
|
||||||
kestractl = pkgs.callPackage ./kestractl {};
|
kestractl = pkgs.callPackage ./kestractl {};
|
||||||
|
openshell = pkgs.callPackage ./openshell {};
|
||||||
zellij-ps = pkgs.callPackage ./zellij-ps {};
|
zellij-ps = pkgs.callPackage ./zellij-ps {};
|
||||||
|
vibetyper = pkgs.callPackage ./vibetyper {};
|
||||||
|
|
||||||
# Imported from flake inputs
|
# ── Pass-through packages ──────────────────────────────────────────
|
||||||
opencode-desktop = pkgs.callPackage ./opencode-desktop {inherit inputs;};
|
# Imported directly from flake inputs. No local modifications.
|
||||||
|
|
||||||
|
basecamp = inputs.basecamp.packages.${system}.default;
|
||||||
|
openspec = inputs.openspec.packages.${system}.default;
|
||||||
}
|
}
|
||||||
|
|||||||
74
pkgs/eigent/default.nix
Normal file
74
pkgs/eigent/default.nix
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
{
|
||||||
|
appimageTools,
|
||||||
|
fetchurl,
|
||||||
|
lib,
|
||||||
|
nodejs,
|
||||||
|
uv,
|
||||||
|
python3,
|
||||||
|
nix-update-script,
|
||||||
|
}: let
|
||||||
|
pname = "eigent";
|
||||||
|
version = "0.0.90";
|
||||||
|
src = fetchurl {
|
||||||
|
url = "https://github.com/eigent-ai/eigent/releases/download/v${version}/Eigent-${version}.AppImage";
|
||||||
|
hash = "sha256-mwCBx+D6mgGqQa8bDuUpo3h49EwFVkwasJwaYc6aXFE=";
|
||||||
|
};
|
||||||
|
appimageContents = appimageTools.extractType2 {inherit pname version src;};
|
||||||
|
in
|
||||||
|
appimageTools.wrapType2 {
|
||||||
|
inherit pname version src;
|
||||||
|
|
||||||
|
extraPkgs = _: [
|
||||||
|
nodejs
|
||||||
|
uv
|
||||||
|
python3
|
||||||
|
];
|
||||||
|
|
||||||
|
# Runs before bubblewrap launches — sets up writable state for the sandbox.
|
||||||
|
extraPreBwrapCmds = ''
|
||||||
|
# eigent writes to multiple dirs under resources/ at runtime:
|
||||||
|
# prebuilt/ → pyvenv.cfg, .terminal_venv_fixed sentinel
|
||||||
|
# backend/ → creates runtime/ dir for temporary state
|
||||||
|
# Nix store is read-only → EROFS. Copy to writable location on first
|
||||||
|
# launch (or when the package version changes).
|
||||||
|
DATA_DIR="$HOME/.local/share/${pname}"
|
||||||
|
mkdir -p "$DATA_DIR"
|
||||||
|
for subdir in prebuilt backend; do
|
||||||
|
SRC="${appimageContents}/resources/$subdir"
|
||||||
|
DST="$DATA_DIR/$subdir"
|
||||||
|
if [ ! -f "$DST/.nix-src" ] || [ "$(cat "$DST/.nix-src")" != "${appimageContents}" ]; then
|
||||||
|
rm -rf "$DST"
|
||||||
|
cp -r "$SRC" "$DST"
|
||||||
|
chmod -R u+w "$DST"
|
||||||
|
echo "${appimageContents}" > "$DST/.nix-src"
|
||||||
|
fi
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Bind-mount writable copies over the read-only store paths so the app
|
||||||
|
# sees its files at the expected locations but can write to them.
|
||||||
|
extraBwrapArgs = [
|
||||||
|
"--bind $HOME/.local/share/${pname}/prebuilt ${appimageContents}/resources/prebuilt"
|
||||||
|
"--bind $HOME/.local/share/${pname}/backend ${appimageContents}/resources/backend"
|
||||||
|
];
|
||||||
|
|
||||||
|
extraInstallCommands = ''
|
||||||
|
install -m 444 -D ${appimageContents}/eigent.desktop -t $out/share/applications
|
||||||
|
substituteInPlace $out/share/applications/eigent.desktop \
|
||||||
|
--replace-fail 'Exec=AppRun --no-sandbox %U' 'Exec=${pname} %U'
|
||||||
|
install -m 444 -D ${appimageContents}/eigent.png \
|
||||||
|
$out/share/icons/hicolor/256x256/apps/eigent.png
|
||||||
|
'';
|
||||||
|
|
||||||
|
passthru = {
|
||||||
|
updateScript = nix-update-script {};
|
||||||
|
};
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "Open source AI cowork desktop app — local alternative to Claude Cowork";
|
||||||
|
homepage = "https://github.com/eigent-ai/eigent";
|
||||||
|
license = lib.licenses.asl20;
|
||||||
|
platforms = lib.platforms.linux;
|
||||||
|
mainProgram = "eigent";
|
||||||
|
};
|
||||||
|
}
|
||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"version": "1.0.0",
|
"version": "1.3.0",
|
||||||
"sources": {
|
"sources": {
|
||||||
"x86_64-linux": {
|
|
||||||
"url": "https://github.com/kestra-io/kestractl/releases/download/1.0.0/kestractl_1.0.0_linux_amd64.tar.gz",
|
|
||||||
"hash": "sha256-lX6A+2DJdA5otXZLbBGZSUQUdh7CoHmLoy6GJ/AvUJE="
|
|
||||||
},
|
|
||||||
"aarch64-linux": {
|
"aarch64-linux": {
|
||||||
"url": "https://github.com/kestra-io/kestractl/releases/download/1.0.0/kestractl_1.0.0_linux_arm64.tar.gz",
|
"url": "https://github.com/kestra-io/kestractl/releases/download/1.3.0/kestractl_1.3.0_linux_arm64.tar.gz",
|
||||||
"hash": "sha256-6ashiuDQ6fv5nkFGM7giSUCmHR44QRlDCSD9rPzq1Ac="
|
"hash": "sha256-/18F6CZnnLbet4BmI1oQ5pZWkJwIshCq30qd+cm0GGA="
|
||||||
|
},
|
||||||
|
"x86_64-linux": {
|
||||||
|
"url": "https://github.com/kestra-io/kestractl/releases/download/1.3.0/kestractl_1.3.0_linux_amd64.tar.gz",
|
||||||
|
"hash": "sha256-xmsBiqNKvob8xHDyU253o6c25YIubHanNdLqzWaOvSA="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,9 +1,9 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell --pure -i bash -p bash curl jq nix cacert git
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# Update kestractl sources.json with the latest release from GitHub.
|
# Update kestractl sources.json with the latest release from GitHub.
|
||||||
# Usage: ./update.sh
|
# Usage: ./update.sh (or via nix-update --update-script)
|
||||||
# Called automatically by: nix-update --update-script kestractl
|
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
SOURCES_FILE="$SCRIPT_DIR/sources.json"
|
SOURCES_FILE="$SCRIPT_DIR/sources.json"
|
||||||
@@ -49,3 +49,13 @@ jq -n \
|
|||||||
> "$SOURCES_FILE"
|
> "$SOURCES_FILE"
|
||||||
|
|
||||||
echo "Updated $SOURCES_FILE to $VERSION"
|
echo "Updated $SOURCES_FILE to $VERSION"
|
||||||
|
|
||||||
|
# Commit when running in CI or via nix-update
|
||||||
|
if [[ -d "$SCRIPT_DIR/../../.git" ]] || git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||||
|
NIXPKGS_ROOT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || true)
|
||||||
|
if [[ -n "$NIXPKGS_ROOT" && -n "$(git -C "$NIXPKGS_ROOT" status --porcelain "$SOURCES_FILE")" ]]; then
|
||||||
|
git -C "$NIXPKGS_ROOT" add "$SOURCES_FILE"
|
||||||
|
git -C "$NIXPKGS_ROOT" commit -m "kestractl: ${CURRENT_VERSION} -> ${VERSION}"
|
||||||
|
echo "Committed update to git"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|||||||
@@ -6,14 +6,14 @@
|
|||||||
}:
|
}:
|
||||||
python3.pkgs.buildPythonPackage rec {
|
python3.pkgs.buildPythonPackage rec {
|
||||||
pname = "mem0ai";
|
pname = "mem0ai";
|
||||||
version = "1.0.7";
|
version = "2.0.1";
|
||||||
pyproject = true;
|
pyproject = true;
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "mem0ai";
|
owner = "mem0ai";
|
||||||
repo = "mem0";
|
repo = "mem0";
|
||||||
rev = "v${version}";
|
rev = "v${version}";
|
||||||
hash = "sha256-FF5KJGsMhK58vs0dhc2w/2h6ctG3Jgz9rW6O0EujeDM=";
|
hash = "sha256-lNSE0Yit+FmM8opC4XYtfVef7JfGd3wMKbLj67Kp4Qw=";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Relax Python dependency version constraints
|
# Relax Python dependency version constraints
|
||||||
|
|||||||
@@ -3,7 +3,6 @@
|
|||||||
fetchurl,
|
fetchurl,
|
||||||
lib,
|
lib,
|
||||||
nodejs,
|
nodejs,
|
||||||
nodePackages,
|
|
||||||
uv,
|
uv,
|
||||||
python3,
|
python3,
|
||||||
makeWrapper,
|
makeWrapper,
|
||||||
@@ -22,7 +21,6 @@ in
|
|||||||
|
|
||||||
extraPkgs = pkgs: [
|
extraPkgs = pkgs: [
|
||||||
nodejs
|
nodejs
|
||||||
nodePackages.npm
|
|
||||||
uv
|
uv
|
||||||
python3
|
python3
|
||||||
];
|
];
|
||||||
@@ -34,7 +32,7 @@ in
|
|||||||
install -m 444 -D ${appimageContents}/MstyStudio.png \
|
install -m 444 -D ${appimageContents}/MstyStudio.png \
|
||||||
$out/share/icons/hicolor/256x256/apps/MstyStudio.png
|
$out/share/icons/hicolor/256x256/apps/MstyStudio.png
|
||||||
wrapProgram $out/bin/${pname} \
|
wrapProgram $out/bin/${pname} \
|
||||||
--prefix PATH : ${nodejs}/bin:${nodePackages.npm}/bin:${uv}/bin:${python3}/bin
|
--prefix PATH : ${nodejs}/bin:${uv}/bin:${python3}/bin
|
||||||
'';
|
'';
|
||||||
meta = {
|
meta = {
|
||||||
description = "Msty Studio enables advanced, privacy‑preserving AI workflows entirely on your local machine.";
|
description = "Msty Studio enables advanced, privacy‑preserving AI workflows entirely on your local machine.";
|
||||||
|
|||||||
@@ -11,6 +11,7 @@
|
|||||||
node-gyp,
|
node-gyp,
|
||||||
cctools,
|
cctools,
|
||||||
xcbuild,
|
xcbuild,
|
||||||
|
dart-sass,
|
||||||
libkrb5,
|
libkrb5,
|
||||||
libmongocrypt,
|
libmongocrypt,
|
||||||
libpq,
|
libpq,
|
||||||
@@ -25,20 +26,20 @@
|
|||||||
in
|
in
|
||||||
stdenv.mkDerivation (finalAttrs: {
|
stdenv.mkDerivation (finalAttrs: {
|
||||||
pname = "n8n";
|
pname = "n8n";
|
||||||
version = "2.11.4";
|
version = "2.18.5";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "n8n-io";
|
owner = "n8n-io";
|
||||||
repo = "n8n";
|
repo = "n8n";
|
||||||
tag = "n8n@${finalAttrs.version}";
|
tag = "n8n@${finalAttrs.version}";
|
||||||
hash = "sha256-mhfVipTAoHCY1BPSV5Ge1iQpa/LaUCw2aiI3KFkW0CI=";
|
hash = "sha256-ws0DXGQFR+z3nVyd4Yn9pIM7yh+H6GnuCRSLxgvtPxo=";
|
||||||
};
|
};
|
||||||
|
|
||||||
pnpmDeps = fetchPnpmDeps {
|
pnpmDeps = fetchPnpmDeps {
|
||||||
inherit (finalAttrs) pname version src;
|
inherit (finalAttrs) pname version src;
|
||||||
pnpm = pnpm_10;
|
pnpm = pnpm_10;
|
||||||
fetcherVersion = 3;
|
fetcherVersion = 3;
|
||||||
hash = "sha256-fWE/uJTs7lawbVu7iDSrpufqFaOkzFc5jjTD8u3Drok=";
|
hash = "sha256-Ajgne0neNm6HgMK6z3jnEkUJJxVOTgzjpSaMaJgIndQ=";
|
||||||
};
|
};
|
||||||
|
|
||||||
nativeBuildInputs =
|
nativeBuildInputs =
|
||||||
@@ -61,6 +62,17 @@ in
|
|||||||
libpq
|
libpq
|
||||||
];
|
];
|
||||||
|
|
||||||
|
preBuild = ''
|
||||||
|
# Force sass-embedded to use our dart-sass instead of bundled binaries.
|
||||||
|
# The bundled Dart binary can't run in the Nix sandbox (no /lib64/ld-linux-x86-64.so.2).
|
||||||
|
for dep in node_modules/.pnpm/sass-embedded@*; do
|
||||||
|
substituteInPlace "$dep/node_modules/sass-embedded/dist/lib/src/compiler-path.js" \
|
||||||
|
--replace-fail \
|
||||||
|
'compilerCommand = (() => {' \
|
||||||
|
'compilerCommand = (() => { return ["${lib.getExe dart-sass}"];'
|
||||||
|
done
|
||||||
|
'';
|
||||||
|
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
runHook preBuild
|
runHook preBuild
|
||||||
|
|
||||||
|
|||||||
@@ -1,6 +1,29 @@
|
|||||||
#!/usr/bin/env nix-shell
|
#!/usr/bin/env nix-shell
|
||||||
#!nix-shell --pure -i bash -p bash curl jq nix-update cacert git
|
#!nix-shell -i bash -p bash curl jq nix-update cacert git nix
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
new_version="$(curl -s "https://api.github.com/repos/n8n-io/n8n/releases/latest" | jq --raw-output '.tag_name | ltrimstr("n8n@")')"
|
# n8n releases are published with two tags per version:
|
||||||
nix-update n8n --flake --version "$new_version"
|
# - "n8n@X.Y.Z" - the versioned tag
|
||||||
|
# - "stable" - always points to the latest stable version
|
||||||
|
#
|
||||||
|
# We query the "stable" tag and extract the version from its target commitish (e.g., "release/2.18.5").
|
||||||
|
# This ensures we always get the actual latest stable version, not the most recently created tag.
|
||||||
|
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Get the directory where this script lives (should be pkgs/n8n/)
|
||||||
|
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
|
# Get the nixpkgs root (parent of pkgs/)
|
||||||
|
nixpkgs_root="$(cd "$script_dir/../.." && pwd)"
|
||||||
|
|
||||||
|
cd "$nixpkgs_root"
|
||||||
|
|
||||||
|
# Query the "stable" tag and extract version from target_commitish (e.g., "release/2.18.5")
|
||||||
|
new_version=$(curl -s "https://api.github.com/repos/n8n-io/n8n/releases/tags/stable" | jq --raw-output '.target_commitish | ltrimstr("release/")')
|
||||||
|
|
||||||
|
echo "Latest stable version: n8n@${new_version}"
|
||||||
|
echo "Running from: $(pwd)"
|
||||||
|
|
||||||
|
# Use --flake --system to properly evaluate the flake-based package
|
||||||
|
nix-update --flake --system x86_64-linux n8n --version "$new_version"
|
||||||
|
|||||||
@@ -1,106 +0,0 @@
|
|||||||
{
|
|
||||||
lib,
|
|
||||||
stdenv,
|
|
||||||
symlinkJoin,
|
|
||||||
makeWrapper,
|
|
||||||
rustPlatform,
|
|
||||||
pkg-config,
|
|
||||||
cargo-tauri,
|
|
||||||
bun,
|
|
||||||
nodejs,
|
|
||||||
cargo,
|
|
||||||
rustc,
|
|
||||||
jq,
|
|
||||||
wrapGAppsHook4,
|
|
||||||
dbus,
|
|
||||||
glib,
|
|
||||||
gtk4,
|
|
||||||
libsoup_3,
|
|
||||||
librsvg,
|
|
||||||
libappindicator,
|
|
||||||
glib-networking,
|
|
||||||
openssl,
|
|
||||||
webkitgtk_4_1,
|
|
||||||
gst_all_1,
|
|
||||||
inputs ? null,
|
|
||||||
}: let
|
|
||||||
# Get upstream opencode package for shared attributes
|
|
||||||
opencode = inputs.opencode.packages.${stdenv.hostPlatform.system}.default;
|
|
||||||
|
|
||||||
# Workaround for https://github.com/anomalyco/opencode/issues/11755
|
|
||||||
# Upstream is missing outputHashes for git dependencies
|
|
||||||
opencode-desktop = rustPlatform.buildRustPackage (finalAttrs: {
|
|
||||||
pname = "opencode-desktop";
|
|
||||||
inherit (opencode) version src node_modules patches;
|
|
||||||
|
|
||||||
cargoRoot = "packages/desktop/src-tauri";
|
|
||||||
cargoLock = {
|
|
||||||
lockFile = finalAttrs.src + "/packages/desktop/src-tauri/Cargo.lock";
|
|
||||||
outputHashes = {
|
|
||||||
"specta-2.0.0-rc.22" = "sha256-YsyOAnXELLKzhNlJ35dHA6KGbs0wTAX/nlQoW8wWyJQ=";
|
|
||||||
"tauri-2.9.5" = "sha256-dv5E/+A49ZBvnUQUkCGGJ21iHrVvrhHKNcpUctivJ8M=";
|
|
||||||
"tauri-specta-2.0.0-rc.21" = "sha256-n2VJ+B1nVrh6zQoZyfMoctqP+Csh7eVHRXwUQuiQjaQ=";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
buildAndTestSubdir = finalAttrs.cargoRoot;
|
|
||||||
|
|
||||||
nativeBuildInputs =
|
|
||||||
[pkg-config cargo-tauri.hook bun nodejs cargo rustc jq makeWrapper]
|
|
||||||
++ lib.optionals stdenv.hostPlatform.isLinux [wrapGAppsHook4];
|
|
||||||
|
|
||||||
buildInputs = lib.optionals stdenv.isLinux [
|
|
||||||
dbus
|
|
||||||
glib
|
|
||||||
gtk4
|
|
||||||
libsoup_3
|
|
||||||
librsvg
|
|
||||||
libappindicator
|
|
||||||
glib-networking
|
|
||||||
openssl
|
|
||||||
webkitgtk_4_1
|
|
||||||
gst_all_1.gstreamer
|
|
||||||
gst_all_1.gst-plugins-base
|
|
||||||
gst_all_1.gst-plugins-good
|
|
||||||
gst_all_1.gst-plugins-bad
|
|
||||||
];
|
|
||||||
|
|
||||||
strictDeps = true;
|
|
||||||
|
|
||||||
preBuild = ''
|
|
||||||
cp -a ${finalAttrs.node_modules}/{node_modules,packages} .
|
|
||||||
chmod -R u+w node_modules packages
|
|
||||||
patchShebangs node_modules
|
|
||||||
patchShebangs packages/desktop/node_modules
|
|
||||||
|
|
||||||
mkdir -p packages/desktop/src-tauri/sidecars
|
|
||||||
cp ${opencode}/bin/opencode packages/desktop/src-tauri/sidecars/opencode-cli-${stdenv.hostPlatform.rust.rustcTarget}
|
|
||||||
'';
|
|
||||||
|
|
||||||
tauriBuildFlags = ["--config" "tauri.prod.conf.json" "--no-sign"];
|
|
||||||
|
|
||||||
postFixup = lib.optionalString stdenv.hostPlatform.isLinux ''
|
|
||||||
mv $out/bin/OpenCode $out/bin/opencode-desktop
|
|
||||||
sed -i 's|^Exec=OpenCode$|Exec=opencode-desktop|' $out/share/applications/OpenCode.desktop
|
|
||||||
'';
|
|
||||||
});
|
|
||||||
# Wrapper for Wayland support
|
|
||||||
in
|
|
||||||
symlinkJoin {
|
|
||||||
name = "opencode-desktop";
|
|
||||||
paths = [opencode-desktop];
|
|
||||||
|
|
||||||
nativeBuildInputs = [makeWrapper];
|
|
||||||
|
|
||||||
postBuild = ''
|
|
||||||
wrapProgram $out/bin/opencode-desktop \
|
|
||||||
--run 'if [[ "$NIXOS_OZONE_WL" == "1" ]]; then export OC_ALLOW_WAYLAND=1; fi'
|
|
||||||
'';
|
|
||||||
|
|
||||||
meta = {
|
|
||||||
description = "OpenCode Desktop App with Wayland support";
|
|
||||||
homepage = "https://opencode.ai";
|
|
||||||
license = lib.licenses.mit;
|
|
||||||
platforms = lib.platforms.linux;
|
|
||||||
mainProgram = "opencode-desktop";
|
|
||||||
};
|
|
||||||
}
|
|
||||||
35
pkgs/openshell/default.nix
Normal file
35
pkgs/openshell/default.nix
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
stdenv,
|
||||||
|
fetchurl,
|
||||||
|
}: let
|
||||||
|
sources = lib.importJSON ./sources.json;
|
||||||
|
source = sources.sources.${stdenv.hostPlatform.system};
|
||||||
|
in
|
||||||
|
stdenv.mkDerivation {
|
||||||
|
pname = "openshell";
|
||||||
|
version = lib.removePrefix "v" sources.version;
|
||||||
|
|
||||||
|
src = fetchurl {
|
||||||
|
inherit (source) url hash;
|
||||||
|
};
|
||||||
|
|
||||||
|
unpackPhase = ''
|
||||||
|
tar -xzf $src
|
||||||
|
'';
|
||||||
|
|
||||||
|
installPhase = ''
|
||||||
|
install -Dm755 openshell $out/bin/openshell
|
||||||
|
'';
|
||||||
|
|
||||||
|
passthru.updateScript = ./update.sh;
|
||||||
|
|
||||||
|
meta = with lib; {
|
||||||
|
description = "Safe, private runtime for autonomous AI agents";
|
||||||
|
homepage = "https://github.com/NVIDIA/OpenShell";
|
||||||
|
license = licenses.asl20;
|
||||||
|
platforms = attrNames sources.sources;
|
||||||
|
mainProgram = "openshell";
|
||||||
|
maintainers = [];
|
||||||
|
};
|
||||||
|
}
|
||||||
17
pkgs/openshell/sources.json
Normal file
17
pkgs/openshell/sources.json
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
{
|
||||||
|
"version": "v0.0.23",
|
||||||
|
"sources": {
|
||||||
|
"aarch64-linux": {
|
||||||
|
"url": "https://github.com/NVIDIA/OpenShell/releases/download/v0.0.23/openshell-aarch64-unknown-linux-musl.tar.gz",
|
||||||
|
"hash": "sha256-x+TMlj8sc68rbkxwW80NrmyC0xaeC81TJMNEtUNhOLg="
|
||||||
|
},
|
||||||
|
"x86_64-linux": {
|
||||||
|
"url": "https://github.com/NVIDIA/OpenShell/releases/download/v0.0.23/openshell-x86_64-unknown-linux-musl.tar.gz",
|
||||||
|
"hash": "sha256-WLmYWn7mCC6VzUFEFN/O49hui81U0zPI6f3E5Hc9SjI="
|
||||||
|
},
|
||||||
|
"aarch64-darwin": {
|
||||||
|
"url": "https://github.com/NVIDIA/OpenShell/releases/download/v0.0.23/openshell-aarch64-apple-darwin.tar.gz",
|
||||||
|
"hash": "sha256-Bm8YP+7+CRKjfjNevirKRWHFBrdy3h5XV7gegvvcWXc="
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
63
pkgs/openshell/update.sh
Executable file
63
pkgs/openshell/update.sh
Executable file
@@ -0,0 +1,63 @@
|
|||||||
|
#!/usr/bin/env nix-shell
|
||||||
|
#!nix-shell --pure -i bash -p bash curl jq nix cacert git
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
# Update openshell sources.json with the latest release from GitHub.
|
||||||
|
# Usage: ./update.sh (or via nix-update --update-script)
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
SOURCES_FILE="$SCRIPT_DIR/sources.json"
|
||||||
|
|
||||||
|
# Map Nix system -> GitHub release asset name
|
||||||
|
declare -A SYSTEMS=(
|
||||||
|
["x86_64-linux"]="openshell-x86_64-unknown-linux-musl.tar.gz"
|
||||||
|
["aarch64-linux"]="openshell-aarch64-unknown-linux-musl.tar.gz"
|
||||||
|
["aarch64-darwin"]="openshell-aarch64-apple-darwin.tar.gz"
|
||||||
|
)
|
||||||
|
|
||||||
|
echo "Fetching latest openshell release..."
|
||||||
|
LATEST=$(curl -fsSL "https://api.github.com/repos/NVIDIA/OpenShell/releases/latest")
|
||||||
|
VERSION=$(echo "$LATEST" | jq -r '.tag_name')
|
||||||
|
echo "Latest version: $VERSION"
|
||||||
|
|
||||||
|
CURRENT_VERSION=$(jq -r '.version' "$SOURCES_FILE")
|
||||||
|
if [[ "$VERSION" == "$CURRENT_VERSION" ]]; then
|
||||||
|
echo "Already at latest version $VERSION, nothing to do."
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
NEW_SOURCES="{}"
|
||||||
|
|
||||||
|
for NIX_SYSTEM in "${!SYSTEMS[@]}"; do
|
||||||
|
ASSET_NAME="${SYSTEMS[$NIX_SYSTEM]}"
|
||||||
|
URL="https://github.com/NVIDIA/OpenShell/releases/download/${VERSION}/${ASSET_NAME}"
|
||||||
|
|
||||||
|
echo "Fetching hash for $NIX_SYSTEM ($URL)..."
|
||||||
|
HASH=$(nix-prefetch-url --type sha256 "$URL" 2>/dev/null)
|
||||||
|
SRI=$(nix hash to-sri --type sha256 "$HASH")
|
||||||
|
|
||||||
|
NEW_SOURCES=$(echo "$NEW_SOURCES" | jq \
|
||||||
|
--arg sys "$NIX_SYSTEM" \
|
||||||
|
--arg url "$URL" \
|
||||||
|
--arg hash "$SRI" \
|
||||||
|
'. + {($sys): {url: $url, hash: $hash}}')
|
||||||
|
done
|
||||||
|
|
||||||
|
jq -n \
|
||||||
|
--arg version "$VERSION" \
|
||||||
|
--argjson sources "$NEW_SOURCES" \
|
||||||
|
'{"version": $version, "sources": $sources}' \
|
||||||
|
> "$SOURCES_FILE"
|
||||||
|
|
||||||
|
echo "Updated $SOURCES_FILE to $VERSION"
|
||||||
|
|
||||||
|
# Commit when running in CI or via nix-update
|
||||||
|
if [[ -d "$SCRIPT_DIR/../../.git" ]] || git rev-parse --is-inside-work-tree >/dev/null 2>&1; then
|
||||||
|
NIXPKGS_ROOT=$(git -C "$SCRIPT_DIR" rev-parse --show-toplevel 2>/dev/null || true)
|
||||||
|
if [[ -n "$NIXPKGS_ROOT" && -n "$(git -C "$NIXPKGS_ROOT" status --porcelain "$SOURCES_FILE")" ]]; then
|
||||||
|
CLEAN_VERSION="${VERSION#v}"
|
||||||
|
git -C "$NIXPKGS_ROOT" add "$SOURCES_FILE"
|
||||||
|
git -C "$NIXPKGS_ROOT" commit -m "openshell: ${CURRENT_VERSION#v} -> ${CLEAN_VERSION}"
|
||||||
|
echo "Committed update to git"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
@@ -13,16 +13,16 @@
|
|||||||
}:
|
}:
|
||||||
buildGoModule (finalAttrs: {
|
buildGoModule (finalAttrs: {
|
||||||
pname = "sidecar";
|
pname = "sidecar";
|
||||||
version = "0.78.0";
|
version = "0.84.0";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "marcus";
|
owner = "marcus";
|
||||||
repo = "sidecar";
|
repo = "sidecar";
|
||||||
tag = "v${finalAttrs.version}";
|
tag = "v${finalAttrs.version}";
|
||||||
hash = "sha256-dVRSd8svfuv1+fYbHpFtXYHXvbAqomXKu9qi6Y5Y5S4=";
|
hash = "sha256-80ldZlaZ99ti8dvw+Awev7ucz03iOVD2yzz/+IFHDvA=";
|
||||||
};
|
};
|
||||||
|
|
||||||
vendorHash = "sha256-WIhE4CNbxmXaCczLOpFmAkxFcM37iE2tFuUmRnKRN54=";
|
vendorHash = "sha256-IDD+hQZODNPj+Gy9CX5GFdMcsvt75aFLpabXZehAjaw=";
|
||||||
|
|
||||||
subPackages = ["cmd/sidecar"];
|
subPackages = ["cmd/sidecar"];
|
||||||
|
|
||||||
|
|||||||
@@ -9,16 +9,16 @@
|
|||||||
}:
|
}:
|
||||||
buildGoModule (finalAttrs: {
|
buildGoModule (finalAttrs: {
|
||||||
pname = "td";
|
pname = "td";
|
||||||
version = "0.42.0";
|
version = "0.44.0";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "marcus";
|
owner = "marcus";
|
||||||
repo = "td";
|
repo = "td";
|
||||||
tag = "v${finalAttrs.version}";
|
tag = "v${finalAttrs.version}";
|
||||||
hash = "sha256-fUvEgbwN3zBb4r64GwLlUNVydVacP0wiOIBb1BuPWzQ=";
|
hash = "sha256-k1OCK6LE99fHLuxv8HZUW8cSn2Wmk74J7kb6Mi5ZpVw=";
|
||||||
};
|
};
|
||||||
|
|
||||||
vendorHash = "sha256-8mOebFPbf7+hCpn9hUrE0IGu6deEPSujr+yHqrzYEec=";
|
vendorHash = "sha256-hFFG+vLXcL2NNdLQvQZ1hzu++pp5AkbFOPQS10wtsec=";
|
||||||
|
|
||||||
ldflags = [
|
ldflags = [
|
||||||
"-s"
|
"-s"
|
||||||
|
|||||||
34
pkgs/vibetyper/default.nix
Normal file
34
pkgs/vibetyper/default.nix
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
{
|
||||||
|
appimageTools,
|
||||||
|
fetchurl,
|
||||||
|
lib,
|
||||||
|
}: let
|
||||||
|
pname = "vibetyper";
|
||||||
|
version = "1.2.3";
|
||||||
|
src = fetchurl {
|
||||||
|
url = "https://cdn.vibetyper.com/releases/linux/VibeTyper.AppImage";
|
||||||
|
sha256 = "sha256-6uGXw2nxb0sGkcMDTWBlL3PuwBfVodhgqfgZT1Ncs40=";
|
||||||
|
};
|
||||||
|
appimageContents = appimageTools.extractType2 {inherit pname version src;};
|
||||||
|
in
|
||||||
|
appimageTools.wrapType2 {
|
||||||
|
inherit pname version src;
|
||||||
|
|
||||||
|
extraPkgs = pkgs: [pkgs.fuse2];
|
||||||
|
|
||||||
|
extraInstallCommands = ''
|
||||||
|
install -m 444 -D ${appimageContents}/vibe-typer.desktop -t $out/share/applications
|
||||||
|
substituteInPlace $out/share/applications/vibe-typer.desktop \
|
||||||
|
--replace 'Exec=AppRun --no-sandbox' 'Exec=${pname}'
|
||||||
|
install -m 444 -D ${appimageContents}/vibe-typer.png \
|
||||||
|
$out/share/icons/hicolor/512x512/apps/vibe-typer.png
|
||||||
|
'';
|
||||||
|
|
||||||
|
meta = {
|
||||||
|
description = "VibeTyper - AI-powered typing assistant";
|
||||||
|
homepage = "https://vibetyper.com";
|
||||||
|
license = lib.licenses.unfree;
|
||||||
|
platforms = lib.platforms.linux;
|
||||||
|
mainProgram = "vibetyper";
|
||||||
|
};
|
||||||
|
}
|
||||||
109
shells/coding.nix
Normal file
109
shells/coding.nix
Normal file
@@ -0,0 +1,109 @@
|
|||||||
|
# AI coding agent development environment with coding rules
|
||||||
|
# Sets up coding rules for OpenCode and Pi, plus useful companion tools.
|
||||||
|
# Usage: nix develop .#coding
|
||||||
|
#
|
||||||
|
# To enable coding rules, add the agents input to your flake:
|
||||||
|
# agents = {
|
||||||
|
# url = "git+https://code.m3ta.dev/m3tam3re/AGENTS";
|
||||||
|
# flake = false;
|
||||||
|
# };
|
||||||
|
{
|
||||||
|
pkgs,
|
||||||
|
lib ? pkgs.lib,
|
||||||
|
inputs ? null,
|
||||||
|
agents ? null,
|
||||||
|
}: let
|
||||||
|
# Import the coding-rules library
|
||||||
|
m3taLib = import ../lib {lib = pkgs.lib;};
|
||||||
|
|
||||||
|
# Import custom packages
|
||||||
|
customPackages = import ../pkgs {inherit pkgs inputs;};
|
||||||
|
|
||||||
|
# Create rules configuration only if agents input is provided
|
||||||
|
rulesConfig = lib.optionalAttrs (agents != null) {
|
||||||
|
rules = m3taLib.coding-rules.mkCodingRules {
|
||||||
|
inherit agents;
|
||||||
|
|
||||||
|
# Languages relevant to this repository
|
||||||
|
languages = ["nix" "python" "shell"];
|
||||||
|
|
||||||
|
# Frameworks used in this repo
|
||||||
|
frameworks = ["n8n"];
|
||||||
|
|
||||||
|
# Standard concerns for development
|
||||||
|
concerns = [
|
||||||
|
"coding-style"
|
||||||
|
"naming"
|
||||||
|
"documentation"
|
||||||
|
"testing"
|
||||||
|
"git-workflow"
|
||||||
|
"project-structure"
|
||||||
|
];
|
||||||
|
|
||||||
|
# Also append rules to AGENTS.md for Pi agent discovery
|
||||||
|
forPi = true;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
in
|
||||||
|
pkgs.mkShell {
|
||||||
|
name = "coding";
|
||||||
|
|
||||||
|
# Development tools
|
||||||
|
buildInputs = with pkgs; [
|
||||||
|
# Task management for AI coding sessions
|
||||||
|
customPackages.td
|
||||||
|
|
||||||
|
# Companion tool for CLI agents (diffs, file trees, task management)
|
||||||
|
customPackages.sidecar
|
||||||
|
|
||||||
|
# Code analysis tools
|
||||||
|
|
||||||
|
# Nix development tools (for this repo)
|
||||||
|
nil
|
||||||
|
alejandra
|
||||||
|
statix
|
||||||
|
deadnix
|
||||||
|
];
|
||||||
|
|
||||||
|
shellHook = ''
|
||||||
|
echo "🤖 AI Coding Environment"
|
||||||
|
echo ""
|
||||||
|
|
||||||
|
${
|
||||||
|
if (agents != null)
|
||||||
|
then ''
|
||||||
|
# Set up coding rules for OpenCode + Pi
|
||||||
|
${rulesConfig.rules.shellHook}
|
||||||
|
|
||||||
|
echo "✅ Coding rules configured (OpenCode + Pi)"
|
||||||
|
echo " Languages: nix, python, shell"
|
||||||
|
echo " Frameworks: n8n"
|
||||||
|
echo " Concerns: coding-style, naming, documentation, testing, git-workflow, project-structure"
|
||||||
|
''
|
||||||
|
else ''
|
||||||
|
echo "⚠️ Coding rules not configured"
|
||||||
|
echo ""
|
||||||
|
echo "To enable, add the agents input to your flake.nix:"
|
||||||
|
echo ""
|
||||||
|
echo " agents = {"
|
||||||
|
echo " url = \"git+https://code.m3ta.dev/m3tam3re/AGENTS\";"
|
||||||
|
echo " flake = false;"
|
||||||
|
echo " };"
|
||||||
|
''
|
||||||
|
}
|
||||||
|
|
||||||
|
echo ""
|
||||||
|
echo "Available tools:"
|
||||||
|
echo " opencode - AI coding agent"
|
||||||
|
echo " td usage --new-session - View current tasks"
|
||||||
|
echo " sidecar - Companion tool (diffs, file trees, tasks)"
|
||||||
|
echo " code2prompt - Convert code to prompts"
|
||||||
|
echo ""
|
||||||
|
echo "Nix development tools:"
|
||||||
|
echo " nix flake check - Check flake validity"
|
||||||
|
echo " nix fmt . - Format Nix files"
|
||||||
|
echo " statix check . - Lint Nix files"
|
||||||
|
echo " deadnix . - Find dead code"
|
||||||
|
echo ""
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -4,6 +4,7 @@
|
|||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
inputs,
|
inputs,
|
||||||
|
agents ? null,
|
||||||
}: {
|
}: {
|
||||||
# Default shell for working on this repository
|
# Default shell for working on this repository
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
@@ -32,5 +33,5 @@
|
|||||||
# Import all individual shell environments
|
# Import all individual shell environments
|
||||||
python = import ./python.nix {inherit pkgs inputs;};
|
python = import ./python.nix {inherit pkgs inputs;};
|
||||||
devops = import ./devops.nix {inherit pkgs inputs;};
|
devops = import ./devops.nix {inherit pkgs inputs;};
|
||||||
opencode = import ./opencode.nix {inherit pkgs inputs;};
|
coding = import ./coding.nix {inherit pkgs inputs agents;};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
# OpenCode development environment with AI coding rules
|
|
||||||
# This shell demonstrates the mkOpencodeRules library provided by this repository
|
|
||||||
# Usage: nix develop .#opencode
|
|
||||||
#
|
|
||||||
# To enable OpenCode rules, add the agents input to your flake:
|
|
||||||
# agents = {
|
|
||||||
# url = "git+https://code.m3ta.dev/m3tam3re/AGENTS";
|
|
||||||
# flake = false;
|
|
||||||
# };
|
|
||||||
{
|
|
||||||
pkgs,
|
|
||||||
lib ? pkgs.lib,
|
|
||||||
inputs ? null,
|
|
||||||
agents ? null,
|
|
||||||
}: let
|
|
||||||
# Import the opencode-rules library
|
|
||||||
m3taLib = import ../lib {lib = pkgs.lib;};
|
|
||||||
|
|
||||||
# Import custom packages
|
|
||||||
customPackages = import ../pkgs {inherit pkgs inputs;};
|
|
||||||
|
|
||||||
# Create rules configuration only if agents input is provided
|
|
||||||
# This demonstrates how to use mkOpencodeRules in a real project
|
|
||||||
rulesConfig = lib.optionalAttrs (agents != null) {
|
|
||||||
rules = m3taLib.opencode-rules.mkOpencodeRules {
|
|
||||||
# Pass the AGENTS repository path
|
|
||||||
inherit agents;
|
|
||||||
|
|
||||||
# Languages relevant to this repository
|
|
||||||
languages = ["python" "typescript" "nix"];
|
|
||||||
|
|
||||||
# Frameworks used in this repo
|
|
||||||
frameworks = ["n8n"];
|
|
||||||
|
|
||||||
# Standard concerns for development
|
|
||||||
concerns = [
|
|
||||||
"coding-style"
|
|
||||||
"naming"
|
|
||||||
"documentation"
|
|
||||||
"testing"
|
|
||||||
"git-workflow"
|
|
||||||
"project-structure"
|
|
||||||
];
|
|
||||||
};
|
|
||||||
};
|
|
||||||
in
|
|
||||||
pkgs.mkShell {
|
|
||||||
name = "opencode-dev";
|
|
||||||
|
|
||||||
# Development tools
|
|
||||||
buildInputs = with pkgs;
|
|
||||||
[
|
|
||||||
# OpenCode AI coding agent (if inputs are available)
|
|
||||||
]
|
|
||||||
++ lib.optionals (inputs != null)
|
|
||||||
[inputs.opencode.packages.${pkgs.system}.opencode]
|
|
||||||
++ [
|
|
||||||
# Task management for AI coding sessions
|
|
||||||
customPackages.td
|
|
||||||
|
|
||||||
# Companion tool for CLI agents (diffs, file trees, task management)
|
|
||||||
customPackages.sidecar
|
|
||||||
|
|
||||||
# Code analysis tools
|
|
||||||
customPackages.code2prompt
|
|
||||||
|
|
||||||
# Nix development tools (for this repo)
|
|
||||||
nil
|
|
||||||
alejandra
|
|
||||||
statix
|
|
||||||
deadnix
|
|
||||||
];
|
|
||||||
|
|
||||||
# Shell hook that sets up OpenCode rules
|
|
||||||
shellHook = ''
|
|
||||||
echo "🤖 OpenCode Development Environment"
|
|
||||||
echo ""
|
|
||||||
echo "This environment demonstrates the mkOpencodeRules library"
|
|
||||||
echo "provided by the m3ta-nixpkgs repository."
|
|
||||||
echo ""
|
|
||||||
|
|
||||||
${
|
|
||||||
if (agents != null)
|
|
||||||
then ''
|
|
||||||
# Execute the OpenCode rules shellHook
|
|
||||||
${rulesConfig.rules.shellHook}
|
|
||||||
|
|
||||||
echo "✅ OpenCode rules configured!"
|
|
||||||
''
|
|
||||||
else ''
|
|
||||||
echo "⚠️ OpenCode rules not configured"
|
|
||||||
echo ""
|
|
||||||
echo "To enable OpenCode rules, add the agents input to your flake.nix:"
|
|
||||||
echo ""
|
|
||||||
echo " inputs = {"
|
|
||||||
echo " m3ta-nixpkgs.url = \"git+https://code.m3ta.dev/m3tam3re/nixpkgs\";"
|
|
||||||
echo " agents = {"
|
|
||||||
echo " url = \"git+https://code.m3ta.dev/m3tam3re/AGENTS\";"
|
|
||||||
echo " flake = false;"
|
|
||||||
echo " };"
|
|
||||||
echo " };"
|
|
||||||
echo ""
|
|
||||||
echo "Then pass agents to the shell:"
|
|
||||||
echo " opencode = import ./opencode.nix { inherit pkgs inputs agents; };"
|
|
||||||
''
|
|
||||||
}
|
|
||||||
|
|
||||||
echo ""
|
|
||||||
echo "Available tools:"
|
|
||||||
echo " opencode - AI coding agent"
|
|
||||||
echo " td usage --new-session - View current tasks"
|
|
||||||
echo " sidecar - Companion tool (diffs, file trees, tasks)"
|
|
||||||
echo " code2prompt - Convert code to prompts"
|
|
||||||
echo ""
|
|
||||||
echo "Nix development tools:"
|
|
||||||
echo " nix flake check - Check flake validity"
|
|
||||||
echo " nix fmt . - Format Nix files"
|
|
||||||
echo " statix check . - Lint Nix files"
|
|
||||||
echo " deadnix . - Find dead code"
|
|
||||||
echo ""
|
|
||||||
${
|
|
||||||
if (agents == null)
|
|
||||||
then ''
|
|
||||||
echo "💡 Using mkOpencodeRules in your project:"
|
|
||||||
echo ""
|
|
||||||
echo "Add to your flake.nix:"
|
|
||||||
echo " inputs = {"
|
|
||||||
echo " m3ta-nixpkgs.url = \"git+https://code.m3ta.dev/m3tam3re/nixpkgs\";"
|
|
||||||
echo " agents = {"
|
|
||||||
echo " url = \"git+https://code.m3ta.dev/m3tam3re/AGENTS\";"
|
|
||||||
echo " flake = false;"
|
|
||||||
echo " };"
|
|
||||||
echo " };"
|
|
||||||
echo ""
|
|
||||||
echo " outputs = {self, nixpkgs, m3ta-nixpkgs, agents, ...}:"
|
|
||||||
echo " let"
|
|
||||||
echo " system = \"x86_64-linux\";"
|
|
||||||
echo " pkgs = nixpkgs.legacyPackages.''${system};"
|
|
||||||
echo " m3taLib = m3ta-nixpkgs.lib.''${system};"
|
|
||||||
echo " rules = m3taLib.opencode-rules.mkOpencodeRules {"
|
|
||||||
echo " inherit agents;"
|
|
||||||
echo " languages = [\"python\" \"typescript\"];"
|
|
||||||
echo " frameworks = [\"n8n\"];"
|
|
||||||
echo " };"
|
|
||||||
echo " in {"
|
|
||||||
echo " devShells.''${system}.default = pkgs.mkShell {"
|
|
||||||
echo " shellHook = rules.shellHook;"
|
|
||||||
echo " };"
|
|
||||||
echo " };"
|
|
||||||
''
|
|
||||||
else ""
|
|
||||||
}
|
|
||||||
echo ""
|
|
||||||
'';
|
|
||||||
}
|
|
||||||
31
tests/lib/agents-test.nix
Normal file
31
tests/lib/agents-test.nix
Normal file
@@ -0,0 +1,31 @@
|
|||||||
|
# Smoke tests for lib/agents.nix
|
||||||
|
# Verifies the library imports correctly and exports expected functions.
|
||||||
|
# Actual renderer derivations are verified by flake check building packages.
|
||||||
|
{
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
}: let
|
||||||
|
agentsLib = (import ../../lib {inherit lib;}).agents;
|
||||||
|
in
|
||||||
|
pkgs.runCommand "lib-agents-tests" {} ''
|
||||||
|
echo "Running lib agents smoke tests..."
|
||||||
|
|
||||||
|
# Verify all expected functions exist
|
||||||
|
${lib.optionalString (agentsLib ? loadCanonical) ''echo "1. pass: loadCanonical exists"''}
|
||||||
|
${lib.optionalString (!(agentsLib ? loadCanonical)) ''echo "1. FAIL: loadCanonical missing" && exit 1''}
|
||||||
|
|
||||||
|
${lib.optionalString (agentsLib ? renderForTool) ''echo "2. pass: renderForTool exists"''}
|
||||||
|
${lib.optionalString (!(agentsLib ? renderForTool)) ''echo "2. FAIL: renderForTool missing" && exit 1''}
|
||||||
|
|
||||||
|
${lib.optionalString (agentsLib ? renderForOpencode) ''echo "3. pass: renderForOpencode exists"''}
|
||||||
|
${lib.optionalString (!(agentsLib ? renderForOpencode)) ''echo "3. FAIL: renderForOpencode missing" && exit 1''}
|
||||||
|
|
||||||
|
${lib.optionalString (agentsLib ? renderForPi) ''echo "4. pass: renderForPi exists"''}
|
||||||
|
${lib.optionalString (!(agentsLib ? renderForPi)) ''echo "4. FAIL: renderForPi missing" && exit 1''}
|
||||||
|
|
||||||
|
${lib.optionalString (agentsLib ? shellHookForTool) ''echo "5. pass: shellHookForTool exists"''}
|
||||||
|
${lib.optionalString (!(agentsLib ? shellHookForTool)) ''echo "5. FAIL: shellHookForTool missing" && exit 1''}
|
||||||
|
|
||||||
|
echo "All smoke tests passed"
|
||||||
|
touch $out
|
||||||
|
''
|
||||||
89
tests/lib/coding-rules-test.nix
Normal file
89
tests/lib/coding-rules-test.nix
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
{
|
||||||
|
lib,
|
||||||
|
pkgs,
|
||||||
|
}: let
|
||||||
|
codingRulesLib = (import ../../lib {inherit lib;}).coding-rules;
|
||||||
|
|
||||||
|
# Test 1: instructions are generated correctly with custom rulesDir
|
||||||
|
testInstructions = let
|
||||||
|
rules = codingRulesLib.mkCodingRules {
|
||||||
|
agents = "/tmp/fake-agents";
|
||||||
|
languages = ["python"];
|
||||||
|
concerns = ["naming"];
|
||||||
|
rulesDir = ".coding-rules";
|
||||||
|
};
|
||||||
|
in
|
||||||
|
assert rules.instructions
|
||||||
|
== [
|
||||||
|
".coding-rules/concerns/naming.md"
|
||||||
|
".coding-rules/languages/python.md"
|
||||||
|
]; "pass: instructions";
|
||||||
|
|
||||||
|
# Test 2: default rulesDir is .opencode-rules
|
||||||
|
testDefaultRulesDir = let
|
||||||
|
rules = codingRulesLib.mkCodingRules {
|
||||||
|
agents = "/tmp/fake-agents";
|
||||||
|
};
|
||||||
|
hasCorrectPrefix = builtins.all (s: builtins.substring 0 15 s == ".opencode-rules") rules.instructions;
|
||||||
|
in
|
||||||
|
assert hasCorrectPrefix == true; "pass: default rulesDir";
|
||||||
|
|
||||||
|
# Test 3: shellHook contains both the symlink command and the config generation
|
||||||
|
testShellHook = let
|
||||||
|
rules = codingRulesLib.mkCodingRules {
|
||||||
|
agents = "/tmp/fake-agents";
|
||||||
|
};
|
||||||
|
hook = rules.shellHook;
|
||||||
|
hasSymlink = builtins.match ".*ln -sfn.*" hook != null;
|
||||||
|
hasConfigGen = builtins.match ".*coding-rules.json.*" hook != null;
|
||||||
|
in
|
||||||
|
assert hasSymlink;
|
||||||
|
assert hasConfigGen; "pass: shellHook";
|
||||||
|
|
||||||
|
# Test 4: forPi=false does not include AGENTS.md logic in shellHook
|
||||||
|
testForPiDisabled = let
|
||||||
|
rules = codingRulesLib.mkCodingRules {
|
||||||
|
agents = "/tmp/fake-agents";
|
||||||
|
forPi = false;
|
||||||
|
};
|
||||||
|
hook = rules.shellHook;
|
||||||
|
hasPiBlock = builtins.match ".*CODING-RULES:START.*" hook != null;
|
||||||
|
in
|
||||||
|
assert hasPiBlock == false; "pass: forPi disabled";
|
||||||
|
|
||||||
|
# Test 5: mkRulesMdSection produces empty string for empty concerns
|
||||||
|
testEmptyRulesMdSection = let
|
||||||
|
section = codingRulesLib.mkRulesMdSection {
|
||||||
|
agents = "/tmp/fake-agents";
|
||||||
|
concerns = [];
|
||||||
|
languages = [];
|
||||||
|
frameworks = [];
|
||||||
|
};
|
||||||
|
in
|
||||||
|
assert section == ""; "pass: empty mkRulesMdSection";
|
||||||
|
|
||||||
|
# Test 6: mkRulesMdSection wraps content with markers
|
||||||
|
testRulesMdSection = let
|
||||||
|
# Use a simple file path that won't be read (concatRulesMd returns empty
|
||||||
|
# when files don't exist, so we just verify the function is callable)
|
||||||
|
section = codingRulesLib.mkRulesMdSection {
|
||||||
|
agents = "/tmp/fake-agents";
|
||||||
|
concerns = [];
|
||||||
|
languages = [];
|
||||||
|
frameworks = [];
|
||||||
|
};
|
||||||
|
# After fix: mkRulesMdSection returns "" for empty rules, not a string with markers
|
||||||
|
in
|
||||||
|
assert section == ""; "pass: mkRulesMdSection empty case";
|
||||||
|
in
|
||||||
|
pkgs.runCommand "lib-coding-rules-tests" {} ''
|
||||||
|
echo "Running lib coding-rules tests..."
|
||||||
|
echo "1. ${testInstructions}"
|
||||||
|
echo "2. ${testDefaultRulesDir}"
|
||||||
|
echo "3. ${testShellHook}"
|
||||||
|
echo "4. ${testForPiDisabled}"
|
||||||
|
echo "5. ${testEmptyRulesMdSection}"
|
||||||
|
echo "6. ${testRulesMdSection}"
|
||||||
|
echo "All tests passed"
|
||||||
|
touch $out
|
||||||
|
''
|
||||||
4
tests/lib/default.nix
Normal file
4
tests/lib/default.nix
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
{
|
||||||
|
coding-rules = import ./coding-rules-test.nix;
|
||||||
|
agents = import ./agents-test.nix;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user