Compare commits

...

46 Commits

Author SHA1 Message Date
nix-update bot
c6c3ffb548 chore: update flake inputs 2026-05-01 16:01:13 +02:00
nix-update bot
9b423315b3 chore: update flake inputs
All checks were successful
Update Nix Packages with nix-update / nix-update (push) Successful in 2m42s
2026-05-01 04:01:14 +02:00
nix-update bot
14d906ef93 chore: update flake inputs
All checks were successful
Update Nix Packages with nix-update / nix-update (push) Successful in 2m36s
2026-04-30 16:01:17 +02:00
nix-update bot
e7393d6fa4 chore: update flake inputs
All checks were successful
Update Nix Packages with nix-update / nix-update (push) Successful in 4m6s
2026-04-30 04:00:54 +02:00
1da8c96447 fix(pi): correct guardrails enable option string
Some checks failed
Update Nix Packages with nix-update / nix-update (push) Failing after 34m23s
2026-04-29 20:07:12 +02:00
6a8cb62903 style(pi): format guardrails module with alejandra 2026-04-29 19:51:35 +02:00
a3e247e5af feat(pi): add guardrails config option for pi-guardrails integration
Adds a guardrails submodule option to coding.agents.pi that:
- Generates ~/.pi/agent/extensions/guardrails.json when enabled
- Automatically injects @aliou/pi-guardrails package into settings.packages
- Provides structured options for policies, pathAccess, and permissionGate

The module generates the JSON config that pi-guardrails reads for
its security hooks (policies, permission-gate, path-access).

Limitations documented in option descriptions:
- Path access checks are lexical (not symlink-safe)
- Local project guardrails.json can override global rule IDs
2026-04-29 19:48:10 +02:00
f7f0c4072e fix(n8n): use stable tag target to get actual version
The previous jq filter grabbed the first non-stable release by creation date,
which incorrectly returned 1.123.38 instead of the latest stable 2.18.5.

Now query the 'stable' tag directly and extract version from its
target_commitish (e.g., 'release/2.18.5' -> '2.18.5'), ensuring we always
get the actual latest stable version.

Also bump version from 2.17.8 to 2.18.5 with updated hashes.
2026-04-29 18:40:49 +02:00
m3tm3re
e601fde026 kestractl: 1.2.2 -> 1.3.0 2026-04-29 18:39:03 +02:00
nix-update bot
642e764b81 chore: update flake inputs 2026-04-29 16:01:26 +02:00
nix-update bot
0a224db2fc chore: update flake inputs
All checks were successful
Update Nix Packages with nix-update / nix-update (push) Successful in 2m39s
2026-04-29 04:01:44 +02:00
nix-update bot
69e8fb93be chore: update flake inputs
All checks were successful
Update Nix Packages with nix-update / nix-update (push) Successful in 23m34s
2026-04-28 16:01:26 +02:00
nix-update bot
c63ecc899c td: 0.43.0 -> 0.44.0
All checks were successful
Update Nix Packages with nix-update / nix-update (push) Successful in 2m35s
Diff: https://github.com/marcus/td/compare/v0.43.0...v0.44.0
2026-04-28 04:03:27 +02:00
nix-update bot
5d2bfbd27c sidecar: 0.83.0 -> 0.84.0
Diff: https://github.com/marcus/sidecar/compare/v0.83.0...v0.84.0

Changelog: https://github.com/marcus/sidecar/releases/tag/v0.84.0
2026-04-28 04:02:57 +02:00
nix-update bot
66c398d196 mem0: 1.0.9 -> 2.0.1
Diff: https://github.com/mem0ai/mem0/compare/v1.0.9...v2.0.1

