feat: config with agents rework
This commit is contained in:
348
lib/agents.nix
348
lib/agents.nix
@@ -5,50 +5,53 @@
|
||||
#
|
||||
# Usage in your configuration:
|
||||
#
|
||||
# # In your flake or configuration:
|
||||
# let
|
||||
# m3taLib = inputs.m3ta-nixpkgs.lib.${system};
|
||||
#
|
||||
# # Load canonical agents from the AGENTS flake input
|
||||
# canonical = m3taLib.agents.loadCanonical { agentsInput = inputs.agents; };
|
||||
#
|
||||
# # Render for a specific tool
|
||||
# agentFiles = m3taLib.agents.renderForTool {
|
||||
# inherit pkgs;
|
||||
# agentsInput = inputs.agents;
|
||||
# tool = "opencode";
|
||||
# rendered = m3taLib.agents.renderForOpencode {
|
||||
# inherit pkgs canonical;
|
||||
# modelOverrides = { chiron = "anthropic/claude-sonnet-4"; };
|
||||
# };
|
||||
# in { ... }
|
||||
#
|
||||
# Renderers are stubs for now (Tasks 9-11 will provide real implementations).
|
||||
{lib}: let
|
||||
# ── Shared helpers ─────────────────────────────────────────────
|
||||
# Split a rule string on the LAST colon to get { pattern, action }.
|
||||
# e.g. "rm -rf *:ask" → pattern="rm -rf *", action="ask"
|
||||
# e.g. "/run/agenix/**:deny" → pattern="/run/agenix/**", action="deny"
|
||||
parseRule = ruleStr: let
|
||||
parts = lib.strings.splitString ":" ruleStr;
|
||||
action = lib.last parts;
|
||||
pattern = lib.concatStringsSep ":" (lib.init parts);
|
||||
in {inherit pattern action;};
|
||||
|
||||
agentsLib = {
|
||||
# ── loadCanonical ─────────────────────────────────────────────
|
||||
#
|
||||
# Load canonical agent definitions from the AGENTS flake input.
|
||||
# agentsInput: the AGENTS flake (e.g., inputs.agents in a consuming flake)
|
||||
# Returns the canonical attrset from lib.loadAgents (keyed by slug).
|
||||
|
||||
loadCanonical = {agentsInput}: agentsInput.lib.loadAgents;
|
||||
|
||||
# OpenCode renderer — produces a directory of .opencode/agents/*.md files.
|
||||
# ── OpenCode renderer ─────────────────────────────────────────
|
||||
#
|
||||
# Produces a directory of agent *.md files suitable for
|
||||
# ~/.config/opencode/agents/ (system-level)
|
||||
# .opencode/agents/ (project-level)
|
||||
#
|
||||
# Each file has YAML frontmatter (description, mode, optional model,
|
||||
# optional permission) followed by the agent's systemPrompt content.
|
||||
# The filename (without .md) becomes the agent name in OpenCode.
|
||||
|
||||
renderForOpencode = {
|
||||
pkgs,
|
||||
canonical,
|
||||
modelOverrides ? {},
|
||||
}: let
|
||||
# Split a rule string on the LAST colon to get { pattern, action }.
|
||||
# e.g. "rm -rf *:ask" → pattern="rm -rf *", action="ask"
|
||||
# e.g. "/run/agenix/**:deny" → pattern="/run/agenix/**", action="deny"
|
||||
parseRule = ruleStr: let
|
||||
parts = lib.strings.splitString ":" ruleStr;
|
||||
# last element is the action, everything before joined with ":" is pattern
|
||||
action = lib.last parts;
|
||||
pattern = lib.concatStringsSep ":" (lib.init parts);
|
||||
in {inherit pattern action;};
|
||||
|
||||
# Render one permission section to YAML lines (list of strings).
|
||||
# Render one permission section to YAML lines.
|
||||
# intent-only → single line: " <tool>: <intent>"
|
||||
# intent+rules → nested block starting with "*": <intent>, then specific rules
|
||||
# intent+rules → nested block
|
||||
renderPermSection = tool: section:
|
||||
if !(section ? rules) || section.rules == []
|
||||
then [" ${tool}: ${section.intent}"]
|
||||
@@ -59,8 +62,6 @@
|
||||
in
|
||||
[" ${tool}:"] ++ [wildcardLine] ++ ruleLines;
|
||||
|
||||
# Render the full permission block for an agent.
|
||||
# Returns empty list if no permissions.
|
||||
renderPermBlock = permissions:
|
||||
if permissions == {} || permissions == null
|
||||
then []
|
||||
@@ -70,7 +71,6 @@
|
||||
lib.mapAttrsToList renderPermSection permissions
|
||||
);
|
||||
|
||||
# Build the YAML frontmatter string for one agent.
|
||||
mkFrontmatter = name: agent: let
|
||||
descLine = "description: \"${agent.description}.\"";
|
||||
modeLine = "mode: ${agent.mode}";
|
||||
@@ -85,18 +85,14 @@
|
||||
else lib.concatStringsSep "\n" permBlock + "\n";
|
||||
in "---\n${descLine}\n${modeLine}\n${modelLine}${permLines}---\n";
|
||||
|
||||
# Build the full markdown file content for one agent.
|
||||
mkAgentContent = name: agent:
|
||||
(mkFrontmatter name agent) + agent.systemPrompt;
|
||||
|
||||
# Write each agent as a derivation text file.
|
||||
mkAgentFile = name: agent:
|
||||
pkgs.writeText "${name}.md" (mkAgentContent name agent);
|
||||
|
||||
# Attrset of name → derivation for each agent.
|
||||
agentFiles = lib.mapAttrs mkAgentFile canonical;
|
||||
|
||||
# Shell commands to copy each file into $out.
|
||||
copyCommands = lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (name: file: "cp ${file} $out/${name}.md") agentFiles
|
||||
);
|
||||
@@ -106,25 +102,245 @@
|
||||
${copyCommands}
|
||||
'';
|
||||
|
||||
# Stub: Claude Code renderer — produces .claude/agents/*.md files.
|
||||
# To be implemented in Task 10.
|
||||
# ── Claude Code renderer ──────────────────────────────────────
|
||||
#
|
||||
# Produces a directory containing:
|
||||
# .claude/agents/<name>.md — one per agent with YAML frontmatter
|
||||
# .claude/settings.json — permission rules in Claude Code DSL
|
||||
#
|
||||
# Claude Code requires:
|
||||
# - name field: [a-z0-9-]+ (kebab-case)
|
||||
# - description field: required
|
||||
# - All agents are subagents (no primary/subagent distinction)
|
||||
|
||||
renderForClaudeCode = {
|
||||
pkgs,
|
||||
canonical,
|
||||
modelOverrides ? {},
|
||||
}:
|
||||
pkgs.runCommand "claude-code-agents" {} "echo stub > $out";
|
||||
}: let
|
||||
# Claude Code permission DSL format: "Tool(pattern)" or just "Tool"
|
||||
# Canonical bash rules → "Bash(pattern)" entries
|
||||
# Canonical edit rules → "Edit(pattern)" entries
|
||||
renderPermAllow = permissions: let
|
||||
bashRules =
|
||||
if !(permissions ? bash)
|
||||
then []
|
||||
else if permissions.bash.intent == "allow"
|
||||
then ["Bash"]
|
||||
else
|
||||
map
|
||||
(r: let parsed = parseRule r; in "Bash(${parsed.pattern})")
|
||||
(lib.filter (r: (parseRule r).action == "allow") (permissions.bash.rules or []));
|
||||
editRules =
|
||||
if !(permissions ? edit)
|
||||
then []
|
||||
else if permissions.edit.intent == "allow"
|
||||
then ["Edit"]
|
||||
else
|
||||
map
|
||||
(r: let parsed = parseRule r; in "Edit(${parsed.pattern})")
|
||||
(lib.filter (r: (parseRule r).action == "allow") (permissions.edit.rules or []));
|
||||
webRules =
|
||||
lib.optional (permissions.webfetch.intent or "" == "allow") "WebFetch";
|
||||
in
|
||||
bashRules ++ editRules ++ webRules;
|
||||
|
||||
renderPermDeny = permissions: let
|
||||
bashRules =
|
||||
if !(permissions ? bash)
|
||||
then []
|
||||
else
|
||||
map
|
||||
(r: let parsed = parseRule r; in "Bash(${parsed.pattern})")
|
||||
(lib.filter (r: (parseRule r).action == "deny") (permissions.bash.rules or []));
|
||||
editRules =
|
||||
if !(permissions ? edit)
|
||||
then []
|
||||
else
|
||||
map
|
||||
(r: let parsed = parseRule r; in "Edit(${parsed.pattern})")
|
||||
(lib.filter (r: (parseRule r).action == "deny") (permissions.edit.rules or []));
|
||||
in
|
||||
bashRules ++ editRules;
|
||||
|
||||
# Build YAML frontmatter for one Claude Code agent .md file.
|
||||
mkClaudeFrontmatter = name: agent: let
|
||||
descLine = "description: \"${agent.description}\"";
|
||||
modelLine =
|
||||
lib.optionalString
|
||||
(modelOverrides ? ${name})
|
||||
"model: ${modelOverrides.${name}}\n";
|
||||
skillsLine =
|
||||
if (agent ? skills) && agent.skills != []
|
||||
then "skills:\n" + lib.concatStringsSep "\n" (map (s: " - ${s}") agent.skills) + "\n"
|
||||
else "";
|
||||
in "---\n${descLine}\n${modelLine}${skillsLine}---\n";
|
||||
|
||||
mkClaudeAgentContent = name: agent:
|
||||
(mkClaudeFrontmatter name agent) + agent.systemPrompt;
|
||||
|
||||
mkClaudeAgentFile = name: agent:
|
||||
pkgs.writeText "${name}.md" (mkClaudeAgentContent name agent);
|
||||
|
||||
agentFiles = lib.mapAttrs mkClaudeAgentFile canonical;
|
||||
|
||||
# Build settings.json with permission rules aggregated from all agents.
|
||||
allAllows = lib.flatten (lib.mapAttrsToList (_: agent: renderPermAllow (agent.permissions or {})) canonical);
|
||||
allDenies = lib.flatten (lib.mapAttrsToList (_: agent: renderPermDeny (agent.permissions or {})) canonical);
|
||||
|
||||
settingsJson = builtins.toJSON {
|
||||
permissions = {
|
||||
allow = lib.unique (lib.sort (a: b: a < b) allAllows);
|
||||
deny = lib.unique (lib.sort (a: b: a < b) allDenies);
|
||||
};
|
||||
};
|
||||
|
||||
settingsFile = pkgs.writeText "claude-settings.json" settingsJson;
|
||||
|
||||
copyAgentCommands = lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (name: file: "cp ${file} $out/.claude/agents/${name}.md") agentFiles
|
||||
);
|
||||
in
|
||||
pkgs.runCommand "claude-code-agents" {} ''
|
||||
mkdir -p $out/.claude/agents
|
||||
${copyAgentCommands}
|
||||
cp ${settingsFile} $out/.claude/settings.json
|
||||
'';
|
||||
|
||||
# ── Pi renderer ───────────────────────────────────────────────
|
||||
#
|
||||
# This renderer produces:
|
||||
# AGENTS.md — concatenated agent descriptions + specialist listing
|
||||
# SYSTEM.md — primary agent's system prompt (replaces Pi default)
|
||||
# agents/{name}.md — one per agent for pi-subagents (YAML frontmatter + prompt)
|
||||
#
|
||||
# The agents/ files use pi-subagents frontmatter format:
|
||||
# name, description, tools, extensions, model, thinking, skill,
|
||||
# output, defaultReads, defaultProgress, interactive, maxSubagentDepth
|
||||
|
||||
# Stub: Pi renderer — produces AGENTS.md + SYSTEM.md.
|
||||
# To be implemented in Task 11.
|
||||
renderForPi = {
|
||||
pkgs,
|
||||
canonical,
|
||||
}:
|
||||
pkgs.runCommand "pi-agents" {} "echo stub > $out";
|
||||
modelOverrides ? {},
|
||||
primaryAgent ? null,
|
||||
}: let
|
||||
# Find the primary agent (there should be exactly one).
|
||||
primaryAgents = lib.filterAttrs (_: a: a.mode == "primary") canonical;
|
||||
primaryNames = lib.attrNames primaryAgents;
|
||||
primaryName =
|
||||
if primaryAgent != null
|
||||
then primaryAgent
|
||||
else if primaryNames == []
|
||||
then throw "lib.agents.renderForPi: no primary agent found"
|
||||
else builtins.head primaryNames;
|
||||
primary = builtins.getAttr primaryName primaryAgents;
|
||||
|
||||
# Dispatch renderer by tool name.
|
||||
# Subagents for the specialist listing.
|
||||
subagents = lib.filterAttrs (_: a: a.mode != "primary") canonical;
|
||||
|
||||
# ── Permission → Pi tool mapping ──────────────────────────────
|
||||
#
|
||||
# Pi built-in tools: read, bash, edit, write, grep, find, ls,
|
||||
# mcp, subagent, web_search, fetch_content, etc.
|
||||
# Canonical tools: bash, edit, webfetch, websearch, question, external_directory
|
||||
#
|
||||
# We map canonical permissions to Pi's tool list.
|
||||
# intent=allow → include tool; intent=deny → exclude; intent=ask → include (Pi has no ask granularity)
|
||||
# When specific allow rules exist, the tool is always included (Pi can't restrict by pattern).
|
||||
|
||||
piToolsForAgent = agent: let
|
||||
perms = agent.permissions or {};
|
||||
tools = [];
|
||||
# Always available: read (no permission concept in Pi)
|
||||
addIf = tool: section:
|
||||
if section.intent == "allow" || section.intent == "ask"
|
||||
then [tool]
|
||||
else [];
|
||||
# bash → bash
|
||||
withBash = tools ++ (addIf "bash" (perms.bash or {intent = "ask";}));
|
||||
# edit → edit
|
||||
withEdit = withBash ++ (addIf "edit" (perms.edit or {intent = "deny";}));
|
||||
# webfetch → fetch_content
|
||||
withFetch = withEdit ++ (addIf "fetch_content" (perms.webfetch or {intent = "deny";}));
|
||||
# websearch → web_search
|
||||
withSearch = withFetch ++ (addIf "web_search" (perms.websearch or {intent = "deny";}));
|
||||
in
|
||||
lib.unique (withSearch ++ ["read" "grep" "find" "ls"]);
|
||||
|
||||
# ── Build YAML frontmatter for pi-subagents .md files ──────────
|
||||
mkPiFrontmatter = name: agent: let
|
||||
tools = piToolsForAgent agent;
|
||||
descLine = "description: \"${agent.description}\"";
|
||||
toolsLine = "tools: ${lib.concatStringsSep ", " tools}";
|
||||
model =
|
||||
if modelOverrides ? ${name}
|
||||
then "model: ${modelOverrides.${name}}"
|
||||
else "";
|
||||
skillsLine =
|
||||
if (agent ? skills) && agent.skills != []
|
||||
then "skill: ${lib.concatStringsSep ", " agent.skills}"
|
||||
else "";
|
||||
in
|
||||
"---\n"
|
||||
+ "name: ${name}\n"
|
||||
+ "${descLine}\n"
|
||||
+ "${toolsLine}\n"
|
||||
+ (lib.optionalString (model != "") "${model}\n")
|
||||
+ (lib.optionalString (skillsLine != "") "${skillsLine}\n")
|
||||
+ "---\n";
|
||||
|
||||
mkPiAgentContent = name: agent:
|
||||
(mkPiFrontmatter name agent) + agent.systemPrompt;
|
||||
|
||||
mkPiAgentFile = name: agent:
|
||||
pkgs.writeText "${name}.md" (mkPiAgentContent name agent);
|
||||
|
||||
piAgentFiles = lib.mapAttrs mkPiAgentFile canonical;
|
||||
|
||||
# ── Build AGENTS.md content ───────────────────────────────────
|
||||
primaryDn = primary.display_name or primaryName;
|
||||
specialistEntries = let
|
||||
mkEntry = name: agent: let
|
||||
dn = agent.display_name or name;
|
||||
in
|
||||
"- **" + dn + "**: " + agent.description;
|
||||
in
|
||||
lib.mapAttrsToList mkEntry subagents;
|
||||
agentsMd =
|
||||
"# Agent Instructions\n"
|
||||
+ "\n"
|
||||
+ "## "
|
||||
+ primaryDn
|
||||
+ "\n"
|
||||
+ "\n"
|
||||
+ primary.description
|
||||
+ "\n"
|
||||
+ "\n"
|
||||
+ (
|
||||
if subagents == {}
|
||||
then ""
|
||||
else "## Available Specialists\n\n" + lib.concatStringsSep "\n" specialistEntries + "\n"
|
||||
);
|
||||
|
||||
agentsMdFile = pkgs.writeText "AGENTS.md" agentsMd;
|
||||
systemMdFile = pkgs.writeText "SYSTEM.md" primary.systemPrompt;
|
||||
|
||||
copyAgentCommands = lib.concatStringsSep "\n" (
|
||||
lib.mapAttrsToList (name: file: "cp ${file} $out/agents/${name}.md") piAgentFiles
|
||||
);
|
||||
in
|
||||
pkgs.runCommand "pi-agents" {} ''
|
||||
mkdir -p $out/agents
|
||||
cp ${agentsMdFile} $out/AGENTS.md
|
||||
cp ${systemMdFile} $out/SYSTEM.md
|
||||
${copyAgentCommands}
|
||||
'';
|
||||
|
||||
# ── renderForTool dispatcher ──────────────────────────────────
|
||||
#
|
||||
# Dispatches to the correct renderer by tool name.
|
||||
# tool: "opencode" | "claude-code" | "pi"
|
||||
|
||||
renderForTool = {
|
||||
pkgs,
|
||||
agentsInput,
|
||||
@@ -144,8 +360,60 @@
|
||||
inherit pkgs canonical modelOverrides;
|
||||
}
|
||||
else if tool == "pi"
|
||||
then agentsLib.renderForPi {inherit pkgs canonical;}
|
||||
then
|
||||
agentsLib.renderForPi {
|
||||
inherit pkgs canonical modelOverrides;
|
||||
}
|
||||
else throw "lib.agents.renderForTool: unknown tool '${tool}'. Must be opencode, claude-code, or pi.";
|
||||
|
||||
# ── shellHookForTool ──────────────────────────────────────────
|
||||
#
|
||||
# Generates a shellHook string for use in devShells that symlinks
|
||||
# rendered agent files into the project directory.
|
||||
#
|
||||
# Usage:
|
||||
# devShells.default = pkgs.mkShell {
|
||||
# shellHook = m3taLib.agents.shellHookForTool {
|
||||
# inherit pkgs;
|
||||
# agentsInput = inputs.agents;
|
||||
# tool = "opencode";
|
||||
# modelOverrides = { chiron = "anthropic/claude-sonnet-4"; };
|
||||
# };
|
||||
# };
|
||||
|
||||
shellHookForTool = {
|
||||
pkgs,
|
||||
agentsInput,
|
||||
tool,
|
||||
modelOverrides ? {},
|
||||
}: let
|
||||
rendered = agentsLib.renderForTool {
|
||||
inherit pkgs agentsInput tool modelOverrides;
|
||||
};
|
||||
in
|
||||
if tool == "opencode"
|
||||
then ''
|
||||
# Agent files for OpenCode
|
||||
mkdir -p .opencode/agents
|
||||
ln -sfn ${rendered}/* .opencode/agents/
|
||||
''
|
||||
else if tool == "claude-code"
|
||||
then ''
|
||||
# Agent files for Claude Code
|
||||
mkdir -p .claude/agents
|
||||
ln -sfn ${rendered}/.claude/agents/* .claude/agents/
|
||||
ln -sfn ${rendered}/.claude/settings.json .claude/settings.json
|
||||
''
|
||||
else if tool == "pi"
|
||||
then ''
|
||||
# Agent files for Pi
|
||||
ln -sfn ${rendered}/AGENTS.md AGENTS.md
|
||||
mkdir -p .pi
|
||||
ln -sfn ${rendered}/SYSTEM.md .pi/SYSTEM.md
|
||||
mkdir -p .pi/agents
|
||||
ln -sfn ${rendered}/agents/* .pi/agents/
|
||||
''
|
||||
else throw "lib.agents.shellHookForTool: unknown tool '${tool}'";
|
||||
};
|
||||
in
|
||||
agentsLib
|
||||
|
||||
121
lib/coding-rules.nix
Normal file
121
lib/coding-rules.nix
Normal file
@@ -0,0 +1,121 @@
|
||||
# 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.coding-rules.mkCodingRules {
|
||||
# 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}: let
|
||||
# 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:
|
||||
# mkCodingRules {
|
||||
# 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"
|
||||
# # ];
|
||||
# # }
|
||||
mkCodingRules = {
|
||||
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
|
||||
'';
|
||||
};
|
||||
|
||||
# Backward-compat alias
|
||||
mkOpencodeRules = mkCodingRules;
|
||||
in {
|
||||
inherit mkCodingRules mkOpencodeRules;
|
||||
}
|
||||
@@ -7,8 +7,11 @@
|
||||
# Port management utilities
|
||||
ports = import ./ports.nix {inherit lib;};
|
||||
|
||||
# OpenCode rules injection utilities
|
||||
opencode-rules = import ./opencode-rules.nix {inherit lib;};
|
||||
# Coding rules injection utilities (renamed from opencode-rules)
|
||||
coding-rules = import ./coding-rules.nix {inherit lib;};
|
||||
|
||||
# Backward-compat alias: opencode-rules → coding-rules
|
||||
opencode-rules = import ./coding-rules.nix {inherit lib;};
|
||||
|
||||
# Agent configuration management utilities
|
||||
agents = import ./agents.nix {inherit lib;};
|
||||
|
||||
Reference in New Issue
Block a user