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
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
This commit is contained in:
2
.gitignore
vendored
2
.gitignore
vendored
@@ -43,5 +43,5 @@ flake.lock.bak
|
||||
.sidecar-start.sh
|
||||
.sidecar-base
|
||||
.td-root
|
||||
.pi-lens
|
||||
.cache
|
||||
.pi*
|
||||
|
||||
7
.pi-lens/cache/jscpd.json
vendored
7
.pi-lens/cache/jscpd.json
vendored
@@ -1,7 +0,0 @@
|
||||
{
|
||||
"success": true,
|
||||
"clones": [],
|
||||
"duplicatedLines": 0,
|
||||
"totalLines": 0,
|
||||
"percentage": 0
|
||||
}
|
||||
3
.pi-lens/cache/jscpd.meta.json
vendored
3
.pi-lens/cache/jscpd.meta.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"timestamp": "2026-04-11T04:17:20.531Z"
|
||||
}
|
||||
9
.pi-lens/cache/knip.json
vendored
9
.pi-lens/cache/knip.json
vendored
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"success": false,
|
||||
"issues": [],
|
||||
"unusedExports": [],
|
||||
"unusedFiles": [],
|
||||
"unusedDeps": [],
|
||||
"unlistedDeps": [],
|
||||
"summary": "Failed to parse output"
|
||||
}
|
||||
3
.pi-lens/cache/knip.meta.json
vendored
3
.pi-lens/cache/knip.meta.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"timestamp": "2026-04-11T04:17:21.374Z"
|
||||
}
|
||||
1
.pi-lens/cache/session-start-guidance.json
vendored
1
.pi-lens/cache/session-start-guidance.json
vendored
@@ -1 +0,0 @@
|
||||
null
|
||||
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"timestamp": "2026-04-19T15:52:39.989Z"
|
||||
}
|
||||
3
.pi-lens/cache/todo-baseline.json
vendored
3
.pi-lens/cache/todo-baseline.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"items": []
|
||||
}
|
||||
3
.pi-lens/cache/todo-baseline.meta.json
vendored
3
.pi-lens/cache/todo-baseline.meta.json
vendored
@@ -1,3 +0,0 @@
|
||||
{
|
||||
"timestamp": "2026-04-19T15:42:10.963Z"
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
{
|
||||
"files": {},
|
||||
"turnCycles": 0,
|
||||
"maxCycles": 3,
|
||||
"lastUpdated": "2026-04-11T04:17:22.397Z"
|
||||
}
|
||||
17
flake.lock
generated
17
flake.lock
generated
@@ -1,5 +1,21 @@
|
||||
{
|
||||
"nodes": {
|
||||
"agents": {
|
||||
"flake": false,
|
||||
"locked": {
|
||||
"lastModified": 1776092721,
|
||||
"narHash": "sha256-avV4Snqp0K57I9s8D61+GHlg9DYZFSIvjaS4d4RYpG8=",
|
||||
"ref": "refs/heads/master",
|
||||
"rev": "0ad41acb03eee0e22cba611b2171a3d3ee30cb10",
|
||||
"revCount": 72,
|
||||
"type": "git",
|
||||
"url": "https://code.m3ta.dev/m3tam3re/AGENTS"
|
||||
},
|
||||
"original": {
|
||||
"type": "git",
|
||||
"url": "https://code.m3ta.dev/m3tam3re/AGENTS"
|
||||
}
|
||||
},
|
||||
"basecamp": {
|
||||
"inputs": {
|
||||
"nixpkgs": [
|
||||
@@ -96,6 +112,7 @@
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"agents": "agents",
|
||||
"basecamp": "basecamp",
|
||||
"nixpkgs": "nixpkgs",
|
||||
"nixpkgs-master": "nixpkgs-master",
|
||||
|
||||
10
flake.nix
10
flake.nix
@@ -21,6 +21,12 @@
|
||||
url = "github:Fission-AI/OpenSpec";
|
||||
inputs.nixpkgs.follows = "nixpkgs";
|
||||
};
|
||||
|
||||
# Agent definitions and coding rules
|
||||
agents = {
|
||||
url = "git+https://code.m3ta.dev/m3tam3re/AGENTS";
|
||||
flake = false;
|
||||
};
|
||||
};
|
||||
|
||||
outputs = {
|
||||
@@ -81,11 +87,11 @@
|
||||
|
||||
# Development shells for various programming environments
|
||||
# Usage: nix develop .#<shell-name>
|
||||
# Available shells: default, python, devops, opencode
|
||||
# Available shells: default, python, devops, coding
|
||||
devShells = forAllSystems (system: let
|
||||
pkgs = pkgsFor system;
|
||||
in
|
||||
import ./shells {inherit pkgs inputs;});
|
||||
import ./shells {inherit pkgs inputs; agents = inputs.agents;});
|
||||
|
||||
# Formatter for 'nix fmt'
|
||||
formatter = forAllSystems (system: (pkgsFor system).alejandra);
|
||||
|
||||
@@ -223,7 +223,10 @@
|
||||
canonical,
|
||||
modelOverrides ? {},
|
||||
primaryAgent ? null,
|
||||
codingRules ? null,
|
||||
}: let
|
||||
# Import coding-rules lib for concatRulesMd when codingRules is provided
|
||||
codingRulesLib = (import ./coding-rules.nix {inherit lib;});
|
||||
# Find the primary agent (there should be exactly one).
|
||||
primaryAgents = lib.filterAttrs (_: a: a.mode == "primary") canonical;
|
||||
primaryNames = lib.attrNames primaryAgents;
|
||||
@@ -306,6 +309,17 @@
|
||||
"- **" + dn + "**: " + agent.description;
|
||||
in
|
||||
lib.mapAttrsToList mkEntry subagents;
|
||||
# ── Coding rules section (optional) ────────────────────────
|
||||
# When codingRules is provided, append selected rules to AGENTS.md.
|
||||
# codingRules attrset: { agents, languages, concerns, frameworks }
|
||||
codingRulesSection =
|
||||
if codingRules != null
|
||||
then let
|
||||
section = codingRulesLib.mkRulesMdSection codingRules;
|
||||
in
|
||||
if section != "" then "\n" + section else ""
|
||||
else "";
|
||||
|
||||
agentsMd =
|
||||
"# Agent Instructions\n"
|
||||
+ "\n"
|
||||
@@ -320,7 +334,8 @@
|
||||
if subagents == {}
|
||||
then ""
|
||||
else "## Available Specialists\n\n" + lib.concatStringsSep "\n" specialistEntries + "\n"
|
||||
);
|
||||
)
|
||||
+ codingRulesSection;
|
||||
|
||||
agentsMdFile = pkgs.writeText "AGENTS.md" agentsMd;
|
||||
systemMdFile = pkgs.writeText "SYSTEM.md" primary.systemPrompt;
|
||||
@@ -346,6 +361,7 @@
|
||||
agentsInput,
|
||||
tool,
|
||||
modelOverrides ? {},
|
||||
codingRules ? null,
|
||||
}: let
|
||||
canonical = agentsInput.lib.loadAgents;
|
||||
in
|
||||
@@ -362,7 +378,7 @@
|
||||
else if tool == "pi"
|
||||
then
|
||||
agentsLib.renderForPi {
|
||||
inherit pkgs canonical modelOverrides;
|
||||
inherit pkgs canonical modelOverrides codingRules;
|
||||
}
|
||||
else throw "lib.agents.renderForTool: unknown tool '${tool}'. Must be opencode, claude-code, or pi.";
|
||||
|
||||
@@ -386,9 +402,10 @@
|
||||
agentsInput,
|
||||
tool,
|
||||
modelOverrides ? {},
|
||||
codingRules ? null,
|
||||
}: let
|
||||
rendered = agentsLib.renderForTool {
|
||||
inherit pkgs agentsInput tool modelOverrides;
|
||||
inherit pkgs agentsInput tool modelOverrides codingRules;
|
||||
};
|
||||
in
|
||||
if tool == "opencode"
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
# The shellHook creates:
|
||||
# - A `.opencode-rules/` symlink pointing to the AGENTS repository rules directory
|
||||
# - A `coding-rules.json` file with a $schema reference and instructions list
|
||||
# - (Optional) Appends coding rules to `AGENTS.md` for Pi agent discovery
|
||||
#
|
||||
# The instructions list contains paths relative to the project root, all prefixed
|
||||
# with `.opencode-rules/`, making them portable across different project locations.
|
||||
@@ -43,6 +44,9 @@
|
||||
# (e.g., [ "react" "fastapi" "django" ])
|
||||
# extraInstructions: Optional list of additional instruction paths
|
||||
# (for custom rules outside standard locations)
|
||||
# forPi: Whether to also append rules to AGENTS.md for Pi agent (default: true)
|
||||
# Pi discovers AGENTS.md files by walking parent dirs + cwd and concatenates them.
|
||||
# When enabled, a delimited block is appended to (or created in) AGENTS.md.
|
||||
#
|
||||
# Returns:
|
||||
# An attribute set containing:
|
||||
@@ -83,6 +87,7 @@
|
||||
frameworks ? [],
|
||||
extraInstructions ? [],
|
||||
rulesDir ? ".opencode-rules",
|
||||
forPi ? false,
|
||||
}: let
|
||||
# Build instructions list by mapping concerns, languages, frameworks to their file paths
|
||||
# All paths are relative to project root via the rulesDir symlink
|
||||
@@ -97,11 +102,46 @@
|
||||
"$schema" = "https://opencode.ai/config.json";
|
||||
inherit instructions;
|
||||
};
|
||||
|
||||
# Pi rules content (concatenated markdown) — only computed when forPi is true
|
||||
piRulesSection =
|
||||
if forPi
|
||||
then mkRulesMdSection {inherit agents languages concerns frameworks;}
|
||||
else "";
|
||||
|
||||
# Bash snippet to append rules to AGENTS.md for Pi discovery.
|
||||
# Uses HTML comment markers for idempotent updates:
|
||||
# - Removes any existing CODING-RULES block
|
||||
# - Appends the new block
|
||||
# - Creates AGENTS.md if it doesn't exist
|
||||
# Note: Uses plain if-then-else instead of lib.optionalString to avoid
|
||||
# forcing the `lib` argument (which may come from import <nixpkgs/lib>)
|
||||
# when forPi is false.
|
||||
piShellHook =
|
||||
if forPi && piRulesSection != ""
|
||||
then ''
|
||||
# Pi agent: append coding rules to AGENTS.md
|
||||
if [ -f AGENTS.md ]; then
|
||||
# Remove existing coding-rules block (if any)
|
||||
sed -i '/<!-- CODING-RULES:START -->/,/<!-- CODING-RULES:END -->/d' AGENTS.md
|
||||
# Append new coding-rules block
|
||||
cat >> AGENTS.md <<'PIRULES_EOF'
|
||||
${piRulesSection}
|
||||
PIRULES_EOF
|
||||
else
|
||||
# Create AGENTS.md with just the coding rules
|
||||
cat > AGENTS.md <<'PIRULES_EOF'
|
||||
${piRulesSection}
|
||||
PIRULES_EOF
|
||||
fi
|
||||
''
|
||||
else "";
|
||||
in {
|
||||
inherit instructions;
|
||||
|
||||
# Shell hook to set up rules in the project
|
||||
# Creates a symlink to the AGENTS rules directory and generates coding-rules.json
|
||||
# Optionally appends rules to AGENTS.md for Pi agent discovery
|
||||
shellHook = ''
|
||||
# Create/update symlink to AGENTS rules directory
|
||||
ln -sfn ${agents}/rules ${rulesDir}
|
||||
@@ -110,8 +150,73 @@
|
||||
cat > coding-rules.json <<'RULES_EOF'
|
||||
${builtins.toJSON rulesConfig}
|
||||
RULES_EOF
|
||||
|
||||
${piShellHook}
|
||||
'';
|
||||
};
|
||||
# Concatenate selected rule files from the AGENTS repository into a single
|
||||
# markdown string. Used by Pi (append to AGENTS.md) and could be used by
|
||||
# other tools that don't support an instructions list.
|
||||
#
|
||||
# Args:
|
||||
# agents: Path to the AGENTS repository (non-flake input)
|
||||
# languages: Optional list of language-specific rules to include
|
||||
# concerns: Optional list of concern rules to include
|
||||
# Default: [ "coding-style" "naming" "documentation" "testing" "git-workflow" "project-structure" ]
|
||||
# frameworks: Optional list of framework-specific rules to include
|
||||
#
|
||||
# Returns: A single concatenated markdown string with all selected rules.
|
||||
#
|
||||
# Example:
|
||||
# concatRulesMd {
|
||||
# agents = inputs.agents;
|
||||
# languages = [ "python" ];
|
||||
# concerns = [ "coding-style" ];
|
||||
# }
|
||||
# # Returns: "\n# Coding Style\n\n...python rules...\n"
|
||||
concatRulesMd = {
|
||||
agents,
|
||||
languages ? [],
|
||||
concerns ? [
|
||||
"coding-style"
|
||||
"naming"
|
||||
"documentation"
|
||||
"testing"
|
||||
"git-workflow"
|
||||
"project-structure"
|
||||
],
|
||||
frameworks ? [],
|
||||
}: let
|
||||
rulePaths =
|
||||
(map (c: {kind = "concerns"; name = c;}) concerns)
|
||||
++ (map (l: {kind = "languages"; name = l;}) languages)
|
||||
++ (map (f: {kind = "frameworks"; name = f;}) frameworks);
|
||||
|
||||
readRule = rule: builtins.readFile "${agents}/rules/${rule.kind}/${rule.name}.md";
|
||||
ruleContents = map readRule rulePaths;
|
||||
in
|
||||
lib.concatStringsSep "\n\n" ruleContents;
|
||||
|
||||
# Build a coding rules section suitable for appending to AGENTS.md.
|
||||
# Wraps concatRulesMd output with a header and HTML comment markers
|
||||
# for idempotent updates in project-level shellHooks.
|
||||
#
|
||||
# Args: Same as concatRulesMd
|
||||
#
|
||||
# Returns: A markdown string with start/end markers and a header.
|
||||
mkRulesMdSection = args: let
|
||||
content = concatRulesMd args;
|
||||
in
|
||||
if builtins.stringLength content == 0
|
||||
then ""
|
||||
else ''
|
||||
<!-- CODING-RULES:START -->
|
||||
# Coding Rules
|
||||
|
||||
${content}
|
||||
<!-- CODING-RULES:END -->
|
||||
'';
|
||||
|
||||
in {
|
||||
inherit mkCodingRules;
|
||||
inherit mkCodingRules concatRulesMd mkRulesMdSection;
|
||||
}
|
||||
|
||||
@@ -44,6 +44,60 @@ in
|
||||
'';
|
||||
};
|
||||
|
||||
codingRules = mkOption {
|
||||
type = types.nullOr (types.submodule {
|
||||
options = {
|
||||
languages = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = ''
|
||||
Language-specific coding rules to include
|
||||
(e.g. [ "python" "typescript" "nix" ]).
|
||||
Rule files are read from the AGENTS repo's rules/languages/ directory.
|
||||
'';
|
||||
};
|
||||
|
||||
concerns = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [
|
||||
"coding-style"
|
||||
"naming"
|
||||
"documentation"
|
||||
"testing"
|
||||
"git-workflow"
|
||||
"project-structure"
|
||||
];
|
||||
description = ''
|
||||
Concern rules to include from the AGENTS repo's rules/concerns/ directory.
|
||||
'';
|
||||
};
|
||||
|
||||
frameworks = mkOption {
|
||||
type = types.listOf types.str;
|
||||
default = [];
|
||||
description = ''
|
||||
Framework-specific coding rules to include
|
||||
(e.g. [ "react" "fastapi" ]).
|
||||
Rule files are read from the AGENTS repo's rules/frameworks/ directory.
|
||||
'';
|
||||
};
|
||||
};
|
||||
});
|
||||
default = null;
|
||||
description = ''
|
||||
Coding rules to inject into ~/.pi/agent/AGENTS.md.
|
||||
Rules are read from the AGENTS repository and appended as markdown sections.
|
||||
Requires agentsInput to be set.
|
||||
'';
|
||||
example = literalExpression ''
|
||||
{
|
||||
languages = [ "python" "typescript" ];
|
||||
concerns = [ "coding-style" "testing" ];
|
||||
frameworks = [ "fastapi" ];
|
||||
}
|
||||
'';
|
||||
};
|
||||
|
||||
settings = mkOption {
|
||||
type = types.submodule {
|
||||
freeformType = types.attrsOf types.anything;
|
||||
@@ -166,6 +220,12 @@ in
|
||||
|
||||
piSettings = filterNulls cfg.settings;
|
||||
|
||||
# Coding rules config for renderForPi (only when both agentsInput and codingRules are set)
|
||||
piCodingRules =
|
||||
if cfg.agentsInput != null && cfg.codingRules != null
|
||||
then cfg.codingRules // { agents = cfg.agentsInput; }
|
||||
else null;
|
||||
|
||||
# Rendered agents (only computed when agentsInput is set)
|
||||
rendered =
|
||||
if cfg.agentsInput != null
|
||||
@@ -175,6 +235,7 @@ in
|
||||
canonical = cfg.agentsInput.lib.loadAgents;
|
||||
modelOverrides = cfg.modelOverrides;
|
||||
primaryAgent = cfg.primaryAgent;
|
||||
codingRules = piCodingRules;
|
||||
}
|
||||
else null;
|
||||
|
||||
|
||||
111
shells/coding.nix
Normal file
111
shells/coding.nix
Normal file
@@ -0,0 +1,111 @@
|
||||
# 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
|
||||
customPackages.code2prompt
|
||||
|
||||
# Nix development tools (for this repo)
|
||||
nil
|
||||
alejandra
|
||||
statix
|
||||
deadnix
|
||||
];
|
||||
|
||||
shellHook = ''
|
||||
echo "🤖 AI Coding Environment"
|
||||
echo ""
|
||||
|
||||
${
|
||||
if (agents != null)
|
||||
then ''
|
||||
# Set up coding rules for OpenCode + Pi
|
||||
${rulesConfig.rules.shellHook}
|
||||
|
||||
echo "✅ Coding rules configured (OpenCode + Pi)"
|
||||
echo " Languages: nix, python, shell"
|
||||
echo " Frameworks: n8n"
|
||||
echo " Concerns: coding-style, naming, documentation, testing, git-workflow, project-structure"
|
||||
''
|
||||
else ''
|
||||
echo "⚠️ Coding rules not configured"
|
||||
echo ""
|
||||
echo "To enable, add the agents input to your flake.nix:"
|
||||
echo ""
|
||||
echo " agents = {"
|
||||
echo " url = \"git+https://code.m3ta.dev/m3tam3re/AGENTS\";"
|
||||
echo " flake = false;"
|
||||
echo " };"
|
||||
''
|
||||
}
|
||||
|
||||
echo ""
|
||||
echo "Available tools:"
|
||||
echo " opencode - AI coding agent"
|
||||
echo " td usage --new-session - View current tasks"
|
||||
echo " sidecar - Companion tool (diffs, file trees, tasks)"
|
||||
echo " code2prompt - Convert code to prompts"
|
||||
echo ""
|
||||
echo "Nix development tools:"
|
||||
echo " nix flake check - Check flake validity"
|
||||
echo " nix fmt . - Format Nix files"
|
||||
echo " statix check . - Lint Nix files"
|
||||
echo " deadnix . - Find dead code"
|
||||
echo ""
|
||||
'';
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
{
|
||||
pkgs,
|
||||
inputs,
|
||||
agents ? null,
|
||||
}: {
|
||||
# Default shell for working on this repository
|
||||
default = pkgs.mkShell {
|
||||
@@ -32,5 +33,5 @@
|
||||
# Import all individual shell environments
|
||||
python = import ./python.nix {inherit pkgs inputs;};
|
||||
devops = import ./devops.nix {inherit pkgs inputs;};
|
||||
opencode = import ./opencode.nix {inherit pkgs inputs;};
|
||||
coding = import ./coding.nix {inherit pkgs inputs agents;};
|
||||
}
|
||||
|
||||
@@ -1,155 +0,0 @@
|
||||
# OpenCode development environment with AI coding rules
|
||||
# This shell demonstrates the mkCodingRules 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 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
|
||||
# This demonstrates how to use mkCodingRules in a real project
|
||||
rulesConfig = lib.optionalAttrs (agents != null) {
|
||||
rules = m3taLib.coding-rules.mkCodingRules {
|
||||
# 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 mkCodingRules 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 mkCodingRules 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.coding-rules.mkCodingRules {
|
||||
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 ""
|
||||
'';
|
||||
}
|
||||
@@ -20,7 +20,67 @@ let
|
||||
result = agentsLib.loadCanonical {agentsInput = fakeInput;};
|
||||
in
|
||||
assert result == {test = {description = "test";};}; {result = "pass";};
|
||||
|
||||
# Test 3: renderForPi accepts codingRules parameter without error (null case)
|
||||
# Verifies that passing codingRules = null produces the same result as omitting it.
|
||||
# Uses a minimal fake canonical set instead of a real agents repo.
|
||||
testPiNullCodingRules = let
|
||||
pkgs = import <nixpkgs> {};
|
||||
canonical = {
|
||||
chiron = {
|
||||
mode = "primary";
|
||||
description = "Test primary agent";
|
||||
display_name = "Chiron";
|
||||
systemPrompt = "You are a test agent.";
|
||||
permissions = {};
|
||||
};
|
||||
};
|
||||
result = agentsLib.renderForPi {
|
||||
inherit pkgs canonical;
|
||||
codingRules = null;
|
||||
};
|
||||
agentsMd = builtins.readFile "${result}/AGENTS.md";
|
||||
hasMarkers = builtins.match ".*CODING-RULES:START.*" agentsMd != null;
|
||||
in
|
||||
assert hasMarkers == false; {result = "pass";};
|
||||
|
||||
# Test 4: renderForPi with codingRules includes rules in AGENTS.md
|
||||
# Uses the real AGENTS repo to read rule files (requires --impure or local path)
|
||||
testPiWithCodingRules = let
|
||||
agentsPath = /home/sascha.koenig/p/AI/AGENTS;
|
||||
pkgs = import <nixpkgs> {};
|
||||
canonical = {
|
||||
chiron = {
|
||||
mode = "primary";
|
||||
description = "Test primary agent";
|
||||
display_name = "Chiron";
|
||||
systemPrompt = "You are a test agent.";
|
||||
permissions = {};
|
||||
};
|
||||
};
|
||||
result = agentsLib.renderForPi {
|
||||
inherit pkgs canonical;
|
||||
codingRules = {
|
||||
agents = agentsPath;
|
||||
concerns = ["coding-style"];
|
||||
languages = [];
|
||||
frameworks = [];
|
||||
};
|
||||
};
|
||||
agentsMd = builtins.readFile "${result}/AGENTS.md";
|
||||
hasStartMarker = builtins.match ".*CODING-RULES:START.*" agentsMd != null;
|
||||
hasEndMarker = builtins.match ".*CODING-RULES:END.*" agentsMd != null;
|
||||
hasCodingStyle = builtins.match ".*Coding Style.*" agentsMd != null;
|
||||
# Also verify agent descriptions are still present
|
||||
hasAgentInstructions = builtins.match ".*Agent Instructions.*" agentsMd != null;
|
||||
in
|
||||
assert hasStartMarker == true;
|
||||
assert hasEndMarker == true;
|
||||
assert hasCodingStyle == true;
|
||||
assert hasAgentInstructions == true; {result = "pass";};
|
||||
in {
|
||||
unknown-tool-throws = testUnknownTool;
|
||||
load-canonical = testLoadCanonical;
|
||||
pi-null-coding-rules = testPiNullCodingRules;
|
||||
pi-with-coding-rules = testPiWithCodingRules;
|
||||
}
|
||||
|
||||
@@ -37,8 +37,74 @@ let
|
||||
in
|
||||
assert hasSymlink;
|
||||
assert hasConfigGen; {result = "pass";};
|
||||
|
||||
# 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; {result = "pass";};
|
||||
|
||||
# Test 5: forPi=true adds CODING-RULES markers to shellHook (when agents path has rules)
|
||||
# Note: This test uses the real AGENTS repo at /home/sascha.koenig/p/AI/AGENTS
|
||||
# It is only run when the path exists.
|
||||
testForPiEnabled = let
|
||||
agentsPath = /home/sascha.koenig/p/AI/AGENTS;
|
||||
rules = codingRulesLib.mkCodingRules {
|
||||
agents = agentsPath;
|
||||
forPi = true;
|
||||
concerns = ["coding-style"];
|
||||
languages = [];
|
||||
frameworks = [];
|
||||
};
|
||||
hook = rules.shellHook;
|
||||
hasPiBlock = builtins.match ".*CODING-RULES:START.*" hook != null;
|
||||
hasCodingStyle = builtins.match ".*Coding Style.*" hook != null;
|
||||
in
|
||||
assert hasPiBlock == true;
|
||||
assert hasCodingStyle == true; {result = "pass";};
|
||||
|
||||
# Test 6: concatRulesMd produces concatenated markdown (with real agents path)
|
||||
testConcatRulesMd = let
|
||||
agentsPath = /home/sascha.koenig/p/AI/AGENTS;
|
||||
md = codingRulesLib.concatRulesMd {
|
||||
agents = agentsPath;
|
||||
concerns = ["coding-style"];
|
||||
languages = [];
|
||||
frameworks = [];
|
||||
};
|
||||
hasHeader = builtins.match ".*Coding Style.*" md != null;
|
||||
hasCritical = builtins.match ".*Critical Rules.*" md != null;
|
||||
in
|
||||
assert hasHeader == true;
|
||||
assert hasCritical == true; {result = "pass";};
|
||||
|
||||
# Test 7: mkRulesMdSection wraps content with markers
|
||||
testRulesMdSection = let
|
||||
agentsPath = /home/sascha.koenig/p/AI/AGENTS;
|
||||
section = codingRulesLib.mkRulesMdSection {
|
||||
agents = agentsPath;
|
||||
concerns = ["coding-style"];
|
||||
languages = [];
|
||||
frameworks = [];
|
||||
};
|
||||
hasStartMarker = builtins.match ".*CODING-RULES:START.*" section != null;
|
||||
hasEndMarker = builtins.match ".*CODING-RULES:END.*" section != null;
|
||||
hasHeader = builtins.match ".*# Coding Rules.*" section != null;
|
||||
in
|
||||
assert hasStartMarker == true;
|
||||
assert hasEndMarker == true;
|
||||
assert hasHeader == true; {result = "pass";};
|
||||
in {
|
||||
instructions-correct = testInstructions;
|
||||
default-rules-dir = testDefaultRulesDir;
|
||||
shell-hook = testShellHook;
|
||||
forpi-disabled = testForPiDisabled;
|
||||
forpi-enabled = testForPiEnabled;
|
||||
concat-rules-md = testConcatRulesMd;
|
||||
rules-md-section = testRulesMdSection;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user