Changelog: https://github.com/mem0ai/mem0/releases/tag/v2.0.1
2026-04-28 04:02:08 +02:00
nix-update bot
729ea971c5 chore: update flake inputs 2026-04-28 04:01:09 +02:00
m3tm3re
ce16dfff2e chore(beads): initialize issue tracker
Some checks failed
Update Nix Packages with nix-update / nix-update (push) Failing after 37m14s
Add nixpkgs-ng1 task: Configure agent git identity
2026-04-27 20:16:27 +02:00
m3tm3re
31464e245e chore(nix): remove deprecated packages
- Remove opencode flake input (old v1.4.3 URL, superseded)
- Remove code2prompt package
- Remove opencode-desktop package
2026-04-27 20:14:18 +02:00
251a6892a3 fix(n8n): resolve nix-update hash prefetch failure
- Remove --pure from nix-shell shebang to allow network access
- Add --flake --system x86_64-linux for proper flake evaluation
- Navigate to nixpkgs root before running nix-update
- Also bump version 2.17.5 -> 2.17.8
2026-04-27 19:46:14 +02:00
95aeff28ad Merge pull request 'feature/agent-git-identity' (#16) from feature/agent-git-identity into master
Reviewed-on: #16
2026-04-27 17:56:26 +02:00
m3tm3re
fa339ae8cc fix(agents): correct shared-options.nix import paths from ../ to ./ 2026-04-27 13:17:11 +02:00
m3tm3re
cec0c31d91 fix(agents): correct shared-options.nix import path 2026-04-27 13:11:11 +02:00
m3tm3re
6a8360305d fix(agents): remove shared-options.nix from imports to avoid module system conflict 2026-04-27 13:08:40 +02:00
m3tm3re
5edd0929d0 fix(agents): correct import paths for shared module 2026-04-27 13:00:52 +02:00
m3tm3re
60aeec7cfe feat(agents): add gitIdentity module
- Renamed shared-options.nix to shared/shared-options.nix
- Created shared/default.nix importing git-identity.nix and shared-options.nix
- Created shared/git-identity.nix with gitIdentity option set:
  - enable: Toggle for agent git identity
  - name: Git author name (default: m3ta-chiron)
  - email: Git author email (default: m3ta-chiron@agentmail.to)
  - signingKey: Optional GPG signing key path
  - sshKey: SSH private key path for git push auth
- Updated opencode.nix, pi.nix, claude-code.nix to import shared/default.nix
- Restructured modules to follow proper Nix module syntax with imports at top level
2026-04-27 12:43:56 +02:00
m3tm3re
161be34111 chore: beads init
Some checks failed
Update Nix Packages with nix-update / nix-update (push) Failing after 3h23m59s
2026-04-26 14:06:57 +02:00
m3tm3re
7b6bcfeb1c fix: force overwrite for pi mcp.json and settings.json 2026-04-26 13:25:12 +02:00
m3tm3re
03ad7451fc feat: update documentation, lib functions, modules, and packages
Some checks failed
Update Nix Packages with nix-update / nix-update (push) Failing after 3h23m59s
2026-04-22 18:50:31 +02:00
sascha.koenig
69b736e302 chore: update flake, agents lib, and clean up tracked dotfiles
Some checks failed
Update Nix Packages with nix-update / nix-update (push) Failing after 3m59s
- Remove .pi* and .td-root files from git index (now in .gitignore)
- Update flake.lock and flake.nix
- Add shells/coding.nix, remove shells/opencode.nix
- Update lib/agents.nix, lib/coding-rules.nix
- Update modules/home-manager/coding/agents/pi.nix
- Update tests for agents and coding-rules
- Update .gitignore
2026-04-21 20:24:38 +02:00
300ef0c28f Merge pull request 'refactor/remove-legacy-mkopencoderules' (#15) from refactor/remove-legacy-mkopencoderules into master
Some checks failed
Update Nix Packages with nix-update / nix-update (push) Failing after 8m3s
Reviewed-on: #15
2026-04-20 19:20:49 +02:00
sascha.koenig
57ebad1358 refactor: remove legacy mkOpencodeRules alias and opencode-rules compat entry
- Remove mkOpencodeRules backward-compat alias from lib/coding-rules.nix
- Remove opencode-rules alias from lib/default.nix
- Update shells/opencode.nix to use mkCodingRules / coding-rules
- Remove backward-compat test from tests/lib/coding-rules-test.nix
- Update AGENTS.md and modules/home-manager/AGENTS.md docs
- Apply nix fmt formatting to shared-options.nix
2026-04-20 19:16:22 +02:00
sascha.koenig
35f4821bc5 refactor: centralize agent skills at ~/.agents/skills 2026-04-20 08:58:15 +02:00
bc41c9a428 Merge pull request 'refactor/remove-pi-agent-cleanup' (#14) from refactor/remove-pi-agent-cleanup into master
Some checks failed
Update Nix Packages with nix-update / nix-update (push) Failing after 4m2s
Reviewed-on: #14
2026-04-19 18:16:50 +02:00
m3tm3re
edae9ba3c9 chore: update n8n, vibetyper 2026-04-19 18:11:43 +02:00
Chiron
54fa93574b refactor: remove dead code, extract shared agent options, optimize flake
- Remove dead overlays/default.nix (flake defines overlays inline)
- Remove orphaned overlays/mods/{beads,n8n}.nix (never imported)
- Remove docs/packages/notesmd-cli.md (package doesn't exist)
- Extract externalSkills submodule to shared-options.nix (eliminates
  ~100 lines of duplication across opencode/claude-code/pi modules)
- Fix lib output: use nixpkgs.lib directly instead of instantiating
  a full nixpkgs just to get lib
- Add lib unit tests to flake checks
- Update stale comment in coding-rules.nix
2026-04-18 10:15:50 +00:00
Chiron
c9ecc0809f fix: add externalSkills option to pi agent module
Skills from flake inputs (e.g. Basecamp) were not being passed to
mkOpencodeSkills for the pi agent, so they never appeared in
~/.pi/agent/skills/. This adds the same externalSkills option that
the opencode agent module already has.
2026-04-18 10:07:44 +00:00
Chiron
44c7e0d19a chore: sync non-pi changes from remote (eigent update, formatting fixes, gitignore) 2026-04-18 10:05:59 +00:00
Chiron
a0f4d401df docs: update AGENTS.md to reflect current codebase state 2026-04-18 10:05:16 +00:00
Chiron
d04d405297 test: add basic lib function tests for agents and coding-rules 2026-04-18 10:05:16 +00:00
Chiron
2494da1054 docs: add CHANGELOG.md 2026-04-18 10:05:16 +00:00
Chiron
b2c8c935af refactor: remove redundant 'additions' overlay (identical to 'default') 2026-04-18 10:05:16 +00:00
Chiron
c6d8376dda refactor: tool-agnostic naming in coding-rules.nix internals 2026-04-18 10:05:16 +00:00
Chiron
0331316755 chore: remove dead overlay entries for non-existent flake inputs 2026-04-18 10:05:16 +00:00
Chiron
a4e540630d docs: clarify system binding in pkgs/default.nix 2026-04-18 10:05:16 +00:00
Chiron
6c985c640d refactor: remove duplicate opencode-rules.nix, use alias in default.nix 2026-04-18 10:05:16 +00:00
Chiron
5f90f16d99 docs: add cleanup and improvements plan 2026-04-18 10:05:16 +00:00
68 changed files with 2774 additions and 1459 deletions

73
.beads/.gitignore vendored Normal file
View File

@@ -0,0 +1,73 @@
# Dolt database (managed by Dolt, not git)
dolt/
embeddeddolt/
# Runtime files
bd.sock
bd.sock.startlock
sync-state.json
last-touched
.exclusive-lock
# Daemon runtime (lock, log, pid)
daemon.*
# Interactions log (runtime, not versioned)
interactions.jsonl
# Push state (runtime, per-machine)
push-state.json
# Lock files (various runtime locks)
*.lock
# Credential key (encryption key for federation peer auth — never commit)
.beads-credential-key
# Local version tracking (prevents upgrade notification spam after git ops)
.local_version
# Worktree redirect file (contains relative path to main repo's .beads/)
# Must not be committed as paths would be wrong in other clones
redirect
# Sync state (local-only, per-machine)
# These files are machine-specific and should not be shared across clones
.sync.lock
export-state/
export-state.json
# Ephemeral store (SQLite - wisps/molecules, intentionally not versioned)
ephemeral.sqlite3
ephemeral.sqlite3-journal
ephemeral.sqlite3-wal
ephemeral.sqlite3-shm
# Dolt server management (auto-started by bd)
dolt-server.pid
dolt-server.log
dolt-server.lock
dolt-server.port
dolt-server.activity
# Corrupt backup directories (created by bd doctor --fix recovery)
*.corrupt.backup/
# Backup data (auto-exported JSONL, local-only)
backup/
# Per-project environment file (Dolt connection config, GH#2520)
.env
# Legacy files (from pre-Dolt versions)
*.db
*.db?*
*.db-journal
*.db-wal
*.db-shm
db.sqlite
bd.db
# NOTE: Do NOT add negation patterns here.
# They would override fork protection in .git/info/exclude.
# Config files (metadata.json, config.yaml) are tracked by git by default
# since no pattern above ignores them.

81
.beads/README.md Normal file
View File

@@ -0,0 +1,81 @@
# Beads - AI-Native Issue Tracking
Welcome to Beads! This repository uses **Beads** for issue tracking - a modern, AI-native tool designed to live directly in your codebase alongside your code.
## What is Beads?
Beads is issue tracking that lives in your repo, making it perfect for AI coding agents and developers who want their issues close to their code. No web UI required - everything works through the CLI and integrates seamlessly with git.
**Learn more:** [github.com/steveyegge/beads](https://github.com/steveyegge/beads)
## Quick Start
### Essential Commands
```bash
# Create new issues
bd create "Add user authentication"
# View all issues
bd list
# View issue details
bd show <issue-id>
# Update issue status
bd update <issue-id> --claim
bd update <issue-id> --status done
# Sync with Dolt remote
bd dolt push
```
### Working with Issues
Issues in Beads are:
- **Git-native**: Stored in Dolt database with version control and branching
- **AI-friendly**: CLI-first design works perfectly with AI coding agents
- **Branch-aware**: Issues can follow your branch workflow
- **Always in sync**: Auto-syncs with your commits
## Why Beads?
**AI-Native Design**
- Built specifically for AI-assisted development workflows
- CLI-first interface works seamlessly with AI coding agents
- No context switching to web UIs
🚀 **Developer Focused**
- Issues live in your repo, right next to your code
- Works offline, syncs when you push
- Fast, lightweight, and stays out of your way
🔧 **Git Integration**
- Automatic sync with git commits
- Branch-aware issue tracking
- Dolt-native three-way merge resolution
## Get Started with Beads
Try Beads in your own projects:
```bash
# Install Beads
curl -sSL https://raw.githubusercontent.com/steveyegge/beads/main/scripts/install.sh | bash
# Initialize in your repo
bd init
# Create your first issue
bd create "Try out Beads"
```
## Learn More
- **Documentation**: [github.com/steveyegge/beads/docs](https://github.com/steveyegge/beads/tree/main/docs)
- **Quick Start Guide**: Run `bd quickstart`
- **Examples**: [github.com/steveyegge/beads/examples](https://github.com/steveyegge/beads/tree/main/examples)
---
*Beads: Issue tracking that moves at the speed of thought*

56
.beads/config.yaml Normal file
View File

@@ -0,0 +1,56 @@
# Beads Configuration File
# This file configures default behavior for all bd commands in this repository
# All settings can also be set via environment variables (BD_* prefix)
# or overridden with command-line flags
# Issue prefix for this repository (used by bd init)
# If not set, bd init will auto-detect from directory name
# Example: issue-prefix: "myproject" creates issues like "myproject-1", "myproject-2", etc.
# issue-prefix: ""
# Use no-db mode: JSONL-only, no Dolt database
# When true, bd will use .beads/issues.jsonl as the source of truth
# no-db: false
# Enable JSON output by default
# json: false
# Feedback title formatting for mutating commands (create/update/close/dep/edit)
# 0 = hide titles, N > 0 = truncate to N characters
# output:
# title-length: 255
# Default actor for audit trails (overridden by BEADS_ACTOR or --actor)
# actor: ""
# Export events (audit trail) to .beads/events.jsonl on each flush/sync
# When enabled, new events are appended incrementally using a high-water mark.
# Use 'bd export --events' to trigger manually regardless of this setting.
# events-export: false
# Multi-repo configuration (experimental - bd-307)
# Allows hydrating from multiple repositories and routing writes to the correct database
# repos:
# primary: "." # Primary repo (where this database lives)
# additional: # Additional repos to hydrate from (read-only)
# - ~/beads-planning # Personal planning repo
# - ~/work-planning # Work planning repo
# JSONL backup (periodic export for off-machine recovery)
# Auto-enabled when a git remote exists. Override explicitly:
# backup:
# enabled: false # Disable auto-backup entirely
# interval: 15m # Minimum time between auto-exports
# git-push: false # Disable git push (export locally only)
# git-repo: "" # Separate git repo for backups (default: project repo)
# Integration settings (access with 'bd config get/set')
# These are stored in the database, not in this file:
# - jira.url
# - jira.project
# - linear.url
# - linear.api-key
# - github.org
# - github.repo
sync.remote: "git+ssh://gitea@code.m3ta.dev/m3tam3re/nixpkgs.git"

24
.beads/hooks/post-checkout Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run post-checkout "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'post-checkout' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run post-checkout "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'post-checkout'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.2 ---

24
.beads/hooks/post-merge Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run post-merge "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'post-merge' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run post-merge "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'post-merge'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.2 ---

24
.beads/hooks/pre-commit Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run pre-commit "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'pre-commit' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run pre-commit "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'pre-commit'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.2 ---

24
.beads/hooks/pre-push Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run pre-push "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'pre-push' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run pre-push "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'pre-push'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.2 ---

24
.beads/hooks/prepare-commit-msg Executable file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env sh
# --- BEGIN BEADS INTEGRATION v1.0.2 ---
# This section is managed by beads. Do not remove these markers.
if command -v bd >/dev/null 2>&1; then
export BD_GIT_HOOK=1
_bd_timeout=${BEADS_HOOK_TIMEOUT:-300}
if command -v timeout >/dev/null 2>&1; then
timeout "$_bd_timeout" bd hooks run prepare-commit-msg "$@"
_bd_exit=$?
if [ $_bd_exit -eq 124 ]; then
echo >&2 "beads: hook 'prepare-commit-msg' timed out after ${_bd_timeout}s — continuing without beads"
_bd_exit=0
fi
else
bd hooks run prepare-commit-msg "$@"
_bd_exit=$?
fi
if [ $_bd_exit -eq 3 ]; then
echo >&2 "beads: database not initialized — skipping hook 'prepare-commit-msg'"
_bd_exit=0
fi
if [ $_bd_exit -ne 0 ]; then exit $_bd_exit; fi
fi
# --- END BEADS INTEGRATION v1.0.2 ---

1
.beads/issues.jsonl Normal file
View File

@@ -0,0 +1 @@
{"id":"nixpkgs-ng1","title":"Configure agent git identity in nixpkgs repo","description":"Git commits are using p@m3ta.dev instead of m3ta-chiron@agentmail.to. The GIT_AUTHOR_NAME and GIT_AUTHOR_EMAIL environment variables are not set in this environment. Need to configure the agent git identity for this repository following the pattern in AGENTS.md","status":"open","priority":2,"issue_type":"task","owner":"p@m3ta.dev","created_at":"2026-04-27T18:16:17Z","created_by":"m3tm3re","updated_at":"2026-04-27T18:16:17Z","dependency_count":0,"dependent_count":0,"comment_count":0}

7
.beads/metadata.json Normal file
View File

@@ -0,0 +1,7 @@
{
"database": "dolt",
"backend": "dolt",
"dolt_mode": "embedded",
"dolt_database": "nixpkgs",
"project_id": "b57a167a-6526-4211-a6c1-51686e431912"
}

7
.gitignore vendored
View File

@@ -43,3 +43,10 @@ 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

View File

@@ -1,7 +0,0 @@
{
"success": true,
"clones": [],
"duplicatedLines": 0,
"totalLines": 0,
"percentage": 0
}

View File

@@ -1,3 +0,0 @@
{
"timestamp": "2026-04-11T04:17:20.531Z"
}

View File

@@ -1,9 +0,0 @@
{
"success": false,
"issues": [],
"unusedExports": [],
"unusedFiles": [],
"unusedDeps": [],
"unlistedDeps": [],
"summary": "Failed to parse output"
}

View File

@@ -1,3 +0,0 @@
{
"timestamp": "2026-04-11T04:17:21.374Z"
}

View File

@@ -1,3 +0,0 @@
{
"content": "📌 pi-lens active — as you work on this project, fix any errors you encounter (including pre-existing). Prefer: lsp_navigation for definitions/references, ast_grep_search for code patterns, grep for text/TODO search."
}

View File

@@ -1,3 +0,0 @@
{
"timestamp": "2026-04-11T04:21:36.939Z"
}

View File

@@ -1,3 +0,0 @@
{
"items": []
}

View File

@@ -1,3 +0,0 @@
{
"timestamp": "2026-04-11T04:21:36.940Z"
}

View File

@@ -1,6 +0,0 @@
{
"files": {},
"turnCycles": 0,
"maxCycles": 3,
"lastUpdated": "2026-04-11T04:17:22.397Z"
}

View File

@@ -1 +0,0 @@
/home/sascha.koenig/p/NIX/nixpkgs

265
AGENTS.md
View File

@@ -1,225 +1,84 @@
# m3ta-nixpkgs Knowledge Base # Agent Instructions
## MANDATORY: Use td for Task Management This project uses **bd** (beads) for issue tracking. Run `bd prime` for full workflow context.
You must run td usage --new-session at conversation start (or after /clear) to see current work. ## Quick Reference
Use td usage -q for subsequent reads.
**Generated:** 2026-02-14
**Commit:** dc2f3b6
**Branch:** master
## OVERVIEW
Personal Nix flake: custom packages, overlays, NixOS/Home Manager modules, dev shells. Flakes-only (no channels).
## STRUCTURE
```
.
├── flake.nix # Entry: packages, overlays, modules, shells, lib
├── pkgs/ # Custom packages (one dir each, callPackage registry)
├── modules/
│ ├── nixos/ # System modules (ports.nix)
│ └── home-manager/ # User modules by category (cli/, coding/, ports.nix)
├── lib/ # Shared utilities (ports.nix)
├── shells/ # Dev environments (default, python, devops)
├── overlays/mods/ # Package modifications (n8n version bump)
├── templates/ # Boilerplate for new packages/modules
├── examples/ # Usage examples
└── .gitea/workflows/ # CI/CD workflows (nix-update automation)
```
## WHERE TO LOOK
| Task | Location | Notes |
| -------------------- | ---------------------------------- | ------------------------------------- |
| Add package | `pkgs/<name>/default.nix` | Register in `pkgs/default.nix` |
| Add NixOS module | `modules/nixos/<name>.nix` | Import in `modules/nixos/default.nix` |
| Add HM module | `modules/home-manager/<category>/` | Category: cli, coding, or root |
| Override nixpkgs pkg | `overlays/mods/<name>.nix` | Import in `overlays/mods/default.nix` |
| Add dev shell | `shells/<name>.nix` | Register in `shells/default.nix` |
| Use port management | `config.m3ta.ports.get "service"` | Host-specific via `hostOverrides` |
| CI/CD workflows | `.gitea/workflows/<name>.yml` | Automated package updates (nix-update) |
## CONVENTIONS
**Formatter**: `nix fmt` before commit (alejandra)
**Naming**:
- Packages: `lowercase-hyphen` (e.g., `hyprpaper-random`)
- Variables: `camelCase` (e.g., `portHelpers`)
- Module options: `m3ta.*` namespace
**Imports**: Multi-line, trailing commas:
```nix
{
lib,
stdenv,
fetchFromGitHub,
}:
```
**Modules**: Standard pattern:
```nix
{ config, lib, pkgs, ... }:
with lib; let
cfg = config.m3ta.myModule;
in {
options.m3ta.myModule = {
enable = mkEnableOption "description";
};
config = mkIf cfg.enable { ... };
}
```
**Meta**: Always include all fields:
```nix
meta = with lib; {
description = "...";
homepage = "...";
license = licenses.mit;
platforms = platforms.linux;
mainProgram = "...";
};
```
## PACKAGE PATTERNS
**Rust**: `rustPlatform.buildRustPackage rec { cargoLock.lockFile = src + "/Cargo.lock"; }`
**Shell**: `writeShellScriptBin "name" ''script''` or `mkDerivation` with custom `installPhase`
**AppImage**: `appimageTools.wrapType2 { ... }`
**Custom fetcher**: `fetchFromGitea { domain = "code.m3ta.dev"; owner = "m3tam3re"; ... }`
## MODULE PATTERNS
**Simple**: `options.cli.name = { enable = mkEnableOption "..."; }; config = mkIf cfg.enable { ... };`
**Multiple**: `config = mkMerge [ (mkIf cfg.x.enable { ... }) (mkIf cfg.y.enable { ... }) ];`
**Shared lib**: `portsLib = import ../../lib/ports.nix { inherit lib; }; portHelpers = portsLib.mkPortHelpers { ... };`
## 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 ```bash
nix flake check # Validate flake bd ready # Find available work
nix fmt # Format (alejandra) bd show <id> # View issue details
nix build .#<pkg> # Build package bd update <id> --claim # Claim work atomically
nix flake show # List outputs bd close <id> # Complete work
nix develop # Enter dev shell bd dolt push # Push beads data to remote
nix develop .#python # Python shell
nix develop .#devops # DevOps shell
# In dev shell only:
statix check . # Lint
deadnix . # Find dead code
``` ```
## ANTI-PATTERNS ## Non-Interactive Shell Commands
| Don't | Do Instead | **ALWAYS use non-interactive flags** with file operations to avoid hanging on confirmation prompts.
| ------------------------- | ------------------------------------------------------------------- |
| `lib.fakeHash` in commits | Get real hash: `nix build`, copy from error |
| Flat module files | Organize by category (`cli/`, `coding/`) |
| Hardcode ports | Use `m3ta.ports` module |
| Skip meta fields | Include all: description, homepage, license, platforms, mainProgram |
| `with pkgs;` in modules | Explicit `pkgs.package` or `with pkgs; [ ... ]` in lists only |
## COMMIT FORMAT Shell commands like `cp`, `mv`, and `rm` may be aliased to include `-i` (interactive) mode on some systems, causing the agent to hang indefinitely waiting for y/n input.
``` **Use these forms instead:**
type: brief description ```bash
# Force overwrite without prompting
cp -f source dest # NOT: cp source dest
mv -f source dest # NOT: mv source dest
rm -f file # NOT: rm file
# For recursive operations
rm -rf directory # NOT: rm -r directory
cp -rf source dest # NOT: cp -r source dest
``` ```
Types: `feat`, `fix`, `docs`, `style`, `refactor`, `chore` **Other commands that may prompt:**
- `scp` - use `-o BatchMode=yes` for non-interactive
- `ssh` - use `-o BatchMode=yes` to fail instead of prompting
- `apt-get` - use `-y` flag
- `brew` - use `HOMEBREW_NO_AUTO_UPDATE=1` env var
## NOTES <!-- BEGIN BEADS INTEGRATION v:1 profile:minimal hash:ca08a54f -->
## Beads Issue Tracker
- **Hash fetching**: Use `lib.fakeHash` initially, build to get real hash This project uses **bd (beads)** for issue tracking. Run `bd prime` to see full workflow context and commands.
- **HM modules**: Category subdirs (`cli/`, `coding/`) have own `default.nix` aggregators
- **Ports module**: Different for NixOS vs HM (HM adds `generateEnvVars` option)
- **Overlays**: `modifications` overlay uses `{prev}:` pattern, not `{final, prev}:`
- **Dev shell tools**: `statix`, `deadnix` only available inside `nix develop`
- **Automated package updates**: Packages are automatically updated weekly via Gitea Actions using `nix-update`. Review PRs from the automation before merging. For urgent updates, manually run the workflow or update manually.
## Task Management ### Quick Reference
This project uses **td** for tracking tasks across AI coding sessions. ```bash
Run `td usage --new-session` at conversation start to see current work. bd ready # Find available work
Use `td usage -q` for subsequent reads. bd show <id> # View issue details
bd update <id> --claim # Claim work
bd close <id> # Complete work
```
**Quick reference:** ### Rules
- `td usage --new-session` - Start new session and view tasks - Use `bd` for ALL task tracking — do NOT use TodoWrite, TaskCreate, or markdown TODO lists
- `td usage -q` - Quick view of current tasks (subsequent reads) - Run `bd prime` for detailed command reference and session close protocol
- `td version` - Check version - Use `bd remember` for persistent knowledge — do NOT use MEMORY.md files
For full workflow details, see the [td documentation](./docs/packages/td.md). ## Session Completion
## MIGRATION: Agent System (OpenCode → Canonical TOML) **When ending a work session**, you MUST complete ALL steps below. Work is NOT complete until `git push` succeeds.
The agent system was migrated from embedded `agents.json` to harness-agnostic **MANDATORY WORKFLOW:**
canonical `agent.toml` + `system-prompt.md` in the AGENTS repo. Renderers in
`lib/agents.nix` generate tool-specific configs.
### What changed in this repo 1. **File issues for remaining work** - Create issues for anything that needs follow-up
2. **Run quality gates** (if code changed) - Tests, linters, builds
3. **Update issue status** - Close finished work, update in-progress items
4. **PUSH TO REMOTE** - This is MANDATORY:
```bash
git pull --rebase
bd dolt push
git push
git status # MUST show "up to date with origin"
```
5. **Clean up** - Clear stashes, prune remote branches
6. **Verify** - All changes committed AND pushed
7. **Hand off** - Provide context for next session
- **`lib/agents.nix`**: New — 3 renderers (OpenCode, Claude Code, Pi) + dispatcher + shellHook **CRITICAL RULES:**
- **`lib/coding-rules.nix`**: Renamed from `opencode-rules.nix`, `mkCodingRules` replaces `mkOpencodeRules` - Work is NOT complete until `git push` succeeds
- **`modules/home-manager/coding/agents/`**: New — per-tool HM sub-modules - NEVER stop before pushing - that leaves work stranded locally
- **`modules/home-manager/coding/opencode.nix`**: Slimmed — no longer handles agents/skills/context - NEVER say "ready to push when you are" - YOU must push
- **`flake.nix`**: Exports new `agents` HM module - If push fails, resolve and retry until it succeeds
<!-- END BEADS INTEGRATION -->
### 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 Normal file
View File

@@ -0,0 +1,108 @@
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/).
## [Unreleased]
### Changed
- Remove duplicate opencode-rules.nix (backward-compat alias preserved)
- Tool-agnostic naming in coding-rules lib internals
- Remove redundant overlay entries for non-existent flake inputs
- Remove redundant 'additions' overlay (identical to 'default')
### Removed
- Dead overlay entries for non-existent flake inputs
- Legacy `mkOpencodeRules` alias and `lib.opencode-rules` backward-compat entry (use `mkCodingRules` / `lib.coding-rules`)
## [0.4.0] - 2026-04-15
### Added
- Pi-agent wrapper with systemd sandbox and per-host-user policy
- Containerized Pi agent
- `lib.agents.nix` with loadCanonical, renderers (OpenCode, Claude Code, Pi), and shellHook
- `lib.coding-rules` helper for per-project rule injection (renamed from opencode-rules)
- Home Manager modules for coding agents: `claude-code`, `opencode`, `pi`
- Agents rework with canonical TOML format and harness-agnostic renderers
- `vibetyper` and `eigent` packages
- `openspec` package
- `basecamp-cli` package
- `openshell` package (0.0.14 through 0.0.23)
- `openwork` package
- Opencode config moved into m3ta-nixpkgs
- Opencode dev shell with mkCodingRules demo
### Changed
- OpenCode flake input updated through v1.1.65 to v1.3.6
- Switched from local opencode package to upstream flake input
- Removed opencode-desktop (awaiting upstream fix), later re-enabled
- Nix eval warnings resolved
- Flake inputs updated throughout
### Fixed
- Pi settings sync
- Remove openwork sidecars in preFixup to prevent .opencode-wrapped conflict
- Remove sidecar binaries from openwork $out/bin to fix buildEnv conflict
- Vibetyper .desktop entry
- Opencode module formatting
- Formatting opencode module
## [0.3.0] - 2026-02-20
### Added
- `notesmd-cli` package with flake checks
- `sidecar` and `td` packages
- `opencode-desktop` package with Wayland support
- `mem0` package (1.0.2 through 1.0.9)
- `kestracli` / `kestractl` package (1.0.0 to 1.2.2)
### Changed
- Nix-update CI workflow optimized with caching and parallel processing
- Restructured n8n version handling for nix-update compatibility
- Switched formatter from nixpkgs-fmt to alejandra
- Replace local opencode with upstream flake input v1.1.27
### Fixed
- n8n build error
- n8n pnpm hash
- n8n update script
- Gitea runner opencode.url flake input
- nix-update workflow: YAML syntax, jobs indentation, PR body formatting
- Arithmetic increment failing with set -e in nix-update workflow
- Removed magic-nix-cache-action causing platform mapping error
- Opencode bun version requirement patched to match upstream lockfile
- Deprecated opencode update logic removed
- nix fmt without arg in workflow
- Extra Lua config renamed initLua
- Stt-ptt use pkill for better process management
## [0.2.0] - 2026-01-13
### Added
- Gitea Actions workflow for automated package updates with nix-update
- `n8n`, `beads`, and `opencode` packages
- `stt-ptt` package with auto-language detection
- `rofi-project-opener` for rofi-based project launching
- Hierarchical AGENTS.md knowledge base
- Dev shell structure with python and devops shells
- Port management modules (NixOS + Home Manager)
- Port helper library (`lib/ports.nix`)
### Changed
- Beads updated through v0.49.1
- N8n updated through v2.8.1
- Opencode updated through v1.1.18
- Documentation expanded with comprehensive patterns and HM module docs
### Fixed
- Python env version fix for marimo
## [0.1.0] - 2025-10-04
### Added
- Initial flake setup with packages, overlays, modules, and shells
- NixOS and Home Manager module infrastructure
- `lib/` shared utilities
- `overlays/mods/` for package modifications
- `templates/` for new packages/modules
- `examples/` for usage documentation

View File

@@ -38,24 +38,16 @@ nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#zellij-ps
## Available Packages ## Available Packages
| Package | Description | See [📦 Packages](./docs/packages/) for the full index with descriptions.
| ------------------ | ------------------------------------- |
| `code2prompt` | Convert code to prompts | Quick reference — build any package directly:
| `hyprpaper-random` | Random wallpaper setter for Hyprpaper |
| `kestractl` | CLI for the Kestra workflow orchestration platform | ```bash
| `launch-webapp` | Launch web applications | nix build git+https://code.m3ta.dev/m3tam3re/nixpkgs#<package-name>
| `mem0` | AI memory assistant with vector storage | nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#<package-name>
| `msty-studio` | Msty Studio application | ```
| `n8n` | Free and source-available fair-code licensed workflow automation tool |
| `notesmd-cli` | Obsidian CLI (Community) - Interact with Obsidian in the terminal | Notable packages: `sidecar`, `td`, `code2prompt`, `mem0`, `n8n`, `zellij-ps`.
| `opencode-desktop` | OpenCode Desktop App with Wayland support (includes workaround for upstream issue #11755) |
| `pomodoro-timer` | Pomodoro timer utility |
| `rofi-project-opener` | Rofi-based project launcher |
| `sidecar` | Companion tool for CLI agents with diffs, file trees, and task management |
| `stt-ptt` | Push to Talk Speech to Text |
| `td` | Minimalist CLI for tracking tasks across AI coding sessions |
| `tuxedo-backlight` | Backlight control for Tuxedo laptops |
| `zellij-ps` | Project switcher for Zellij |
## Automated Package Updates ## Automated Package Updates

View File

@@ -20,29 +20,16 @@ Step-by-step guides for common tasks:
- [Getting Started](./guides/getting-started.md) - Initial setup and basic usage - [Getting Started](./guides/getting-started.md) - Initial setup and basic usage
- [Adding Packages](./guides/adding-packages.md) - How to add new packages - [Adding Packages](./guides/adding-packages.md) - How to add new packages
- [Adding Modules](./guides/adding-modules.md) - How to add new NixOS or Home Manager modules
- [Port Management](./guides/port-management.md) - Managing service ports across hosts - [Port Management](./guides/port-management.md) - Managing service ports across hosts
- [Using Modules](./guides/using-modules.md) - Using NixOS and Home Manager modules - [Using Modules](./guides/using-modules.md) - Using NixOS and Home Manager modules
- [Development Workflow](./guides/development-workflow.md) - Development and testing workflow - [Development Workflow](./guides/development-workflow.md) - Development and testing workflow
### 📦 Packages ### 📦 Packages
Documentation for all custom packages: - [Packages Index](./packages/) - All packages with descriptions
- [Adding Packages](../guides/adding-packages.md) - How to add new packages
- [code2prompt](./packages/code2prompt.md) - Convert code to prompts - [Templates](../templates.md) - Boilerplate templates
- [hyprpaper-random](./packages/hyprpaper-random.md) - Random wallpaper setter for Hyprpaper
- [kestractl](./packages/kestractl.md) - CLI for the Kestra workflow orchestration platform
- [launch-webapp](./packages/launch-webapp.md) - Launch web applications
- [mem0](./packages/mem0.md) - AI memory assistant with vector storage
- [msty-studio](./packages/msty-studio.md) - Msty Studio application
- [n8n](./packages/n8n.md) - Free and source-available fair-code licensed workflow automation tool
- [notesmd-cli](./packages/notesmd-cli.md) - Obsidian CLI (Community) - Interact with Obsidian in the terminal
- [pomodoro-timer](./packages/pomodoro-timer.md) - Pomodoro timer utility
- [rofi-project-opener](./packages/rofi-project-opener.md) - Rofi-based project launcher with custom args
- [sidecar](./packages/sidecar.md) - Companion tool for CLI agents with diffs, file trees, and task management
- [stt-ptt](./packages/stt-ptt.md) - Push to Talk Speech to Text using Whisper
- [td](./packages/td.md) - Minimalist CLI for tracking tasks across AI coding sessions
- [tuxedo-backlight](./packages/tuxedo-backlight.md) - Backlight control for Tuxedo laptops
- [zellij-ps](./packages/zellij-ps.md) - Project switcher for Zellij
### ⚙️ Modules ### ⚙️ Modules
@@ -68,6 +55,7 @@ Technical references and APIs:
- [Functions](./reference/functions.md) - Library functions documentation - [Functions](./reference/functions.md) - Library functions documentation
- [Patterns](./reference/patterns.md) - Code patterns and anti-patterns - [Patterns](./reference/patterns.md) - Code patterns and anti-patterns
- [Templates](../templates.md) - Boilerplate for packages and modules
## Repository Structure ## Repository Structure

View File

@@ -0,0 +1,261 @@
# Adding Modules Guide
How to add new NixOS and Home Manager modules to m3ta-nixpkgs.
## Overview
Modules extend your system or user configuration with reusable, declarative options. m3ta-nixpkgs uses the standard NixOS module system with a `m3ta.*` namespace.
## Quick Start
Use a template for quick setup:
```bash
# NixOS module
nix flake init -t .#nixos-module my-module
# Home Manager module
nix flake init -t .#home-manager-module my-module
```
This copies the template into `templates/` — move it to the appropriate location and customize.
## Adding a NixOS Module
### 1. Create the Module File
Create `modules/nixos/<my-module>.nix`:
```nix
{config, lib, pkgs, ...}:
with lib; let
cfg = config.m3ta.myModule;
in {
options.m3ta.myModule = {
enable = mkEnableOption "my module description";
# Add custom options here
someOption = mkOption {
type = types.str;
default = "default-value";
description = "Description of this option";
};
};
config = mkIf cfg.enable {
# System configuration goes here
environment.systemPackages = [pkgs.some-package];
# Or systemd services
systemd.services.my-service = {
enable = true;
description = "My service";
wantedBy = ["multi-user.target"];
serviceConfig = {
ExecStart = "${pkgs.some-package}/bin/some-daemon";
};
};
};
}
```
### 2. Register in the Aggregator
Add to `modules/nixos/default.nix`:
```nix
{
imports = [
./ports.nix
./mem0.nix
./<my-module>.nix # ← add your module
];
}
```
### 3. Export from flake.nix
Add to the `nixosModules` output in `flake.nix` (optional, for direct import):
```nix
nixosModules = {
default = ./modules/nixos;
ports = ./modules/nixos/ports.nix;
mem0 = ./modules/nixos/mem0.nix;
my-module = ./modules/nixos/<my-module>.nix; # ← add this
};
```
## Adding a Home Manager Module
Home Manager modules are organized by category under `modules/home-manager/`.
### Categories
| Category | Purpose | Location |
|----------|---------|----------|
| `cli/` | Command-line tools and utilities | `modules/home-manager/cli/` |
| `coding/` | Development tools, editors, agents | `modules/home-manager/coding/` |
| Root | Cross-cutting concerns (e.g., ports) | `modules/home-manager/` |
### 1. Choose a Category
- **CLI tools** (zsh plugins, tmux config, etc.) → `cli/`
- **Development tools** (editor config, linters, etc.) → `coding/`
- **System-wide settings** (ports, environment) → root level
### 2. Create the Module File
Create `modules/home-manager/<category>/<my-module>.nix`:
```nix
{config, lib, pkgs, ...}:
with lib; let
cfg = config.m3ta.myModule;
in {
options.m3ta.myModule = {
enable = mkEnableOption "my user module description";
someOption = mkOption {
type = types.str;
default = "value";
description = "An option for this module";
};
};
config = mkIf cfg.enable {
home.packages = [pkgs.some-package];
# Or Home Manager-specific options
programs.zsh.enable = true;
};
}
```
### 3. Register in the Category Aggregator
For `cli/` modules, add to `modules/home-manager/cli/default.nix`:
```nix
{
imports = [
./rofi-project-opener.nix
./stt-ptt.nix
./zellij-ps.nix
./<my-module>.nix # ← add your module
];
}
```
For `coding/` modules, add to `modules/home-manager/coding/default.nix`:
```nix
{
imports = [
./editors.nix
./opencode.nix
./agents
./<my-module>.nix # ← add your module
];
}
```
### 4. Export from flake.nix
Add to `homeManagerModules` in `flake.nix`:
```nix
homeManagerModules = {
default = import ./modules/home-manager;
my-module = import ./modules/home-manager/<category>/<my-module>.nix; # ← add this
};
```
## Module Patterns
### Standard Enable Option
Always start with `mkEnableOption`:
```nix
options.m3ta.myModule = {
enable = mkEnableOption "my module";
};
```
### Conditional Configuration
Use `mkIf` for conditional config:
```nix
config = mkIf cfg.enable {
# Only applied when enabled
};
```
### Multiple Conditions
Use `mkMerge` when combining multiple conditional blocks:
```nix
config = mkMerge [
(mkIf cfg.feature1.enable { ... })
(mkIf cfg.feature2.enable { ... })
];
```
### Nested Namespaces
For logically grouped options, use nested namespaces:
```nix
options.m3ta.coding = {
myTool = {
enable = mkEnableOption "my coding tool";
# ...
};
};
```
Usage: `m3ta.coding.myTool.enable = true;`
### Shared Library Functions
For shared utilities (port helpers, etc.), import from `lib/`:
```nix
let
portsLib = import ../../lib/ports.nix {inherit lib;};
portHelpers = portsLib.mkPortHelpers { /* ... */ };
in {
# use portHelpers
}
```
## Documentation
Add documentation for your module:
1. Create `docs/modules/nixos/<my-module>.md` (NixOS) or `docs/modules/home-manager/<category>/<my-module>.md` (HM)
2. Follow the existing format in `docs/modules/`
3. Add it to the appropriate overview page's "Available Modules" list
4. Link it from `docs/guides/using-modules.md`
## Testing
```bash
# Validate the module loads correctly
nix flake check
# Test with a minimal configuration (NixOS)
nixos-rebuild dry-build -I nixpkgs=. --option experimental-features flakes
# Format before commit
nix fmt
```
## Related
- [Using Modules](./using-modules.md) - How to use existing modules
- [Port Management](./port-management.md) - Centralized port management
- [Development Workflow](./development-workflow.md) - Local development
- [Adding Packages](./adding-packages.md) - Adding packages (not modules)
- [Architecture](../ARCHITECTURE.md) - Repository structure

View File

@@ -662,5 +662,7 @@ nix eval .#nixosConfigurations.hostname.config.m3ta --apply builtins.attrNames
- [Port Management](./port-management.md) - Detailed port management guide - [Port Management](./port-management.md) - Detailed port management guide
- [Adding Packages](./adding-packages.md) - How to add new packages - [Adding Packages](./adding-packages.md) - How to add new packages
- [Adding Modules](./adding-modules.md) - How to add new NixOS or Home Manager modules
- [Templates](../templates.md) - Boilerplate for new packages and modules
- [Architecture](../ARCHITECTURE.md) - Understanding module structure - [Architecture](../ARCHITECTURE.md) - Understanding module structure
- [Contributing](../CONTRIBUTING.md) - Code style and guidelines - [Contributing](../CONTRIBUTING.md) - Code style and guidelines

53
docs/packages/README.md Normal file
View File

@@ -0,0 +1,53 @@
# Packages
Documentation for packages in m3ta-nixpkgs. Each package directory may contain a `README.md` with detailed documentation.
## Index
Packages are organized in `pkgs/<name>/`. Add a `README.md` inside a package directory to document it here.
### Local Packages
These packages are built from source in `pkgs/<name>/`:
| Package | Description | Type | Location |
|---------|-------------|------|----------|
| `sidecar` | Companion tool for CLI agents with diffs, file trees, and task management | Go | `pkgs/sidecar/` |
| `td` | Minimalist CLI for tracking tasks across AI coding sessions | Go | `pkgs/td/` |
| `code2prompt` | Convert code to prompts | Go | `pkgs/code2prompt/` |
| `eigent` | Eigenvalue tool | Python | `pkgs/eigent/` |
| `hyprpaper-random` | Random wallpaper setter for Hyprpaper | Shell | `pkgs/hyprpaper-random/` |
| `kestractl` | CLI for Kestra workflow orchestration | Go | `pkgs/kestractl/` |
| `launch-webapp` | Launch web applications | Shell | `pkgs/launch-webapp/` |
| `mem0` | AI memory assistant with vector storage | Python | `pkgs/mem0/` |
| `msty-studio` | Msty Studio application | Python | `pkgs/msty-studio/` |
| `n8n` | Workflow automation tool | Node.js | `pkgs/n8n/` |
| `openshell` | AI shell assistant | Go | `pkgs/openshell/` |
| `pomodoro-timer` | Pomodoro timer utility | Shell | `pkgs/pomodoro-timer/` |
| `rofi-project-opener` | Rofi-based project launcher | Shell | `pkgs/rofi-project-opener/` |
| `stt-ptt` | Push to Talk Speech to Text | Python | `pkgs/stt-ptt/` |
| `tuxedo-backlight` | Backlight control for Tuxedo laptops | C | `pkgs/tuxedo-backlight/` |
| `vibetyper` | Typing practice tool | Python | `pkgs/vibetyper/` |
| `zellij-ps` | Project switcher for Zellij | Rust | `pkgs/zellij-ps/` |
### Pass-Through Packages
These packages are imported directly from flake inputs with minor modifications:
| Package | Source | Modification | Location |
|---------|--------|-------------|----------|
| `opencode-desktop` | `inputs.opencode` | Tauri desktop wrapper + Wayland fix | `pkgs/opencode-desktop/` |
## Adding Package Documentation
To document a package in detail, add a `README.md` inside the package directory (e.g., `pkgs/sidecar/README.md`). This guide indexes all packages and provides a quick overview.
## Automated Updates
Packages are automatically updated weekly by the Gitea Actions `nix-update` workflow. See the main README for details.
## Related
- [Adding Packages](../guides/adding-packages.md) - How to add new packages
- [Architecture](../ARCHITECTURE.md) - Repository structure
- [Quick Start](../QUICKSTART.md) - Getting started

View File

@@ -1,117 +0,0 @@
# notesmd-cli
Obsidian CLI (Community) - Interact with Obsidian in the terminal.
## Description
notesmd-cli is a command-line interface for interacting with Obsidian, the popular knowledge management and note-taking application. It allows you to create, search, and manipulate notes directly from the terminal.
## Features
- 📝 **Note Creation**: Create new notes from the command line
- 🔍 **Search**: Search through your Obsidian vault
- 📂 **Vault Management**: Interact with your vault structure
- 🔗 **WikiLink Support**: Work with Obsidian's WikiLink format
- 🏷️ **Tag Support**: Manage and search by tags
-**Fast**: Lightweight Go binary with no external dependencies
## Installation
### Via Overlay
```nix
{pkgs, ...}: {
environment.systemPackages = with pkgs; [
notesmd-cli
];
}
```
### Direct Reference
```nix
{pkgs, ...}: {
environment.systemPackages = with pkgs; [
inputs.m3ta-nixpkgs.packages.${pkgs.system}.notesmd-cli
];
}
```
### Run Directly
```bash
nix run git+https://code.m3ta.dev/m3tam3re/nixpkgs#notesmd-cli
```
## Usage
### Basic Commands
```bash
# Show help
notesmd-cli --help
# Create a new note
notesmd-cli new "My Note Title"
# Search notes
notesmd-cli search "search term"
# List notes
notesmd-cli list
```
### Working with Vaults
```bash
# Specify vault path
notesmd-cli --vault /path/to/vault new "Note Title"
# Open a note in Obsidian
notesmd-cli open "Note Name"
```
### Advanced Usage
```bash
# Search with tags
notesmd-cli search --tag "project"
# Append to existing note
notesmd-cli append "Note Name" "Additional content"
```
## Configuration
### Environment Variables
- `OBSIDIAN_VAULT`: Default vault path
### Command Line Options
Run `notesmd-cli --help` for a complete list of options.
## Build Information
- **Version**: 0.3.0
- **Language**: Go
- **License**: MIT
- **Source**: [GitHub](https://github.com/Yakitrak/notesmd-cli)
- **Vendor Hash**: null (no external dependencies)
## Platform Support
- Linux
- macOS (Unix systems)
## Notes
- No vendor dependencies (pure Go stdlib)
- The binary is named `notesmd-cli` (not `notesmd`)
- This is the community CLI, not the official Obsidian CLI
## Related
- [Obsidian](https://obsidian.md) - The Obsidian application
- [Adding Packages](../guides/adding-packages.md) - How to add new packages
- [Quick Start](../QUICKSTART.md) - Getting started guide

View File

@@ -0,0 +1,637 @@
# m3ta-nixpkgs: Cleanup & Improvements Plan
> **For Hermes:** Use subagent-driven-development skill to implement this plan task-by-task.
**Goal:** Address 10 issues identified in codebase review — reduce duplication, improve naming consistency, extract inline scripts, add testing, and update documentation.
**Architecture:** Incremental improvements across lib/, modules/, overlays/, docs/, and CI. Each change is self-contained and can be merged independently. No breaking changes to public API (backward-compat aliases preserved where needed).
**Repo:** `gitea@code.m3ta.dev:m3tam3re/nixpkgs.git` (master branch)
---
## Phase 1: Deduplication & Naming (Low Risk)
### Task 1: Remove duplicate opencode-rules.nix file
**Objective:** Eliminate the duplicate file import. The `coding-rules.nix` is the canonical source; `opencode-rules.nix` is an identical copy. Make the alias a one-liner in `lib/default.nix`.
**Files:**
- Delete: `lib/opencode-rules.nix`
- Modify: `lib/default.nix`
**Step 1: Update lib/default.nix to alias directly**
```nix
{lib}: {
ports = import ./ports.nix {inherit lib;};
coding-rules = import ./coding-rules.nix {inherit lib;};
# Backward-compat alias: opencode-rules → coding-rules
opencode-rules = import ./coding-rules.nix {inherit lib;};
opencode = import ./coding-rules.nix {inherit lib;};
}
```
**Step 2: Delete the duplicate file**
```bash
git rm lib/opencode-rules.nix
```
**Step 3: Verify nothing breaks**
```bash
nix flake check
```
**Step 4: Commit**
```bash
git commit -m "refactor: remove duplicate opencode-rules.nix, use alias in default.nix"
```
---
### Task 2: Tool-agnostic naming in coding-rules.nix internals
**Objective:** Rename internal variables and output artifacts in `coding-rules.nix` from opencode-specific names to generic names, while keeping the backward-compat alias `mkOpencodeRules`.
**Files:**
- Modify: `lib/coding-rules.nix`
**Step 1: Rename internal symbols**
In `lib/coding-rules.nix`, rename:
- `rulesDir` stays `.opencode-rules` (this is a filesystem path used by existing projects, changing it would break)
- `opencodeConfig``rulesConfig`
- `opencode.json` output → `coding-rules.json` (add a comment noting it was renamed)
- Add `rulesDir` option to function signature with default `.opencode-rules`
Updated function:
```nix
{lib}: let
mkCodingRules = {
agents,
languages ? [],
concerns ? [
"coding-style"
"naming"
"documentation"
"testing"
"git-workflow"
"project-structure"
],
frameworks ? [],
extraInstructions ? [],
rulesDir ? ".opencode-rules",
}: let
instructions =
(map (c: "${rulesDir}/concerns/${c}.md") concerns)
++ (map (l: "${rulesDir}/languages/${l}.md") languages)
++ (map (f: "${rulesDir}/frameworks/${f}.md") frameworks)
++ extraInstructions;
rulesConfig = {
"$schema" = "https://opencode.ai/config.json";
inherit instructions;
};
in {
inherit instructions;
shellHook = ''
# Create/update symlink to AGENTS rules directory
ln -sfn ${agents}/rules ${rulesDir}
# Generate coding-rules configuration file
cat > coding-rules.json <<'RULES_EOF'
${builtins.toJSON rulesConfig}
RULES_EOF
'';
};
# Backward-compat alias
mkOpencodeRules = mkCodingRules;
in {
inherit mkCodingRules mkOpencodeRules;
};
```
**Step 2: Update shellHook comment in AGENTS.md**
In `AGENTS.md`, update the coding-rules section to mention the new `rulesDir` parameter and the `coding-rules.json` output file.
**Step 3: Verify**
```bash
nix flake check
```
**Step 4: Commit**
```bash
git commit -m "refactor: tool-agnostic naming in coding-rules.nix internals"
```
---
### Task 3: Remove redundant overlays entry in flake.nix
**Objective:** The `default` and `additions` overlays in `flake.nix` produce identical output. Remove `additions` if not referenced elsewhere, or document why both exist.
**Files:**
- Modify: `flake.nix`
- Check: all consumer repos for references to `overlays.additions`
**Step 1: Search for consumers of overlays.additions**
```bash
# Check nixos-config and other repos
grep -r "overlays.additions" /data/.hermes/repos/nixos-config/
grep -r "additions" /data/.hermes/repos/nixos-config/ --include="*.nix" | grep overlay
```
**Step 2: If no consumers found, remove additions**
In `flake.nix`, simplify overlays to:
```nix
overlays = {
default = final: prev:
import ./pkgs {
pkgs = final;
inputs = inputs;
};
modifications = final: prev: import ./overlays/mods {inherit prev;};
};
```
**Step 3: Verify**
```bash
nix flake check
```
**Step 4: Commit**
```bash
git commit -m "refactor: remove redundant 'additions' overlay (identical to 'default')"
```
**Note:** If `additions` IS used elsewhere, add a comment explaining the convention and skip this task.
---
## Phase 2: Extract Inline Scripts (Medium Risk)
### Task 4: Extract pi-agent runner script to standalone file
**Objective:** Move the ~200-line inline bash script in `modules/nixos/pi-agent.nix` (the `runner` variable) to a separate file `modules/nixos/pi-agent-runner.sh` that gets imported via `builtins.readFile` + `pkgs.writeShellApplication`.
**Files:**
- Create: `modules/nixos/pi-agent-runner.sh`
- Modify: `modules/nixos/pi-agent.nix`
**Step 1: Create the runner script file**
Extract the body of the `runner` script (everything inside the `pkgs.writeShellScriptBin cfg.wrapper.runnerName '' ... ''`) into `modules/nixos/pi-agent-runner.sh`.
The script uses Nix-style variable interpolation (`${...}`). We need to keep Nix template variables as `${...}` and convert runtime bash variables to use `$` prefix. Since the script already uses Nix `escapeShellArg` and `escapeShellArg` calls, the cleanest approach is:
Create `modules/nixos/pi-agent-runner.sh` as a template that `pkgs.substituteAll` or `builtins.readFile` + string replacement can process. However, given the heavy Nix interpolation, the pragmatic approach is to use `pkgs.writeShellApplication` with the script body inline but extracted to a `let` binding:
```nix
# In pi-agent.nix, replace the inline runner with:
let
runnerScript = builtins.readFile ./pi-agent-runner.sh;
# ... or keep as let binding but move the body to a separate derivation
```
**Important caveat:** The script has ~30 Nix variable interpolations (`${cfg.user}`, `${escapeShellArg ...}`, etc.). Full extraction to a .sh file would require either:
- (a) `substituteAll` with `--replace` for each variable — unwieldy at 30+ substitutions
- (b) Converting to env vars passed at runtime — cleaner but changes security posture
- (c) Keeping the Nix interpolation but extracting to a `let` block in a separate `.nix` file
**Recommended approach: Option (c)** — Create `modules/nixos/pi-agent-runner.nix` as a function that takes `cfg` and returns the script:
```nix
# modules/nixos/pi-agent-runner.nix
{cfg, pkgs, lib, ...}:
with lib; let
# ... all the helper variables from pi-agent.nix ...
in
pkgs.writeShellScriptBin cfg.wrapper.runnerName ''
# ... the script body ...
'';
```
Then in `pi-agent.nix`:
```nix
runner = import ./pi-agent-runner.nix {inherit cfg pkgs lib;};
```
**Step 2: Similarly extract the wrapper script**
Create `modules/nixos/pi-agent-wrapper.nix` for the `wrapper` variable.
**Step 3: Verify**
```bash
nix flake check
# Also test in a nixos-rebuild if possible
```
**Step 4: Commit**
```bash
git add modules/nixos/pi-agent-runner.nix modules/nixos/pi-agent-wrapper.nix
git commit -m "refactor: extract pi-agent runner and wrapper to separate files"
```
---
## Phase 3: Testing (Higher Value)
### Task 5: Add basic lib function tests
**Objective:** Add `nix eval`-based tests for `lib/agents.nix` parseRule logic and `lib/coding-rules.nix` instruction generation.
**Files:**
- Create: `tests/lib/agents-test.nix`
- Create: `tests/lib/coding-rules-test.nix`
- Modify: `flake.nix` (add checks)
**Step 1: Create test infrastructure**
```nix
# tests/lib/default.nix
{
agents = import ./agents-test.nix;
coding-rules = import ./coding-rules-test.nix;
}
```
**Step 2: Write agents.nix parseRule test**
```nix
# tests/lib/agents-test.nix
let
lib = import <nixpkgs/lib>;
agentsLib = (import ../../lib {inherit lib;}).agents;
# Test parseRule helper
test1 = let
result = builtins.tryEval (
let
# We can't directly test parseRule since it's internal.
# Instead, test the renderer with minimal input.
canonical = {
test-agent = {
description = "Test agent";
mode = "primary";
systemPrompt = "You are a test.";
permissions = {
bash = { intent = "allow"; };
edit = { intent = "ask"; rules = ["rm -rf *:deny"]; };
};
};
};
pkgs = import <nixpkgs> { system = "x86_64-linux"; };
rendered = agentsLib.renderForOpencode {
inherit pkgs canonical;
};
in
# Verify the derivation builds
builtins.pathExists "${rendered}/test-agent.md"
);
in assert result.value == true; true;
in {
parseRule-basic = test1;
}
```
**Step 3: Write coding-rules test**
```nix
# tests/lib/coding-rules-test.nix
let
lib = import <nixpkgs/lib>;
codingRulesLib = (import ../../lib {inherit lib;}).coding-rules;
rules = codingRulesLib.mkCodingRules {
agents = "/tmp/fake-agents";
languages = ["python"];
concerns = ["naming"];
rulesDir = ".coding-rules";
};
# Verify instructions are generated correctly
test1 = assert rules.instructions == [
".coding-rules/concerns/naming.md"
".coding-rules/languages/python.md"
]; true;
# Verify backward-compat alias exists
test2 = assert codingRulesLib.mkOpencodeRules == codingRulesLib.mkCodingRules; true;
in {
instructions-correct = test1;
backward-compat = test2;
}
```
**Step 4: Add to flake.nix checks**
In `flake.nix`, extend the `checks` attribute:
```nix
checks = forAllSystems (system: let
pkgs = pkgsFor system;
packages = import ./pkgs {inherit pkgs inputs;};
in
builtins.mapAttrs (name: pkg: pkgs.lib.hydraJob pkg) packages
// {
formatting = pkgs.runCommand "check-formatting" {} ''
${pkgs.alejandra}/bin/alejandra --check ${./.}
touch $out
'';
lib-tests = pkgs.runCommand "lib-tests" {} ''
${pkgs.nix}/bin/nix-instantiate --eval ${./tests/lib/default.nix}
touch $out
'';
});
```
**Step 5: Verify**
```bash
nix flake check
```
**Step 6: Commit**
```bash
git add tests/
git commit -m "test: add basic lib function tests for agents and coding-rules"
```
---
### Task 6: Add NixOS VM test for pi-agent module
**Objective:** Add a basic NixOS VM test that verifies the pi-agent module can be evaluated and the wrapper/runner scripts exist.
**Files:**
- Create: `tests/nixos/pi-agent-test.nix`
- Modify: `flake.nix` (add to checks)
**Step 1: Write the VM test**
```nix
# tests/nixos/pi-agent-test.nix
{pkgs, ...}: {
name = "pi-agent";
nodes.machine = {config, ...}: {
imports = [
${(pkgs.path + "/nixos/modules/module-list.nix")}
];
# Minimal pi-agent config
m3ta.pi-agent = {
enable = true;
package = pkgs.writeScriptBin "pi-agent" ''
#!/bin/sh
echo "pi-agent mock"
'';
createUser = true;
hostUsers = {
testuser = {
projectRoots = ["/tmp/test-project"];
};
};
};
users.users.testuser = {
isNormalUser = true;
};
};
testScript = ''
machine.start()
machine.wait_for_unit("multi-user.target")
# Verify user was created
machine.succeed("id pi-agent")
# Verify wrapper exists
machine.succeed("which pi")
# Verify state directory
machine.succeed("test -d /var/lib/pi-agent")
machine.succeed("test -d /var/lib/pi-agent/.pi")
'';
}
```
**Step 2: Add to flake.nix checks**
```nix
# In the checks attrset:
pi-agent-vm-test = pkgs.nixosTest (import ./tests/nixos/pi-agent-test.nix {inherit pkgs;});
```
**Step 3: Verify**
```bash
nix build .#checks.x86_64-linux.pi-agent-vm-test
```
**Step 4: Commit**
```bash
git add tests/nixos/
git commit -m "test: add NixOS VM test for pi-agent module"
```
---
## Phase 4: Documentation (Low Risk, High Value)
### Task 7: Update AGENTS.md to reflect current state
**Objective:** Remove outdated migration sections, update function signatures, and align with current code.
**Files:**
- Modify: `AGENTS.md`
**Step 1: Update the AGENTS REWORK migration section**
The section starting with `## MIGRATION: Agent System (OpenCode → Canonical TOML)` describes a completed migration. Convert it to a brief "Architecture" section that describes the current state, not the migration path.
**Step 2: Update lib.agents function table**
Verify that the function signatures and descriptions in the AGENTS.md table match the actual functions in `lib/agents.nix`. Specifically:
- `loadCanonical` takes `{agentsInput}` — confirm docs match
- `renderForPi` now has `primaryAgent` parameter — confirm documented
- `shellHookForTool` exists — confirm documented
**Step 3: Update coding-rules documentation**
Replace references to `mkOpencodeRules` with `mkCodingRules` as primary, `mkOpencodeRules` as backward-compat alias. Document the new `rulesDir` parameter.
**Step 4: Update overlay documentation**
Remove or annotate the `additions` overlay depending on Task 3 outcome.
**Step 5: Commit**
```bash
git commit -m "docs: update AGENTS.md to reflect current codebase state"
```
---
### Task 8: Add CHANGELOG.md
**Objective:** Create a changelog that captures recent work (from git log) so consumers can track changes.
**Files:**
- Create: `CHANGELOG.md`
**Step 1: Generate changelog from git history**
```bash
cd /data/.hermes/repos/nixpkgs-review
git log --oneline --no-merges master | head -30
```
**Step 2: Write CHANGELOG.md**
Structure as Keep a Changelog format:
```markdown
# Changelog
All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/).
## [Unreleased]
## [0.4.0] - 2026-04-15
### Added
- Pi agent wrapper with per-host-user policy enforcement (`m3ta.pi-agent` NixOS module)
- `coding.agents.pi` Home Manager module with settings, MCP, and skills support
- `coding.agents.claude-code` Home Manager module with MCP integration
- Automated package updates via Gitea Actions (`nix-update` workflow)
- `lib.agents.renderForPi` with primaryAgent selection and pi-subagents format
- `pkgs/td` - Task management CLI for AI coding sessions
### Changed
- Renamed `lib.opencode-rules``lib.coding-rules` (backward-compat alias preserved)
- Agent system migrated to harness-agnostic canonical format
- Pi settings sync now merges host and Nix-managed values via deep_merge
### Fixed
- Pi settings sync race condition on first run
```
**Step 3: Commit**
```bash
git commit -m "docs: add CHANGELOG.md"
```
---
## Phase 5: Minor Cleanups (Low Risk)
### Task 9: Clean up pkgs/default.nix unused `system` binding
**Objective:** The `system = pkgs.stdenv.hostPlatform.system;` binding in `pkgs/default.nix` is only used for the two input-pass-throughs. If those are the only consumers, it's fine, but add a clarifying comment.
**Files:**
- Modify: `pkgs/default.nix`
**Step 1: Add clarifying comment**
```nix
{
pkgs,
inputs,
...
}: let
# Only used for flake input pass-throughs below
system = pkgs.stdenv.hostPlatform.system;
in {
...
```
**Step 2: Commit**
```bash
git commit -m "docs: clarify system binding in pkgs/default.nix"
```
---
### Task 10: Remove commented-out overlay entries in overlays/default.nix
**Objective:** Clean up the large block of commented-out code in `overlays/default.nix` (nodejs_24, paperless-ngx, anytype-heart, hyprpanel, etc.). These belong in git history, not in active code.
**Files:**
- Modify: `overlays/default.nix`
**Step 1: Remove commented-out blocks**
Remove:
- The `rose-pine-hyprcursor` addition from `additions` (if it's unused — check with grep)
- The commented-out `nodejs_24`, `paperless-ngx`, `anytype-heart`, `trezord`, `mesa`, `hyprpanel` blocks from `modifications`
- The commented-out overlay inputs (`temp-packages`, `stable-packages`, `pinned-packages`, `locked-packages`, `master-packages`) if they reference inputs not in `flake.nix`
Actually, `nixpkgs-stable`, `nixpkgs-9e9486b`, `nixpkgs-9472de4`, `nixpkgs-locked`, `nixpkgs-master` are NOT in the current `flake.nix` inputs. These overlays will fail if referenced. They should either be removed or the inputs should be added.
**Action:**
- Keep `master-packages` IF `nixpkgs-master` is in flake.nix inputs (it IS — good)
- Remove `temp-packages`, `pinned-packages`, `locked-packages` (inputs don't exist)
- Keep `stable-packages` IF `nixpkgs-stable` exists in inputs (check — it does NOT currently exist)
- Keep `additions` with `rose-pine-hyprcursor` IF `rose-pine-hyprcursor` input exists (check)
**Step 2: Verify**
```bash
nix flake check
```
**Step 3: Commit**
```bash
git commit -m "chore: remove dead overlay entries for non-existent flake inputs"
```
---
## Execution Order & Priority
| Task | Risk | Effort | Impact | Dependencies |
|------|------|--------|--------|-------------|
| T1: Remove opencode-rules.nix | Low | 5min | Clean | None |
| T2: Tool-agnostic naming | Low | 15min | Consistency | None |
| T3: Remove redundant overlay | Low | 10min | Clean | Check consumers |
| T9: Clarify system binding | Low | 2min | Docs | None |
| T10: Remove dead overlays | Low | 10min | Clean | None |
| T7: Update AGENTS.md | Low | 20min | Docs | After T1, T2 |
| T8: Add CHANGELOG.md | Low | 15min | Docs | None |
| T4: Extract pi-agent scripts | Medium | 45min | Maintainability | None |
| T5: Lib function tests | Medium | 30min | Quality | None |
| T6: NixOS VM test | Medium | 45min | Quality | None |
**Recommended order:** T1 → T9 → T10 → T3 → T2 → T7 → T8 → T5 → T4 → T6
**Branching strategy:** Create a feature branch `chore/cleanup-review` from master, implement all tasks, open PR for review before merging.

View File

@@ -153,33 +153,6 @@ allServices = portHelpers.listServices;
# Returns: ["nginx" "grafana" "prometheus" "homepage"] # Returns: ["nginx" "grafana" "prometheus" "homepage"]
``` ```
### `getDefaultPort`
Simple helper to get a port without host override.
#### Signature
```nix
getDefaultPort :: portsConfig -> string -> int-or-null
```
#### Arguments
1. `portsConfig` - Same structure as `mkPortHelpers`
2. `service` - The service name (string)
#### Returns
Port number (int) or `null` if service not found.
#### Usage
```nix
services.my-service = {
port = m3taLib.ports.getDefaultPort myPorts "my-service";
};
```
## Using Library Functions ## Using Library Functions
### Importing ### Importing
@@ -262,7 +235,7 @@ in {
| `getPort` | Get port with optional host override | `int or null` | | `getPort` | Get port with optional host override | `int or null` |
| `getHostPorts` | Get all ports for host | `attrs` | | `getHostPorts` | Get all ports for host | `attrs` |
| `listServices` | List all service names | `[string]` | | `listServices` | List all service names | `[string]` |
| `getDefaultPort` | Get default port only | `int or null` |
## Related ## Related

162
docs/templates.md Normal file
View File

@@ -0,0 +1,162 @@
# Templates
Boilerplate templates for quickly adding new packages or modules to m3ta-nixpkgs.
## Available Templates
| Template | Command | Creates |
|---------|---------|---------|
| Package | `nix flake init -t .#package` | `templates/package/` |
| NixOS Module | `nix flake init -t .#nixos-module` | `templates/nixos-module/` |
| Home Manager Module | `nix flake init -t .#home-manager-module` | `templates/home-manager-module/` |
## Using Templates
### 1. List Available Templates
```bash
nix flake show --templates .
```
### 2. Initialize from a Template
```bash
# Package
nix flake init -t .#package
# NixOS Module
nix flake init -t .#nixos-module
# Home Manager Module
nix flake init -t .#home-manager-module
```
Note: `nix flake init` copies the template contents into the current directory. Use a subdirectory name:
```bash
mkdir new-package && cd new-package
nix flake init -t ..#package
```
## Package Template
Creates a complete package structure:
```
templates/package/
├── default.nix # Package definition with comments
```
### Fields to Fill In
| Field | Location | Notes |
|-------|----------|-------|
| `pname` | `default.nix` | Package name (kebab-case) |
| `version` | `default.nix` | Semantic version |
| `src` | `default.nix` | Fetcher (GitHub, URL, Git, etc.) |
| `hash` | `default.nix` | Use `lib.fakeHash`, build to get real hash |
| `meta.description` | `default.nix` | Short one-line description |
| `meta.homepage` | `default.nix` | Project URL |
| `meta.license` | `default.nix` | Use `lib.licenses.*` |
| `meta.platforms` | `default.nix` | Usually `platforms.linux` |
| `meta.mainProgram` | `default.nix` | Main binary name |
### Common Build Systems
```nix
# Rust (recommended)
rustPlatform.buildRustPackage rec { ... }
# Python
python3.pkgs.buildPythonPackage rec { ... }
# Node.js
pkg-config, nodejs, npm2nix, or pnpm + prisma
# Shell script
writeShellScriptBin "name" ''echo hello''
# Go
go mdbook build
# Generic C/Make
stdenv.mkDerivation { ... }
```
See [Adding Packages](./guides/adding-packages.md) for detailed instructions.
## NixOS Module Template
Creates a complete NixOS module:
```
templates/nixos-module/
├── default.nix # Module with options
└── README.md # Module documentation
```
### Fields to Fill In
| Field | Location | Notes |
|-------|----------|-------|
| Module name | `default.nix` | File name matches `m3ta.<name>` |
| Options | `default.nix` | Add under `options.m3ta.<name>` |
| Config | `default.nix` | Add under `config.m3ta.<name>` |
| Description | `README.md` | What the module does |
### After Creating
1. Add to `modules/nixos/default.nix` imports
2. Optionally export from `flake.nix` `nixosModules`
3. Add documentation to `docs/modules/nixos/`
4. Run `nix flake check`
## Home Manager Module Template
Creates a complete Home Manager module:
```
templates/home-manager-module/
├── default.nix # Module with options
└── README.md # Module documentation
```
### Fields to Fill In
| Field | Location | Notes |
|-------|----------|-------|
| Category | Directory | Choose `cli/` or `coding/` |
| Options | `default.nix` | Add under `options.m3ta.<name>` |
| Config | `default.nix` | Add under `config.m3ta.<name>` |
| Description | `README.md` | What the module does |
### After Creating
1. Add to the appropriate category aggregator (`cli/default.nix` or `coding/default.nix`)
2. Optionally export from `flake.nix` `homeManagerModules`
3. Add documentation to `docs/modules/home-manager/`
4. Run `nix flake check`
## Template Variables
Templates use Nix attribute references. After copying, search for these placeholders:
| Placeholder | Replace With |
|-------------|--------------|
| `package-name` | Your package name (kebab-case) |
| `owner-name` / `repo-name` | GitHub owner and repo |
| `0.1.0` | Initial version |
| `lib.fakeHash` | Real hash after first build |
| `lib.licenses.mit` | Appropriate license |
| `A short description` | One-line description |
## Automated Updates
Packages created from templates are automatically updated weekly by the Gitea Actions workflow. See the main README for details on the `nix-update` automation.
## Related
- [Adding Packages](./guides/adding-packages.md) - Detailed package guide
- [Adding Modules](./guides/adding-modules.md) - Detailed module guide
- [Development Workflow](./guides/development-workflow.md) - Local development
- [Architecture](./ARCHITECTURE.md) - Repository structure

57
flake.lock generated
View File

@@ -1,5 +1,21 @@
{ {
"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": [
@@ -23,11 +39,11 @@
}, },
"nixpkgs": { "nixpkgs": {
"locked": { "locked": {
"lastModified": 1775423009, "lastModified": 1777268161,
"narHash": "sha256-vPKLpjhIVWdDrfiUM8atW6YkIggCEKdSAlJPzzhkQlw=", "narHash": "sha256-bxrdOn8SCOv8tN4JbTF/TXq7kjo9ag4M+C8yzzIRYbE=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "68d8aa3d661f0e6bd5862291b5bb263b2a6595c9", "rev": "1c3fe55ad329cbcb28471bb30f05c9827f724c76",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -39,11 +55,11 @@
}, },
"nixpkgs-master": { "nixpkgs-master": {
"locked": { "locked": {
"lastModified": 1775657231, "lastModified": 1777643636,
"narHash": "sha256-DP8FfybiZPp5WLB9eIk0TC2mdvuYzxLGgrBODDrwPEI=", "narHash": "sha256-7vvm5Ia8o3g7YNErFcDsbCx+Pk8HbnA+ZYuA5Zga7hY=",
"owner": "NixOS", "owner": "NixOS",
"repo": "nixpkgs", "repo": "nixpkgs",
"rev": "4e03baaa39b7746eac5704d623461422131cd03d", "rev": "da2366fac507ce7bd31852e7351e55b951656999",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -53,27 +69,6 @@
"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": [
@@ -81,11 +76,11 @@
] ]
}, },
"locked": { "locked": {
"lastModified": 1775372219, "lastModified": 1777556999,
"narHash": "sha256-MJakKC026Sarz7nMmiFrfONWc4xgaw8ApV0Hhp4ebhM=", "narHash": "sha256-HfFlRwR8IMjudRttN4T8L3DJKnNlpWfeNzQPly/HaRY=",
"owner": "Fission-AI", "owner": "Fission-AI",
"repo": "OpenSpec", "repo": "OpenSpec",
"rev": "64d476f8b924bb9b74b896ea0aa784970e37da69", "rev": "347f0277e3be3549cd85cdea364fbd7710f1922b",
"type": "github" "type": "github"
}, },
"original": { "original": {
@@ -96,10 +91,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"
} }
} }

View File

@@ -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,13 +56,6 @@
inputs = inputs; inputs = inputs;
}; };
# Individual overlays for more granular control
additions = final: prev:
import ./pkgs {
pkgs = final;
inputs = inputs;
};
modifications = final: prev: import ./overlays/mods {inherit prev;}; modifications = final: prev: import ./overlays/mods {inherit prev;};
}; };
@@ -84,18 +77,18 @@
}; };
# Library functions - helper utilities for your configuration # Library functions - helper utilities for your configuration
lib = forAllSystems (system: let lib = forAllSystems (system: import ./lib {lib = nixpkgs.lib;});
pkgs = pkgsFor system;
in
import ./lib {lib = pkgs.lib;});
# Development shells for various programming environments # Development shells for various programming environments
# Usage: nix develop .#<shell-name> # Usage: nix develop .#<shell-name>
# Available shells: default, python, devops, opencode # Available shells: default, python, devops, coding
devShells = forAllSystems (system: let devShells = forAllSystems (system: let
pkgs = pkgsFor system; pkgs = pkgsFor system;
in in
import ./shells {inherit pkgs inputs;}); import ./shells {
inherit pkgs inputs;
agents = inputs.agents;
});
# Formatter for 'nix fmt' # Formatter for 'nix fmt'
formatter = forAllSystems (system: (pkgsFor system).alejandra); formatter = forAllSystems (system: (pkgsFor system).alejandra);
@@ -111,6 +104,15 @@
${pkgs.alejandra}/bin/alejandra --check ${./.} ${pkgs.alejandra}/bin/alejandra --check ${./.}
touch $out touch $out
''; '';
# Lib unit tests
lib-agents = import ./tests/lib/agents-test.nix {
inherit pkgs;
lib = pkgs.lib;
};
lib-coding-rules = import ./tests/lib/coding-rules-test.nix {
inherit pkgs;
lib = pkgs.lib;
};
}); });
# Templates for creating new packages/modules # Templates for creating new packages/modules

View File

@@ -26,6 +26,27 @@
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 ─────────────────────────────────────────────
# #
@@ -87,20 +108,8 @@
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
pkgs.runCommand "opencode-agents" {} '' renderAgentFiles pkgs canonical mkAgentContent "opencode-agents";
mkdir -p $out
${copyCommands}
'';
# ── Claude Code renderer ────────────────────────────────────── # ── Claude Code renderer ──────────────────────────────────────
# #
@@ -179,10 +188,7 @@
mkClaudeAgentContent = name: agent: mkClaudeAgentContent = name: agent:
(mkClaudeFrontmatter name agent) + agent.systemPrompt; (mkClaudeFrontmatter name agent) + agent.systemPrompt;
mkClaudeAgentFile = name: agent: agentFiles = renderAgentFiles pkgs canonical mkClaudeAgentContent "claude-code-agent-files";
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);
@@ -196,14 +202,10 @@
}; };
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
${copyAgentCommands} cp -r ${agentFiles}/* $out/.claude/agents/
cp ${settingsFile} $out/.claude/settings.json cp ${settingsFile} $out/.claude/settings.json
''; '';
@@ -223,7 +225,10 @@
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;
@@ -292,10 +297,7 @@
mkPiAgentContent = name: agent: mkPiAgentContent = name: agent:
(mkPiFrontmatter name agent) + agent.systemPrompt; (mkPiFrontmatter name agent) + agent.systemPrompt;
mkPiAgentFile = name: agent: piAgentFiles = renderAgentFiles pkgs canonical mkPiAgentContent "pi-agent-files";
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;
@@ -306,6 +308,19 @@
"- **" + 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"
@@ -320,20 +335,17 @@
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
${copyAgentCommands} cp -r ${piAgentFiles}/* $out/agents/
''; '';
# ── renderForTool dispatcher ────────────────────────────────── # ── renderForTool dispatcher ──────────────────────────────────
@@ -346,6 +358,7 @@
agentsInput, agentsInput,
tool, tool,
modelOverrides ? {}, modelOverrides ? {},
codingRules ? null,
}: let }: let
canonical = agentsInput.lib.loadAgents; canonical = agentsInput.lib.loadAgents;
in in
@@ -362,7 +375,7 @@
else if tool == "pi" else if tool == "pi"
then then
agentsLib.renderForPi { agentsLib.renderForPi {
inherit pkgs canonical modelOverrides; inherit pkgs canonical modelOverrides codingRules;
} }
else throw "lib.agents.renderForTool: unknown tool '${tool}'. Must be opencode, claude-code, or pi."; else throw "lib.agents.renderForTool: unknown tool '${tool}'. Must be opencode, claude-code, or pi.";
@@ -386,9 +399,10 @@
agentsInput, agentsInput,
tool, tool,
modelOverrides ? {}, modelOverrides ? {},
codingRules ? null,
}: let }: let
rendered = agentsLib.renderForTool { rendered = agentsLib.renderForTool {
inherit pkgs agentsInput tool modelOverrides; inherit pkgs agentsInput tool modelOverrides codingRules;
}; };
in in
if tool == "opencode" if tool == "opencode"

View File

@@ -1,4 +1,4 @@
# Opencode rules management utilities # Coding 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,7 +26,8 @@
# #
# 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
# - An `opencode.json` file with a $schema reference and instructions list # - A `coding-rules.json` file with a $schema reference and instructions list
# - (Optional) Appends coding rules to `AGENTS.md` for Pi agent discovery
# #
# The instructions list contains paths relative to the project root, all prefixed # 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.
@@ -43,10 +44,13 @@
# (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 opencode.json # - shellHook: Bash code to create symlink and coding-rules.json
# - instructions: List of rule file paths (relative to project root) # - instructions: List of rule file paths (relative to project root)
# #
# Example: # Example:
@@ -82,9 +86,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 =
@@ -93,29 +97,137 @@
++ (map (f: "${rulesDir}/frameworks/${f}.md") frameworks) ++ (map (f: "${rulesDir}/frameworks/${f}.md") frameworks)
++ extraInstructions; ++ extraInstructions;
# Generate JSON configuration for Opencode # Generate JSON configuration for coding rules
opencodeConfig = { rulesConfig = {
"$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 opencode.json # Creates a symlink to the AGENTS rules directory and generates coding-rules.json
# Optionally appends rules to AGENTS.md for Pi agent discovery
shellHook = '' 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 opencode.json configuration file # Generate coding-rules.json configuration file
cat > opencode.json <<'OPENCODE_EOF' cat > coding-rules.json <<'RULES_EOF'
${builtins.toJSON opencodeConfig} ${builtins.toJSON rulesConfig}
OPENCODE_EOF RULES_EOF
${piShellHook}
''; '';
}; };
# Concatenate selected rule files from the AGENTS repository into a single
# markdown string. Used by Pi (append to AGENTS.md) and could be used by
# other tools that don't support an instructions list.
#
# Args:
# agents: Path to the AGENTS repository (non-flake input)
# languages: Optional list of language-specific rules to include
# concerns: Optional list of concern rules to include
# Default: [ "coding-style" "naming" "documentation" "testing" "git-workflow" "project-structure" ]
# frameworks: Optional list of framework-specific rules to include
#
# Returns: A single concatenated markdown string with all selected rules.
#
# Example:
# concatRulesMd {
# agents = inputs.agents;
# languages = [ "python" ];
# concerns = [ "coding-style" ];
# }
# # Returns: "\n# Coding Style\n\n...python rules...\n"
concatRulesMd = {
agents,
languages ? [],
concerns ? [
"coding-style"
"naming"
"documentation"
"testing"
"git-workflow"
"project-structure"
],
frameworks ? [],
}: let
rulePaths =
(map (c: {
kind = "concerns";
name = c;
})
concerns)
++ (map (l: {
kind = "languages";
name = l;
})
languages)
++ (map (f: {
kind = "frameworks";
name = f;
})
frameworks);
# Backward-compat alias readRule = rule: builtins.readFile "${agents}/rules/${rule.kind}/${rule.name}.md";
mkOpencodeRules = mkCodingRules; ruleContents = map readRule rulePaths;
in
lib.concatStringsSep "\n\n" ruleContents;
# Build a coding rules section suitable for appending to AGENTS.md.
# Wraps concatRulesMd output with a header and HTML comment markers
# for idempotent updates in project-level shellHooks.
#
# Args: Same as concatRulesMd
#
# Returns: A markdown string with start/end markers and a header.
mkRulesMdSection = args: let
content = concatRulesMd args;
in
if builtins.stringLength content == 0
then ""
else ''
<!-- CODING-RULES:START -->
# Coding Rules
${content}
<!-- CODING-RULES:END -->
'';
in { in {
inherit mkCodingRules mkOpencodeRules; inherit mkCodingRules concatRulesMd mkRulesMdSection;
} }

View File

@@ -7,12 +7,9 @@
# Port management utilities # Port management utilities
ports = import ./ports.nix {inherit lib;}; ports = import ./ports.nix {inherit lib;};
# Coding rules injection utilities (renamed from opencode-rules) # Coding rules injection utilities
coding-rules = import ./coding-rules.nix {inherit lib;}; 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;};
} }

View File

@@ -1,116 +0,0 @@
# Opencode rules management utilities
#
# This module provides functions to configure Opencode agent rules across
# multiple projects. Rules are defined in the AGENTS repository and can be
# selectively included based on language, framework, and concerns.
#
# Usage in your configuration:
#
# # In your flake or configuration:
# let
# m3taLib = inputs.m3ta-nixpkgs.lib.${system};
#
# rules = m3taLib.opencode-rules.mkOpencodeRules {
# agents = inputs.agents;
# languages = [ "python" "typescript" ];
# concerns = [ "coding-style" "naming" "documentation" ];
# frameworks = [ "react" "fastapi" ];
# };
# in {
# # Use in your devShell:
# devShells.default = pkgs.mkShell {
# shellHook = rules.shellHook;
# inherit (rules) instructions;
# };
# }
#
# The shellHook creates:
# - A `.opencode-rules/` symlink pointing to the AGENTS repository rules directory
# - An `opencode.json` file with a $schema reference and instructions list
#
# The instructions list contains paths relative to the project root, all prefixed
# with `.opencode-rules/`, making them portable across different project locations.
{lib}: {
# Create Opencode rules configuration from AGENTS repository
#
# Args:
# agents: Path to the AGENTS repository (non-flake input)
# languages: Optional list of language-specific rules to include
# (e.g., [ "python" "typescript" "rust" ])
# concerns: Optional list of concern rules to include
# Default: [ "coding-style" "naming" "documentation" "testing" "git-workflow" "project-structure" ]
# frameworks: Optional list of framework-specific rules to include
# (e.g., [ "react" "fastapi" "django" ])
# extraInstructions: Optional list of additional instruction paths
# (for custom rules outside standard locations)
#
# Returns:
# An attribute set containing:
# - shellHook: Bash code to create symlink and opencode.json
# - instructions: List of rule file paths (relative to project root)
#
# Example:
# mkOpencodeRules {
# agents = inputs.agents;
# languages = [ "python" ];
# frameworks = [ "fastapi" ];
# }
# # Returns:
# # {
# # shellHook = "...";
# # instructions = [
# # ".opencode-rules/concerns/coding-style.md"
# # ".opencode-rules/concerns/naming.md"
# # ".opencode-rules/concerns/documentation.md"
# # ".opencode-rules/concerns/testing.md"
# # ".opencode-rules/concerns/git-workflow.md"
# # ".opencode-rules/concerns/project-structure.md"
# # ".opencode-rules/languages/python.md"
# # ".opencode-rules/frameworks/fastapi.md"
# # ];
# # }
mkOpencodeRules = {
agents,
languages ? [],
concerns ? [
"coding-style"
"naming"
"documentation"
"testing"
"git-workflow"
"project-structure"
],
frameworks ? [],
extraInstructions ? [],
}: let
rulesDir = ".opencode-rules";
# Build instructions list by mapping concerns, languages, frameworks to their file paths
# All paths are relative to project root via the rulesDir symlink
instructions =
(map (c: "${rulesDir}/concerns/${c}.md") concerns)
++ (map (l: "${rulesDir}/languages/${l}.md") languages)
++ (map (f: "${rulesDir}/frameworks/${f}.md") frameworks)
++ extraInstructions;
# Generate JSON configuration for Opencode
opencodeConfig = {
"$schema" = "https://opencode.ai/config.json";
inherit instructions;
};
in {
inherit instructions;
# Shell hook to set up rules in the project
# Creates a symlink to the AGENTS rules directory and generates opencode.json
shellHook = ''
# Create/update symlink to AGENTS rules directory
ln -sfn ${agents}/rules ${rulesDir}
# Generate opencode.json configuration file
cat > opencode.json <<'OPENCODE_EOF'
${builtins.toJSON opencodeConfig}
OPENCODE_EOF
'';
};
}

View File

@@ -95,19 +95,4 @@
# List of service names (strings) # List of service names (strings)
listServices = lib.attrNames ports; listServices = lib.attrNames ports;
}; };
# Simple helper to get a port without host override
# Useful when you don't need host-specific ports
#
# Args:
# portsConfig: Same structure as mkPortHelpers
# service: The service name (string)
#
# Returns:
# Port number (int) or null if service not found
#
# Example:
# getDefaultPort myPorts "nginx" # Returns default port only
getDefaultPort = portsConfig: service:
portsConfig.ports.${service} or null;
} }

View File

@@ -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` (old name still works) | | `mkOpencodeRules` | `mkCodingRules` |
### Migration steps ### Migration steps

View File

@@ -3,42 +3,34 @@
lib, lib,
pkgs, pkgs,
... ...
}: }: {
with lib; let imports = [
cfg = config.coding.agents.claude-code; ./shared/default.nix
];
options.coding.agents.claude-code = let
shared = import ./shared/shared-options.nix {inherit lib;};
mcpCfg = config.programs.mcp or null; mcpCfg = config.programs.mcp or null;
in { in
options.coding.agents.claude-code = { with lib; {
enable = mkEnableOption "Claude Code agent management via canonical agent.toml definitions"; enable = mkEnableOption "Claude Code agent management via canonical agent.toml definitions";
agentsInput = mkOption { agentsInput = shared.mkAgentsInputOption ''
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, 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 = mkOption { modelOverrides = shared.mkModelOverridesOption;
type = types.attrsOf types.str;
default = {}; externalSkills = shared.externalSkillsOption;
description = ''
Per-agent model overrides. Maps agent slug to model alias or ID.
Example: { chiron = "claude-sonnet-4-20250514"; }
'';
example = literalExpression ''
{
chiron = "claude-sonnet-4-20250514";
"chiron-forge" = "claude-sonnet-4-20250514";
}
'';
};
mcpServers = mkOption { mcpServers = mkOption {
type = types.attrsOf types.anything; type = types.attrsOf types.anything;
default = if mcpCfg != null then mcpCfg.servers else {}; default =
if mcpCfg != null
then mcpCfg.servers
else {};
defaultText = literalExpression "config.programs.mcp.servers"; defaultText = literalExpression "config.programs.mcp.servers";
description = '' description = ''
MCP server configurations for Claude Code. MCP server configurations for Claude Code.
@@ -48,9 +40,12 @@ in {
}; };
}; };
config = mkIf cfg.enable (let config = with lib; let
shared = import ./shared/shared-options.nix {inherit lib;};
cfg = config.coding.agents.claude-code;
agentsLib = (import ../../../../lib {inherit lib;}).agents; agentsLib = (import ../../../../lib {inherit lib;}).agents;
in
mkIf cfg.enable (let
# Rendered agents + permissions (only if agentsInput is set) # Rendered agents + permissions (only if agentsInput is set)
rendered = mkIf (cfg.agentsInput != null) ( rendered = mkIf (cfg.agentsInput != null) (
agentsLib.renderForClaudeCode { agentsLib.renderForClaudeCode {
@@ -82,6 +77,15 @@ in {
source = "${rendered}/.claude/agents"; source = "${rendered}/.claude/agents";
}; };
# Skills (merged from personal AGENTS repo + optional external skills)
home.file.".claude/skills" = mkIf (cfg.agentsInput != null) {
source = cfg.agentsInput.lib.mkOpencodeSkills {
inherit pkgs;
customSkills = "${cfg.agentsInput}/skills";
externalSkills = shared.mapExternalSkills cfg.externalSkills;
};
};
# Rendered settings.json with permissions + MCP servers # Rendered settings.json with permissions + MCP servers
home.file.".claude/settings.json" = mkIf (settingsJson != null) { home.file.".claude/settings.json" = mkIf (settingsJson != null) {
source = "${settingsJson}"; source = "${settingsJson}";

View File

@@ -1,10 +1,39 @@
# 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;
};
};
} }

View File

@@ -3,102 +3,43 @@
lib, lib,
pkgs, pkgs,
... ...
}: }: {
with lib; let imports = [
cfg = config.coding.agents.opencode; ./shared/default.nix
in { ];
options.coding.agents.opencode = {
options.coding.agents.opencode = let
shared = import ./shared/shared-options.nix {inherit lib;};
in
with lib; {
enable = mkEnableOption "OpenCode agent management via canonical agent.toml definitions"; enable = mkEnableOption "OpenCode agent management via canonical agent.toml definitions";
agentsInput = mkOption { agentsInput = shared.mkAgentsInputOption ''
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, 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;
}; };
modelOverrides = mkOption { config = with lib; let
type = types.attrsOf types.str; shared = import ./shared/shared-options.nix {inherit lib;};
default = {}; cfg = config.coding.agents.opencode;
description = '' in
Per-agent model overrides. Maps agent slug to model string. mkIf cfg.enable {
Example: { chiron = "anthropic/claude-sonnet-4"; }
'';
example = literalExpression ''
{
chiron = "anthropic/claude-sonnet-4";
"chiron-forge" = "anthropic/claude-sonnet-4";
}
'';
};
externalSkills = mkOption {
type = types.listOf (types.submodule {
options = {
src = mkOption {
type = types.anything;
description = "Flake input pointing to a skills repository root.";
};
skillsDir = mkOption {
type = types.str;
default = "skills";
description = ''
Subdirectory inside src that contains skill folders.
'';
};
selectSkills = mkOption {
type = types.nullOr (types.listOf types.str);
default = null;
description = ''
List of skill names to cherry-pick from this source.
null means include every skill found in skillsDir.
'';
};
};
});
default = [];
description = ''
External skill sources passed to mkOpencodeSkills.
Each entry maps directly to an element of the externalSkills
list accepted by the AGENTS flake's lib.mkOpencodeSkills.
'';
example = literalExpression ''
[
{ src = inputs.skills-anthropic; selectSkills = [ "claude-api" ]; }
{ src = inputs.skills-vercel; }
]
'';
};
};
config = mkIf cfg.enable {
# Rendered agent files symlinked to ~/.config/opencode/agents/ # Rendered agent files symlinked to ~/.config/opencode/agents/
xdg.configFile."opencode/agents" = mkIf (cfg.agentsInput != null) { xdg.configFile."opencode/agents" = let
source = (import ../../../../lib {inherit lib;}).agents.renderForOpencode { agentsLib = (import ../../../../lib {inherit lib;}).agents;
in
mkIf (cfg.agentsInput != null) {
source = agentsLib.renderForOpencode {
inherit pkgs; inherit pkgs;
canonical = cfg.agentsInput.lib.loadAgents; canonical = cfg.agentsInput.lib.loadAgents;
modelOverrides = cfg.modelOverrides; modelOverrides = cfg.modelOverrides;
}; };
}; };
# Skills (merged from personal AGENTS repo + optional external skills)
xdg.configFile."opencode/skills" = mkIf (cfg.agentsInput != null) {
source = cfg.agentsInput.lib.mkOpencodeSkills {
inherit pkgs;
customSkills = "${cfg.agentsInput}/skills";
externalSkills =
map (
entry:
{inherit (entry) src skillsDir;}
// optionalAttrs (entry.selectSkills != null) {inherit (entry) selectSkills;}
)
cfg.externalSkills;
};
};
# Static config dirs from AGENTS repo # Static config dirs from AGENTS repo
xdg.configFile."opencode/context" = mkIf (cfg.agentsInput != null) { xdg.configFile."opencode/context" = mkIf (cfg.agentsInput != null) {
source = "${cfg.agentsInput}/context"; source = "${cfg.agentsInput}/context";

View File

@@ -3,17 +3,24 @@
lib, lib,
pkgs, pkgs,
... ...
}: }: {
with lib; let imports = [
cfg = config.coding.agents.pi; ./shared/default.nix
];
options.coding.agents.pi = let
shared = import ./shared/shared-options.nix {inherit lib;};
mcpCfg = config.programs.mcp or null; mcpCfg = config.programs.mcp or null;
in { in
options.coding.agents.pi = { with lib; {
enable = mkEnableOption "Pi agent management via canonical agent.toml definitions"; enable = mkEnableOption "Pi agent management via canonical agent.toml definitions";
mcpServers = mkOption { mcpServers = mkOption {
type = types.attrsOf types.anything; type = types.attrsOf types.anything;
default = if mcpCfg != null then mcpCfg.servers else {}; default =
if mcpCfg != null
then mcpCfg.servers
else {};
defaultText = literalExpression "config.programs.mcp.servers"; defaultText = literalExpression "config.programs.mcp.servers";
description = '' description = ''
MCP server configurations for Pi (pi-mcp-adapter). MCP server configurations for Pi (pi-mcp-adapter).
@@ -22,25 +29,13 @@ in {
''; '';
}; };
agentsInput = mkOption { agentsInput = shared.mkAgentsInputOption ''
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 = mkOption { modelOverrides = shared.mkModelOverridesOption;
type = types.attrsOf types.str;
default = {};
description = ''
Per-agent model overrides for Pi subagents.
Maps agent slug to model string, e.g.:
{ chiron = "anthropic/claude-sonnet-4"; chiron-forge = "anthropic/claude-sonnet-4"; }
'';
};
primaryAgent = mkOption { primaryAgent = mkOption {
type = types.nullOr types.str; type = types.nullOr types.str;
@@ -51,6 +46,60 @@ in {
''; '';
}; };
codingRules = mkOption {
type = types.nullOr (types.submodule {
options = {
languages = mkOption {
type = types.listOf types.str;
default = [];
description = ''
Language-specific coding rules to include
(e.g. [ "python" "typescript" "nix" ]).
Rule files are read from the AGENTS repo's rules/languages/ directory.
'';
};
concerns = mkOption {
type = types.listOf types.str;
default = [
"coding-style"
"naming"
"documentation"
"testing"
"git-workflow"
"project-structure"
];
description = ''
Concern rules to include from the AGENTS repo's rules/concerns/ directory.
'';
};
frameworks = mkOption {
type = types.listOf types.str;
default = [];
description = ''
Framework-specific coding rules to include
(e.g. [ "react" "fastapi" ]).
Rule files are read from the AGENTS repo's rules/frameworks/ directory.
'';
};
};
});
default = null;
description = ''
Coding rules to inject into ~/.pi/agent/AGENTS.md.
Rules are read from the AGENTS repository and appended as markdown sections.
Requires agentsInput to be set.
'';
example = literalExpression ''
{
languages = [ "python" "typescript" ];
concerns = [ "coding-style" "testing" ];
frameworks = [ "fastapi" ];
}
'';
};
settings = mkOption { settings = mkOption {
type = types.submodule { type = types.submodule {
freeformType = types.attrsOf types.anything; freeformType = types.attrsOf types.anything;
@@ -153,9 +202,45 @@ in {
See pi docs/settings.md for all options. See pi docs/settings.md for all options.
''; '';
}; };
# ── Pi Guardrails ─────────────────────────────────────────────
guardrails = mkOption {
type = types.nullOr (types.submodule {
options = {
enable =
mkEnableOption
("Generate ~/.pi/agent/extensions/guardrails.json for pi-guardrails. "
+ "Adds @aliou/pi-guardrails to packages automatically.");
config = mkOption {
type = types.attrsOf types.anything;
default = {};
description = ''
Guardrails configuration written to ~/.pi/agent/extensions/guardrails.json.
See https://github.com/aliou/pi-guardrails for config schema.
IMPORTANT: Path access checks are lexical (not symlink-safe).
Local project .pi/extensions/guardrails.json can override same rule IDs
(memory > local > global > defaults). For immutable global policies,
consider a wrapper or upstream patch.
'';
};
};
});
default = null;
description = ''
Pi Guardrails security configuration.
Generates ~/.pi/agent/extensions/guardrails.json when enabled.
The @aliou/pi-guardrails package is added to settings.packages automatically.
'';
};
}; };
config = mkIf cfg.enable (let config = with lib; let
shared = import ./shared/shared-options.nix {inherit lib;};
cfg = config.coding.agents.pi;
in
mkIf cfg.enable (let
# Build settings.json by filtering out null values recursively # Build settings.json by filtering out null values recursively
filterNulls = attrs: filterNulls = attrs:
lib.filterAttrs (_: v: v != null) ( lib.filterAttrs (_: v: v != null) (
@@ -164,12 +249,50 @@ in {
then let then let
filtered = filterNulls v; filtered = filterNulls v;
in in
if filtered == {} then null else filtered if filtered == {}
else v) attrs then null
else filtered
else v)
attrs
); );
# Base settings (already filtered)
piSettings = filterNulls cfg.settings; piSettings = filterNulls cfg.settings;
# Guardrails package to inject when guardrails is enabled
guardrailsPackage = "npm:@aliou/pi-guardrails@0.11.1";
# Guardrails config (only when guardrails is enabled)
guardrailsJson =
if (cfg.guardrails != null && cfg.guardrails.enable)
then builtins.toJSON cfg.guardrails.config
else null;
# Merge guardrails package into settings.packages when guardrails is enabled
piSettingsWithGuardrails = let
baseSettings = cfg.settings;
basePackages = baseSettings.packages or [];
hasGuardrailsPackage =
lib.any
(p:
lib.hasPrefix "npm:@aliou/pi-guardrails" p
|| (lib.hasPrefix "git:" p && lib.hasSuffix "/pi-guardrails" p))
basePackages;
packagesWithGuardrails =
if (cfg.guardrails != null && cfg.guardrails.enable && !hasGuardrailsPackage)
then basePackages ++ [guardrailsPackage]
else basePackages;
in
if packagesWithGuardrails != basePackages
then filterNulls (baseSettings // {packages = packagesWithGuardrails;})
else piSettings;
# Coding rules config for renderForPi (only when both agentsInput and codingRules are set)
piCodingRules =
if cfg.agentsInput != null && cfg.codingRules != null
then cfg.codingRules // {agents = cfg.agentsInput;}
else null;
# Rendered agents (only computed when agentsInput is set) # Rendered agents (only computed when agentsInput is set)
rendered = rendered =
if cfg.agentsInput != null if cfg.agentsInput != null
@@ -179,14 +302,14 @@ in {
canonical = cfg.agentsInput.lib.loadAgents; canonical = cfg.agentsInput.lib.loadAgents;
modelOverrides = cfg.modelOverrides; modelOverrides = cfg.modelOverrides;
primaryAgent = cfg.primaryAgent; primaryAgent = cfg.primaryAgent;
codingRules = piCodingRules;
} }
else null; else null;
# Dynamic home.file entries for agent .md files # Dynamic home.file entries for agent .md files
agentFiles = agentFiles =
if cfg.agentsInput != null if cfg.agentsInput != null
then then let
let
agentNames = builtins.attrNames cfg.agentsInput.lib.loadAgents; agentNames = builtins.attrNames cfg.agentsInput.lib.loadAgents;
in in
builtins.listToAttrs ( builtins.listToAttrs (
@@ -202,13 +325,21 @@ in {
# ── MCP servers from programs.mcp → ~/.pi/agent/mcp.json ─────── # ── MCP servers from programs.mcp → ~/.pi/agent/mcp.json ───────
(mkIf (cfg.mcpServers != {}) { (mkIf (cfg.mcpServers != {}) {
".pi/agent/mcp.json".text = builtins.toJSON {mcpServers = cfg.mcpServers;}; ".pi/agent/mcp.json".text = builtins.toJSON {mcpServers = cfg.mcpServers;};
".pi/agent/mcp.json".force = true;
}) })
# ── ~/.pi/agent/settings.json ────────────────────────────────── # ── ~/.pi/agent/settings.json ──────────────────────────────────
{ {
".pi/agent/settings.json".text = builtins.toJSON piSettings; ".pi/agent/settings.json".text = builtins.toJSON piSettingsWithGuardrails;
".pi/agent/settings.json".force = true;
} }
# ── pi-guardrails config ─────────────────────────────────────
(mkIf (guardrailsJson != null) {
".pi/agent/extensions/guardrails.json".text = guardrailsJson;
".pi/agent/extensions/guardrails.json".force = true;
})
# ── AGENTS.md — agent descriptions and specialist listing ────── # ── AGENTS.md — agent descriptions and specialist listing ──────
(mkIf (cfg.agentsInput != null) { (mkIf (cfg.agentsInput != null) {
".pi/agent/AGENTS.md".source = "${rendered}/AGENTS.md"; ".pi/agent/AGENTS.md".source = "${rendered}/AGENTS.md";
@@ -221,14 +352,6 @@ in {
# ── Agents — pi-subagents .md files ──────────────────────────── # ── Agents — pi-subagents .md files ────────────────────────────
agentFiles agentFiles
# ── Skills symlinked from AGENTS repo ──────────────────────────
(mkIf (cfg.agentsInput != null) {
".pi/agent/skills".source = cfg.agentsInput.lib.mkOpencodeSkills {
inherit pkgs;
customSkills = "${cfg.agentsInput}/skills";
};
})
]; ];
}); });
} }

View File

@@ -0,0 +1,7 @@
# Shared agent module exports
# Imports all shared modules for the coding.agents namespace.
{
imports = [
./git-identity.nix
];
}

View File

@@ -0,0 +1,64 @@
# Git identity module for agent commits.
# Sets GIT_AUTHOR_*, GIT_COMMITTER_*, and GIT_SSH_COMMAND environment variables.
{
pkgs,
lib,
config,
...
}: let
cfg = config.coding.agents.gitIdentity;
in {
options.coding.agents.gitIdentity = {
enable = lib.mkEnableOption ''
Agent Git identity for commits. When enabled, sets GIT_AUTHOR_* and
GIT_COMMITTER_* environment variables for consistent bot identity.
'';
name = lib.mkOption {
type = lib.types.str;
default = "m3ta-chiron";
description = "Git user name for agent commits.";
example = "m3ta-chiron";
};
email = lib.mkOption {
type = lib.types.str;
default = "m3ta-chiron@agentmail.to";
description = "Git email for agent commits.";
example = "m3ta-chiron@agentmail.to";
};
signingKey = lib.mkOption {
type = lib.types.nullOr lib.types.path;
default = null;
description = ''
Optional GPG signing key for verified commits.
Set to null to disable signing.
'';
example = "/home/user/.gnupg/sign_key.gpg";
};
sshKey = lib.mkOption {
type = lib.types.path;
description = ''
Path to SSH private key for git push authentication.
Use agenix-managed paths like /run/agenix/m3ta-chiron-ssh-key
for secure secret management.
'';
example = "/run/agenix/m3ta-chiron-ssh-key";
};
};
config = lib.mkIf cfg.enable {
home.sessionVariables = {
# Git author/committer identity
GIT_AUTHOR_NAME = cfg.name;
GIT_AUTHOR_EMAIL = cfg.email;
GIT_COMMITTER_NAME = cfg.name;
GIT_COMMITTER_EMAIL = cfg.email;
# SSH command for git push
GIT_SSH_COMMAND = "ssh -i ${cfg.sshKey} -o IdentitiesOnly=yes -o StrictHostKeyChecking=accept-new";
};
};
}

View File

@@ -0,0 +1,77 @@
# Shared option definitions for agent modules.
# Prevents copy-pasting the externalSkills submodule across opencode/claude-code/pi.
{lib}: let
inherit (lib) mkOption mkEnableOption types literalExpression;
in {
# Common agentsInput option used by all agent modules.
mkAgentsInputOption = description:
mkOption {
type = types.nullOr types.anything;
default = null;
inherit description;
};
# Common modelOverrides option.
mkModelOverridesOption = mkOption {
type = types.attrsOf types.str;
default = {};
description = ''
Per-agent model overrides. Maps agent slug to model string.
Example: { chiron = "anthropic/claude-sonnet-4"; }
'';
example = literalExpression ''
{
chiron = "anthropic/claude-sonnet-4";
"chiron-forge" = "anthropic/claude-sonnet-4";
}
'';
};
# External skills submodule — used by opencode, claude-code, and pi modules.
externalSkillsOption = mkOption {
type = types.listOf (types.submodule {
options = {
src = mkOption {
type = types.anything;
description = "Flake input pointing to a skills repository root.";
};
skillsDir = mkOption {
type = types.str;
default = "skills";
description = ''
Subdirectory inside src that contains skill folders.
'';
};
selectSkills = mkOption {
type = types.nullOr (types.listOf types.str);
default = null;
description = ''
List of skill names to cherry-pick from this source.
null means include every skill found in skillsDir.
'';
};
};
});
default = [];
description = ''
External skill sources passed to mkOpencodeSkills.
Each entry maps directly to an element of the externalSkills
list accepted by the AGENTS flake's lib.mkOpencodeSkills.
'';
example = literalExpression ''
[
{ src = inputs.skills-anthropic; selectSkills = [ "claude-api" ]; }
{ src = inputs.basecamp; }
]
'';
};
# Helper to map externalSkills from module config to mkOpencodeSkills format.
mapExternalSkills = cfgEntries:
map (
entry:
{inherit (entry) src skillsDir;}
// lib.optionalAttrs (entry.selectSkills != null) {inherit (entry) selectSkills;}
)
cfgEntries;
}

View File

@@ -1,70 +0,0 @@
{inputs, ...}: {
# This one brings our custom packages from the 'pkgs' directory
additions = final: prev:
(import ../pkgs {pkgs = final;})
# // (inputs.hyprpanel.overlay final prev)
// {rose-pine-hyprcursor = inputs.rose-pine-hyprcursor.packages.${prev.stdenv.hostPlatform.system}.default;};
# This one contains whatever you want to overlay
# You can change versions, add patches, set compilation flags, anything really.
# https://nixos.wiki/wiki/Overlays
modifications = final: prev:
# Import all package modifications from mods directory
(import ./mods/default.nix {inherit prev;})
// {
# Direct configuration overrides
brave = prev.brave.override {
commandLineArgs = "--password-store=gnome-libsecret";
};
# nodejs_24 = inputs.nixpkgs-stable.legacyPackages.${prev.system}.nodejs_24;
# paperless-ngx = inputs.nixpkgs-45570c2.legacyPackages.${prev.system}.paperless-ngx;
# anytype-heart = inputs.nixpkgs-9e58ed7.legacyPackages.${prev.system}.anytype-heart;
# trezord = inputs.nixpkgs-2744d98.legacyPackages.${prev.system}.trezord;
# mesa = inputs.nixpkgs-master.legacyPackages.${prev.system}.mesa;
# hyprpanel = inputs.hyprpanel.packages.${prev.system}.default.overrideAttrs (prev: {
# version = "latest"; # or whatever version you want
# src = final.fetchFromGitHub {
# owner = "Jas-SinghFSU";
# repo = "HyprPanel";
# rev = "master"; # or a specific commit hash
# hash = "sha256-l623fIVhVCU/ylbBmohAtQNbK0YrWlEny0sC/vBJ+dU=";
# };
# });
};
temp-packages = final: _prev: {
temp = import inputs.nixpkgs-9e9486b {
system = final.stdenv.hostPlatform.system;
config.allowUnfree = true;
};
};
stable-packages = final: _prev: {
stable = import inputs.nixpkgs-stable {
system = final.stdenv.hostPlatform.system;
config.allowUnfree = true;
};
};
pinned-packages = final: _prev: {
pinned = import inputs.nixpkgs-9472de4 {
system = final.stdenv.hostPlatform.system;
config.allowUnfree = true;
};
};
locked-packages = final: _prev: {
locked = import inputs.nixpkgs-locked {
system = final.stdenv.hostPlatform.system;
config.allowUnfree = true;
};
};
master-packages = final: _prev: {
master = import inputs.nixpkgs-master {
system = final.stdenv.hostPlatform.system;
config.allowUnfree = true;
};
};
}

View File

@@ -1,16 +0,0 @@
{prev}:
prev.beads.overrideAttrs (oldAttrs: rec {
version = "0.47.1";
src = prev.fetchFromGitHub {
owner = "steveyegge";
repo = "beads";
tag = "v${version}";
hash = "sha256-DwIR/r1TJnpVd/CT1E2OTkAjU7k9/KHbcVwg5zziFVg=";
};
vendorHash = "sha256-pY5m5ODRgqghyELRwwxOr+xlW41gtJWLXaW53GlLaFw=";
# Tests require git worktree operations that fail in Nix sandbox
doCheck = false;
})

View File

@@ -2,9 +2,6 @@
# Package modifications # Package modifications
# This overlay contains package overrides and modifications # This overlay contains package overrides and modifications
# n8n = import ./n8n.nix {inherit prev;};
# beads = import ./beads.nix {inherit prev;};
# Add more modifications here as needed # Add more modifications here as needed
# example-package = prev.example-package.override { ... }; # example-package = prev.example-package.override { ... };
} }

View File

@@ -1,18 +0,0 @@
{prev}:
prev.n8n.overrideAttrs (oldAttrs: rec {
version = "2.4.1";
src = prev.fetchFromGitHub {
owner = "n8n-io";
repo = "n8n";
rev = "n8n@${version}";
hash = "sha256-EQP9ZI8kt30SUYE1+/UUpxQXpavzKqDu8qE24zsNifg=";
};
pnpmDeps = prev.pnpm_10.fetchDeps {
pname = oldAttrs.pname;
inherit version src;
fetcherVersion = 1;
hash = "sha256-Q30IuFEQD3896Hg0HCLd38YE2i8fJn74JY0o95LKJis=";
};
})

View File

@@ -1,40 +0,0 @@
{
lib,
fetchFromGitHub,
nix-update-script,
rustPlatform,
pkg-config,
perl,
openssl,
}:
rustPlatform.buildRustPackage rec {
pname = "code2prompt";
version = "4.2.0";
src = fetchFromGitHub {
owner = "mufeedvh";
repo = "code2prompt";
rev = "v${version}";
hash = "sha256-Gh8SsSTZW7QlyyC3SWJ5pOK2x85/GT7+LPJn2Jeczpc=";
};
cargoLock = {
lockFile = src + "/Cargo.lock";
};
buildAndTestSubdir = "crates/code2prompt";
nativeBuildInputs = [pkg-config perl];
buildInputs = [openssl];
passthru.updateScript = nix-update-script {};
meta = with lib; {
description = "A CLI tool that converts your codebase into a single LLM prompt with a source tree, prompt templating, and token counting";
homepage = "https://github.com/mufeedvh/code2prompt";
license = licenses.mit;
platforms = platforms.linux;
mainProgram = "code2prompt";
};
}

View File

@@ -1,3 +1,10 @@
# m3ta-nixpkgs package registry
#
# Flake inputs used:
# inputs.basecamp → basecamp (pass-through)
# inputs.openspec → openspec (pass-through)
# inputs.opencode → opencode-desktop (build inputs + patches)
# inputs.agents → not used directly here (used by lib/)
{ {
pkgs, pkgs,
inputs, inputs,
@@ -5,11 +12,12 @@
}: let }: let
system = pkgs.stdenv.hostPlatform.system; system = pkgs.stdenv.hostPlatform.system;
in { in {
# Custom packages registry # ── Local packages ────────────────────────────────────────────────
# Each package is defined in its own directory under pkgs/ # Standard packages built from source in ./<name>/default.nix.
# No flake inputs required.
sidecar = pkgs.callPackage ./sidecar {}; sidecar = pkgs.callPackage ./sidecar {};
td = pkgs.callPackage ./td {}; td = pkgs.callPackage ./td {};
code2prompt = pkgs.callPackage ./code2prompt {};
eigent = pkgs.callPackage ./eigent {}; 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 {};
@@ -25,11 +33,9 @@ in {
zellij-ps = pkgs.callPackage ./zellij-ps {}; zellij-ps = pkgs.callPackage ./zellij-ps {};
vibetyper = pkgs.callPackage ./vibetyper {}; vibetyper = pkgs.callPackage ./vibetyper {};
# Imported from flake inputs (pass-through, no modifications) # ── Pass-through packages ──────────────────────────────────────────
# Imported directly from flake inputs. No local modifications.
basecamp = inputs.basecamp.packages.${system}.default; 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;
} }

View File

@@ -8,10 +8,10 @@
nix-update-script, nix-update-script,
}: let }: let
pname = "eigent"; pname = "eigent";
version = "0.0.89"; version = "0.0.90";
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-9KuiFjegfXhCu1W/FCinWX4ae/DsNPudeBcXFfW18Hc="; hash = "sha256-mwCBx+D6mgGqQa8bDuUpo3h49EwFVkwasJwaYc6aXFE=";
}; };
appimageContents = appimageTools.extractType2 {inherit pname version src;}; appimageContents = appimageTools.extractType2 {inherit pname version src;};
in in

View File

@@ -1,13 +1,13 @@
{ {
"version": "1.2.2", "version": "1.3.0",
"sources": { "sources": {
"aarch64-linux": { "aarch64-linux": {
"url": "https://github.com/kestra-io/kestractl/releases/download/1.2.2/kestractl_1.2.2_linux_arm64.tar.gz", "url": "https://github.com/kestra-io/kestractl/releases/download/1.3.0/kestractl_1.3.0_linux_arm64.tar.gz",
"hash": "sha256-sidFsCZPnJ07PM5QayPBqaqlBBJTLEdecfd0AWnL7Yo=" "hash": "sha256-/18F6CZnnLbet4BmI1oQ5pZWkJwIshCq30qd+cm0GGA="
}, },
"x86_64-linux": { "x86_64-linux": {
"url": "https://github.com/kestra-io/kestractl/releases/download/1.2.2/kestractl_1.2.2_linux_amd64.tar.gz", "url": "https://github.com/kestra-io/kestractl/releases/download/1.3.0/kestractl_1.3.0_linux_amd64.tar.gz",
"hash": "sha256-0C2naN2ougBJSY2z2m6eORnLkLen87HD+a+gvtrUvdw=" "hash": "sha256-xmsBiqNKvob8xHDyU253o6c25YIubHanNdLqzWaOvSA="
} }
} }
} }

View File

@@ -6,14 +6,14 @@
}: }:
python3.pkgs.buildPythonPackage rec { python3.pkgs.buildPythonPackage rec {
pname = "mem0ai"; pname = "mem0ai";
version = "1.0.9"; version = "2.0.1";
pyproject = true; pyproject = true;
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "mem0ai"; owner = "mem0ai";
repo = "mem0"; repo = "mem0";
rev = "v${version}"; rev = "v${version}";
hash = "sha256-tcWH5VbjIBSHinfjirxbUhxqgU0xOUlcHTQHraMuALg="; hash = "sha256-lNSE0Yit+FmM8opC4XYtfVef7JfGd3wMKbLj67Kp4Qw=";
}; };
# Relax Python dependency version constraints # Relax Python dependency version constraints

View File

@@ -11,6 +11,7 @@
node-gyp, node-gyp,
cctools, cctools,
xcbuild, xcbuild,
dart-sass,
libkrb5, libkrb5,
libmongocrypt, libmongocrypt,
libpq, libpq,
@@ -25,20 +26,20 @@
in in
stdenv.mkDerivation (finalAttrs: { stdenv.mkDerivation (finalAttrs: {
pname = "n8n"; pname = "n8n";
version = "stable"; version = "2.18.5";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "n8n-io"; owner = "n8n-io";
repo = "n8n"; repo = "n8n";
tag = "${finalAttrs.version}"; tag = "n8n@${finalAttrs.version}";
hash = "sha256-/atba0ymCqhh5Rt61UxwC2xf8SGrRsEKtlsDCIkg37Y="; hash = "sha256-ws0DXGQFR+z3nVyd4Yn9pIM7yh+H6GnuCRSLxgvtPxo=";
}; };
pnpmDeps = fetchPnpmDeps { pnpmDeps = fetchPnpmDeps {
inherit (finalAttrs) pname version src; inherit (finalAttrs) pname version src;
pnpm = pnpm_10; pnpm = pnpm_10;
fetcherVersion = 3; fetcherVersion = 3;
hash = "sha256-YGplNNvIOIY1BthWmejAzucXujq8AkgPJus774GmWCA="; hash = "sha256-Ajgne0neNm6HgMK6z3jnEkUJJxVOTgzjpSaMaJgIndQ=";
}; };
nativeBuildInputs = nativeBuildInputs =
@@ -61,6 +62,17 @@ in
libpq libpq
]; ];
preBuild = ''
# Force sass-embedded to use our dart-sass instead of bundled binaries.
# The bundled Dart binary can't run in the Nix sandbox (no /lib64/ld-linux-x86-64.so.2).
for dep in node_modules/.pnpm/sass-embedded@*; do
substituteInPlace "$dep/node_modules/sass-embedded/dist/lib/src/compiler-path.js" \
--replace-fail \
'compilerCommand = (() => {' \
'compilerCommand = (() => { return ["${lib.getExe dart-sass}"];'
done
'';
buildPhase = '' buildPhase = ''
runHook preBuild runHook preBuild

View File

@@ -1,6 +1,29 @@
#!/usr/bin/env nix-shell #!/usr/bin/env nix-shell
#!nix-shell --pure -i bash -p bash curl jq nix-update cacert git #!nix-shell -i bash -p bash curl jq nix-update cacert git nix
set -euo pipefail set -euo pipefail
new_version="$(curl -s "https://api.github.com/repos/n8n-io/n8n/releases/latest" | jq --raw-output '.tag_name | ltrimstr("n8n@")')" # n8n releases are published with two tags per version:
nix-update n8n --flake --version "$new_version" # - "n8n@X.Y.Z" - the versioned tag
# - "stable" - always points to the latest stable version
#
# We query the "stable" tag and extract the version from its target commitish (e.g., "release/2.18.5").
# This ensures we always get the actual latest stable version, not the most recently created tag.
set -euo pipefail
# Get the directory where this script lives (should be pkgs/n8n/)
script_dir="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# Get the nixpkgs root (parent of pkgs/)
nixpkgs_root="$(cd "$script_dir/../.." && pwd)"
cd "$nixpkgs_root"
# Query the "stable" tag and extract version from target_commitish (e.g., "release/2.18.5")
new_version=$(curl -s "https://api.github.com/repos/n8n-io/n8n/releases/tags/stable" | jq --raw-output '.target_commitish | ltrimstr("release/")')
echo "Latest stable version: n8n@${new_version}"
echo "Running from: $(pwd)"
# Use --flake --system to properly evaluate the flake-based package
nix-update --flake --system x86_64-linux n8n --version "$new_version"

View File

@@ -1,114 +0,0 @@
{
lib,
stdenv,
symlinkJoin,
makeWrapper,
rustPlatform,
pkg-config,
cargo-tauri,
bun,
nodejs,
cargo,
rustc,
jq,
wrapGAppsHook4,
dbus,
glib,
gtk4,
libsoup_3,
librsvg,
libappindicator,
glib-networking,
openssl,
webkitgtk_4_1,
gst_all_1,
inputs ? null,
}: let
# Get upstream opencode package for shared attributes
opencode = inputs.opencode.packages.${stdenv.hostPlatform.system}.default;
# Workaround for https://github.com/anomalyco/opencode/issues/11755
# Upstream is missing outputHashes for git dependencies
# Also fix stale npm deps hash in upstream node_modules FOD
fixedNodeModules = opencode.node_modules.overrideAttrs {
outputHash = "sha256-LRhPPrOKCGUSCEWTpAxPdWKTKVNkg82WrvD25cP3jts=";
};
opencode-desktop = rustPlatform.buildRustPackage (finalAttrs: {
pname = "opencode-desktop";
version = opencode.version;
src = opencode.src;
node_modules = fixedNodeModules;
patches = opencode.patches;
cargoRoot = "packages/desktop/src-tauri";
cargoLock = {
lockFile = finalAttrs.src + "/packages/desktop/src-tauri/Cargo.lock";
outputHashes = {
"specta-2.0.0-rc.22" = "sha256-YsyOAnXELLKzhNlJ35dHA6KGbs0wTAX/nlQoW8wWyJQ=";
"tauri-2.9.5" = "sha256-dv5E/+A49ZBvnUQUkCGGJ21iHrVvrhHKNcpUctivJ8M=";
"tauri-specta-2.0.0-rc.21" = "sha256-n2VJ+B1nVrh6zQoZyfMoctqP+Csh7eVHRXwUQuiQjaQ=";
};
};
buildAndTestSubdir = finalAttrs.cargoRoot;
nativeBuildInputs =
[pkg-config cargo-tauri.hook bun nodejs cargo rustc jq makeWrapper]
++ lib.optionals stdenv.hostPlatform.isLinux [wrapGAppsHook4];
buildInputs = lib.optionals stdenv.isLinux [
dbus
glib
gtk4
libsoup_3
librsvg
libappindicator
glib-networking
openssl
webkitgtk_4_1
gst_all_1.gstreamer
gst_all_1.gst-plugins-base
gst_all_1.gst-plugins-good
gst_all_1.gst-plugins-bad
];
strictDeps = true;
preBuild = ''
cp -a ${finalAttrs.node_modules}/{node_modules,packages} .
chmod -R u+w node_modules packages
patchShebangs node_modules
patchShebangs packages/desktop/node_modules
mkdir -p packages/desktop/src-tauri/sidecars
cp ${opencode}/bin/opencode packages/desktop/src-tauri/sidecars/opencode-cli-${stdenv.hostPlatform.rust.rustcTarget}
'';
tauriBuildFlags = ["--config" "tauri.prod.conf.json" "--no-sign"];
postFixup = lib.optionalString stdenv.hostPlatform.isLinux ''
mv $out/bin/OpenCode $out/bin/opencode-desktop
sed -i 's|^Exec=OpenCode$|Exec=opencode-desktop|' $out/share/applications/OpenCode.desktop
'';
});
# Wrapper for Wayland support
in
symlinkJoin {
name = "opencode-desktop";
paths = [opencode-desktop];
nativeBuildInputs = [makeWrapper];
postBuild = ''
wrapProgram $out/bin/opencode-desktop \
--run 'if [[ "$NIXOS_OZONE_WL" == "1" ]]; then export OC_ALLOW_WAYLAND=1; fi'
'';
meta = {
description = "OpenCode Desktop App with Wayland support";
homepage = "https://opencode.ai";
license = lib.licenses.mit;
platforms = lib.platforms.linux;
mainProgram = "opencode-desktop";
};
}

View File

@@ -13,16 +13,16 @@
}: }:
buildGoModule (finalAttrs: { buildGoModule (finalAttrs: {
pname = "sidecar"; pname = "sidecar";
version = "0.83.0"; version = "0.84.0";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "marcus"; owner = "marcus";
repo = "sidecar"; repo = "sidecar";
tag = "v${finalAttrs.version}"; tag = "v${finalAttrs.version}";
hash = "sha256-L6q2eZO1rNngWwHVhBJ2ftVbvYTConpqYHEb3nwiXxs="; hash = "sha256-80ldZlaZ99ti8dvw+Awev7ucz03iOVD2yzz/+IFHDvA=";
}; };
vendorHash = "sha256-fIaHzc0L4jwVSh/YjrXBB7nENqCgOfHF5bnljFsGbVo="; vendorHash = "sha256-IDD+hQZODNPj+Gy9CX5GFdMcsvt75aFLpabXZehAjaw=";
subPackages = ["cmd/sidecar"]; subPackages = ["cmd/sidecar"];

View File

@@ -9,13 +9,13 @@
}: }:
buildGoModule (finalAttrs: { buildGoModule (finalAttrs: {
pname = "td"; pname = "td";
version = "0.43.0"; version = "0.44.0";
src = fetchFromGitHub { src = fetchFromGitHub {
owner = "marcus"; owner = "marcus";
repo = "td"; repo = "td";
tag = "v${finalAttrs.version}"; tag = "v${finalAttrs.version}";
hash = "sha256-DwzuXumEEQWfZW+GbbY9kyqkEFZQ9sC+sSbVxfrY6bM="; hash = "sha256-k1OCK6LE99fHLuxv8HZUW8cSn2Wmk74J7kb6Mi5ZpVw=";
}; };
vendorHash = "sha256-hFFG+vLXcL2NNdLQvQZ1hzu++pp5AkbFOPQS10wtsec="; vendorHash = "sha256-hFFG+vLXcL2NNdLQvQZ1hzu++pp5AkbFOPQS10wtsec=";

View File

@@ -4,10 +4,10 @@
lib, lib,
}: let }: let
pname = "vibetyper"; pname = "vibetyper";
version = "1.2.2"; version = "1.2.3";
src = fetchurl { src = fetchurl {
url = "https://cdn.vibetyper.com/releases/linux/VibeTyper.AppImage"; url = "https://cdn.vibetyper.com/releases/linux/VibeTyper.AppImage";
sha256 = "sha256-AUjrSVxyaI8Ok4pnoqaW4fGAd4GtSc0mEjDhkqdifY0="; sha256 = "sha256-6uGXw2nxb0sGkcMDTWBlL3PuwBfVodhgqfgZT1Ncs40=";
}; };
appimageContents = appimageTools.extractType2 {inherit pname version src;}; appimageContents = appimageTools.extractType2 {inherit pname version src;};
in in

109
shells/coding.nix Normal file
View File

@@ -0,0 +1,109 @@
# AI coding agent development environment with coding rules
# Sets up coding rules for OpenCode and Pi, plus useful companion tools.
# Usage: nix develop .#coding
#
# To enable coding rules, add the agents input to your flake:
# agents = {
# url = "git+https://code.m3ta.dev/m3tam3re/AGENTS";
# flake = false;
# };
{
pkgs,
lib ? pkgs.lib,
inputs ? null,
agents ? null,
}: let
# Import the coding-rules library
m3taLib = import ../lib {lib = pkgs.lib;};
# Import custom packages
customPackages = import ../pkgs {inherit pkgs inputs;};
# Create rules configuration only if agents input is provided
rulesConfig = lib.optionalAttrs (agents != null) {
rules = m3taLib.coding-rules.mkCodingRules {
inherit agents;
# Languages relevant to this repository
languages = ["nix" "python" "shell"];
# Frameworks used in this repo
frameworks = ["n8n"];
# Standard concerns for development
concerns = [
"coding-style"
"naming"
"documentation"
"testing"
"git-workflow"
"project-structure"
];
# Also append rules to AGENTS.md for Pi agent discovery
forPi = true;
};
};
in
pkgs.mkShell {
name = "coding";
# Development tools
buildInputs = with pkgs; [
# Task management for AI coding sessions
customPackages.td
# Companion tool for CLI agents (diffs, file trees, task management)
customPackages.sidecar
# Code analysis tools
# Nix development tools (for this repo)
nil
alejandra
statix
deadnix
];
shellHook = ''
echo "🤖 AI Coding Environment"
echo ""
${
if (agents != null)
then ''
# Set up coding rules for OpenCode + Pi
${rulesConfig.rules.shellHook}
echo " Coding rules configured (OpenCode + Pi)"
echo " Languages: nix, python, shell"
echo " Frameworks: n8n"
echo " Concerns: coding-style, naming, documentation, testing, git-workflow, project-structure"
''
else ''
echo " Coding rules not configured"
echo ""
echo "To enable, add the agents input to your flake.nix:"
echo ""
echo " agents = {"
echo " url = \"git+https://code.m3ta.dev/m3tam3re/AGENTS\";"
echo " flake = false;"
echo " };"
''
}
echo ""
echo "Available tools:"
echo " opencode - AI coding agent"
echo " td usage --new-session - View current tasks"
echo " sidecar - Companion tool (diffs, file trees, tasks)"
echo " code2prompt - Convert code to prompts"
echo ""
echo "Nix development tools:"
echo " nix flake check - Check flake validity"
echo " nix fmt . - Format Nix files"
echo " statix check . - Lint Nix files"
echo " deadnix . - Find dead code"
echo ""
'';
}

View File

@@ -4,6 +4,7 @@
{ {
pkgs, pkgs,
inputs, inputs,
agents ? null,
}: { }: {
# Default shell for working on this repository # Default shell for working on this repository
default = pkgs.mkShell { default = pkgs.mkShell {
@@ -32,5 +33,5 @@
# Import all individual shell environments # Import all individual shell environments
python = import ./python.nix {inherit pkgs inputs;}; python = import ./python.nix {inherit pkgs inputs;};
devops = import ./devops.nix {inherit pkgs inputs;}; devops = import ./devops.nix {inherit pkgs inputs;};
opencode = import ./opencode.nix {inherit pkgs inputs;}; coding = import ./coding.nix {inherit pkgs inputs agents;};
} }

View File

@@ -1,155 +0,0 @@
# OpenCode development environment with AI coding rules
# This shell demonstrates the mkOpencodeRules library provided by this repository
# Usage: nix develop .#opencode
#
# To enable OpenCode rules, add the agents input to your flake:
# agents = {
# url = "git+https://code.m3ta.dev/m3tam3re/AGENTS";
# flake = false;
# };
{
pkgs,
lib ? pkgs.lib,
inputs ? null,
agents ? null,
}: let
# Import the opencode-rules library
m3taLib = import ../lib {lib = pkgs.lib;};
# Import custom packages
customPackages = import ../pkgs {inherit pkgs inputs;};
# Create rules configuration only if agents input is provided
# This demonstrates how to use mkOpencodeRules in a real project
rulesConfig = lib.optionalAttrs (agents != null) {
rules = m3taLib.opencode-rules.mkOpencodeRules {
# Pass the AGENTS repository path
inherit agents;
# Languages relevant to this repository
languages = ["python" "typescript" "nix"];
# Frameworks used in this repo
frameworks = ["n8n"];
# Standard concerns for development
concerns = [
"coding-style"
"naming"
"documentation"
"testing"
"git-workflow"
"project-structure"
];
};
};
in
pkgs.mkShell {
name = "opencode-dev";
# Development tools
buildInputs = with pkgs;
[
# OpenCode AI coding agent (if inputs are available)
]
++ lib.optionals (inputs != null)
[inputs.opencode.packages.${pkgs.stdenv.hostPlatform.system}.opencode]
++ [
# Task management for AI coding sessions
customPackages.td
# Companion tool for CLI agents (diffs, file trees, task management)
customPackages.sidecar
# Code analysis tools
customPackages.code2prompt
# Nix development tools (for this repo)
nil
alejandra
statix
deadnix
];
# Shell hook that sets up OpenCode rules
shellHook = ''
echo "🤖 OpenCode Development Environment"
echo ""
echo "This environment demonstrates the mkOpencodeRules library"
echo "provided by the m3ta-nixpkgs repository."
echo ""
${
if (agents != null)
then ''
# Execute the OpenCode rules shellHook
${rulesConfig.rules.shellHook}
echo " OpenCode rules configured!"
''
else ''
echo " OpenCode rules not configured"
echo ""
echo "To enable OpenCode rules, add the agents input to your flake.nix:"
echo ""
echo " inputs = {"
echo " m3ta-nixpkgs.url = \"git+https://code.m3ta.dev/m3tam3re/nixpkgs\";"
echo " agents = {"
echo " url = \"git+https://code.m3ta.dev/m3tam3re/AGENTS\";"
echo " flake = false;"
echo " };"
echo " };"
echo ""
echo "Then pass agents to the shell:"
echo " opencode = import ./opencode.nix { inherit pkgs inputs agents; };"
''
}
echo ""
echo "Available tools:"
echo " opencode - AI coding agent"
echo " td usage --new-session - View current tasks"
echo " sidecar - Companion tool (diffs, file trees, tasks)"
echo " code2prompt - Convert code to prompts"
echo ""
echo "Nix development tools:"
echo " nix flake check - Check flake validity"
echo " nix fmt . - Format Nix files"
echo " statix check . - Lint Nix files"
echo " deadnix . - Find dead code"
echo ""
${
if (agents == null)
then ''
echo "💡 Using mkOpencodeRules in your project:"
echo ""
echo "Add to your flake.nix:"
echo " inputs = {"
echo " m3ta-nixpkgs.url = \"git+https://code.m3ta.dev/m3tam3re/nixpkgs\";"
echo " agents = {"
echo " url = \"git+https://code.m3ta.dev/m3tam3re/AGENTS\";"
echo " flake = false;"
echo " };"
echo " };"
echo ""
echo " outputs = {self, nixpkgs, m3ta-nixpkgs, agents, ...}:"
echo " let"
echo " system = \"x86_64-linux\";"
echo " pkgs = nixpkgs.legacyPackages.''${system};"
echo " m3taLib = m3ta-nixpkgs.lib.''${system};"
echo " rules = m3taLib.opencode-rules.mkOpencodeRules {"
echo " inherit agents;"
echo " languages = [\"python\" \"typescript\"];"
echo " frameworks = [\"n8n\"];"
echo " };"
echo " in {"
echo " devShells.''${system}.default = pkgs.mkShell {"
echo " shellHook = rules.shellHook;"
echo " };"
echo " };"
''
else ""
}
echo ""
'';
}

31
tests/lib/agents-test.nix Normal file
View File

@@ -0,0 +1,31 @@
# Smoke tests for lib/agents.nix
# Verifies the library imports correctly and exports expected functions.
# Actual renderer derivations are verified by flake check building packages.
{
lib,
pkgs,
}: let
agentsLib = (import ../../lib {inherit lib;}).agents;
in
pkgs.runCommand "lib-agents-tests" {} ''
echo "Running lib agents smoke tests..."
# Verify all expected functions exist
${lib.optionalString (agentsLib ? loadCanonical) ''echo "1. pass: loadCanonical exists"''}
${lib.optionalString (!(agentsLib ? loadCanonical)) ''echo "1. FAIL: loadCanonical missing" && exit 1''}
${lib.optionalString (agentsLib ? renderForTool) ''echo "2. pass: renderForTool exists"''}
${lib.optionalString (!(agentsLib ? renderForTool)) ''echo "2. FAIL: renderForTool missing" && exit 1''}
${lib.optionalString (agentsLib ? renderForOpencode) ''echo "3. pass: renderForOpencode exists"''}
${lib.optionalString (!(agentsLib ? renderForOpencode)) ''echo "3. FAIL: renderForOpencode missing" && exit 1''}
${lib.optionalString (agentsLib ? renderForPi) ''echo "4. pass: renderForPi exists"''}
${lib.optionalString (!(agentsLib ? renderForPi)) ''echo "4. FAIL: renderForPi missing" && exit 1''}
${lib.optionalString (agentsLib ? shellHookForTool) ''echo "5. pass: shellHookForTool exists"''}
${lib.optionalString (!(agentsLib ? shellHookForTool)) ''echo "5. FAIL: shellHookForTool missing" && exit 1''}
echo "All smoke tests passed"
touch $out
''

View File

@@ -0,0 +1,89 @@
{
lib,
pkgs,
}: let
codingRulesLib = (import ../../lib {inherit lib;}).coding-rules;
# Test 1: instructions are generated correctly with custom rulesDir
testInstructions = let
rules = codingRulesLib.mkCodingRules {
agents = "/tmp/fake-agents";
languages = ["python"];
concerns = ["naming"];
rulesDir = ".coding-rules";
};
in
assert rules.instructions
== [
".coding-rules/concerns/naming.md"
".coding-rules/languages/python.md"
]; "pass: instructions";
# Test 2: default rulesDir is .opencode-rules
testDefaultRulesDir = let
rules = codingRulesLib.mkCodingRules {
agents = "/tmp/fake-agents";
};
hasCorrectPrefix = builtins.all (s: builtins.substring 0 15 s == ".opencode-rules") rules.instructions;
in
assert hasCorrectPrefix == true; "pass: default rulesDir";
# Test 3: shellHook contains both the symlink command and the config generation
testShellHook = let
rules = codingRulesLib.mkCodingRules {
agents = "/tmp/fake-agents";
};
hook = rules.shellHook;
hasSymlink = builtins.match ".*ln -sfn.*" hook != null;
hasConfigGen = builtins.match ".*coding-rules.json.*" hook != null;
in
assert hasSymlink;
assert hasConfigGen; "pass: shellHook";
# Test 4: forPi=false does not include AGENTS.md logic in shellHook
testForPiDisabled = let
rules = codingRulesLib.mkCodingRules {
agents = "/tmp/fake-agents";
forPi = false;
};
hook = rules.shellHook;
hasPiBlock = builtins.match ".*CODING-RULES:START.*" hook != null;
in
assert hasPiBlock == false; "pass: forPi disabled";
# Test 5: mkRulesMdSection produces empty string for empty concerns
testEmptyRulesMdSection = let
section = codingRulesLib.mkRulesMdSection {
agents = "/tmp/fake-agents";
concerns = [];
languages = [];
frameworks = [];
};
in
assert section == ""; "pass: empty mkRulesMdSection";
# Test 6: mkRulesMdSection wraps content with markers
testRulesMdSection = let
# Use a simple file path that won't be read (concatRulesMd returns empty
# when files don't exist, so we just verify the function is callable)
section = codingRulesLib.mkRulesMdSection {
agents = "/tmp/fake-agents";
concerns = [];
languages = [];
frameworks = [];
};
# After fix: mkRulesMdSection returns "" for empty rules, not a string with markers
in
assert section == ""; "pass: mkRulesMdSection empty case";
in
pkgs.runCommand "lib-coding-rules-tests" {} ''
echo "Running lib coding-rules tests..."
echo "1. ${testInstructions}"
echo "2. ${testDefaultRulesDir}"
echo "3. ${testShellHook}"
echo "4. ${testForPiDisabled}"
echo "5. ${testEmptyRulesMdSection}"
echo "6. ${testRulesMdSection}"
echo "All tests passed"
touch $out
''

4
tests/lib/default.nix Normal file
View File

@@ -0,0 +1,4 @@
{
coding-rules = import ./coding-rules-test.nix;
agents = import ./agents-test.nix;
}