Compare commits
46 Commits
9adfa185bb
...
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 |
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"
|
||||
}
|
||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -43,3 +43,10 @@ flake.lock.bak
|
||||
.sidecar-start.sh
|
||||
.sidecar-base
|
||||
.td-root
|
||||
.cache
|
||||
.pi*
|
||||
|
||||
# Beads / Dolt files (added by bd init)
|
||||
.dolt/
|
||||
*.db
|
||||
.beads-credential-key
|
||||
|
||||
7
.pi-lens/cache/jscpd.json
vendored
7
.pi-lens/cache/jscpd.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"success": true,
|
||||
"clones": [],
|
||||
"duplicatedLines": 0,
|
||||
"totalLines": 0,
|
||||
"percentage": 0
|
||||
}
|
||||
3
.pi-lens/cache/jscpd.meta.json
vendored
3
.pi-lens/cache/jscpd.meta.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"timestamp": "2026-04-11T04:17:20.531Z"
|
||||
}
|
||||
9
.pi-lens/cache/knip.json
vendored
9
.pi-lens/cache/knip.json
vendored
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"success": false,
|
||||
"issues": [],
|
||||
"unusedExports": [],
|
||||
"unusedFiles": [],
|
||||
"unusedDeps": [],
|
||||
"unlistedDeps": [],
|
||||
"summary": "Failed to parse output"
|
||||
}
|
||||
3
.pi-lens/cache/knip.meta.json
vendored
3
.pi-lens/cache/knip.meta.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"timestamp": "2026-04-11T04:17:21.374Z"
|
||||
}
|
||||
3
.pi-lens/cache/session-start-guidance.json
vendored
3
.pi-lens/cache/session-start-guidance.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"content": "📌 pi-lens active — as you work on this project, fix any errors you encounter (including pre-existing). Prefer: lsp_navigation for definitions/references, ast_grep_search for code patterns, grep for text/TODO search."
|
||||
}
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"timestamp": "2026-04-11T04:21:36.939Z"
|
||||
}
|
||||
3
.pi-lens/cache/todo-baseline.json
vendored
3
.pi-lens/cache/todo-baseline.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"items": []
|
||||
}
|
||||
3
.pi-lens/cache/todo-baseline.meta.json
vendored
3
.pi-lens/cache/todo-baseline.meta.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"timestamp": "2026-04-11T04:21:36.940Z"
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"files": {},
|
||||
"turnCycles": 0,
|
||||
"maxCycles": 3,
|
||||
"lastUpdated": "2026-04-11T04:17:22.397Z"
|
||||
}
|
||||
265
AGENTS.md
265
AGENTS.md
@@ -1,225 +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.
|
||||
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 { ... };`
|
||||
|
||||
## LIBRARY FUNCTIONS
|
||||
|
||||
### `lib.ports`
|
||||
|
||||
Port management utilities. See [Port Management](#port-management).
|
||||
|
||||
### `lib.agents`
|
||||
|
||||
Harness-agnostic agent management. Reads canonical `agent.toml` from the AGENTS
|
||||
flake input and renders tool-specific configs.
|
||||
|
||||
**Functions:**
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|--------|
|
||||
| `loadCanonical { agentsInput }` | Load canonical agents from AGENTS flake |
|
||||
| `renderForOpencode { pkgs, canonical, modelOverrides }` | Render to OpenCode file-based agents |
|
||||
| `renderForClaudeCode { pkgs, canonical, modelOverrides }` | Render to Claude Code agents + settings.json |
|
||||
| `renderForPi { pkgs, canonical }` | Render to Pi AGENTS.md + SYSTEM.md |
|
||||
| `renderForTool { pkgs, agentsInput, tool, modelOverrides }` | Dispatch to correct renderer |
|
||||
| `shellHookForTool { pkgs, agentsInput, tool, modelOverrides }` | Generate devShell shellHook |
|
||||
|
||||
### `lib.coding-rules`
|
||||
|
||||
Coding rules injection (renamed from `lib.opencode-rules`). The old name still works.
|
||||
|
||||
| Function | Purpose |
|
||||
|----------|--------|
|
||||
| `mkCodingRules { agents, languages, concerns, frameworks }` | Generate rules config + shellHook |
|
||||
| `mkOpencodeRules` | Backward-compat alias for `mkCodingRules` |
|
||||
|
||||
## 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
|
||||
## Quick Reference
|
||||
|
||||
```bash
|
||||
nix flake check # Validate flake
|
||||
nix fmt # Format (alejandra)
|
||||
nix build .#<pkg> # Build package
|
||||
nix flake show # List outputs
|
||||
nix develop # Enter dev shell
|
||||
nix develop .#python # Python shell
|
||||
nix develop .#devops # DevOps shell
|
||||
|
||||
# In dev shell only:
|
||||
statix check . # Lint
|
||||
deadnix . # Find dead code
|
||||
bd ready # Find available work
|
||||
bd show <id> # View issue details
|
||||
bd update <id> --claim # Claim work atomically
|
||||
bd close <id> # Complete work
|
||||
bd dolt push # Push beads data to remote
|
||||
```
|
||||
|
||||
## ANTI-PATTERNS
|
||||
## Non-Interactive Shell Commands
|
||||
|
||||
| Don't | Do Instead |
|
||||
| ------------------------- | ------------------------------------------------------------------- |
|
||||
| `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 |
|
||||
**ALWAYS use non-interactive flags** with file operations to avoid hanging on confirmation prompts.
|
||||
|
||||
## 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.
|
||||
|
||||
```
|
||||
type: brief description
|
||||
**Use these forms instead:**
|
||||
```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
|
||||
- **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.
|
||||
This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands.
|
||||
|
||||
## Task Management
|
||||
### Quick Reference
|
||||
|
||||
This project uses **td** for tracking tasks across AI coding sessions.
|
||||
Run `td usage --new-session` at conversation start to see current work.
|
||||
Use `td usage -q` for subsequent reads.
|
||||
```bash
|
||||
bd ready # Find available work
|
||||
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
|
||||
- `td usage -q` - Quick view of current tasks (subsequent reads)
|
||||
- `td version` - Check version
|
||||
- Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
|
||||
- Run `bd prime` for detailed command reference and session close protocol
|
||||
- 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
|
||||
|
||||
## MIGRATION: Agent System (OpenCode → Canonical TOML)
|
||||
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
|
||||
|
||||
The agent system was migrated from embedded `agents.json` to harness-agnostic
|
||||
canonical `agent.toml` + `system-prompt.md` in the AGENTS repo. Renderers in
|
||||
`lib/agents.nix` generate tool-specific configs.
|
||||
**MANDATORY WORKFLOW:**
|
||||
|
||||
### What changed in this repo
|
||||
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
|
||||
|
||||
- **`lib/agents.nix`**: New — 3 renderers (OpenCode, Claude Code, Pi) + dispatcher + shellHook
|
||||
- **`lib/coding-rules.nix`**: Renamed from `opencode-rules.nix`, `mkCodingRules` replaces `mkOpencodeRules`
|
||||
- **`modules/home-manager/coding/agents/`**: New — per-tool HM sub-modules
|
||||
- **`modules/home-manager/coding/opencode.nix`**: Slimmed — no longer handles agents/skills/context
|
||||
- **`flake.nix`**: Exports new `agents` HM module
|
||||
|
||||
### What the user must do
|
||||
|
||||
See `modules/home-manager/AGENTS.md` for the full migration guide. Summary:
|
||||
|
||||
1. Move `agentsInput`/`externalSkills` from `coding.opencode` to `coding.agents.opencode`
|
||||
2. Add `modelOverrides` with previously hardcoded model strings
|
||||
3. Run `home-manager switch`
|
||||
4. Remove legacy `agents.json` + `prompts/*.txt` from AGENTS repo
|
||||
5. Remove `lib.agentsJson` backward-compat bridge from AGENTS `flake.nix`
|
||||
**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
|
||||
|
||||
| Package | Description |
|
||||
| ------------------ | ------------------------------------- |
|
||||
| `code2prompt` | Convert code to prompts |
|
||||
| `hyprpaper-random` | Random wallpaper setter for Hyprpaper |
|
||||
| `kestractl` | CLI for the Kestra workflow orchestration platform |
|
||||
| `launch-webapp` | Launch web applications |
|
||||
| `mem0` | AI memory assistant with vector storage |
|
||||
| `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 |
|
||||
| `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 |
|
||||
See [📦 Packages](./docs/packages/) for the full index with descriptions.
|
||||
|
||||
Quick reference — build any package directly:
|
||||
|
||||
```bash
|
||||
nix build git+https://code.m3ta.dev/m3tam3re/nixpkgs#<package-name>
|
||||
nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#<package-name>
|
||||
```
|
||||
|
||||
Notable packages: `sidecar`, `td`, `code2prompt`, `mem0`, `n8n`, `zellij-ps`.
|
||||
|
||||
## 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
|
||||
- [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
|
||||
- [Using Modules](./guides/using-modules.md) - Using NixOS and Home Manager modules
|
||||
- [Development Workflow](./guides/development-workflow.md) - Development and testing workflow
|
||||
|
||||
### 📦 Packages
|
||||
|
||||
Documentation for all custom packages:
|
||||
|
||||
- [code2prompt](./packages/code2prompt.md) - Convert code to prompts
|
||||
- [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
|
||||
- [Packages Index](./packages/) - All packages with descriptions
|
||||
- [Adding Packages](../guides/adding-packages.md) - How to add new packages
|
||||
- [Templates](../templates.md) - Boilerplate templates
|
||||
|
||||
### ⚙️ Modules
|
||||
|
||||
@@ -68,6 +55,7 @@ Technical references and APIs:
|
||||
|
||||
- [Functions](./reference/functions.md) - Library functions documentation
|
||||
- [Patterns](./reference/patterns.md) - Code patterns and anti-patterns
|
||||
- [Templates](../templates.md) - Boilerplate for packages and modules
|
||||
|
||||
## 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
|
||||
@@ -662,5 +662,7 @@ nix eval .#nixosConfigurations.hostname.config.m3ta --apply builtins.attrNames
|
||||
|
||||
- [Port Management](./port-management.md) - Detailed port management guide
|
||||
- [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
|
||||
- [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"]
|
||||
```
|
||||
|
||||
### `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
|
||||
|
||||
### Importing
|
||||
@@ -262,7 +235,7 @@ in {
|
||||
| `getPort` | Get port with optional host override | `int or null` |
|
||||
| `getHostPorts` | Get all ports for host | `attrs` |
|
||||
| `listServices` | List all service names | `[string]` |
|
||||
| `getDefaultPort` | Get default port only | `int or null` |
|
||||
|
||||
|
||||
## 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
|
||||
57
flake.lock
generated
57
flake.lock
generated
@@ -1,5 +1,21 @@
|
||||
{
|
||||
"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": [
|
||||
@@ -23,11 +39,11 @@
|
||||
},
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1775423009,
|
||||
"narHash": "sha256-vPKLpjhIVWdDrfiUM8atW6YkIggCEKdSAlJPzzhkQlw=",
|
||||
"lastModified": 1777268161,
|
||||
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "68d8aa3d661f0e6bd5862291b5bb263b2a6595c9",
|
||||
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -39,11 +55,11 @@
|
||||
},
|
||||
"nixpkgs-master": {
|
||||
"locked": {
|
||||
"lastModified": 1775657231,
|
||||
"narHash": "sha256-DP8FfybiZPp5WLB9eIk0TC2mdvuYzxLGgrBODDrwPEI=",
|
||||
"lastModified": 1777643636,
|
||||
"narHash": "sha256-7vvm5Ia8o3g7YNErFcDsbCx+Pk8HbnA+ZYuA5Zga7hY=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "4e03baaa39b7746eac5704d623461422131cd03d",
|
||||
"rev": "da2366fac507ce7bd31852e7351e55b951656999",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -53,27 +69,6 @@
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"opencode": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
"nixpkgs-master"
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775782812,
|
||||
"narHash": "sha256-m+Ue7FWiTjKMAn1QefAwOMfOb2Vybk0mJPV9zcbkOmE=",
|
||||
"owner": "anomalyco",
|
||||
"repo": "opencode",
|
||||
"rev": "877be7e8e04142cd8fbebcb5e6c4b9617bf28cce",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "anomalyco",
|
||||
"ref": "v1.4.3",
|
||||
"repo": "opencode",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"openspec": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -81,11 +76,11 @@
|
||||
]
|
||||
},
|
||||
"locked": {
|
||||
"lastModified": 1775372219,
|
||||
"narHash": "sha256-MJakKC026Sarz7nMmiFrfONWc4xgaw8ApV0Hhp4ebhM=",
|
||||
"lastModified": 1777556999,
|
||||
"narHash": "sha256-HfFlRwR8IMjudRttN4T8L3DJKnNlpWfeNzQPly/HaRY=",
|
||||
"owner": "Fission-AI",
|
||||
"repo": "OpenSpec",
|
||||
"rev": "64d476f8b924bb9b74b896ea0aa784970e37da69",
|
||||
"rev": "347f0277e3be3549cd85cdea364fbd7710f1922b",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
@@ -96,10 +91,10 @@
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"agents": "agents",
|
||||
"basecamp": "basecamp",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-master": "nixpkgs-master",
|
||||
"opencode": "opencode",
|
||||
"openspec": "openspec"
|
||||
}
|
||||
}
|
||||
|
||||
40
flake.nix
40
flake.nix
@@ -10,17 +10,17 @@
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
# opencode needs newer bun from master
|
||||
opencode = {
|
||||
url = "github:anomalyco/opencode/v1.4.3";
|
||||
inputs.nixpkgs.follows = "nixpkgs-master";
|
||||
};
|
||||
|
||||
# openspec - spec-driven development for AI coding assistants
|
||||
openspec = {
|
||||
url = "github:Fission-AI/OpenSpec";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
# Agent definitions and coding rules
|
||||
agents = {
|
||||
url = "git+https://code.m3ta.dev/m3tam3re/AGENTS";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
@@ -56,13 +56,6 @@
|
||||
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;};
|
||||
};
|
||||
|
||||
@@ -84,18 +77,18 @@
|
||||
};
|
||||
|
||||
# Library functions - helper utilities for your configuration
|
||||
lib = forAllSystems (system: let
|
||||
pkgs = pkgsFor system;
|
||||
in
|
||||
import ./lib {lib = pkgs.lib;});
|
||||
lib = forAllSystems (system: import ./lib {lib = nixpkgs.lib;});
|
||||
|
||||
# Development shells for various programming environments
|
||||
# Usage: nix develop .#<shell-name>
|
||||
# Available shells: default, python, devops, opencode
|
||||
# Available shells: default, python, devops, coding
|
||||
devShells = forAllSystems (system: let
|
||||
pkgs = pkgsFor system;
|
||||
in
|
||||
import ./shells {inherit pkgs inputs;});
|
||||
import ./shells {
|
||||
inherit pkgs inputs;
|
||||
agents = inputs.agents;
|
||||
});
|
||||
|
||||
# Formatter for 'nix fmt'
|
||||
formatter = forAllSystems (system: (pkgsFor system).alejandra);
|
||||
@@ -111,6 +104,15 @@
|
||||
${pkgs.alejandra}/bin/alejandra --check ${./.}
|
||||
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
|
||||
|
||||
@@ -26,6 +26,27 @@
|
||||
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 ─────────────────────────────────────────────
|
||||
#
|
||||
@@ -87,20 +108,8 @@
|
||||
|
||||
mkAgentContent = name: agent:
|
||||
(mkFrontmatter name agent) + agent.systemPrompt;
|
||||
|
||||
mkAgentFile = name: agent:
|
||||
pkgs.writeText "${name}.md" (mkAgentContent name agent);
|
||||
|
||||
agentFiles = lib.mapAttrs mkAgentFile canonical;
|
||||
|
||||
copyCommands = lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (name: file: "cp ${file} $out/${name}.md") agentFiles
|
||||
);
|
||||
in
|
||||
pkgs.runCommand "opencode-agents" {} ''
|
||||
mkdir -p $out
|
||||
${copyCommands}
|
||||
'';
|
||||
renderAgentFiles pkgs canonical mkAgentContent "opencode-agents";
|
||||
|
||||
# ── Claude Code renderer ──────────────────────────────────────
|
||||
#
|
||||
@@ -179,10 +188,7 @@
|
||||
mkClaudeAgentContent = name: agent:
|
||||
(mkClaudeFrontmatter name agent) + agent.systemPrompt;
|
||||
|
||||
mkClaudeAgentFile = name: agent:
|
||||
pkgs.writeText "${name}.md" (mkClaudeAgentContent name agent);
|
||||
|
||||
agentFiles = lib.mapAttrs mkClaudeAgentFile canonical;
|
||||
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);
|
||||
@@ -196,14 +202,10 @@
|
||||
};
|
||||
|
||||
settingsFile = pkgs.writeText "claude-settings.json" settingsJson;
|
||||
|
||||
copyAgentCommands = lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (name: file: "cp ${file} $out/.claude/agents/${name}.md") agentFiles
|
||||
);
|
||||
in
|
||||
pkgs.runCommand "claude-code-agents" {} ''
|
||||
mkdir -p $out/.claude/agents
|
||||
${copyAgentCommands}
|
||||
cp -r ${agentFiles}/* $out/.claude/agents/
|
||||
cp ${settingsFile} $out/.claude/settings.json
|
||||
'';
|
||||
|
||||
@@ -223,7 +225,10 @@
|
||||
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;
|
||||
@@ -292,10 +297,7 @@
|
||||
mkPiAgentContent = name: agent:
|
||||
(mkPiFrontmatter name agent) + agent.systemPrompt;
|
||||
|
||||
mkPiAgentFile = name: agent:
|
||||
pkgs.writeText "${name}.md" (mkPiAgentContent name agent);
|
||||
|
||||
piAgentFiles = lib.mapAttrs mkPiAgentFile canonical;
|
||||
piAgentFiles = renderAgentFiles pkgs canonical mkPiAgentContent "pi-agent-files";
|
||||
|
||||
# ── Build AGENTS.md content ───────────────────────────────────
|
||||
primaryDn = primary.display_name or primaryName;
|
||||
@@ -306,6 +308,19 @@
|
||||
"- **" + 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"
|
||||
@@ -320,20 +335,17 @@
|
||||
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;
|
||||
|
||||
copyAgentCommands = lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (name: file: "cp ${file} $out/agents/${name}.md") piAgentFiles
|
||||
);
|
||||
in
|
||||
pkgs.runCommand "pi-agents" {} ''
|
||||
mkdir -p $out/agents
|
||||
cp ${agentsMdFile} $out/AGENTS.md
|
||||
cp ${systemMdFile} $out/SYSTEM.md
|
||||
${copyAgentCommands}
|
||||
cp -r ${piAgentFiles}/* $out/agents/
|
||||
'';
|
||||
|
||||
# ── renderForTool dispatcher ──────────────────────────────────
|
||||
@@ -346,6 +358,7 @@
|
||||
agentsInput,
|
||||
tool,
|
||||
modelOverrides ? {},
|
||||
codingRules ? null,
|
||||
}: let
|
||||
canonical = agentsInput.lib.loadAgents;
|
||||
in
|
||||
@@ -362,7 +375,7 @@
|
||||
else if tool == "pi"
|
||||
then
|
||||
agentsLib.renderForPi {
|
||||
inherit pkgs canonical modelOverrides;
|
||||
inherit pkgs canonical modelOverrides codingRules;
|
||||
}
|
||||
else throw "lib.agents.renderForTool: unknown tool '${tool}'. Must be opencode, claude-code, or pi.";
|
||||
|
||||
@@ -386,9 +399,10 @@
|
||||
agentsInput,
|
||||
tool,
|
||||
modelOverrides ? {},
|
||||
codingRules ? null,
|
||||
}: let
|
||||
rendered = agentsLib.renderForTool {
|
||||
inherit pkgs agentsInput tool modelOverrides;
|
||||
inherit pkgs agentsInput tool modelOverrides codingRules;
|
||||
};
|
||||
in
|
||||
if tool == "opencode"
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
# Opencode rules management utilities
|
||||
# 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
|
||||
@@ -26,7 +26,8 @@
|
||||
#
|
||||
# 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
|
||||
# - 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.
|
||||
@@ -43,10 +44,13 @@
|
||||
# (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 opencode.json
|
||||
# - shellHook: Bash code to create symlink and coding-rules.json
|
||||
# - instructions: List of rule file paths (relative to project root)
|
||||
#
|
||||
# Example:
|
||||
@@ -82,9 +86,9 @@
|
||||
],
|
||||
frameworks ? [],
|
||||
extraInstructions ? [],
|
||||
rulesDir ? ".opencode-rules",
|
||||
forPi ? false,
|
||||
}: 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 =
|
||||
@@ -93,29 +97,137 @@
|
||||
++ (map (f: "${rulesDir}/frameworks/${f}.md") frameworks)
|
||||
++ extraInstructions;
|
||||
|
||||
# Generate JSON configuration for Opencode
|
||||
opencodeConfig = {
|
||||
# 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 opencode.json
|
||||
# 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 opencode.json configuration file
|
||||
cat > opencode.json <<'OPENCODE_EOF'
|
||||
${builtins.toJSON opencodeConfig}
|
||||
OPENCODE_EOF
|
||||
# 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);
|
||||
|
||||
# Backward-compat alias
|
||||
mkOpencodeRules = mkCodingRules;
|
||||
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 mkOpencodeRules;
|
||||
inherit mkCodingRules concatRulesMd mkRulesMdSection;
|
||||
}
|
||||
|
||||
@@ -7,12 +7,9 @@
|
||||
# Port management utilities
|
||||
ports = import ./ports.nix {inherit lib;};
|
||||
|
||||
# Coding rules injection utilities (renamed from opencode-rules)
|
||||
# Coding rules injection utilities
|
||||
coding-rules = import ./coding-rules.nix {inherit lib;};
|
||||
|
||||
# Backward-compat alias: opencode-rules → coding-rules
|
||||
opencode-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)
|
||||
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;
|
||||
}
|
||||
|
||||
@@ -163,7 +163,7 @@ The agent system was migrated from embedded `agents.json` to file-based canonica
|
||||
| `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` (old name still works) |
|
||||
| `mkOpencodeRules` | `mkCodingRules` |
|
||||
|
||||
### Migration steps
|
||||
|
||||
|
||||
@@ -3,42 +3,34 @@
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
cfg = config.coding.agents.claude-code;
|
||||
}: {
|
||||
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 {
|
||||
options.coding.agents.claude-code = {
|
||||
in
|
||||
with lib; {
|
||||
enable = mkEnableOption "Claude Code agent management via canonical agent.toml definitions";
|
||||
|
||||
agentsInput = mkOption {
|
||||
type = types.nullOr types.anything;
|
||||
default = null;
|
||||
description = ''
|
||||
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 = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
description = ''
|
||||
Per-agent model overrides. Maps agent slug to model alias or ID.
|
||||
Example: { chiron = "claude-sonnet-4-20250514"; }
|
||||
'';
|
||||
example = literalExpression ''
|
||||
{
|
||||
chiron = "claude-sonnet-4-20250514";
|
||||
"chiron-forge" = "claude-sonnet-4-20250514";
|
||||
}
|
||||
'';
|
||||
};
|
||||
modelOverrides = shared.mkModelOverridesOption;
|
||||
|
||||
externalSkills = shared.externalSkillsOption;
|
||||
|
||||
mcpServers = mkOption {
|
||||
type = types.attrsOf types.anything;
|
||||
default = if mcpCfg != null then mcpCfg.servers else {};
|
||||
default =
|
||||
if mcpCfg != null
|
||||
then mcpCfg.servers
|
||||
else {};
|
||||
defaultText = literalExpression "config.programs.mcp.servers";
|
||||
description = ''
|
||||
MCP server configurations for Claude Code.
|
||||
@@ -48,9 +40,12 @@ in {
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable (let
|
||||
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 {
|
||||
@@ -82,6 +77,15 @@ in {
|
||||
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}";
|
||||
|
||||
@@ -1,10 +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;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
@@ -3,102 +3,43 @@
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
cfg = config.coding.agents.opencode;
|
||||
in {
|
||||
options.coding.agents.opencode = {
|
||||
}: {
|
||||
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 = mkOption {
|
||||
type = types.nullOr types.anything;
|
||||
default = null;
|
||||
description = ''
|
||||
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;
|
||||
};
|
||||
|
||||
modelOverrides = 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";
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
externalSkills = 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.skills-vercel; }
|
||||
]
|
||||
'';
|
||||
};
|
||||
};
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
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" = mkIf (cfg.agentsInput != null) {
|
||||
source = (import ../../../../lib {inherit lib;}).agents.renderForOpencode {
|
||||
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;
|
||||
};
|
||||
};
|
||||
|
||||
# Skills (merged from personal AGENTS repo + optional external skills)
|
||||
xdg.configFile."opencode/skills" = mkIf (cfg.agentsInput != null) {
|
||||
source = cfg.agentsInput.lib.mkOpencodeSkills {
|
||||
inherit pkgs;
|
||||
customSkills = "${cfg.agentsInput}/skills";
|
||||
externalSkills =
|
||||
map (
|
||||
entry:
|
||||
{inherit (entry) src skillsDir;}
|
||||
// optionalAttrs (entry.selectSkills != null) {inherit (entry) selectSkills;}
|
||||
)
|
||||
cfg.externalSkills;
|
||||
};
|
||||
};
|
||||
|
||||
# Static config dirs from AGENTS repo
|
||||
xdg.configFile."opencode/context" = mkIf (cfg.agentsInput != null) {
|
||||
source = "${cfg.agentsInput}/context";
|
||||
|
||||
@@ -3,17 +3,24 @@
|
||||
lib,
|
||||
pkgs,
|
||||
...
|
||||
}:
|
||||
with lib; let
|
||||
cfg = config.coding.agents.pi;
|
||||
}: {
|
||||
imports = [
|
||||
./shared/default.nix
|
||||
];
|
||||
|
||||
options.coding.agents.pi = let
|
||||
shared = import ./shared/shared-options.nix {inherit lib;};
|
||||
mcpCfg = config.programs.mcp or null;
|
||||
in {
|
||||
options.coding.agents.pi = {
|
||||
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 {};
|
||||
default =
|
||||
if mcpCfg != null
|
||||
then mcpCfg.servers
|
||||
else {};
|
||||
defaultText = literalExpression "config.programs.mcp.servers";
|
||||
description = ''
|
||||
MCP server configurations for Pi (pi-mcp-adapter).
|
||||
@@ -22,25 +29,13 @@ in {
|
||||
'';
|
||||
};
|
||||
|
||||
agentsInput = mkOption {
|
||||
type = types.nullOr types.anything;
|
||||
default = null;
|
||||
description = ''
|
||||
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 = mkOption {
|
||||
type = types.attrsOf types.str;
|
||||
default = {};
|
||||
description = ''
|
||||
Per-agent model overrides for Pi subagents.
|
||||
Maps agent slug to model string, e.g.:
|
||||
{ chiron = "anthropic/claude-sonnet-4"; chiron-forge = "anthropic/claude-sonnet-4"; }
|
||||
'';
|
||||
};
|
||||
modelOverrides = shared.mkModelOverridesOption;
|
||||
|
||||
primaryAgent = mkOption {
|
||||
type = types.nullOr types.str;
|
||||
@@ -51,6 +46,60 @@ in {
|
||||
'';
|
||||
};
|
||||
|
||||
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;
|
||||
@@ -153,9 +202,45 @@ in {
|
||||
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 = mkIf cfg.enable (let
|
||||
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) (
|
||||
@@ -164,12 +249,50 @@ in {
|
||||
then let
|
||||
filtered = filterNulls v;
|
||||
in
|
||||
if filtered == {} then null else filtered
|
||||
else v) attrs
|
||||
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
|
||||
@@ -179,14 +302,14 @@ in {
|
||||
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
|
||||
then let
|
||||
agentNames = builtins.attrNames cfg.agentsInput.lib.loadAgents;
|
||||
in
|
||||
builtins.listToAttrs (
|
||||
@@ -202,13 +325,21 @@ in {
|
||||
# ── 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 piSettings;
|
||||
".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";
|
||||
@@ -221,14 +352,6 @@ in {
|
||||
|
||||
# ── Agents — pi-subagents .md files ────────────────────────────
|
||||
agentFiles
|
||||
|
||||
# ── Skills symlinked from AGENTS repo ──────────────────────────
|
||||
(mkIf (cfg.agentsInput != null) {
|
||||
".pi/agent/skills".source = cfg.agentsInput.lib.mkOpencodeSkills {
|
||||
inherit pkgs;
|
||||
customSkills = "${cfg.agentsInput}/skills";
|
||||
};
|
||||
})
|
||||
];
|
||||
});
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
@@ -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.stdenv.hostPlatform.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.stdenv.hostPlatform.system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
};
|
||||
|
||||
stable-packages = final: _prev: {
|
||||
stable = import inputs.nixpkgs-stable {
|
||||
system = final.stdenv.hostPlatform.system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
};
|
||||
|
||||
pinned-packages = final: _prev: {
|
||||
pinned = import inputs.nixpkgs-9472de4 {
|
||||
system = final.stdenv.hostPlatform.system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
};
|
||||
|
||||
locked-packages = final: _prev: {
|
||||
locked = import inputs.nixpkgs-locked {
|
||||
system = final.stdenv.hostPlatform.system;
|
||||
config.allowUnfree = true;
|
||||
};
|
||||
};
|
||||
|
||||
master-packages = final: _prev: {
|
||||
master = import inputs.nixpkgs-master {
|
||||
system = final.stdenv.hostPlatform.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
|
||||
# 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
|
||||
# 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,3 +1,10 @@
|
||||
# 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,
|
||||
inputs,
|
||||
@@ -5,11 +12,12 @@
|
||||
}: let
|
||||
system = pkgs.stdenv.hostPlatform.system;
|
||||
in {
|
||||
# Custom packages registry
|
||||
# Each package is defined in its own directory under pkgs/
|
||||
# ── Local packages ────────────────────────────────────────────────
|
||||
# Standard packages built from source in ./<name>/default.nix.
|
||||
# No flake inputs required.
|
||||
|
||||
sidecar = pkgs.callPackage ./sidecar {};
|
||||
td = pkgs.callPackage ./td {};
|
||||
code2prompt = pkgs.callPackage ./code2prompt {};
|
||||
eigent = pkgs.callPackage ./eigent {};
|
||||
hyprpaper-random = pkgs.callPackage ./hyprpaper-random {};
|
||||
launch-webapp = pkgs.callPackage ./launch-webapp {};
|
||||
@@ -25,11 +33,9 @@ in {
|
||||
zellij-ps = pkgs.callPackage ./zellij-ps {};
|
||||
vibetyper = pkgs.callPackage ./vibetyper {};
|
||||
|
||||
# Imported from flake inputs (pass-through, no modifications)
|
||||
# ── Pass-through packages ──────────────────────────────────────────
|
||||
# Imported directly from flake inputs. No local modifications.
|
||||
|
||||
basecamp = inputs.basecamp.packages.${system}.default;
|
||||
openspec = inputs.openspec.packages.${system}.default;
|
||||
|
||||
# Imported from flake inputs (with local modifications)
|
||||
opencode-desktop = pkgs.callPackage ./opencode-desktop {inherit inputs;};
|
||||
# opencode-desktop = inputs.opencode.packages.${pkgs.system}.desktop;
|
||||
}
|
||||
|
||||
@@ -8,10 +8,10 @@
|
||||
nix-update-script,
|
||||
}: let
|
||||
pname = "eigent";
|
||||
version = "0.0.89";
|
||||
version = "0.0.90";
|
||||
src = fetchurl {
|
||||
url = "https://github.com/eigent-ai/eigent/releases/download/v${version}/Eigent-${version}.AppImage";
|
||||
hash = "sha256-9KuiFjegfXhCu1W/FCinWX4ae/DsNPudeBcXFfW18Hc=";
|
||||
hash = "sha256-mwCBx+D6mgGqQa8bDuUpo3h49EwFVkwasJwaYc6aXFE=";
|
||||
};
|
||||
appimageContents = appimageTools.extractType2 {inherit pname version src;};
|
||||
in
|
||||
|
||||
@@ -1,13 +1,13 @@
|
||||
{
|
||||
"version": "1.2.2",
|
||||
"version": "1.3.0",
|
||||
"sources": {
|
||||
"aarch64-linux": {
|
||||
"url": "https://github.com/kestra-io/kestractl/releases/download/1.2.2/kestractl_1.2.2_linux_arm64.tar.gz",
|
||||
"hash": "sha256-sidFsCZPnJ07PM5QayPBqaqlBBJTLEdecfd0AWnL7Yo="
|
||||
"url": "https://github.com/kestra-io/kestractl/releases/download/1.3.0/kestractl_1.3.0_linux_arm64.tar.gz",
|
||||
"hash": "sha256-/18F6CZnnLbet4BmI1oQ5pZWkJwIshCq30qd+cm0GGA="
|
||||
},
|
||||
"x86_64-linux": {
|
||||
"url": "https://github.com/kestra-io/kestractl/releases/download/1.2.2/kestractl_1.2.2_linux_amd64.tar.gz",
|
||||
"hash": "sha256-0C2naN2ougBJSY2z2m6eORnLkLen87HD+a+gvtrUvdw="
|
||||
"url": "https://github.com/kestra-io/kestractl/releases/download/1.3.0/kestractl_1.3.0_linux_amd64.tar.gz",
|
||||
"hash": "sha256-xmsBiqNKvob8xHDyU253o6c25YIubHanNdLqzWaOvSA="
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,14 +6,14 @@
|
||||
}:
|
||||
python3.pkgs.buildPythonPackage rec {
|
||||
pname = "mem0ai";
|
||||
version = "1.0.9";
|
||||
version = "2.0.1";
|
||||
pyproject = true;
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "mem0ai";
|
||||
repo = "mem0";
|
||||
rev = "v${version}";
|
||||
hash = "sha256-tcWH5VbjIBSHinfjirxbUhxqgU0xOUlcHTQHraMuALg=";
|
||||
hash = "sha256-lNSE0Yit+FmM8opC4XYtfVef7JfGd3wMKbLj67Kp4Qw=";
|
||||
};
|
||||
|
||||
# Relax Python dependency version constraints
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
node-gyp,
|
||||
cctools,
|
||||
xcbuild,
|
||||
dart-sass,
|
||||
libkrb5,
|
||||
libmongocrypt,
|
||||
libpq,
|
||||
@@ -25,20 +26,20 @@
|
||||
in
|
||||
stdenv.mkDerivation (finalAttrs: {
|
||||
pname = "n8n";
|
||||
version = "stable";
|
||||
version = "2.18.5";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "n8n-io";
|
||||
repo = "n8n";
|
||||
tag = "${finalAttrs.version}";
|
||||
hash = "sha256-/atba0ymCqhh5Rt61UxwC2xf8SGrRsEKtlsDCIkg37Y=";
|
||||
tag = "n8n@${finalAttrs.version}";
|
||||
hash = "sha256-ws0DXGQFR+z3nVyd4Yn9pIM7yh+H6GnuCRSLxgvtPxo=";
|
||||
};
|
||||
|
||||
pnpmDeps = fetchPnpmDeps {
|
||||
inherit (finalAttrs) pname version src;
|
||||
pnpm = pnpm_10;
|
||||
fetcherVersion = 3;
|
||||
hash = "sha256-YGplNNvIOIY1BthWmejAzucXujq8AkgPJus774GmWCA=";
|
||||
hash = "sha256-Ajgne0neNm6HgMK6z3jnEkUJJxVOTgzjpSaMaJgIndQ=";
|
||||
};
|
||||
|
||||
nativeBuildInputs =
|
||||
@@ -61,6 +62,17 @@ in
|
||||
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 = ''
|
||||
runHook preBuild
|
||||
|
||||
|
||||
@@ -1,6 +1,29 @@
|
||||
#!/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
|
||||
|
||||
new_version="$(curl -s "https://api.github.com/repos/n8n-io/n8n/releases/latest" | jq --raw-output '.tag_name | ltrimstr("n8n@")')"
|
||||
nix-update n8n --flake --version "$new_version"
|
||||
# n8n releases are published with two tags per 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,114 +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
|
||||
# Also fix stale npm deps hash in upstream node_modules FOD
|
||||
fixedNodeModules = opencode.node_modules.overrideAttrs {
|
||||
outputHash = "sha256-LRhPPrOKCGUSCEWTpAxPdWKTKVNkg82WrvD25cP3jts=";
|
||||
};
|
||||
|
||||
opencode-desktop = rustPlatform.buildRustPackage (finalAttrs: {
|
||||
pname = "opencode-desktop";
|
||||
version = opencode.version;
|
||||
src = opencode.src;
|
||||
node_modules = fixedNodeModules;
|
||||
patches = opencode.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";
|
||||
};
|
||||
}
|
||||
@@ -13,16 +13,16 @@
|
||||
}:
|
||||
buildGoModule (finalAttrs: {
|
||||
pname = "sidecar";
|
||||
version = "0.83.0";
|
||||
version = "0.84.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "marcus";
|
||||
repo = "sidecar";
|
||||
tag = "v${finalAttrs.version}";
|
||||
hash = "sha256-L6q2eZO1rNngWwHVhBJ2ftVbvYTConpqYHEb3nwiXxs=";
|
||||
hash = "sha256-80ldZlaZ99ti8dvw+Awev7ucz03iOVD2yzz/+IFHDvA=";
|
||||
};
|
||||
|
||||
vendorHash = "sha256-fIaHzc0L4jwVSh/YjrXBB7nENqCgOfHF5bnljFsGbVo=";
|
||||
vendorHash = "sha256-IDD+hQZODNPj+Gy9CX5GFdMcsvt75aFLpabXZehAjaw=";
|
||||
|
||||
subPackages = ["cmd/sidecar"];
|
||||
|
||||
|
||||
@@ -9,13 +9,13 @@
|
||||
}:
|
||||
buildGoModule (finalAttrs: {
|
||||
pname = "td";
|
||||
version = "0.43.0";
|
||||
version = "0.44.0";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "marcus";
|
||||
repo = "td";
|
||||
tag = "v${finalAttrs.version}";
|
||||
hash = "sha256-DwzuXumEEQWfZW+GbbY9kyqkEFZQ9sC+sSbVxfrY6bM=";
|
||||
hash = "sha256-k1OCK6LE99fHLuxv8HZUW8cSn2Wmk74J7kb6Mi5ZpVw=";
|
||||
};
|
||||
|
||||
vendorHash = "sha256-hFFG+vLXcL2NNdLQvQZ1hzu++pp5AkbFOPQS10wtsec=";
|
||||
|
||||
@@ -4,10 +4,10 @@
|
||||
lib,
|
||||
}: let
|
||||
pname = "vibetyper";
|
||||
version = "1.2.2";
|
||||
version = "1.2.3";
|
||||
src = fetchurl {
|
||||
url = "https://cdn.vibetyper.com/releases/linux/VibeTyper.AppImage";
|
||||
sha256 = "sha256-AUjrSVxyaI8Ok4pnoqaW4fGAd4GtSc0mEjDhkqdifY0=";
|
||||
sha256 = "sha256-6uGXw2nxb0sGkcMDTWBlL3PuwBfVodhgqfgZT1Ncs40=";
|
||||
};
|
||||
appimageContents = appimageTools.extractType2 {inherit pname version src;};
|
||||
in
|
||||
|
||||
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,
|
||||
inputs,
|
||||
agents ? null,
|
||||
}: {
|
||||
# Default shell for working on this repository
|
||||
default = pkgs.mkShell {
|
||||
@@ -32,5 +33,5 @@
|
||||
# Import all individual shell environments
|
||||
python = import ./python.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.stdenv.hostPlatform.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