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-start.sh
|
||||||
.sidecar-base
|
.sidecar-base
|
||||||
.td-root
|
.td-root
|
||||||
.pi-lens
|
|
||||||
.cache
|
.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": {
|
"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": {
|
"basecamp": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
"nixpkgs": [
|
"nixpkgs": [
|
||||||
@@ -96,6 +112,7 @@
|
|||||||
},
|
},
|
||||||
"root": {
|
"root": {
|
||||||
"inputs": {
|
"inputs": {
|
||||||
|
"agents": "agents",
|
||||||
"basecamp": "basecamp",
|
"basecamp": "basecamp",
|
||||||
"nixpkgs": "nixpkgs",
|
"nixpkgs": "nixpkgs",
|
||||||
"nixpkgs-master": "nixpkgs-master",
|
"nixpkgs-master": "nixpkgs-master",
|
||||||
|
|||||||
10
flake.nix
10
flake.nix
@@ -21,6 +21,12 @@
|
|||||||
url = "github:Fission-AI/OpenSpec";
|
url = "github:Fission-AI/OpenSpec";
|
||||||
inputs.nixpkgs.follows = "nixpkgs";
|
inputs.nixpkgs.follows = "nixpkgs";
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Agent definitions and coding rules
|
||||||
|
agents = {
|
||||||
|
url = "git+https://code.m3ta.dev/m3tam3re/AGENTS";
|
||||||
|
flake = false;
|
||||||
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
outputs = {
|
outputs = {
|
||||||
@@ -81,11 +87,11 @@
|
|||||||
|
|
||||||
# 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);
|
||||||
|
|||||||
@@ -223,7 +223,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;
|
||||||
@@ -306,6 +309,17 @@
|
|||||||
"- **" + 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,7 +334,8 @@
|
|||||||
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;
|
||||||
@@ -346,6 +361,7 @@
|
|||||||
agentsInput,
|
agentsInput,
|
||||||
tool,
|
tool,
|
||||||
modelOverrides ? {},
|
modelOverrides ? {},
|
||||||
|
codingRules ? null,
|
||||||
}: let
|
}: let
|
||||||
canonical = agentsInput.lib.loadAgents;
|
canonical = agentsInput.lib.loadAgents;
|
||||||
in
|
in
|
||||||
@@ -362,7 +378,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 +402,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"
|
||||||
|
|||||||
@@ -27,6 +27,7 @@
|
|||||||
# The shellHook creates:
|
# The shellHook creates:
|
||||||
# - A `.opencode-rules/` symlink pointing to the AGENTS repository rules directory
|
# - A `.opencode-rules/` symlink pointing to the AGENTS repository rules directory
|
||||||
# - A `coding-rules.json` file with a $schema reference and instructions list
|
# - 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,6 +44,9 @@
|
|||||||
# (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:
|
||||||
@@ -83,6 +87,7 @@
|
|||||||
frameworks ? [],
|
frameworks ? [],
|
||||||
extraInstructions ? [],
|
extraInstructions ? [],
|
||||||
rulesDir ? ".opencode-rules",
|
rulesDir ? ".opencode-rules",
|
||||||
|
forPi ? false,
|
||||||
}: let
|
}: let
|
||||||
# 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
|
||||||
@@ -97,11 +102,46 @@
|
|||||||
"$schema" = "https://opencode.ai/config.json";
|
"$schema" = "https://opencode.ai/config.json";
|
||||||
inherit instructions;
|
inherit instructions;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
# Pi rules content (concatenated markdown) — only computed when forPi is true
|
||||||
|
piRulesSection =
|
||||||
|
if forPi
|
||||||
|
then mkRulesMdSection {inherit agents languages concerns frameworks;}
|
||||||
|
else "";
|
||||||
|
|
||||||
|
# Bash snippet to append rules to AGENTS.md for Pi discovery.
|
||||||
|
# Uses HTML comment markers for idempotent updates:
|
||||||
|
# - Removes any existing CODING-RULES block
|
||||||
|
# - Appends the new block
|
||||||
|
# - Creates AGENTS.md if it doesn't exist
|
||||||
|
# Note: Uses plain if-then-else instead of lib.optionalString to avoid
|
||||||
|
# forcing the `lib` argument (which may come from import <nixpkgs/lib>)
|
||||||
|
# when forPi is false.
|
||||||
|
piShellHook =
|
||||||
|
if forPi && piRulesSection != ""
|
||||||
|
then ''
|
||||||
|
# Pi agent: append coding rules to AGENTS.md
|
||||||
|
if [ -f AGENTS.md ]; then
|
||||||
|
# Remove existing coding-rules block (if any)
|
||||||
|
sed -i '/<!-- CODING-RULES:START -->/,/<!-- CODING-RULES:END -->/d' AGENTS.md
|
||||||
|
# Append new coding-rules block
|
||||||
|
cat >> AGENTS.md <<'PIRULES_EOF'
|
||||||
|
${piRulesSection}
|
||||||
|
PIRULES_EOF
|
||||||
|
else
|
||||||
|
# Create AGENTS.md with just the coding rules
|
||||||
|
cat > AGENTS.md <<'PIRULES_EOF'
|
||||||
|
${piRulesSection}
|
||||||
|
PIRULES_EOF
|
||||||
|
fi
|
||||||
|
''
|
||||||
|
else "";
|
||||||
in {
|
in {
|
||||||
inherit instructions;
|
inherit instructions;
|
||||||
|
|
||||||
# Shell hook to set up rules in the project
|
# Shell hook to set up rules in the project
|
||||||
# Creates a symlink to the AGENTS rules directory and generates coding-rules.json
|
# Creates a symlink to the AGENTS rules directory and generates 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}
|
||||||
@@ -110,8 +150,73 @@
|
|||||||
cat > coding-rules.json <<'RULES_EOF'
|
cat > coding-rules.json <<'RULES_EOF'
|
||||||
${builtins.toJSON rulesConfig}
|
${builtins.toJSON rulesConfig}
|
||||||
RULES_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);
|
||||||
|
|
||||||
|
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 {
|
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 {
|
settings = mkOption {
|
||||||
type = types.submodule {
|
type = types.submodule {
|
||||||
freeformType = types.attrsOf types.anything;
|
freeformType = types.attrsOf types.anything;
|
||||||
@@ -166,6 +220,12 @@ in
|
|||||||
|
|
||||||
piSettings = filterNulls cfg.settings;
|
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 agents (only computed when agentsInput is set)
|
||||||
rendered =
|
rendered =
|
||||||
if cfg.agentsInput != null
|
if cfg.agentsInput != null
|
||||||
@@ -175,6 +235,7 @@ 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;
|
||||||
|
|
||||||
|
|||||||
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,
|
pkgs,
|
||||||
inputs,
|
inputs,
|
||||||
|
agents ? null,
|
||||||
}: {
|
}: {
|
||||||
# Default shell for working on this repository
|
# Default shell for working on this repository
|
||||||
default = pkgs.mkShell {
|
default = pkgs.mkShell {
|
||||||
@@ -32,5 +33,5 @@
|
|||||||
# Import all individual shell environments
|
# Import all individual shell environments
|
||||||
python = import ./python.nix {inherit pkgs inputs;};
|
python = import ./python.nix {inherit pkgs inputs;};
|
||||||
devops = import ./devops.nix {inherit pkgs inputs;};
|
devops = import ./devops.nix {inherit pkgs inputs;};
|
||||||
opencode = import ./opencode.nix {inherit pkgs inputs;};
|
coding = import ./coding.nix {inherit pkgs inputs agents;};
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,155 +0,0 @@
|
|||||||
# OpenCode development environment with AI coding rules
|
|
||||||
# This shell demonstrates the 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;};
|
result = agentsLib.loadCanonical {agentsInput = fakeInput;};
|
||||||
in
|
in
|
||||||
assert result == {test = {description = "test";};}; {result = "pass";};
|
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 {
|
in {
|
||||||
unknown-tool-throws = testUnknownTool;
|
unknown-tool-throws = testUnknownTool;
|
||||||
load-canonical = testLoadCanonical;
|
load-canonical = testLoadCanonical;
|
||||||
|
pi-null-coding-rules = testPiNullCodingRules;
|
||||||
|
pi-with-coding-rules = testPiWithCodingRules;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,8 +37,74 @@ let
|
|||||||
in
|
in
|
||||||
assert hasSymlink;
|
assert hasSymlink;
|
||||||
assert hasConfigGen; {result = "pass";};
|
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 {
|
in {
|
||||||
instructions-correct = testInstructions;
|
instructions-correct = testInstructions;
|
||||||
default-rules-dir = testDefaultRulesDir;
|
default-rules-dir = testDefaultRulesDir;
|
||||||
shell-hook = testShellHook;
|
shell-hook = testShellHook;
|
||||||
|
forpi-disabled = testForPiDisabled;
|
||||||
|
forpi-enabled = testForPiEnabled;
|
||||||
|
concat-rules-md = testConcatRulesMd;
|
||||||
|
rules-md-section = testRulesMdSection;
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user