Compare commits
2 Commits
master
...
0867492170
| Author | SHA1 | Date | |
|---|---|---|---|
| 0867492170 | |||
|
|
cab1f73c89 |
73
.beads/.gitignore
vendored
73
.beads/.gitignore
vendored
@@ -1,73 +0,0 @@
|
|||||||
# 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.
|
|
||||||
@@ -1,81 +0,0 @@
|
|||||||
# 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* ⚡
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
# 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"
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/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 ---
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/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 ---
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/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 ---
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/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 ---
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
#!/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 +0,0 @@
|
|||||||
{"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}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"database": "dolt",
|
|
||||||
"backend": "dolt",
|
|
||||||
"dolt_mode": "embedded",
|
|
||||||
"dolt_database": "nixpkgs",
|
|
||||||
"project_id": "b57a167a-6526-4211-a6c1-51686e431912"
|
|
||||||
}
|
|
||||||
7
.gitignore
vendored
7
.gitignore
vendored
@@ -43,10 +43,3 @@ flake.lock.bak
|
|||||||
.sidecar-start.sh
|
.sidecar-start.sh
|
||||||
.sidecar-base
|
.sidecar-base
|
||||||
.td-root
|
.td-root
|
||||||
.cache
|
|
||||||
.pi*
|
|
||||||
|
|
||||||
# Beads / Dolt files (added by bd init)
|
|
||||||
.dolt/
|
|
||||||
*.db
|
|
||||||
.beads-credential-key
|
|
||||||
|
|||||||
7
.pi-lens/cache/jscpd.json
vendored
Normal file
7
.pi-lens/cache/jscpd.json
vendored
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
{
|
||||||
|
"success": true,
|
||||||
|
"clones": [],
|
||||||
|
"duplicatedLines": 0,
|
||||||
|
"totalLines": 0,
|
||||||
|
"percentage": 0
|
||||||
|
}
|
||||||
3
.pi-lens/cache/jscpd.meta.json
vendored
Normal file
3
.pi-lens/cache/jscpd.meta.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"timestamp": "2026-04-13T19:16:03.510Z"
|
||||||
|
}
|
||||||
9
.pi-lens/cache/knip.json
vendored
Normal file
9
.pi-lens/cache/knip.json
vendored
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
{
|
||||||
|
"success": false,
|
||||||
|
"issues": [],
|
||||||
|
"unusedExports": [],
|
||||||
|
"unusedFiles": [],
|
||||||
|
"unusedDeps": [],
|
||||||
|
"unlistedDeps": [],
|
||||||
|
"summary": "Failed to parse output"
|
||||||
|
}
|
||||||
3
.pi-lens/cache/knip.meta.json
vendored
Normal file
3
.pi-lens/cache/knip.meta.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"timestamp": "2026-04-13T19:16:06.847Z"
|
||||||
|
}
|
||||||
1
.pi-lens/cache/session-start-guidance.json
vendored
Normal file
1
.pi-lens/cache/session-start-guidance.json
vendored
Normal file
@@ -0,0 +1 @@
|
|||||||
|
null
|
||||||
3
.pi-lens/cache/session-start-guidance.meta.json
vendored
Normal file
3
.pi-lens/cache/session-start-guidance.meta.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"timestamp": "2026-04-13T18:05:03.813Z"
|
||||||
|
}
|
||||||
3
.pi-lens/cache/todo-baseline.json
vendored
Normal file
3
.pi-lens/cache/todo-baseline.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"items": []
|
||||||
|
}
|
||||||
3
.pi-lens/cache/todo-baseline.meta.json
vendored
Normal file
3
.pi-lens/cache/todo-baseline.meta.json
vendored
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
{
|
||||||
|
"timestamp": "2026-04-13T18:04:03.698Z"
|
||||||
|
}
|
||||||
6
.pi-lens/turn-state.json
Normal file
6
.pi-lens/turn-state.json
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"files": {},
|
||||||
|
"turnCycles": 0,
|
||||||
|
"maxCycles": 3,
|
||||||
|
"lastUpdated": "2026-04-13T19:16:06.848Z"
|
||||||
|
}
|
||||||
269
AGENTS.md
269
AGENTS.md
@@ -1,84 +1,225 @@
|
|||||||
# Agent Instructions
|
# m3ta-nixpkgs Knowledge Base
|
||||||
|
|
||||||
This project uses **bd** (beads) for issue tracking. Run `bd prime` for full workflow context.
|
## MANDATORY: Use td for Task Management
|
||||||
|
|
||||||
## Quick Reference
|
You must run td usage --new-session at conversation start (or after /clear) to see current work.
|
||||||
|
Use td usage -q for subsequent reads.
|
||||||
|
|
||||||
```bash
|
**Generated:** 2026-02-14
|
||||||
bd ready # Find available work
|
**Commit:** dc2f3b6
|
||||||
bd show <id> # View issue details
|
**Branch:** master
|
||||||
bd update <id> --claim # Claim work atomically
|
|
||||||
bd close <id> # Complete work
|
## OVERVIEW
|
||||||
bd dolt push # Push beads data to remote
|
|
||||||
|
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)
|
||||||
```
|
```
|
||||||
|
|
||||||
## Non-Interactive Shell Commands
|
## WHERE TO LOOK
|
||||||
|
|
||||||
**ALWAYS use non-interactive flags** with file operations to avoid hanging on confirmation prompts.
|
| 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) |
|
||||||
|
|
||||||
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.
|
## CONVENTIONS
|
||||||
|
|
||||||
**Use these forms instead:**
|
**Formatter**: `nix fmt` before commit (alejandra)
|
||||||
```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
|
**Naming**:
|
||||||
rm -rf directory # NOT: rm -r directory
|
|
||||||
cp -rf source dest # NOT: cp -r source dest
|
- 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,
|
||||||
|
}:
|
||||||
```
|
```
|
||||||
|
|
||||||
**Other commands that may prompt:**
|
**Modules**: Standard pattern:
|
||||||
- `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
|
|
||||||
|
|
||||||
<!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:ca08a54f -->
|
```nix
|
||||||
## Beads Issue Tracker
|
{ config, lib, pkgs, ... }:
|
||||||
|
with lib; let
|
||||||
This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands.
|
cfg = config.m3ta.myModule;
|
||||||
|
in {
|
||||||
### Quick Reference
|
options.m3ta.myModule = {
|
||||||
|
enable = mkEnableOption "description";
|
||||||
```bash
|
};
|
||||||
bd ready # Find available work
|
config = mkIf cfg.enable { ... };
|
||||||
bd show <id> # View issue details
|
}
|
||||||
bd update <id> --claim # Claim work
|
|
||||||
bd close <id> # Complete work
|
|
||||||
```
|
```
|
||||||
|
|
||||||
### Rules
|
**Meta**: Always include all fields:
|
||||||
|
|
||||||
- Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
|
```nix
|
||||||
- Run `bd prime` for detailed command reference and session close protocol
|
meta = with lib; {
|
||||||
- Use `bd remember` for persistent knowledge — do NOT use MEMORY.md files
|
description = "...";
|
||||||
|
homepage = "...";
|
||||||
|
license = licenses.mit;
|
||||||
|
platforms = platforms.linux;
|
||||||
|
mainProgram = "...";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## Session Completion
|
## PACKAGE PATTERNS
|
||||||
|
|
||||||
**When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
|
**Rust**: `rustPlatform.buildRustPackage rec { cargoLock.lockFile = src + "/Cargo.lock"; }`
|
||||||
|
|
||||||
**MANDATORY WORKFLOW:**
|
**Shell**: `writeShellScriptBin "name" ''script''` or `mkDerivation` with custom `installPhase`
|
||||||
|
|
||||||
1. **File issues for remaining work** - Create issues for anything that needs follow-up
|
**AppImage**: `appimageTools.wrapType2 { ... }`
|
||||||
2. **Run quality gates** (if code changed) - Tests, linters, builds
|
|
||||||
3. **Update issue status** - Close finished work, update in-progress items
|
|
||||||
4. **PUSH TO REMOTE** - This is MANDATORY:
|
|
||||||
```bash
|
|
||||||
git pull --rebase
|
|
||||||
bd dolt push
|
|
||||||
git push
|
|
||||||
git status # MUST show "up to date with origin"
|
|
||||||
```
|
|
||||||
5. **Clean up** - Clear stashes, prune remote branches
|
|
||||||
6. **Verify** - All changes committed AND pushed
|
|
||||||
7. **Hand off** - Provide context for next session
|
|
||||||
|
|
||||||
**CRITICAL RULES:**
|
**Custom fetcher**: `fetchFromGitea { domain = "code.m3ta.dev"; owner = "m3tam3re"; ... }`
|
||||||
- Work is NOT complete until `git push` succeeds
|
|
||||||
- NEVER stop before pushing - that leaves work stranded locally
|
## MODULE PATTERNS
|
||||||
- NEVER say "ready to push when you are" - YOU must push
|
|
||||||
- If push fails, resolve and retry until it succeeds
|
**Simple**: `options.cli.name = { enable = mkEnableOption "..."; }; config = mkIf cfg.enable { ... };`
|
||||||
<!-- END BEADS INTEGRATION -->
|
|
||||||
|
**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
|
||||||
|
|
||||||
|
```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
|
||||||
|
```
|
||||||
|
|
||||||
|
## ANTI-PATTERNS
|
||||||
|
|
||||||
|
| 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 |
|
||||||
|
|
||||||
|
## COMMIT FORMAT
|
||||||
|
|
||||||
|
```
|
||||||
|
type: brief description
|
||||||
|
```
|
||||||
|
|
||||||
|
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `chore`
|
||||||
|
|
||||||
|
## NOTES
|
||||||
|
|
||||||
|
- **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.
|
||||||
|
|
||||||
|
## Task Management
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
**Quick reference:**
|
||||||
|
|
||||||
|
- `td usage --new-session` - Start new session and view tasks
|
||||||
|
- `td usage -q` - Quick view of current tasks (subsequent reads)
|
||||||
|
- `td version` - Check version
|
||||||
|
|
||||||
|
For full workflow details, see the [td documentation](./docs/packages/td.md).
|
||||||
|
|
||||||
|
## MIGRATION: Agent System (OpenCode → Canonical TOML)
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
### What changed in this repo
|
||||||
|
|
||||||
|
- **`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`
|
||||||
|
|||||||
108
CHANGELOG.md
108
CHANGELOG.md
@@ -1,108 +0,0 @@
|
|||||||
# 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
|
|
||||||
57
PLAN.md
Normal file
57
PLAN.md
Normal file
@@ -0,0 +1,57 @@
|
|||||||
|
# PLAN
|
||||||
|
|
||||||
|
## Context
|
||||||
|
- Implement **Option A**: run `pi` through a **rootless Podman** container while keeping a native terminal UX.
|
||||||
|
- Preserve `flake.nix` + `nix develop` workflows by using the **host Nix daemon** from inside the container.
|
||||||
|
- Keep logic centralized in `nixpkgs` and host-specific values in `nixos-config`.
|
||||||
|
|
||||||
|
## Approach
|
||||||
|
- Extend the existing Home Manager module at `modules/home-manager/coding/agents/pi.nix` with a `coding.agents.pi.container.*` option set.
|
||||||
|
- Implement **Option A defaults** from your decisions:
|
||||||
|
- wrapper command name is `pi` (native command replacement),
|
||||||
|
- project roots are mounted read-write,
|
||||||
|
- `autoStart = true` by default,
|
||||||
|
- `autoNixDevelop = false` by default,
|
||||||
|
- `image` default set to `docker.io/nixos/nix:latest` as a conservative base and overridden in host config for a Pi-ready image.
|
||||||
|
- Generate a deterministic wrapper script (installed via Home Manager) that:
|
||||||
|
- verifies cwd is within allowed project roots,
|
||||||
|
- ensures rootless container exists/runs,
|
||||||
|
- maps cwd and runs `podman exec -it <container> pi "$@"`,
|
||||||
|
- optionally runs via `nix develop -c pi ...` when `autoNixDevelop=true` and `flake.nix` is present.
|
||||||
|
- Configure safe Podman mounts:
|
||||||
|
- allowed project roots only,
|
||||||
|
- host Nix daemon socket (Option A),
|
||||||
|
- minimal Nix config/certs needed for CLI operation.
|
||||||
|
- Wire host-specific config in `nixos-config/home/features/coding/pi.nix` and remove direct host `pi` binary installation from the coding package list to avoid command-path ambiguity.
|
||||||
|
|
||||||
|
## Files to modify
|
||||||
|
- `modules/home-manager/coding/agents/pi.nix` (new container options + wrapper + container lifecycle logic)
|
||||||
|
- `/home/m3tam3re/p/NIX/nixos-config/home/features/coding/pi.nix` (host-specific container settings)
|
||||||
|
|
||||||
|
## Reuse
|
||||||
|
- Existing Pi HM module and option namespace:
|
||||||
|
- `modules/home-manager/coding/agents/pi.nix`
|
||||||
|
- Existing coding feature wiring in nixos-config:
|
||||||
|
- `/home/m3tam3re/p/NIX/nixos-config/home/features/coding/default.nix`
|
||||||
|
- `/home/m3tam3re/p/NIX/nixos-config/home/features/coding/pi.nix`
|
||||||
|
|
||||||
|
## Steps
|
||||||
|
- [ ] Add `coding.agents.pi.container` options (enable/name/image/projectRoots/autoStart/autoNixDevelop/extraRunArgs/extraEnv) with defaults matching your preferences (`autoStart=true`, `autoNixDevelop=false`, default image as above).
|
||||||
|
- [ ] Implement wrapper script generation in HM module with cwd allowlist checks and container create/start/exec behavior.
|
||||||
|
- [ ] Make wrapper binary name `pi` (native UX) when container mode is enabled.
|
||||||
|
- [ ] Add deterministic container run/create args with safe mounts and host Nix daemon socket.
|
||||||
|
- [ ] Add optional in-container `nix develop -c pi` path when flake project is detected.
|
||||||
|
- [ ] Wire host-specific values in nixos-config `home/features/coding/pi.nix`.
|
||||||
|
- [ ] Remove direct host `pi` package install in nixos-config coding packages so wrapper is the effective `pi` command.
|
||||||
|
- [ ] Validate eval/build and document command outputs for flake and non-flake wrapper behavior.
|
||||||
|
|
||||||
|
## Verification
|
||||||
|
- Static checks for both repos (module eval/build where appropriate).
|
||||||
|
- Home Manager evaluation/switch check in nixos-config.
|
||||||
|
- Manual wrapper checks:
|
||||||
|
- Inside a flake project: `pi` resolves via `nix develop -c pi ...` when enabled.
|
||||||
|
- Outside flake project: `pi` runs directly via container exec.
|
||||||
|
- Capture exact commands + outputs for report.
|
||||||
|
|
||||||
|
## Open questions
|
||||||
|
- None currently blocking; proceed with conservative default image and host override guidance.
|
||||||
28
README.md
28
README.md
@@ -38,16 +38,24 @@ nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#zellij-ps
|
|||||||
|
|
||||||
## Available Packages
|
## Available Packages
|
||||||
|
|
||||||
See [📦 Packages](./docs/packages/) for the full index with descriptions.
|
| Package | Description |
|
||||||
|
| ------------------ | ------------------------------------- |
|
||||||
Quick reference — build any package directly:
|
| `code2prompt` | Convert code to prompts |
|
||||||
|
| `hyprpaper-random` | Random wallpaper setter for Hyprpaper |
|
||||||
```bash
|
| `kestractl` | CLI for the Kestra workflow orchestration platform |
|
||||||
nix build git+https://code.m3ta.dev/m3tam3re/nixpkgs#<package-name>
|
| `launch-webapp` | Launch web applications |
|
||||||
nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#<package-name>
|
| `mem0` | AI memory assistant with vector storage |
|
||||||
```
|
| `msty-studio` | Msty Studio application |
|
||||||
|
| `n8n` | Free and source-available fair-code licensed workflow automation tool |
|
||||||
Notable packages: `sidecar`, `td`, `code2prompt`, `mem0`, `n8n`, `zellij-ps`.
|
| `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 |
|
||||||
|
|
||||||
## Automated Package Updates
|
## Automated Package Updates
|
||||||
|
|
||||||
|
|||||||
@@ -20,16 +20,29 @@ Step-by-step guides for common tasks:
|
|||||||
|
|
||||||
- [Getting Started](./guides/getting-started.md) - Initial setup and basic usage
|
- [Getting Started](./guides/getting-started.md) - Initial setup and basic usage
|
||||||
- [Adding Packages](./guides/adding-packages.md) - How to add new packages
|
- [Adding Packages](./guides/adding-packages.md) - How to add new packages
|
||||||
- [Adding Modules](./guides/adding-modules.md) - How to add new NixOS or Home Manager modules
|
|
||||||
- [Port Management](./guides/port-management.md) - Managing service ports across hosts
|
- [Port Management](./guides/port-management.md) - Managing service ports across hosts
|
||||||
- [Using Modules](./guides/using-modules.md) - Using NixOS and Home Manager modules
|
- [Using Modules](./guides/using-modules.md) - Using NixOS and Home Manager modules
|
||||||
- [Development Workflow](./guides/development-workflow.md) - Development and testing workflow
|
- [Development Workflow](./guides/development-workflow.md) - Development and testing workflow
|
||||||
|
|
||||||
### 📦 Packages
|
### 📦 Packages
|
||||||
|
|
||||||
- [Packages Index](./packages/) - All packages with descriptions
|
Documentation for all custom packages:
|
||||||
- [Adding Packages](../guides/adding-packages.md) - How to add new packages
|
|
||||||
- [Templates](../templates.md) - Boilerplate templates
|
- [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
|
||||||
|
|
||||||
### ⚙️ Modules
|
### ⚙️ Modules
|
||||||
|
|
||||||
@@ -55,7 +68,6 @@ Technical references and APIs:
|
|||||||
|
|
||||||
- [Functions](./reference/functions.md) - Library functions documentation
|
- [Functions](./reference/functions.md) - Library functions documentation
|
||||||
- [Patterns](./reference/patterns.md) - Code patterns and anti-patterns
|
- [Patterns](./reference/patterns.md) - Code patterns and anti-patterns
|
||||||
- [Templates](../templates.md) - Boilerplate for packages and modules
|
|
||||||
|
|
||||||
## Repository Structure
|
## Repository Structure
|
||||||
|
|
||||||
|
|||||||
@@ -1,261 +0,0 @@
|
|||||||
# 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,7 +662,5 @@ nix eval .#nixosConfigurations.hostname.config.m3ta --apply builtins.attrNames
|
|||||||
|
|
||||||
- [Port Management](./port-management.md) - Detailed port management guide
|
- [Port Management](./port-management.md) - Detailed port management guide
|
||||||
- [Adding Packages](./adding-packages.md) - How to add new packages
|
- [Adding Packages](./adding-packages.md) - How to add new packages
|
||||||
- [Adding Modules](./adding-modules.md) - How to add new NixOS or Home Manager modules
|
|
||||||
- [Templates](../templates.md) - Boilerplate for new packages and modules
|
|
||||||
- [Architecture](../ARCHITECTURE.md) - Understanding module structure
|
- [Architecture](../ARCHITECTURE.md) - Understanding module structure
|
||||||
- [Contributing](../CONTRIBUTING.md) - Code style and guidelines
|
- [Contributing](../CONTRIBUTING.md) - Code style and guidelines
|
||||||
|
|||||||
@@ -1,53 +0,0 @@
|
|||||||
# 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
|
|
||||||
117
docs/packages/notesmd-cli.md
Normal file
117
docs/packages/notesmd-cli.md
Normal file
@@ -0,0 +1,117 @@
|
|||||||
|
# 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
|
||||||
@@ -1,637 +0,0 @@
|
|||||||
# 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,6 +153,33 @@ allServices = portHelpers.listServices;
|
|||||||
# Returns: ["nginx" "grafana" "prometheus" "homepage"]
|
# Returns: ["nginx" "grafana" "prometheus" "homepage"]
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### `getDefaultPort`
|
||||||
|
|
||||||
|
Simple helper to get a port without host override.
|
||||||
|
|
||||||
|
#### Signature
|
||||||
|
|
||||||
|
```nix
|
||||||
|
getDefaultPort :: portsConfig -> string -> int-or-null
|
||||||
|
```
|
||||||
|
|
||||||
|
#### Arguments
|
||||||
|
|
||||||
|
1. `portsConfig` - Same structure as `mkPortHelpers`
|
||||||
|
2. `service` - The service name (string)
|
||||||
|
|
||||||
|
#### Returns
|
||||||
|
|
||||||
|
Port number (int) or `null` if service not found.
|
||||||
|
|
||||||
|
#### Usage
|
||||||
|
|
||||||
|
```nix
|
||||||
|
services.my-service = {
|
||||||
|
port = m3taLib.ports.getDefaultPort myPorts "my-service";
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
## Using Library Functions
|
## Using Library Functions
|
||||||
|
|
||||||
### Importing
|
### Importing
|
||||||
@@ -235,7 +262,7 @@ in {
|
|||||||
| `getPort` | Get port with optional host override | `int or null` |
|
| `getPort` | Get port with optional host override | `int or null` |
|
||||||
| `getHostPorts` | Get all ports for host | `attrs` |
|
| `getHostPorts` | Get all ports for host | `attrs` |
|
||||||
| `listServices` | List all service names | `[string]` |
|
| `listServices` | List all service names | `[string]` |
|
||||||
|
| `getDefaultPort` | Get default port only | `int or null` |
|
||||||
|
|
||||||
## Related
|
## Related
|
||||||
|
|
||||||
|
|||||||
@@ -1,162 +0,0 @@
|
|||||||
# 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,21 +1,5 @@
|
|||||||
{
|
{
|
||||||
"nodes": {
|
"nodes": {
|
||||||
"agents": {
|
|
||||||
"flake": false,
|
|
||||||
"locked": {
|
|
||||||
"lastModified": 1777399938,
|
|
||||||
"narHash": "sha256-xXPqUQezDdDtF8MbpZnwD1HkybOYwF92evx8rJ6OXCU=",
|
|
||||||
"ref": "refs/heads/master",
|
|
||||||
"rev": "9a91f1ee0cf011a7eaf1f16a9e17610b0457e055",
|
|
||||||
"revCount": 85,
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://code.m3ta.dev/m3tam3re/AGENTS"
|
|
||||||
},
|
|
||||||
"original": {
|
|
||||||
"type": "git",
|
|
||||||
"url": "https://code.m3ta.dev/m3tam3re/AGENTS"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"basecamp": {
|
"basecamp": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -39,11 +23,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs": {
|
"nixpkgs": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1777268161,
|
"lastModified": 1775423009,
|
||||||
"narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
|
"narHash": "sha256-vPKLpjhIVWdDrfiUM8atW6YkIggCEKdSAlJPzzhkQlw=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
|
"rev": "68d8aa3d661f0e6bd5862291b5bb263b2a6595c9",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -55,11 +39,11 @@
|
|||||||
},
|
},
|
||||||
"nixpkgs-master": {
|
"nixpkgs-master": {
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1777684196,
|
"lastModified": 1775657231,
|
||||||
"narHash": "sha256-irZjT++CZFBGHsuHNqPTa6AE1wVVuxdoR7pcdp6hq0A=",
|
"narHash": "sha256-DP8FfybiZPp5WLB9eIk0TC2mdvuYzxLGgrBODDrwPEI=",
|
||||||
"owner": "NixOS",
|
"owner": "NixOS",
|
||||||
"repo": "nixpkgs",
|
"repo": "nixpkgs",
|
||||||
"rev": "1cad9ada6da0658a588196ddcb2836004caa2293",
|
"rev": "4e03baaa39b7746eac5704d623461422131cd03d",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -69,6 +53,27 @@
|
|||||||
"type": "github"
|
"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": {
|
"openspec": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -76,11 +81,11 @@
|
|||||||
]
|
]
|
||||||
},
|
},
|
||||||
"locked": {
|
"locked": {
|
||||||
"lastModified": 1777679960,
|
"lastModified": 1775372219,
|
||||||
"narHash": "sha256-yLc5BzIecR3L9lPILImNqRgOqqXCZH902CSBLn/5UJI=",
|
"narHash": "sha256-MJakKC026Sarz7nMmiFrfONWc4xgaw8ApV0Hhp4ebhM=",
|
||||||
"owner": "Fission-AI",
|
"owner": "Fission-AI",
|
||||||
"repo": "OpenSpec",
|
"repo": "OpenSpec",
|
||||||
"rev": "0ca74762dc03ee25f8651eaa7c33866170112031",
|
"rev": "64d476f8b924bb9b74b896ea0aa784970e37da69",
|
||||||
"type": "github"
|
"type": "github"
|
||||||
},
|
},
|
||||||
"original": {
|
"original": {
|
||||||
@@ -91,10 +96,10 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"agents": "agents",
|
|
||||||
"basecamp": "basecamp",
|
"basecamp": "basecamp",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"nixpkgs-master": "nixpkgs-master",
|
"nixpkgs-master": "nixpkgs-master",
|
||||||
|
"opencode": "opencode",
|
||||||
"openspec": "openspec"
|
"openspec": "openspec"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
40
flake.nix
40
flake.nix
@@ -10,17 +10,17 @@
|
|||||||
inputs.nixpkgs.follows = "nixpkgs";
|
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 - spec-driven development for AI coding assistants
|
||||||
openspec = {
|
openspec = {
|
||||||
url = "github:Fission-AI/OpenSpec";
|
url = "github:Fission-AI/OpenSpec";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Agent definitions and coding rules
|
|
||||||
agents = {
|
|
||||||
url = "git+https://code.m3ta.dev/m3tam3re/AGENTS";
|
|
||||||
flake = false;
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
@@ -56,6 +56,13 @@
|
|||||||
inputs = inputs;
|
inputs = inputs;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Individual overlays for more granular control
|
||||||
|
additions = final: prev:
|
||||||
|
import ./pkgs {
|
||||||
|
pkgs = final;
|
||||||
|
inputs = inputs;
|
||||||
|
};
|
||||||
|
|
||||||
modifications = final: prev: import ./overlays/mods {inherit prev;};
|
modifications = final: prev: import ./overlays/mods {inherit prev;};
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -77,18 +84,18 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
# Library functions - helper utilities for your configuration
|
# Library functions - helper utilities for your configuration
|
||||||
lib = forAllSystems (system: import ./lib {lib = nixpkgs.lib;});
|
lib = forAllSystems (system: let
|
||||||
|
pkgs = pkgsFor system;
|
||||||
|
in
|
||||||
|
import ./lib {lib = pkgs.lib;});
|
||||||
|
|
||||||
# Development shells for various programming environments
|
# Development shells for various programming environments
|
||||||
# Usage: nix develop .#<shell-name>
|
# Usage: nix develop .#<shell-name>
|
||||||
# Available shells: default, python, devops, coding
|
# Available shells: default, python, devops, opencode
|
||||||
devShells = forAllSystems (system: let
|
devShells = forAllSystems (system: let
|
||||||
pkgs = pkgsFor system;
|
pkgs = pkgsFor system;
|
||||||
in
|
in
|
||||||
import ./shells {
|
import ./shells {inherit pkgs inputs;});
|
||||||
inherit pkgs inputs;
|
|
||||||
agents = inputs.agents;
|
|
||||||
});
|
|
||||||
|
|
||||||
# Formatter for 'nix fmt'
|
# Formatter for 'nix fmt'
|
||||||
formatter = forAllSystems (system: (pkgsFor system).alejandra);
|
formatter = forAllSystems (system: (pkgsFor system).alejandra);
|
||||||
@@ -104,15 +111,6 @@
|
|||||||
${pkgs.alejandra}/bin/alejandra --check ${./.}
|
${pkgs.alejandra}/bin/alejandra --check ${./.}
|
||||||
touch $out
|
touch $out
|
||||||
'';
|
'';
|
||||||
# Lib unit tests
|
|
||||||
lib-agents = import ./tests/lib/agents-test.nix {
|
|
||||||
inherit pkgs;
|
|
||||||
lib = pkgs.lib;
|
|
||||||
};
|
|
||||||
lib-coding-rules = import ./tests/lib/coding-rules-test.nix {
|
|
||||||
inherit pkgs;
|
|
||||||
lib = pkgs.lib;
|
|
||||||
};
|
|
||||||
});
|
});
|
||||||
|
|
||||||
# Templates for creating new packages/modules
|
# Templates for creating new packages/modules
|
||||||
|
|||||||
@@ -26,27 +26,6 @@
|
|||||||
pattern = lib.concatStringsSep ":" (lib.init parts);
|
pattern = lib.concatStringsSep ":" (lib.init parts);
|
||||||
in {inherit pattern action;};
|
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 = {
|
agentsLib = {
|
||||||
# ── loadCanonical ─────────────────────────────────────────────
|
# ── loadCanonical ─────────────────────────────────────────────
|
||||||
#
|
#
|
||||||
@@ -108,8 +87,20 @@
|
|||||||
|
|
||||||
mkAgentContent = name: agent:
|
mkAgentContent = name: agent:
|
||||||
(mkFrontmatter name agent) + agent.systemPrompt;
|
(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
|
in
|
||||||
renderAgentFiles pkgs canonical mkAgentContent "opencode-agents";
|
pkgs.runCommand "opencode-agents" {} ''
|
||||||
|
mkdir -p $out
|
||||||
|
${copyCommands}
|
||||||
|
'';
|
||||||
|
|
||||||
# ── Claude Code renderer ──────────────────────────────────────
|
# ── Claude Code renderer ──────────────────────────────────────
|
||||||
#
|
#
|
||||||
@@ -188,7 +179,10 @@
|
|||||||
mkClaudeAgentContent = name: agent:
|
mkClaudeAgentContent = name: agent:
|
||||||
(mkClaudeFrontmatter name agent) + agent.systemPrompt;
|
(mkClaudeFrontmatter name agent) + agent.systemPrompt;
|
||||||
|
|
||||||
agentFiles = renderAgentFiles pkgs canonical mkClaudeAgentContent "claude-code-agent-files";
|
mkClaudeAgentFile = name: agent:
|
||||||
|
pkgs.writeText "${name}.md" (mkClaudeAgentContent name agent);
|
||||||
|
|
||||||
|
agentFiles = lib.mapAttrs mkClaudeAgentFile canonical;
|
||||||
|
|
||||||
# Build settings.json with permission rules aggregated from all agents.
|
# Build settings.json with permission rules aggregated from all agents.
|
||||||
allAllows = lib.flatten (lib.mapAttrsToList (_: agent: renderPermAllow (agent.permissions or {})) canonical);
|
allAllows = lib.flatten (lib.mapAttrsToList (_: agent: renderPermAllow (agent.permissions or {})) canonical);
|
||||||
@@ -202,10 +196,14 @@
|
|||||||
};
|
};
|
||||||
|
|
||||||
settingsFile = pkgs.writeText "claude-settings.json" settingsJson;
|
settingsFile = pkgs.writeText "claude-settings.json" settingsJson;
|
||||||
|
|
||||||
|
copyAgentCommands = lib.concatStringsSep "\n" (
|
||||||
|
lib.mapAttrsToList (name: file: "cp ${file} $out/.claude/agents/${name}.md") agentFiles
|
||||||
|
);
|
||||||
in
|
in
|
||||||
pkgs.runCommand "claude-code-agents" {} ''
|
pkgs.runCommand "claude-code-agents" {} ''
|
||||||
mkdir -p $out/.claude/agents
|
mkdir -p $out/.claude/agents
|
||||||
cp -r ${agentFiles}/* $out/.claude/agents/
|
${copyAgentCommands}
|
||||||
cp ${settingsFile} $out/.claude/settings.json
|
cp ${settingsFile} $out/.claude/settings.json
|
||||||
'';
|
'';
|
||||||
|
|
||||||
@@ -225,10 +223,7 @@
|
|||||||
canonical,
|
canonical,
|
||||||
modelOverrides ? {},
|
modelOverrides ? {},
|
||||||
primaryAgent ? null,
|
primaryAgent ? null,
|
||||||
codingRules ? null,
|
|
||||||
}: let
|
}: 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).
|
# Find the primary agent (there should be exactly one).
|
||||||
primaryAgents = lib.filterAttrs (_: a: a.mode == "primary") canonical;
|
primaryAgents = lib.filterAttrs (_: a: a.mode == "primary") canonical;
|
||||||
primaryNames = lib.attrNames primaryAgents;
|
primaryNames = lib.attrNames primaryAgents;
|
||||||
@@ -297,7 +292,10 @@
|
|||||||
mkPiAgentContent = name: agent:
|
mkPiAgentContent = name: agent:
|
||||||
(mkPiFrontmatter name agent) + agent.systemPrompt;
|
(mkPiFrontmatter name agent) + agent.systemPrompt;
|
||||||
|
|
||||||
piAgentFiles = renderAgentFiles pkgs canonical mkPiAgentContent "pi-agent-files";
|
mkPiAgentFile = name: agent:
|
||||||
|
pkgs.writeText "${name}.md" (mkPiAgentContent name agent);
|
||||||
|
|
||||||
|
piAgentFiles = lib.mapAttrs mkPiAgentFile canonical;
|
||||||
|
|
||||||
# ── Build AGENTS.md content ───────────────────────────────────
|
# ── Build AGENTS.md content ───────────────────────────────────
|
||||||
primaryDn = primary.display_name or primaryName;
|
primaryDn = primary.display_name or primaryName;
|
||||||
@@ -308,19 +306,6 @@
|
|||||||
"- **" + dn + "**: " + agent.description;
|
"- **" + dn + "**: " + agent.description;
|
||||||
in
|
in
|
||||||
lib.mapAttrsToList mkEntry subagents;
|
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 =
|
agentsMd =
|
||||||
"# Agent Instructions\n"
|
"# Agent Instructions\n"
|
||||||
+ "\n"
|
+ "\n"
|
||||||
@@ -335,17 +320,20 @@
|
|||||||
if subagents == {}
|
if subagents == {}
|
||||||
then ""
|
then ""
|
||||||
else "## Available Specialists\n\n" + lib.concatStringsSep "\n" specialistEntries + "\n"
|
else "## Available Specialists\n\n" + lib.concatStringsSep "\n" specialistEntries + "\n"
|
||||||
)
|
);
|
||||||
+ codingRulesSection;
|
|
||||||
|
|
||||||
agentsMdFile = pkgs.writeText "AGENTS.md" agentsMd;
|
agentsMdFile = pkgs.writeText "AGENTS.md" agentsMd;
|
||||||
systemMdFile = pkgs.writeText "SYSTEM.md" primary.systemPrompt;
|
systemMdFile = pkgs.writeText "SYSTEM.md" primary.systemPrompt;
|
||||||
|
|
||||||
|
copyAgentCommands = lib.concatStringsSep "\n" (
|
||||||
|
lib.mapAttrsToList (name: file: "cp ${file} $out/agents/${name}.md") piAgentFiles
|
||||||
|
);
|
||||||
in
|
in
|
||||||
pkgs.runCommand "pi-agents" {} ''
|
pkgs.runCommand "pi-agents" {} ''
|
||||||
mkdir -p $out/agents
|
mkdir -p $out/agents
|
||||||
cp ${agentsMdFile} $out/AGENTS.md
|
cp ${agentsMdFile} $out/AGENTS.md
|
||||||
cp ${systemMdFile} $out/SYSTEM.md
|
cp ${systemMdFile} $out/SYSTEM.md
|
||||||
cp -r ${piAgentFiles}/* $out/agents/
|
${copyAgentCommands}
|
||||||
'';
|
'';
|
||||||
|
|
||||||
# ── renderForTool dispatcher ──────────────────────────────────
|
# ── renderForTool dispatcher ──────────────────────────────────
|
||||||
@@ -358,7 +346,6 @@
|
|||||||
agentsInput,
|
agentsInput,
|
||||||
tool,
|
tool,
|
||||||
modelOverrides ? {},
|
modelOverrides ? {},
|
||||||
codingRules ? null,
|
|
||||||
}: let
|
}: let
|
||||||
canonical = agentsInput.lib.loadAgents;
|
canonical = agentsInput.lib.loadAgents;
|
||||||
in
|
in
|
||||||
@@ -375,7 +362,7 @@
|
|||||||
else if tool == "pi"
|
else if tool == "pi"
|
||||||
then
|
then
|
||||||
agentsLib.renderForPi {
|
agentsLib.renderForPi {
|
||||||
inherit pkgs canonical modelOverrides codingRules;
|
inherit pkgs canonical modelOverrides;
|
||||||
}
|
}
|
||||||
else throw "lib.agents.renderForTool: unknown tool '${tool}'. Must be opencode, claude-code, or pi.";
|
else throw "lib.agents.renderForTool: unknown tool '${tool}'. Must be opencode, claude-code, or pi.";
|
||||||
|
|
||||||
@@ -399,10 +386,9 @@
|
|||||||
agentsInput,
|
agentsInput,
|
||||||
tool,
|
tool,
|
||||||
modelOverrides ? {},
|
modelOverrides ? {},
|
||||||
codingRules ? null,
|
|
||||||
}: let
|
}: let
|
||||||
rendered = agentsLib.renderForTool {
|
rendered = agentsLib.renderForTool {
|
||||||
inherit pkgs agentsInput tool modelOverrides codingRules;
|
inherit pkgs agentsInput tool modelOverrides;
|
||||||
};
|
};
|
||||||
in
|
in
|
||||||
if tool == "opencode"
|
if tool == "opencode"
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
# Coding rules management utilities
|
# Opencode rules management utilities
|
||||||
#
|
#
|
||||||
# This module provides functions to configure Opencode agent rules across
|
# This module provides functions to configure Opencode agent rules across
|
||||||
# multiple projects. Rules are defined in the AGENTS repository and can be
|
# multiple projects. Rules are defined in the AGENTS repository and can be
|
||||||
@@ -26,8 +26,7 @@
|
|||||||
#
|
#
|
||||||
# The shellHook creates:
|
# The shellHook creates:
|
||||||
# - A `.opencode-rules/` symlink pointing to the AGENTS repository rules directory
|
# - A `.opencode-rules/` symlink pointing to the AGENTS repository rules directory
|
||||||
# - A `coding-rules.json` file with a $schema reference and instructions list
|
# - An `opencode.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
|
# The instructions list contains paths relative to the project root, all prefixed
|
||||||
# with `.opencode-rules/`, making them portable across different project locations.
|
# with `.opencode-rules/`, making them portable across different project locations.
|
||||||
@@ -44,13 +43,10 @@
|
|||||||
# (e.g., [ "react" "fastapi" "django" ])
|
# (e.g., [ "react" "fastapi" "django" ])
|
||||||
# extraInstructions: Optional list of additional instruction paths
|
# extraInstructions: Optional list of additional instruction paths
|
||||||
# (for custom rules outside standard locations)
|
# (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:
|
# Returns:
|
||||||
# An attribute set containing:
|
# An attribute set containing:
|
||||||
# - shellHook: Bash code to create symlink and coding-rules.json
|
# - shellHook: Bash code to create symlink and opencode.json
|
||||||
# - instructions: List of rule file paths (relative to project root)
|
# - instructions: List of rule file paths (relative to project root)
|
||||||
#
|
#
|
||||||
# Example:
|
# Example:
|
||||||
@@ -86,9 +82,9 @@
|
|||||||
],
|
],
|
||||||
frameworks ? [],
|
frameworks ? [],
|
||||||
extraInstructions ? [],
|
extraInstructions ? [],
|
||||||
rulesDir ? ".opencode-rules",
|
|
||||||
forPi ? false,
|
|
||||||
}: let
|
}: let
|
||||||
|
rulesDir = ".opencode-rules";
|
||||||
|
|
||||||
# Build instructions list by mapping concerns, languages, frameworks to their file paths
|
# Build instructions list by mapping concerns, languages, frameworks to their file paths
|
||||||
# All paths are relative to project root via the rulesDir symlink
|
# All paths are relative to project root via the rulesDir symlink
|
||||||
instructions =
|
instructions =
|
||||||
@@ -97,137 +93,29 @@
|
|||||||
++ (map (f: "${rulesDir}/frameworks/${f}.md") frameworks)
|
++ (map (f: "${rulesDir}/frameworks/${f}.md") frameworks)
|
||||||
++ extraInstructions;
|
++ extraInstructions;
|
||||||
|
|
||||||
# Generate JSON configuration for coding rules
|
# Generate JSON configuration for Opencode
|
||||||
rulesConfig = {
|
opencodeConfig = {
|
||||||
"$schema" = "https://opencode.ai/config.json";
|
"$schema" = "https://opencode.ai/config.json";
|
||||||
inherit instructions;
|
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 {
|
in {
|
||||||
inherit instructions;
|
inherit instructions;
|
||||||
|
|
||||||
# Shell hook to set up rules in the project
|
# Shell hook to set up rules in the project
|
||||||
# Creates a symlink to the AGENTS rules directory and generates coding-rules.json
|
# Creates a symlink to the AGENTS rules directory and generates opencode.json
|
||||||
# Optionally appends rules to AGENTS.md for Pi agent discovery
|
|
||||||
shellHook = ''
|
shellHook = ''
|
||||||
# Create/update symlink to AGENTS rules directory
|
# Create/update symlink to AGENTS rules directory
|
||||||
ln -sfn ${agents}/rules ${rulesDir}
|
ln -sfn ${agents}/rules ${rulesDir}
|
||||||
|
|
||||||
# Generate coding-rules.json configuration file
|
# Generate opencode.json configuration file
|
||||||
cat > coding-rules.json <<'RULES_EOF'
|
cat > opencode.json <<'OPENCODE_EOF'
|
||||||
${builtins.toJSON rulesConfig}
|
${builtins.toJSON opencodeConfig}
|
||||||
RULES_EOF
|
OPENCODE_EOF
|
||||||
|
|
||||||
${piShellHook}
|
|
||||||
'';
|
'';
|
||||||
};
|
};
|
||||||
# Concatenate selected rule files from the AGENTS repository into a single
|
|
||||||
# markdown string. Used by Pi (append to AGENTS.md) and could be used by
|
|
||||||
# other tools that don't support an instructions list.
|
|
||||||
#
|
|
||||||
# Args:
|
|
||||||
# agents: Path to the AGENTS repository (non-flake input)
|
|
||||||
# languages: Optional list of language-specific rules to include
|
|
||||||
# concerns: Optional list of concern rules to include
|
|
||||||
# Default: [ "coding-style" "naming" "documentation" "testing" "git-workflow" "project-structure" ]
|
|
||||||
# frameworks: Optional list of framework-specific rules to include
|
|
||||||
#
|
|
||||||
# Returns: A single concatenated markdown string with all selected rules.
|
|
||||||
#
|
|
||||||
# Example:
|
|
||||||
# concatRulesMd {
|
|
||||||
# agents = inputs.agents;
|
|
||||||
# languages = [ "python" ];
|
|
||||||
# concerns = [ "coding-style" ];
|
|
||||||
# }
|
|
||||||
# # Returns: "\n# Coding Style\n\n...python rules...\n"
|
|
||||||
concatRulesMd = {
|
|
||||||
agents,
|
|
||||||
languages ? [],
|
|
||||||
concerns ? [
|
|
||||||
"coding-style"
|
|
||||||
"naming"
|
|
||||||
"documentation"
|
|
||||||
"testing"
|
|
||||||
"git-workflow"
|
|
||||||
"project-structure"
|
|
||||||
],
|
|
||||||
frameworks ? [],
|
|
||||||
}: let
|
|
||||||
rulePaths =
|
|
||||||
(map (c: {
|
|
||||||
kind = "concerns";
|
|
||||||
name = c;
|
|
||||||
})
|
|
||||||
concerns)
|
|
||||||
++ (map (l: {
|
|
||||||
kind = "languages";
|
|
||||||
name = l;
|
|
||||||
})
|
|
||||||
languages)
|
|
||||||
++ (map (f: {
|
|
||||||
kind = "frameworks";
|
|
||||||
name = f;
|
|
||||||
})
|
|
||||||
frameworks);
|
|
||||||
|
|
||||||
readRule = rule: builtins.readFile "${agents}/rules/${rule.kind}/${rule.name}.md";
|
# Backward-compat alias
|
||||||
ruleContents = map readRule rulePaths;
|
mkOpencodeRules = mkCodingRules;
|
||||||
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 {
|
in {
|
||||||
inherit mkCodingRules concatRulesMd mkRulesMdSection;
|
inherit mkCodingRules mkOpencodeRules;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,9 +7,12 @@
|
|||||||
# Port management utilities
|
# Port management utilities
|
||||||
ports = import ./ports.nix {inherit lib;};
|
ports = import ./ports.nix {inherit lib;};
|
||||||
|
|
||||||
# Coding rules injection utilities
|
# Coding rules injection utilities (renamed from opencode-rules)
|
||||||
coding-rules = import ./coding-rules.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;};
|
||||||
|
|
||||||
# Agent configuration management utilities
|
# Agent configuration management utilities
|
||||||
agents = import ./agents.nix {inherit lib;};
|
agents = import ./agents.nix {inherit lib;};
|
||||||
}
|
}
|
||||||
|
|||||||
116
lib/opencode-rules.nix
Normal file
116
lib/opencode-rules.nix
Normal file
@@ -0,0 +1,116 @@
|
|||||||
|
# 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,4 +95,19 @@
|
|||||||
# List of service names (strings)
|
# List of service names (strings)
|
||||||
listServices = lib.attrNames ports;
|
listServices = lib.attrNames ports;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Simple helper to get a port without host override
|
||||||
|
# Useful when you don't need host-specific ports
|
||||||
|
#
|
||||||
|
# Args:
|
||||||
|
# portsConfig: Same structure as mkPortHelpers
|
||||||
|
# service: The service name (string)
|
||||||
|
#
|
||||||
|
# Returns:
|
||||||
|
# Port number (int) or null if service not found
|
||||||
|
#
|
||||||
|
# Example:
|
||||||
|
# getDefaultPort myPorts "nginx" # Returns default port only
|
||||||
|
getDefaultPort = portsConfig: service:
|
||||||
|
portsConfig.ports.${service} or null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,7 +163,7 @@ The agent system was migrated from embedded `agents.json` to file-based canonica
|
|||||||
| `coding.opencode.externalSkills` | `coding.agents.opencode.externalSkills` |
|
| `coding.opencode.externalSkills` | `coding.agents.opencode.externalSkills` |
|
||||||
| Agents embedded in `config.json` | File-based `~/.config/opencode/agents/*.md` |
|
| Agents embedded in `config.json` | File-based `~/.config/opencode/agents/*.md` |
|
||||||
| Model hardcoded in `agents.json` | Per-machine `modelOverrides` |
|
| Model hardcoded in `agents.json` | Per-machine `modelOverrides` |
|
||||||
| `mkOpencodeRules` | `mkCodingRules` |
|
| `mkOpencodeRules` | `mkCodingRules` (old name still works) |
|
||||||
|
|
||||||
### Migration steps
|
### Migration steps
|
||||||
|
|
||||||
|
|||||||
@@ -3,92 +3,88 @@
|
|||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}: {
|
}:
|
||||||
imports = [
|
with lib; let
|
||||||
./shared/default.nix
|
cfg = config.coding.agents.claude-code;
|
||||||
];
|
mcpCfg = config.programs.mcp or null;
|
||||||
|
in {
|
||||||
|
options.coding.agents.claude-code = {
|
||||||
|
enable = mkEnableOption "Claude Code agent management via canonical agent.toml definitions";
|
||||||
|
|
||||||
options.coding.agents.claude-code = let
|
agentsInput = mkOption {
|
||||||
shared = import ./shared/shared-options.nix {inherit lib;};
|
type = types.nullOr types.anything;
|
||||||
mcpCfg = config.programs.mcp or null;
|
default = null;
|
||||||
in
|
description = ''
|
||||||
with lib; {
|
|
||||||
enable = mkEnableOption "Claude Code agent management via canonical agent.toml definitions";
|
|
||||||
|
|
||||||
agentsInput = shared.mkAgentsInputOption ''
|
|
||||||
The `agents` flake input (your personal AGENTS repo).
|
The `agents` flake input (your personal AGENTS repo).
|
||||||
When set, agents are rendered from canonical agent.toml files
|
When set, agents are rendered from canonical agent.toml files
|
||||||
and symlinked to ~/.claude/agents/.
|
and symlinked to ~/.claude/agents/.
|
||||||
'';
|
'';
|
||||||
|
|
||||||
modelOverrides = shared.mkModelOverridesOption;
|
|
||||||
|
|
||||||
externalSkills = shared.externalSkillsOption;
|
|
||||||
|
|
||||||
mcpServers = mkOption {
|
|
||||||
type = types.attrsOf types.anything;
|
|
||||||
default =
|
|
||||||
if mcpCfg != null
|
|
||||||
then mcpCfg.servers
|
|
||||||
else {};
|
|
||||||
defaultText = literalExpression "config.programs.mcp.servers";
|
|
||||||
description = ''
|
|
||||||
MCP server configurations for Claude Code.
|
|
||||||
Merged into ~/.claude/settings.json alongside permissions.
|
|
||||||
Automatically inherits from config.programs.mcp.servers.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = with lib; let
|
modelOverrides = mkOption {
|
||||||
shared = import ./shared/shared-options.nix {inherit lib;};
|
type = types.attrsOf types.str;
|
||||||
cfg = config.coding.agents.claude-code;
|
default = {};
|
||||||
agentsLib = (import ../../../../lib {inherit lib;}).agents;
|
description = ''
|
||||||
in
|
Per-agent model overrides. Maps agent slug to model alias or ID.
|
||||||
mkIf cfg.enable (let
|
Example: { chiron = "claude-sonnet-4-20250514"; }
|
||||||
# Rendered agents + permissions (only if agentsInput is set)
|
'';
|
||||||
rendered = mkIf (cfg.agentsInput != null) (
|
example = literalExpression ''
|
||||||
agentsLib.renderForClaudeCode {
|
{
|
||||||
inherit pkgs;
|
chiron = "claude-sonnet-4-20250514";
|
||||||
canonical = cfg.agentsInput.lib.loadAgents;
|
"chiron-forge" = "claude-sonnet-4-20250514";
|
||||||
modelOverrides = cfg.modelOverrides;
|
|
||||||
}
|
}
|
||||||
);
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
# Merge MCP servers into the rendered settings.json.
|
mcpServers = mkOption {
|
||||||
# The renderer produces { permissions: { allow, deny } }.
|
type = types.attrsOf types.anything;
|
||||||
# We add mcpServers on top.
|
default = if mcpCfg != null then mcpCfg.servers else {};
|
||||||
settingsJson =
|
defaultText = literalExpression "config.programs.mcp.servers";
|
||||||
if cfg.agentsInput != null
|
description = ''
|
||||||
then let
|
MCP server configurations for Claude Code.
|
||||||
renderedSettings = builtins.fromJSON (builtins.readFile "${rendered}/.claude/settings.json");
|
Merged into ~/.claude/settings.json alongside permissions.
|
||||||
withMcp =
|
Automatically inherits from config.programs.mcp.servers.
|
||||||
if cfg.mcpServers != {}
|
'';
|
||||||
then renderedSettings // {mcpServers = cfg.mcpServers;}
|
};
|
||||||
else renderedSettings;
|
};
|
||||||
in
|
|
||||||
pkgs.writeText "claude-settings.json" (builtins.toJSON withMcp)
|
|
||||||
else if cfg.mcpServers != {}
|
|
||||||
then pkgs.writeText "claude-settings.json" (builtins.toJSON {mcpServers = cfg.mcpServers;})
|
|
||||||
else null;
|
|
||||||
in {
|
|
||||||
# Rendered agent files symlinked to ~/.claude/agents/
|
|
||||||
home.file.".claude/agents" = mkIf (cfg.agentsInput != null) {
|
|
||||||
source = "${rendered}/.claude/agents";
|
|
||||||
};
|
|
||||||
|
|
||||||
# Skills (merged from personal AGENTS repo + optional external skills)
|
config = mkIf cfg.enable (let
|
||||||
home.file.".claude/skills" = mkIf (cfg.agentsInput != null) {
|
agentsLib = (import ../../../../lib {inherit lib;}).agents;
|
||||||
source = cfg.agentsInput.lib.mkOpencodeSkills {
|
|
||||||
inherit pkgs;
|
|
||||||
customSkills = "${cfg.agentsInput}/skills";
|
|
||||||
externalSkills = shared.mapExternalSkills cfg.externalSkills;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
# Rendered settings.json with permissions + MCP servers
|
# Rendered agents + permissions (only if agentsInput is set)
|
||||||
home.file.".claude/settings.json" = mkIf (settingsJson != null) {
|
rendered = mkIf (cfg.agentsInput != null) (
|
||||||
source = "${settingsJson}";
|
agentsLib.renderForClaudeCode {
|
||||||
};
|
inherit pkgs;
|
||||||
});
|
canonical = cfg.agentsInput.lib.loadAgents;
|
||||||
|
modelOverrides = cfg.modelOverrides;
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
# Merge MCP servers into the rendered settings.json.
|
||||||
|
# The renderer produces { permissions: { allow, deny } }.
|
||||||
|
# We add mcpServers on top.
|
||||||
|
settingsJson =
|
||||||
|
if cfg.agentsInput != null
|
||||||
|
then let
|
||||||
|
renderedSettings = builtins.fromJSON (builtins.readFile "${rendered}/.claude/settings.json");
|
||||||
|
withMcp =
|
||||||
|
if cfg.mcpServers != {}
|
||||||
|
then renderedSettings // {mcpServers = cfg.mcpServers;}
|
||||||
|
else renderedSettings;
|
||||||
|
in
|
||||||
|
pkgs.writeText "claude-settings.json" (builtins.toJSON withMcp)
|
||||||
|
else if cfg.mcpServers != {}
|
||||||
|
then pkgs.writeText "claude-settings.json" (builtins.toJSON {mcpServers = cfg.mcpServers;})
|
||||||
|
else null;
|
||||||
|
in {
|
||||||
|
# Rendered agent files symlinked to ~/.claude/agents/
|
||||||
|
home.file.".claude/agents" = mkIf (cfg.agentsInput != null) {
|
||||||
|
source = "${rendered}/.claude/agents";
|
||||||
|
};
|
||||||
|
|
||||||
|
# Rendered settings.json with permissions + MCP servers
|
||||||
|
home.file.".claude/settings.json" = mkIf (settingsJson != null) {
|
||||||
|
source = "${settingsJson}";
|
||||||
|
};
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,39 +1,10 @@
|
|||||||
# Per-tool agent sub-modules
|
# Per-tool agent sub-modules
|
||||||
# Each module handles rendering canonical agent.toml definitions
|
# Each module handles rendering canonical agent.toml definitions
|
||||||
# for a specific AI coding tool.
|
# 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 = [
|
imports = [
|
||||||
./opencode.nix
|
./opencode.nix
|
||||||
./claude-code.nix
|
./claude-code.nix
|
||||||
./pi.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,52 +3,111 @@
|
|||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}: {
|
}:
|
||||||
imports = [
|
with lib; let
|
||||||
./shared/default.nix
|
cfg = config.coding.agents.opencode;
|
||||||
];
|
in {
|
||||||
|
options.coding.agents.opencode = {
|
||||||
|
enable = mkEnableOption "OpenCode agent management via canonical agent.toml definitions";
|
||||||
|
|
||||||
options.coding.agents.opencode = let
|
agentsInput = mkOption {
|
||||||
shared = import ./shared/shared-options.nix {inherit lib;};
|
type = types.nullOr types.anything;
|
||||||
in
|
default = null;
|
||||||
with lib; {
|
description = ''
|
||||||
enable = mkEnableOption "OpenCode agent management via canonical agent.toml definitions";
|
|
||||||
|
|
||||||
agentsInput = shared.mkAgentsInputOption ''
|
|
||||||
The `agents` flake input (your personal AGENTS repo).
|
The `agents` flake input (your personal AGENTS repo).
|
||||||
When set, agents are rendered from canonical agent.toml files
|
When set, agents are rendered from canonical agent.toml files
|
||||||
and symlinked to ~/.config/opencode/agents/.
|
and symlinked to ~/.config/opencode/agents/.
|
||||||
'';
|
'';
|
||||||
|
|
||||||
modelOverrides = shared.mkModelOverridesOption;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = with lib; let
|
modelOverrides = mkOption {
|
||||||
shared = import ./shared/shared-options.nix {inherit lib;};
|
type = types.attrsOf types.str;
|
||||||
cfg = config.coding.agents.opencode;
|
default = {};
|
||||||
in
|
description = ''
|
||||||
mkIf cfg.enable {
|
Per-agent model overrides. Maps agent slug to model string.
|
||||||
# Rendered agent files symlinked to ~/.config/opencode/agents/
|
Example: { chiron = "anthropic/claude-sonnet-4"; }
|
||||||
xdg.configFile."opencode/agents" = let
|
'';
|
||||||
agentsLib = (import ../../../../lib {inherit lib;}).agents;
|
example = literalExpression ''
|
||||||
in
|
{
|
||||||
mkIf (cfg.agentsInput != null) {
|
chiron = "anthropic/claude-sonnet-4";
|
||||||
source = agentsLib.renderForOpencode {
|
"chiron-forge" = "anthropic/claude-sonnet-4";
|
||||||
inherit pkgs;
|
}
|
||||||
canonical = cfg.agentsInput.lib.loadAgents;
|
'';
|
||||||
modelOverrides = cfg.modelOverrides;
|
};
|
||||||
|
|
||||||
|
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; }
|
||||||
|
]
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
# Static config dirs from AGENTS repo
|
config = mkIf cfg.enable {
|
||||||
xdg.configFile."opencode/context" = mkIf (cfg.agentsInput != null) {
|
# Rendered agent files symlinked to ~/.config/opencode/agents/
|
||||||
source = "${cfg.agentsInput}/context";
|
xdg.configFile."opencode/agents" = mkIf (cfg.agentsInput != null) {
|
||||||
};
|
source = (import ../../../../lib {inherit lib;}).agents.renderForOpencode {
|
||||||
xdg.configFile."opencode/commands" = mkIf (cfg.agentsInput != null) {
|
inherit pkgs;
|
||||||
source = "${cfg.agentsInput}/commands";
|
canonical = cfg.agentsInput.lib.loadAgents;
|
||||||
};
|
modelOverrides = cfg.modelOverrides;
|
||||||
xdg.configFile."opencode/prompts" = mkIf (cfg.agentsInput != null) {
|
|
||||||
source = "${cfg.agentsInput}/prompts";
|
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# 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";
|
||||||
|
};
|
||||||
|
xdg.configFile."opencode/commands" = mkIf (cfg.agentsInput != null) {
|
||||||
|
source = "${cfg.agentsInput}/commands";
|
||||||
|
};
|
||||||
|
xdg.configFile."opencode/prompts" = mkIf (cfg.agentsInput != null) {
|
||||||
|
source = "${cfg.agentsInput}/prompts";
|
||||||
|
};
|
||||||
|
};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,355 +3,546 @@
|
|||||||
lib,
|
lib,
|
||||||
pkgs,
|
pkgs,
|
||||||
...
|
...
|
||||||
}: {
|
}:
|
||||||
imports = [
|
with lib; let
|
||||||
./shared/default.nix
|
cfg = config.coding.agents.pi;
|
||||||
];
|
mcpCfg = config.programs.mcp or null;
|
||||||
|
|
||||||
options.coding.agents.pi = let
|
hasPiPackage = pkgs ? pi;
|
||||||
shared = import ./shared/shared-options.nix {inherit lib;};
|
|
||||||
mcpCfg = config.programs.mcp or null;
|
|
||||||
in
|
|
||||||
with lib; {
|
|
||||||
enable = mkEnableOption "Pi agent management via canonical agent.toml definitions";
|
|
||||||
|
|
||||||
mcpServers = mkOption {
|
defaultPiImageArchive =
|
||||||
type = types.attrsOf types.anything;
|
if hasPiPackage
|
||||||
default =
|
then
|
||||||
if mcpCfg != null
|
pkgs.dockerTools.buildLayeredImage {
|
||||||
then mcpCfg.servers
|
name = "pi-agent";
|
||||||
else {};
|
tag = "latest";
|
||||||
defaultText = literalExpression "config.programs.mcp.servers";
|
contents = with pkgs; [
|
||||||
description = ''
|
bashInteractive
|
||||||
MCP server configurations for Pi (pi-mcp-adapter).
|
bun
|
||||||
Written to ~/.pi/agent/mcp.json.
|
cacert
|
||||||
Automatically inherits from config.programs.mcp.servers.
|
coreutils
|
||||||
'';
|
findutils
|
||||||
|
git
|
||||||
|
gnugrep
|
||||||
|
gnused
|
||||||
|
nix
|
||||||
|
nodejs
|
||||||
|
pi
|
||||||
|
];
|
||||||
|
config = {
|
||||||
|
Env = [
|
||||||
|
"PATH=/bin:/usr/bin"
|
||||||
|
"NIX_REMOTE=daemon"
|
||||||
|
"SSL_CERT_FILE=${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt"
|
||||||
|
];
|
||||||
|
WorkingDir = "/tmp";
|
||||||
|
Cmd = ["${pkgs.coreutils}/bin/sleep" "infinity"];
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else null;
|
||||||
|
in {
|
||||||
|
options.coding.agents.pi = {
|
||||||
|
enable = mkEnableOption "Pi agent management via canonical agent.toml definitions";
|
||||||
|
|
||||||
|
container = mkOption {
|
||||||
|
description = "Run Pi through a rootless Podman container while keeping a native host UX.";
|
||||||
|
default = {};
|
||||||
|
type = types.submodule {
|
||||||
|
options = {
|
||||||
|
enable = mkEnableOption "Containerized Pi wrapper";
|
||||||
|
|
||||||
|
name = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = "pi-agent";
|
||||||
|
description = "Container name used by the Pi wrapper.";
|
||||||
|
};
|
||||||
|
|
||||||
|
image = mkOption {
|
||||||
|
type = types.str;
|
||||||
|
default = if hasPiPackage then "pi-agent:latest" else "docker.io/nixos/nix:latest";
|
||||||
|
description = ''
|
||||||
|
Podman image to run for Pi.
|
||||||
|
Defaults to a local declarative Pi-ready image when `pkgs.pi` exists,
|
||||||
|
otherwise falls back to docker.io/nixos/nix:latest.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
imageArchive = mkOption {
|
||||||
|
type = types.nullOr types.path;
|
||||||
|
default = defaultPiImageArchive;
|
||||||
|
description = ''
|
||||||
|
Optional OCI/Docker archive path to load into Podman when `image`
|
||||||
|
is missing locally. By default, a Pi-ready local image archive is
|
||||||
|
generated when `pkgs.pi` is available.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
projectRoots = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
Allowlisted absolute host roots that may be mounted into the container.
|
||||||
|
Wrapper exits with a clear error when cwd is outside these roots.
|
||||||
|
'';
|
||||||
|
example = ["/home/m3tam3re/p"];
|
||||||
|
};
|
||||||
|
|
||||||
|
autoStart = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = true;
|
||||||
|
description = "Automatically start container when wrapper is invoked and it is not running.";
|
||||||
|
};
|
||||||
|
|
||||||
|
autoNixDevelop = mkOption {
|
||||||
|
type = types.bool;
|
||||||
|
default = false;
|
||||||
|
description = ''
|
||||||
|
If true and cwd contains flake.nix, run Pi as:
|
||||||
|
nix develop -c pi ...
|
||||||
|
inside the container.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
extraRunArgs = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = "Additional Podman create args appended after safe defaults.";
|
||||||
|
};
|
||||||
|
|
||||||
|
extraEnv = mkOption {
|
||||||
|
type = types.attrsOf types.str;
|
||||||
|
default = {};
|
||||||
|
description = "Extra environment variables passed to the container.";
|
||||||
|
};
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
};
|
||||||
|
|
||||||
agentsInput = shared.mkAgentsInputOption ''
|
mcpServers = mkOption {
|
||||||
|
type = types.attrsOf types.anything;
|
||||||
|
default = if mcpCfg != null then mcpCfg.servers else {};
|
||||||
|
defaultText = literalExpression "config.programs.mcp.servers";
|
||||||
|
description = ''
|
||||||
|
MCP server configurations for Pi (pi-mcp-adapter).
|
||||||
|
Written to ~/.pi/agent/mcp.json.
|
||||||
|
Automatically inherits from config.programs.mcp.servers.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
|
agentsInput = mkOption {
|
||||||
|
type = types.nullOr types.anything;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
The `agents` flake input (your personal AGENTS repo).
|
The `agents` flake input (your personal AGENTS repo).
|
||||||
When set, the primary agent's system prompt is rendered as SYSTEM.md,
|
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.
|
all agents are listed in AGENTS.md, and subagent .md files are deployed.
|
||||||
'';
|
'';
|
||||||
|
|
||||||
modelOverrides = shared.mkModelOverridesOption;
|
|
||||||
|
|
||||||
primaryAgent = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
Override which canonical agent is used as primary for SYSTEM.md.
|
|
||||||
When null, the first agent with mode="primary" is used.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
codingRules = mkOption {
|
|
||||||
type = types.nullOr (types.submodule {
|
|
||||||
options = {
|
|
||||||
languages = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [];
|
|
||||||
description = ''
|
|
||||||
Language-specific coding rules to include
|
|
||||||
(e.g. [ "python" "typescript" "nix" ]).
|
|
||||||
Rule files are read from the AGENTS repo's rules/languages/ directory.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
concerns = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [
|
|
||||||
"coding-style"
|
|
||||||
"naming"
|
|
||||||
"documentation"
|
|
||||||
"testing"
|
|
||||||
"git-workflow"
|
|
||||||
"project-structure"
|
|
||||||
];
|
|
||||||
description = ''
|
|
||||||
Concern rules to include from the AGENTS repo's rules/concerns/ directory.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
frameworks = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [];
|
|
||||||
description = ''
|
|
||||||
Framework-specific coding rules to include
|
|
||||||
(e.g. [ "react" "fastapi" ]).
|
|
||||||
Rule files are read from the AGENTS repo's rules/frameworks/ directory.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
});
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
Coding rules to inject into ~/.pi/agent/AGENTS.md.
|
|
||||||
Rules are read from the AGENTS repository and appended as markdown sections.
|
|
||||||
Requires agentsInput to be set.
|
|
||||||
'';
|
|
||||||
example = literalExpression ''
|
|
||||||
{
|
|
||||||
languages = [ "python" "typescript" ];
|
|
||||||
concerns = [ "coding-style" "testing" ];
|
|
||||||
frameworks = [ "fastapi" ];
|
|
||||||
}
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
settings = mkOption {
|
|
||||||
type = types.submodule {
|
|
||||||
freeformType = types.attrsOf types.anything;
|
|
||||||
options = {
|
|
||||||
packages = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [];
|
|
||||||
description = ''
|
|
||||||
Pi packages to install (npm:, git:, or local paths).
|
|
||||||
These are written to ~/.pi/agent/settings.json.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
defaultProvider = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = "Default LLM provider (e.g. 'anthropic', 'openai', 'zai').";
|
|
||||||
};
|
|
||||||
|
|
||||||
defaultModel = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = "Default model ID.";
|
|
||||||
};
|
|
||||||
|
|
||||||
defaultThinkingLevel = mkOption {
|
|
||||||
type = types.nullOr (types.enum ["off" "minimal" "low" "medium" "high" "xhigh"]);
|
|
||||||
default = null;
|
|
||||||
description = "Default extended thinking level.";
|
|
||||||
};
|
|
||||||
|
|
||||||
theme = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = "Pi theme name.";
|
|
||||||
};
|
|
||||||
|
|
||||||
hideThinkingBlock = mkOption {
|
|
||||||
type = types.nullOr types.bool;
|
|
||||||
default = null;
|
|
||||||
description = "Hide thinking blocks in output.";
|
|
||||||
};
|
|
||||||
|
|
||||||
quietStartup = mkOption {
|
|
||||||
type = types.nullOr types.bool;
|
|
||||||
default = null;
|
|
||||||
description = "Hide startup header.";
|
|
||||||
};
|
|
||||||
|
|
||||||
compaction = mkOption {
|
|
||||||
type = types.nullOr (types.submodule {
|
|
||||||
options = {
|
|
||||||
enabled = mkOption {
|
|
||||||
type = types.nullOr types.bool;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
reserveTokens = mkOption {
|
|
||||||
type = types.nullOr types.int;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
keepRecentTokens = mkOption {
|
|
||||||
type = types.nullOr types.int;
|
|
||||||
default = null;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
});
|
|
||||||
default = null;
|
|
||||||
description = "Auto-compaction settings.";
|
|
||||||
};
|
|
||||||
|
|
||||||
enabledModels = mkOption {
|
|
||||||
type = types.nullOr (types.listOf types.str);
|
|
||||||
default = null;
|
|
||||||
description = "Model patterns for Ctrl+P cycling.";
|
|
||||||
};
|
|
||||||
|
|
||||||
sessionDir = mkOption {
|
|
||||||
type = types.nullOr types.str;
|
|
||||||
default = null;
|
|
||||||
description = "Directory where session files are stored.";
|
|
||||||
};
|
|
||||||
|
|
||||||
extensions = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [];
|
|
||||||
description = "Local extension file paths or directories.";
|
|
||||||
};
|
|
||||||
|
|
||||||
skills = mkOption {
|
|
||||||
type = types.listOf types.str;
|
|
||||||
default = [];
|
|
||||||
description = "Local skill file paths or directories.";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
};
|
|
||||||
default = {};
|
|
||||||
description = ''
|
|
||||||
Pi settings written to ~/.pi/agent/settings.json.
|
|
||||||
Only non-null values are included in the generated JSON.
|
|
||||||
See pi docs/settings.md for all options.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
|
|
||||||
# ── Pi Guardrails ─────────────────────────────────────────────
|
|
||||||
guardrails = mkOption {
|
|
||||||
type = types.nullOr (types.submodule {
|
|
||||||
options = {
|
|
||||||
enable =
|
|
||||||
mkEnableOption
|
|
||||||
("Generate ~/.pi/agent/extensions/guardrails.json for pi-guardrails. "
|
|
||||||
+ "Adds @aliou/pi-guardrails to packages automatically.");
|
|
||||||
|
|
||||||
config = mkOption {
|
|
||||||
type = types.attrsOf types.anything;
|
|
||||||
default = {};
|
|
||||||
description = ''
|
|
||||||
Guardrails configuration written to ~/.pi/agent/extensions/guardrails.json.
|
|
||||||
See https://github.com/aliou/pi-guardrails for config schema.
|
|
||||||
|
|
||||||
IMPORTANT: Path access checks are lexical (not symlink-safe).
|
|
||||||
Local project .pi/extensions/guardrails.json can override same rule IDs
|
|
||||||
(memory > local > global > defaults). For immutable global policies,
|
|
||||||
consider a wrapper or upstream patch.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
|
||||||
});
|
|
||||||
default = null;
|
|
||||||
description = ''
|
|
||||||
Pi Guardrails security configuration.
|
|
||||||
Generates ~/.pi/agent/extensions/guardrails.json when enabled.
|
|
||||||
The @aliou/pi-guardrails package is added to settings.packages automatically.
|
|
||||||
'';
|
|
||||||
};
|
|
||||||
};
|
};
|
||||||
|
|
||||||
config = with lib; let
|
modelOverrides = mkOption {
|
||||||
shared = import ./shared/shared-options.nix {inherit lib;};
|
type = types.attrsOf types.str;
|
||||||
cfg = config.coding.agents.pi;
|
default = {};
|
||||||
in
|
description = ''
|
||||||
mkIf cfg.enable (let
|
Per-agent model overrides for Pi subagents.
|
||||||
# Build settings.json by filtering out null values recursively
|
Maps agent slug to model string, e.g.:
|
||||||
filterNulls = attrs:
|
{ chiron = "anthropic/claude-sonnet-4"; chiron-forge = "anthropic/claude-sonnet-4"; }
|
||||||
lib.filterAttrs (_: v: v != null) (
|
'';
|
||||||
builtins.mapAttrs (_: v:
|
};
|
||||||
if builtins.isAttrs v
|
|
||||||
then let
|
|
||||||
filtered = filterNulls v;
|
|
||||||
in
|
|
||||||
if filtered == {}
|
|
||||||
then null
|
|
||||||
else filtered
|
|
||||||
else v)
|
|
||||||
attrs
|
|
||||||
);
|
|
||||||
|
|
||||||
# Base settings (already filtered)
|
primaryAgent = mkOption {
|
||||||
piSettings = filterNulls cfg.settings;
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = ''
|
||||||
|
Override which canonical agent is used as primary for SYSTEM.md.
|
||||||
|
When null, the first agent with mode="primary" is used.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
# Guardrails package to inject when guardrails is enabled
|
settings = mkOption {
|
||||||
guardrailsPackage = "npm:@aliou/pi-guardrails@0.11.1";
|
type = types.submodule {
|
||||||
|
freeformType = types.attrsOf types.anything;
|
||||||
|
options = {
|
||||||
|
packages = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = ''
|
||||||
|
Pi packages to install (npm:, git:, or local paths).
|
||||||
|
These are written to ~/.pi/agent/settings.json.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
|
||||||
# Guardrails config (only when guardrails is enabled)
|
defaultProvider = mkOption {
|
||||||
guardrailsJson =
|
type = types.nullOr types.str;
|
||||||
if (cfg.guardrails != null && cfg.guardrails.enable)
|
default = null;
|
||||||
then builtins.toJSON cfg.guardrails.config
|
description = "Default LLM provider (e.g. 'anthropic', 'openai', 'zai').";
|
||||||
else null;
|
};
|
||||||
|
|
||||||
# Merge guardrails package into settings.packages when guardrails is enabled
|
defaultModel = mkOption {
|
||||||
piSettingsWithGuardrails = let
|
type = types.nullOr types.str;
|
||||||
baseSettings = cfg.settings;
|
default = null;
|
||||||
basePackages = baseSettings.packages or [];
|
description = "Default model ID.";
|
||||||
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)
|
defaultThinkingLevel = mkOption {
|
||||||
piCodingRules =
|
type = types.nullOr (types.enum ["off" "minimal" "low" "medium" "high" "xhigh"]);
|
||||||
if cfg.agentsInput != null && cfg.codingRules != null
|
default = null;
|
||||||
then cfg.codingRules // {agents = cfg.agentsInput;}
|
description = "Default extended thinking level.";
|
||||||
else null;
|
};
|
||||||
|
|
||||||
# Rendered agents (only computed when agentsInput is set)
|
theme = mkOption {
|
||||||
rendered =
|
type = types.nullOr types.str;
|
||||||
if cfg.agentsInput != null
|
default = null;
|
||||||
then
|
description = "Pi theme name.";
|
||||||
(import ../../../../lib {inherit lib;}).agents.renderForPi {
|
};
|
||||||
inherit pkgs;
|
|
||||||
canonical = cfg.agentsInput.lib.loadAgents;
|
|
||||||
modelOverrides = cfg.modelOverrides;
|
|
||||||
primaryAgent = cfg.primaryAgent;
|
|
||||||
codingRules = piCodingRules;
|
|
||||||
}
|
|
||||||
else null;
|
|
||||||
|
|
||||||
# Dynamic home.file entries for agent .md files
|
hideThinkingBlock = mkOption {
|
||||||
agentFiles =
|
type = types.nullOr types.bool;
|
||||||
if cfg.agentsInput != null
|
default = null;
|
||||||
then let
|
description = "Hide thinking blocks in output.";
|
||||||
|
};
|
||||||
|
|
||||||
|
quietStartup = mkOption {
|
||||||
|
type = types.nullOr types.bool;
|
||||||
|
default = null;
|
||||||
|
description = "Hide startup header.";
|
||||||
|
};
|
||||||
|
|
||||||
|
compaction = mkOption {
|
||||||
|
type = types.nullOr (types.submodule {
|
||||||
|
options = {
|
||||||
|
enabled = mkOption {
|
||||||
|
type = types.nullOr types.bool;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
reserveTokens = mkOption {
|
||||||
|
type = types.nullOr types.int;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
keepRecentTokens = mkOption {
|
||||||
|
type = types.nullOr types.int;
|
||||||
|
default = null;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
});
|
||||||
|
default = null;
|
||||||
|
description = "Auto-compaction settings.";
|
||||||
|
};
|
||||||
|
|
||||||
|
enabledModels = mkOption {
|
||||||
|
type = types.nullOr (types.listOf types.str);
|
||||||
|
default = null;
|
||||||
|
description = "Model patterns for Ctrl+P cycling.";
|
||||||
|
};
|
||||||
|
|
||||||
|
sessionDir = mkOption {
|
||||||
|
type = types.nullOr types.str;
|
||||||
|
default = null;
|
||||||
|
description = "Directory where session files are stored.";
|
||||||
|
};
|
||||||
|
|
||||||
|
extensions = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = "Local extension file paths or directories.";
|
||||||
|
};
|
||||||
|
|
||||||
|
skills = mkOption {
|
||||||
|
type = types.listOf types.str;
|
||||||
|
default = [];
|
||||||
|
description = "Local skill file paths or directories.";
|
||||||
|
};
|
||||||
|
};
|
||||||
|
};
|
||||||
|
default = {};
|
||||||
|
description = ''
|
||||||
|
Pi settings written to ~/.pi/agent/settings.json.
|
||||||
|
Only non-null values are included in the generated JSON.
|
||||||
|
See pi docs/settings.md for all options.
|
||||||
|
'';
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
config = mkIf cfg.enable (let
|
||||||
|
# Build settings.json by filtering out null values recursively
|
||||||
|
filterNulls = attrs:
|
||||||
|
lib.filterAttrs (_: v: v != null) (
|
||||||
|
builtins.mapAttrs (_: v:
|
||||||
|
if builtins.isAttrs v
|
||||||
|
then let
|
||||||
|
filtered = filterNulls v;
|
||||||
|
in
|
||||||
|
if filtered == {} then null else filtered
|
||||||
|
else v) attrs
|
||||||
|
);
|
||||||
|
|
||||||
|
piSettings = filterNulls cfg.settings;
|
||||||
|
|
||||||
|
projectRoots = map toString cfg.container.projectRoots;
|
||||||
|
projectRootsShell = concatStringsSep " " (map escapeShellArg projectRoots);
|
||||||
|
extraRunArgsShell = concatStringsSep " " (map escapeShellArg cfg.container.extraRunArgs);
|
||||||
|
extraEnvPairs = map (k: "${k}=${cfg.container.extraEnv.${k}}") (builtins.attrNames cfg.container.extraEnv);
|
||||||
|
extraEnvShell = concatStringsSep " " (map escapeShellArg extraEnvPairs);
|
||||||
|
hostPiDir = "${config.home.homeDirectory}/.pi";
|
||||||
|
hostPiDirShell = escapeShellArg hostPiDir;
|
||||||
|
imageArchiveShell =
|
||||||
|
if cfg.container.imageArchive != null
|
||||||
|
then escapeShellArg (toString cfg.container.imageArchive)
|
||||||
|
else "";
|
||||||
|
|
||||||
|
piWrapper = pkgs.writeShellScriptBin "pi" ''
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
PODMAN="${pkgs.podman}/bin/podman"
|
||||||
|
REALPATH="${pkgs.coreutils}/bin/realpath"
|
||||||
|
|
||||||
|
CONTAINER_NAME=${escapeShellArg cfg.container.name}
|
||||||
|
IMAGE=${escapeShellArg cfg.container.image}
|
||||||
|
IMAGE_ARCHIVE=${imageArchiveShell}
|
||||||
|
AUTO_START=${if cfg.container.autoStart then "1" else "0"}
|
||||||
|
AUTO_NIX_DEVELOP=${if cfg.container.autoNixDevelop then "1" else "0"}
|
||||||
|
HOST_PI_DIR=${hostPiDirShell}
|
||||||
|
|
||||||
|
PROJECT_ROOTS=(${projectRootsShell})
|
||||||
|
EXTRA_RUN_ARGS=(${extraRunArgsShell})
|
||||||
|
EXTRA_ENV_VARS=(${extraEnvShell})
|
||||||
|
|
||||||
|
err() {
|
||||||
|
printf "pi-wrapper: %s\n" "$1" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "''${#PROJECT_ROOTS[@]}" -eq 0 ]; then
|
||||||
|
err "No allowed projectRoots configured. Set coding.agents.pi.container.projectRoots."
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! command -v "$PODMAN" >/dev/null 2>&1; then
|
||||||
|
err "podman binary not found at $PODMAN"
|
||||||
|
fi
|
||||||
|
|
||||||
|
CWD="$($REALPATH -m "$PWD")"
|
||||||
|
|
||||||
|
cwd_allowed=0
|
||||||
|
NORMALIZED_ROOTS=()
|
||||||
|
for root in "''${PROJECT_ROOTS[@]}"; do
|
||||||
|
norm_root="$($REALPATH -m "$root")"
|
||||||
|
NORMALIZED_ROOTS+=("$norm_root")
|
||||||
|
case "$CWD/" in
|
||||||
|
"$norm_root/"*)
|
||||||
|
cwd_allowed=1
|
||||||
|
;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ "$cwd_allowed" -ne 1 ]; then
|
||||||
|
{
|
||||||
|
printf "pi-wrapper: cwd '%s' is outside allowed projectRoots.\n" "$CWD"
|
||||||
|
printf "Allowed roots:\n"
|
||||||
|
for root in "''${NORMALIZED_ROOTS[@]}"; do
|
||||||
|
printf " - %s\n" "$root"
|
||||||
|
done
|
||||||
|
} >&2
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
tty_args=()
|
||||||
|
if [ -t 0 ] && [ -t 1 ]; then
|
||||||
|
tty_args=(-it)
|
||||||
|
fi
|
||||||
|
|
||||||
|
ensure_image_available() {
|
||||||
|
if [ -n "$IMAGE_ARCHIVE" ] && [ -f "$IMAGE_ARCHIVE" ]; then
|
||||||
|
"$PODMAN" load -i "$IMAGE_ARCHIVE" >/dev/null
|
||||||
|
fi
|
||||||
|
|
||||||
|
if ! "$PODMAN" image exists "$IMAGE"; then
|
||||||
|
err "Container image '$IMAGE' is not available and no valid imageArchive was provided."
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
create_container() {
|
||||||
|
mount_args=()
|
||||||
|
|
||||||
|
for root in "''${NORMALIZED_ROOTS[@]}"; do
|
||||||
|
mount_args+=("-v" "$root:$root:rw")
|
||||||
|
done
|
||||||
|
|
||||||
|
if [ ! -S /nix/var/nix/daemon-socket/socket ]; then
|
||||||
|
err "Host Nix daemon socket not found at /nix/var/nix/daemon-socket/socket"
|
||||||
|
fi
|
||||||
|
|
||||||
|
mount_args+=("-v" "/nix/var/nix/daemon-socket/socket:/nix/var/nix/daemon-socket/socket:rw")
|
||||||
|
|
||||||
|
mkdir -p "$HOST_PI_DIR"
|
||||||
|
mount_args+=("-v" "$HOST_PI_DIR:/tmp/.pi:rw")
|
||||||
|
|
||||||
|
if [ -d /nix/store ]; then
|
||||||
|
mount_args+=("-v" "/nix/store:/nix/store:ro")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -e /etc/nix/nix.conf ]; then
|
||||||
|
mount_args+=("-v" "/etc/nix/nix.conf:/etc/nix/nix.conf:ro")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d /etc/ssl/certs ]; then
|
||||||
|
mount_args+=("-v" "/etc/ssl/certs:/etc/ssl/certs:ro")
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -d /etc/pki ]; then
|
||||||
|
mount_args+=("-v" "/etc/pki:/etc/pki:ro")
|
||||||
|
fi
|
||||||
|
|
||||||
|
env_args=()
|
||||||
|
for kv in "''${EXTRA_ENV_VARS[@]}"; do
|
||||||
|
env_args+=("--env" "$kv")
|
||||||
|
done
|
||||||
|
|
||||||
|
"$PODMAN" create \
|
||||||
|
--name "$CONTAINER_NAME" \
|
||||||
|
--hostname "$CONTAINER_NAME" \
|
||||||
|
--userns keep-id \
|
||||||
|
--user "$(${pkgs.coreutils}/bin/id -u):$(${pkgs.coreutils}/bin/id -g)" \
|
||||||
|
--security-opt no-new-privileges \
|
||||||
|
--workdir /tmp \
|
||||||
|
--tmpfs /tmp:rw,nodev,nosuid \
|
||||||
|
--env HOME=/tmp \
|
||||||
|
--env NIX_REMOTE=daemon \
|
||||||
|
--env NPM_CONFIG_PREFIX=/tmp/.npm-global \
|
||||||
|
--env npm_config_prefix=/tmp/.npm-global \
|
||||||
|
--env NPM_CONFIG_CACHE=/tmp/.npm \
|
||||||
|
--env npm_config_cache=/tmp/.npm \
|
||||||
|
--env PATH=/tmp/.npm-global/bin:/bin:/usr/bin \
|
||||||
|
"''${mount_args[@]}" \
|
||||||
|
"''${env_args[@]}" \
|
||||||
|
"''${EXTRA_RUN_ARGS[@]}" \
|
||||||
|
"$IMAGE" \
|
||||||
|
sleep infinity >/dev/null
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_container_running() {
|
||||||
|
if ! "$PODMAN" container exists "$CONTAINER_NAME"; then
|
||||||
|
ensure_image_available
|
||||||
|
create_container
|
||||||
|
fi
|
||||||
|
|
||||||
|
running="$($PODMAN inspect -f '{{.State.Running}}' "$CONTAINER_NAME" 2>/dev/null || true)"
|
||||||
|
if [ "$running" != "true" ]; then
|
||||||
|
if [ "$AUTO_START" = "1" ]; then
|
||||||
|
"$PODMAN" start "$CONTAINER_NAME" >/dev/null
|
||||||
|
else
|
||||||
|
err "Container '$CONTAINER_NAME' is not running and autoStart=false. Start it manually with: podman start $CONTAINER_NAME"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
ensure_container_running
|
||||||
|
|
||||||
|
if [ "$AUTO_NIX_DEVELOP" = "1" ] && [ -f "$CWD/flake.nix" ]; then
|
||||||
|
exec "$PODMAN" exec "''${tty_args[@]}" --workdir "$CWD" "$CONTAINER_NAME" nix develop -c pi "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
if "$PODMAN" exec --workdir "$CWD" "$CONTAINER_NAME" sh -lc 'command -v pi >/dev/null 2>&1'; then
|
||||||
|
exec "$PODMAN" exec "''${tty_args[@]}" --workdir "$CWD" "$CONTAINER_NAME" pi "$@"
|
||||||
|
fi
|
||||||
|
|
||||||
|
err "Container '$CONTAINER_NAME' does not have 'pi' in PATH (image: $IMAGE). Use a Pi-ready image or run from a flake project with autoNixDevelop=true."
|
||||||
|
'';
|
||||||
|
|
||||||
|
# Rendered agents (only computed when agentsInput is set)
|
||||||
|
rendered =
|
||||||
|
if cfg.agentsInput != null
|
||||||
|
then
|
||||||
|
(import ../../../../lib {inherit lib;}).agents.renderForPi {
|
||||||
|
inherit pkgs;
|
||||||
|
canonical = cfg.agentsInput.lib.loadAgents;
|
||||||
|
modelOverrides = cfg.modelOverrides;
|
||||||
|
primaryAgent = cfg.primaryAgent;
|
||||||
|
}
|
||||||
|
else null;
|
||||||
|
|
||||||
|
# Dynamic home.file entries for agent .md files
|
||||||
|
agentFiles =
|
||||||
|
if cfg.agentsInput != null
|
||||||
|
then
|
||||||
|
let
|
||||||
agentNames = builtins.attrNames cfg.agentsInput.lib.loadAgents;
|
agentNames = builtins.attrNames cfg.agentsInput.lib.loadAgents;
|
||||||
in
|
in
|
||||||
builtins.listToAttrs (
|
builtins.listToAttrs (
|
||||||
map (name: {
|
map (name: {
|
||||||
name = ".pi/agent/agents/${name}.md";
|
name = ".pi/agent/agents/${name}.md";
|
||||||
value = {source = "${rendered}/agents/${name}.md";};
|
value = {text = builtins.readFile "${rendered}/agents/${name}.md";};
|
||||||
})
|
})
|
||||||
agentNames
|
agentNames
|
||||||
)
|
)
|
||||||
else {};
|
else {};
|
||||||
in {
|
|
||||||
home.file = mkMerge [
|
|
||||||
# ── MCP servers from programs.mcp → ~/.pi/agent/mcp.json ───────
|
|
||||||
(mkIf (cfg.mcpServers != {}) {
|
|
||||||
".pi/agent/mcp.json".text = builtins.toJSON {mcpServers = cfg.mcpServers;};
|
|
||||||
".pi/agent/mcp.json".force = true;
|
|
||||||
})
|
|
||||||
|
|
||||||
# ── ~/.pi/agent/settings.json ──────────────────────────────────
|
skillsSource =
|
||||||
{
|
if cfg.agentsInput != null
|
||||||
".pi/agent/settings.json".text = builtins.toJSON piSettingsWithGuardrails;
|
then
|
||||||
".pi/agent/settings.json".force = true;
|
cfg.agentsInput.lib.mkOpencodeSkills {
|
||||||
|
inherit pkgs;
|
||||||
|
customSkills = "${cfg.agentsInput}/skills";
|
||||||
}
|
}
|
||||||
|
else null;
|
||||||
|
in {
|
||||||
|
assertions =
|
||||||
|
[
|
||||||
|
{
|
||||||
|
assertion = cfg.container.enable || hasPiPackage;
|
||||||
|
message = "coding.agents.pi.enable requires pkgs.pi when container mode is disabled.";
|
||||||
|
}
|
||||||
|
]
|
||||||
|
++ optional cfg.container.enable {
|
||||||
|
assertion = cfg.container.projectRoots != [];
|
||||||
|
message = "coding.agents.pi.container.projectRoots must contain at least one absolute path when container mode is enabled.";
|
||||||
|
}
|
||||||
|
++ optional cfg.container.enable {
|
||||||
|
assertion = all (path: hasPrefix "/" (toString path)) cfg.container.projectRoots;
|
||||||
|
message = "coding.agents.pi.container.projectRoots entries must be absolute paths.";
|
||||||
|
};
|
||||||
|
|
||||||
# ── pi-guardrails config ─────────────────────────────────────
|
home.packages =
|
||||||
(mkIf (guardrailsJson != null) {
|
(optional cfg.container.enable piWrapper)
|
||||||
".pi/agent/extensions/guardrails.json".text = guardrailsJson;
|
++ (optional (!cfg.container.enable && hasPiPackage) pkgs.pi);
|
||||||
".pi/agent/extensions/guardrails.json".force = true;
|
|
||||||
})
|
|
||||||
|
|
||||||
# ── AGENTS.md — agent descriptions and specialist listing ──────
|
home.file = mkMerge [
|
||||||
(mkIf (cfg.agentsInput != null) {
|
# ── MCP servers from programs.mcp → ~/.pi/agent/mcp.json ───────
|
||||||
".pi/agent/AGENTS.md".source = "${rendered}/AGENTS.md";
|
(mkIf (cfg.mcpServers != {}) {
|
||||||
})
|
".pi/agent/mcp.json".text = builtins.toJSON {mcpServers = cfg.mcpServers;};
|
||||||
|
})
|
||||||
|
|
||||||
# ── SYSTEM.md — primary agent's system prompt ──────────────────
|
# ── ~/.pi/agent/settings.json ──────────────────────────────────
|
||||||
(mkIf (cfg.agentsInput != null) {
|
{
|
||||||
".pi/agent/SYSTEM.md".source = "${rendered}/SYSTEM.md";
|
".pi/agent/settings.json".text = builtins.toJSON piSettings;
|
||||||
})
|
}
|
||||||
|
|
||||||
# ── Agents — pi-subagents .md files ────────────────────────────
|
# ── AGENTS.md — agent descriptions and specialist listing ──────
|
||||||
agentFiles
|
(mkIf (cfg.agentsInput != null) {
|
||||||
];
|
".pi/agent/AGENTS.md".text = builtins.readFile "${rendered}/AGENTS.md";
|
||||||
});
|
})
|
||||||
|
|
||||||
|
# ── SYSTEM.md — primary agent's system prompt ──────────────────
|
||||||
|
(mkIf (cfg.agentsInput != null) {
|
||||||
|
".pi/agent/SYSTEM.md".text = builtins.readFile "${rendered}/SYSTEM.md";
|
||||||
|
})
|
||||||
|
|
||||||
|
# ── Agents — pi-subagents .md files ────────────────────────────
|
||||||
|
agentFiles
|
||||||
|
|
||||||
|
# ── Skills symlinked from AGENTS repo (non-container mode) ─────
|
||||||
|
(mkIf (cfg.agentsInput != null && !cfg.container.enable) {
|
||||||
|
".pi/agent/skills".source = skillsSource;
|
||||||
|
})
|
||||||
|
];
|
||||||
|
|
||||||
|
home.activation.piMaterializeSkills = mkIf (cfg.container.enable && cfg.agentsInput != null) (
|
||||||
|
lib.hm.dag.entryAfter ["writeBoundary"] ''
|
||||||
|
skillsSrc=${escapeShellArg "${skillsSource}"}
|
||||||
|
skillsDst=${escapeShellArg "${config.home.homeDirectory}/.pi/agent/skills"}
|
||||||
|
|
||||||
|
${pkgs.coreutils}/bin/rm -rf "$skillsDst"
|
||||||
|
${pkgs.coreutils}/bin/mkdir -p "$skillsDst"
|
||||||
|
${pkgs.coreutils}/bin/cp -aL "$skillsSrc"/. "$skillsDst"/
|
||||||
|
''
|
||||||
|
);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +0,0 @@
|
|||||||
# Shared agent module exports
|
|
||||||
# Imports all shared modules for the coding.agents namespace.
|
|
||||||
{
|
|
||||||
imports = [
|
|
||||||
./git-identity.nix
|
|
||||||
];
|
|
||||||
}
|
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
# 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";
|
|
||||||
};
|
|
||||||
};
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
# 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;
|
|
||||||
}
|
|
||||||
70
overlays/default.nix
Normal file
70
overlays/default.nix
Normal file
@@ -0,0 +1,70 @@
|
|||||||
|
{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;
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
||||||
16
overlays/mods/beads.nix
Normal file
16
overlays/mods/beads.nix
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
{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,6 +2,9 @@
|
|||||||
# Package modifications
|
# Package modifications
|
||||||
# This overlay contains package overrides and modifications
|
# This overlay contains package overrides and modifications
|
||||||
|
|
||||||
|
# n8n = import ./n8n.nix {inherit prev;};
|
||||||
|
# beads = import ./beads.nix {inherit prev;};
|
||||||
|
|
||||||
# Add more modifications here as needed
|
# Add more modifications here as needed
|
||||||
# example-package = prev.example-package.override { ... };
|
# example-package = prev.example-package.override { ... };
|
||||||
}
|
}
|
||||||
|
|||||||
18
overlays/mods/n8n.nix
Normal file
18
overlays/mods/n8n.nix
Normal file
@@ -0,0 +1,18 @@
|
|||||||
|
{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=";
|
||||||
|
};
|
||||||
|
})
|
||||||
40
pkgs/code2prompt/default.nix
Normal file
40
pkgs/code2prompt/default.nix
Normal file
@@ -0,0 +1,40 @@
|
|||||||
|
{
|
||||||
|
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,10 +1,3 @@
|
|||||||
# m3ta-nixpkgs package registry
|
|
||||||
#
|
|
||||||
# Flake inputs used:
|
|
||||||
# inputs.basecamp → basecamp (pass-through)
|
|
||||||
# inputs.openspec → openspec (pass-through)
|
|
||||||
# inputs.opencode → opencode-desktop (build inputs + patches)
|
|
||||||
# inputs.agents → not used directly here (used by lib/)
|
|
||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
inputs,
|
inputs,
|
||||||
@@ -12,12 +5,11 @@
|
|||||||
}: let
|
}: let
|
||||||
system = pkgs.stdenv.hostPlatform.system;
|
system = pkgs.stdenv.hostPlatform.system;
|
||||||
in {
|
in {
|
||||||
# ── Local packages ────────────────────────────────────────────────
|
# Custom packages registry
|
||||||
# Standard packages built from source in ./<name>/default.nix.
|
# Each package is defined in its own directory under pkgs/
|
||||||
# No flake inputs required.
|
|
||||||
|
|
||||||
sidecar = pkgs.callPackage ./sidecar {};
|
sidecar = pkgs.callPackage ./sidecar {};
|
||||||
td = pkgs.callPackage ./td {};
|
td = pkgs.callPackage ./td {};
|
||||||
|
code2prompt = pkgs.callPackage ./code2prompt {};
|
||||||
eigent = pkgs.callPackage ./eigent {};
|
eigent = pkgs.callPackage ./eigent {};
|
||||||
hyprpaper-random = pkgs.callPackage ./hyprpaper-random {};
|
hyprpaper-random = pkgs.callPackage ./hyprpaper-random {};
|
||||||
launch-webapp = pkgs.callPackage ./launch-webapp {};
|
launch-webapp = pkgs.callPackage ./launch-webapp {};
|
||||||
@@ -33,9 +25,11 @@ in {
|
|||||||
zellij-ps = pkgs.callPackage ./zellij-ps {};
|
zellij-ps = pkgs.callPackage ./zellij-ps {};
|
||||||
vibetyper = pkgs.callPackage ./vibetyper {};
|
vibetyper = pkgs.callPackage ./vibetyper {};
|
||||||
|
|
||||||
# ── Pass-through packages ──────────────────────────────────────────
|
# Imported from flake inputs (pass-through, no modifications)
|
||||||
# Imported directly from flake inputs. No local modifications.
|
|
||||||
|
|
||||||
basecamp = inputs.basecamp.packages.${system}.default;
|
basecamp = inputs.basecamp.packages.${system}.default;
|
||||||
openspec = inputs.openspec.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,
|
nix-update-script,
|
||||||
}: let
|
}: let
|
||||||
pname = "eigent";
|
pname = "eigent";
|
||||||
version = "0.0.90";
|
version = "0.0.89";
|
||||||
src = fetchurl {
|
src = fetchurl {
|
||||||
url = "https://github.com/eigent-ai/eigent/releases/download/v${version}/Eigent-${version}.AppImage";
|
url = "https://github.com/eigent-ai/eigent/releases/download/v${version}/Eigent-${version}.AppImage";
|
||||||
hash = "sha256-mwCBx+D6mgGqQa8bDuUpo3h49EwFVkwasJwaYc6aXFE=";
|
hash = "sha256-9KuiFjegfXhCu1W/FCinWX4ae/DsNPudeBcXFfW18Hc=";
|
||||||
};
|
};
|
||||||
appimageContents = appimageTools.extractType2 {inherit pname version src;};
|
appimageContents = appimageTools.extractType2 {inherit pname version src;};
|
||||||
in
|
in
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
{
|
{
|
||||||
"version": "1.3.0",
|
"version": "1.2.2",
|
||||||
"sources": {
|
"sources": {
|
||||||
"aarch64-linux": {
|
"aarch64-linux": {
|
||||||
"url": "https://github.com/kestra-io/kestractl/releases/download/1.3.0/kestractl_1.3.0_linux_arm64.tar.gz",
|
"url": "https://github.com/kestra-io/kestractl/releases/download/1.2.2/kestractl_1.2.2_linux_arm64.tar.gz",
|
||||||
"hash": "sha256-/18F6CZnnLbet4BmI1oQ5pZWkJwIshCq30qd+cm0GGA="
|
"hash": "sha256-sidFsCZPnJ07PM5QayPBqaqlBBJTLEdecfd0AWnL7Yo="
|
||||||
},
|
},
|
||||||
"x86_64-linux": {
|
"x86_64-linux": {
|
||||||
"url": "https://github.com/kestra-io/kestractl/releases/download/1.3.0/kestractl_1.3.0_linux_amd64.tar.gz",
|
"url": "https://github.com/kestra-io/kestractl/releases/download/1.2.2/kestractl_1.2.2_linux_amd64.tar.gz",
|
||||||
"hash": "sha256-xmsBiqNKvob8xHDyU253o6c25YIubHanNdLqzWaOvSA="
|
"hash": "sha256-0C2naN2ougBJSY2z2m6eORnLkLen87HD+a+gvtrUvdw="
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -6,14 +6,14 @@
|
|||||||
}:
|
}:
|
||||||
python3.pkgs.buildPythonPackage rec {
|
python3.pkgs.buildPythonPackage rec {
|
||||||
pname = "mem0ai";
|
pname = "mem0ai";
|
||||||
version = "2.0.1";
|
version = "1.0.9";
|
||||||
pyproject = true;
|
pyproject = true;
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "mem0ai";
|
owner = "mem0ai";
|
||||||
repo = "mem0";
|
repo = "mem0";
|
||||||
rev = "v${version}";
|
rev = "v${version}";
|
||||||
hash = "sha256-lNSE0Yit+FmM8opC4XYtfVef7JfGd3wMKbLj67Kp4Qw=";
|
hash = "sha256-tcWH5VbjIBSHinfjirxbUhxqgU0xOUlcHTQHraMuALg=";
|
||||||
};
|
};
|
||||||
|
|
||||||
# Relax Python dependency version constraints
|
# Relax Python dependency version constraints
|
||||||
|
|||||||
@@ -11,7 +11,6 @@
|
|||||||
node-gyp,
|
node-gyp,
|
||||||
cctools,
|
cctools,
|
||||||
xcbuild,
|
xcbuild,
|
||||||
dart-sass,
|
|
||||||
libkrb5,
|
libkrb5,
|
||||||
libmongocrypt,
|
libmongocrypt,
|
||||||
libpq,
|
libpq,
|
||||||
@@ -26,20 +25,20 @@
|
|||||||
in
|
in
|
||||||
stdenv.mkDerivation (finalAttrs: {
|
stdenv.mkDerivation (finalAttrs: {
|
||||||
pname = "n8n";
|
pname = "n8n";
|
||||||
version = "2.18.5";
|
version = "stable";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "n8n-io";
|
owner = "n8n-io";
|
||||||
repo = "n8n";
|
repo = "n8n";
|
||||||
tag = "n8n@${finalAttrs.version}";
|
tag = "${finalAttrs.version}";
|
||||||
hash = "sha256-ws0DXGQFR+z3nVyd4Yn9pIM7yh+H6GnuCRSLxgvtPxo=";
|
hash = "sha256-/atba0ymCqhh5Rt61UxwC2xf8SGrRsEKtlsDCIkg37Y=";
|
||||||
};
|
};
|
||||||
|
|
||||||
pnpmDeps = fetchPnpmDeps {
|
pnpmDeps = fetchPnpmDeps {
|
||||||
inherit (finalAttrs) pname version src;
|
inherit (finalAttrs) pname version src;
|
||||||
pnpm = pnpm_10;
|
pnpm = pnpm_10;
|
||||||
fetcherVersion = 3;
|
fetcherVersion = 3;
|
||||||
hash = "sha256-Ajgne0neNm6HgMK6z3jnEkUJJxVOTgzjpSaMaJgIndQ=";
|
hash = "sha256-YGplNNvIOIY1BthWmejAzucXujq8AkgPJus774GmWCA=";
|
||||||
};
|
};
|
||||||
|
|
||||||
nativeBuildInputs =
|
nativeBuildInputs =
|
||||||
@@ -62,17 +61,6 @@ in
|
|||||||
libpq
|
libpq
|
||||||
];
|
];
|
||||||
|
|
||||||
preBuild = ''
|
|
||||||
# Force sass-embedded to use our dart-sass instead of bundled binaries.
|
|
||||||
# The bundled Dart binary can't run in the Nix sandbox (no /lib64/ld-linux-x86-64.so.2).
|
|
||||||
for dep in node_modules/.pnpm/sass-embedded@*; do
|
|
||||||
substituteInPlace "$dep/node_modules/sass-embedded/dist/lib/src/compiler-path.js" \
|
|
||||||
--replace-fail \
|
|
||||||
'compilerCommand = (() => {' \
|
|
||||||
'compilerCommand = (() => { return ["${lib.getExe dart-sass}"];'
|
|
||||||
done
|
|
||||||
'';
|
|
||||||
|
|
||||||
buildPhase = ''
|
buildPhase = ''
|
||||||
runHook preBuild
|
runHook preBuild
|
||||||
|
|
||||||
|
|||||||
@@ -1,29 +1,6 @@
|
|||||||
#!/usr/bin/env nix-shell
|
#!/usr/bin/env nix-shell
|
||||||
#!nix-shell -i bash -p bash curl jq nix-update cacert git nix
|
#!nix-shell --pure -i bash -p bash curl jq nix-update cacert git
|
||||||
set -euo pipefail
|
set -euo pipefail
|
||||||
|
|
||||||
# n8n releases are published with two tags per version:
|
new_version="$(curl -s "https://api.github.com/repos/n8n-io/n8n/releases/latest" | jq --raw-output '.tag_name | ltrimstr("n8n@")')"
|
||||||
# - "n8n@X.Y.Z" - the versioned tag
|
nix-update n8n --flake --version "$new_version"
|
||||||
# - "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"
|
|
||||||
|
|||||||
114
pkgs/opencode-desktop/default.nix
Normal file
114
pkgs/opencode-desktop/default.nix
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
{
|
||||||
|
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: {
|
buildGoModule (finalAttrs: {
|
||||||
pname = "sidecar";
|
pname = "sidecar";
|
||||||
version = "0.84.0";
|
version = "0.83.0";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "marcus";
|
owner = "marcus";
|
||||||
repo = "sidecar";
|
repo = "sidecar";
|
||||||
tag = "v${finalAttrs.version}";
|
tag = "v${finalAttrs.version}";
|
||||||
hash = "sha256-80ldZlaZ99ti8dvw+Awev7ucz03iOVD2yzz/+IFHDvA=";
|
hash = "sha256-L6q2eZO1rNngWwHVhBJ2ftVbvYTConpqYHEb3nwiXxs=";
|
||||||
};
|
};
|
||||||
|
|
||||||
vendorHash = "sha256-IDD+hQZODNPj+Gy9CX5GFdMcsvt75aFLpabXZehAjaw=";
|
vendorHash = "sha256-fIaHzc0L4jwVSh/YjrXBB7nENqCgOfHF5bnljFsGbVo=";
|
||||||
|
|
||||||
subPackages = ["cmd/sidecar"];
|
subPackages = ["cmd/sidecar"];
|
||||||
|
|
||||||
|
|||||||
@@ -9,13 +9,13 @@
|
|||||||
}:
|
}:
|
||||||
buildGoModule (finalAttrs: {
|
buildGoModule (finalAttrs: {
|
||||||
pname = "td";
|
pname = "td";
|
||||||
version = "0.44.0";
|
version = "0.43.0";
|
||||||
|
|
||||||
src = fetchFromGitHub {
|
src = fetchFromGitHub {
|
||||||
owner = "marcus";
|
owner = "marcus";
|
||||||
repo = "td";
|
repo = "td";
|
||||||
tag = "v${finalAttrs.version}";
|
tag = "v${finalAttrs.version}";
|
||||||
hash = "sha256-k1OCK6LE99fHLuxv8HZUW8cSn2Wmk74J7kb6Mi5ZpVw=";
|
hash = "sha256-DwzuXumEEQWfZW+GbbY9kyqkEFZQ9sC+sSbVxfrY6bM=";
|
||||||
};
|
};
|
||||||
|
|
||||||
vendorHash = "sha256-hFFG+vLXcL2NNdLQvQZ1hzu++pp5AkbFOPQS10wtsec=";
|
vendorHash = "sha256-hFFG+vLXcL2NNdLQvQZ1hzu++pp5AkbFOPQS10wtsec=";
|
||||||
|
|||||||
@@ -4,10 +4,10 @@
|
|||||||
lib,
|
lib,
|
||||||
}: let
|
}: let
|
||||||
pname = "vibetyper";
|
pname = "vibetyper";
|
||||||
version = "1.2.3";
|
version = "1.2.2";
|
||||||
src = fetchurl {
|
src = fetchurl {
|
||||||
url = "https://cdn.vibetyper.com/releases/linux/VibeTyper.AppImage";
|
url = "https://cdn.vibetyper.com/releases/linux/VibeTyper.AppImage";
|
||||||
sha256 = "sha256-6uGXw2nxb0sGkcMDTWBlL3PuwBfVodhgqfgZT1Ncs40=";
|
sha256 = "sha256-AUjrSVxyaI8Ok4pnoqaW4fGAd4GtSc0mEjDhkqdifY0=";
|
||||||
};
|
};
|
||||||
appimageContents = appimageTools.extractType2 {inherit pname version src;};
|
appimageContents = appimageTools.extractType2 {inherit pname version src;};
|
||||||
in
|
in
|
||||||
|
|||||||
@@ -1,109 +0,0 @@
|
|||||||
# 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,7 +4,6 @@
|
|||||||
{
|
{
|
||||||
pkgs,
|
pkgs,
|
||||||
inputs,
|
inputs,
|
||||||
agents ? null,
|
|
||||||
}: {
|
}: {
|
||||||
# Default shell for working on this repository
|
# Default shell for working on this repository
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
@@ -33,5 +32,5 @@
|
|||||||
# Import all individual shell environments
|
# Import all individual shell environments
|
||||||
python = import ./python.nix {inherit pkgs inputs;};
|
python = import ./python.nix {inherit pkgs inputs;};
|
||||||
devops = import ./devops.nix {inherit pkgs inputs;};
|
devops = import ./devops.nix {inherit pkgs inputs;};
|
||||||
coding = import ./coding.nix {inherit pkgs inputs agents;};
|
opencode = import ./opencode.nix {inherit pkgs inputs;};
|
||||||
}
|
}
|
||||||
|
|||||||
155
shells/opencode.nix
Normal file
155
shells/opencode.nix
Normal file
@@ -0,0 +1,155 @@
|
|||||||
|
# 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 ""
|
||||||
|
'';
|
||||||
|
}
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# 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
|
|
||||||
''
|
|
||||||
@@ -1,89 +0,0 @@
|
|||||||
{
|
|
||||||
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
|
|
||||||
''
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
{
|
|
||||||
coding-rules = import ./coding-rules-test.nix;
|
|
||||||
agents = import ./agents-test.nix;
|
|
||||||
}
|
|
||||||
Reference in New Issue
Block a user