My collection of nix configurations
1{
2 config,
3 lib,
4 pkgs,
5 ...
6}:
7
8with lib;
9let
10 cfg = config.programs.andrew-opencode;
11 peonPingPkg = pkgs.peon-ping;
12 agentcraft = pkgs.callPackage ./agentcraft.nix { };
13 opencodeGitlabPlugin = pkgs.callPackage ./opencode-gitlab-plugin.nix { };
14 ohMyOpencodeSlim = pkgs.callPackage ./oh-my-opencode-slim.nix { };
15 opencodeForgeSessionTitle = pkgs.callPackage ./opencode-forge-session-title.nix { };
16 ohMyOpencode = pkgs.callPackage ./oh-my-opencode.nix { };
17 openpeonDota2 = pkgs.fetchFromGitHub {
18 owner = "kmelkon";
19 repo = "openpeon-dota2";
20 rev = "main";
21 hash = "sha256-21BZ+pp3N23bIaeo7EripcgilmAlz1k77GrN7sJEFns=";
22 };
23 superpowers = pkgs.fetchFromGitHub {
24 owner = "obra";
25 repo = "superpowers";
26 rev = "363923f74aa9cd7b470c0aaa73dee629a8bfdc90";
27 hash = "sha256-AyRGXwWI9xHGeHw9vD64cnV19txR/lOtxudcHnbV75I=";
28 };
29 compoundEngineering = pkgs.fetchFromGitHub {
30 owner = "EveryInc";
31 repo = "compound-engineering-plugin";
32 rev = "74fb71731a14485b30b8549c93ff3732b5723179";
33 hash = "sha256-rvPC6NWG1vZG3OFpor84wkYuAZCtLyoYtU6q1deQl1c=";
34 };
35 compoundEngineeringPatched =
36 pkgs.runCommand "compound-engineering-patched"
37 {
38 nativeBuildInputs = [
39 pkgs.gnused
40 pkgs.findutils
41 ];
42 }
43 ''
44 cp -r ${compoundEngineering}/. $out
45 chmod -R u+w $out
46
47 # Patch invalid color values, remove model: inherit, and fix haiku model reference
48 find $out/plugins/compound-engineering/agents -name "*.md" -type f -exec sed -i \
49 -e 's/^[[:space:]]*color:[[:space:]]*blue[[:space:]]*$/color: info/' \
50 -e 's/^[[:space:]]*color:[[:space:]]*yellow[[:space:]]*$/color: warning/' \
51 -e 's/^[[:space:]]*color:[[:space:]]*cyan[[:space:]]*$/color: info/' \
52 -e 's/^[[:space:]]*color:[[:space:]]*purple[[:space:]]*$/color: accent/' \
53 -e 's/^[[:space:]]*color:[[:space:]]*violet[[:space:]]*$/color: accent/' \
54 -e '/^[[:space:]]*model:[[:space:]]*inherit[[:space:]]*$/d' \
55 -e 's/^model:[[:space:]]*haiku[[:space:]]*$/model: gitlab\/duo-chat-haiku-4-5/' \
56 {} +
57 '';
58 cePlugin = "${compoundEngineeringPatched}/plugins/compound-engineering";
59 gitlabSkills = builtins.fetchGit {
60 url = "https://gitlab.com/gitlab-org/ai/skills.git";
61 rev = "3c2877076d453dfa1a3ea392e1293473dcae2cd2";
62 narHash = "sha256-mHdoOS/LzaSyuCvTIzxjJiABXcfZA4ljVR0vaDHoTk4=";
63 };
64 glSkillDirs = builtins.readDir "${gitlabSkills}/skills";
65 glSkillFiles = lib.mapAttrs' (
66 name: _: lib.nameValuePair "opencode/skills/${name}" { source = "${gitlabSkills}/skills/${name}"; }
67 ) (lib.filterAttrs (_: type: type == "directory") glSkillDirs);
68
69 ppSkillDirs = builtins.readDir "${peonPingPkg}/share/peon-ping/skills";
70 ppSkillFiles = lib.mapAttrs' (
71 name: _:
72 lib.nameValuePair "opencode/skills/${name}" {
73 source = "${peonPingPkg}/share/peon-ping/skills/${name}";
74 }
75 ) (lib.filterAttrs (_: type: type == "directory") ppSkillDirs);
76
77 # Helper to create xdg.configFile entries for all agent files
78 ceAgentDirs = builtins.readDir "${cePlugin}/agents";
79 ceAgentFiles = lib.concatMapAttrs (
80 category: _:
81 let
82 categoryDir = "${cePlugin}/agents/${category}";
83 files = builtins.readDir categoryDir;
84 in
85 lib.mapAttrs' (
86 name: _: lib.nameValuePair "opencode/agents/${name}" { source = "${categoryDir}/${name}"; }
87 ) (lib.filterAttrs (_: type: type == "regular") files)
88 ) (lib.filterAttrs (_: type: type == "directory") ceAgentDirs);
89
90 # Helper to create xdg.configFile entries for all skill directories
91 ceSkillDirs = builtins.readDir "${cePlugin}/skills";
92 ceSkillFiles = lib.mapAttrs' (
93 name: _: lib.nameValuePair "opencode/skills/${name}" { source = "${cePlugin}/skills/${name}"; }
94 ) (lib.filterAttrs (_: type: type == "directory") ceSkillDirs);
95in
96{
97 options.programs.andrew-opencode = {
98 enable = mkEnableOption "andrew's opencode config";
99 variant = mkOption {
100 type = types.enum [
101 "superpowers"
102 "compound"
103 "omo"
104 ];
105 default = "superpowers";
106 description = "Which opencode plugin system to use";
107 };
108 };
109
110 config = mkIf cfg.enable (mkMerge [
111 # Common config — all variants
112 {
113 home.packages = [
114 agentcraft
115 ];
116 programs.opencode = {
117 enable = true;
118 settings = {
119 theme = "stylix";
120 provider.gitlab.models = {
121 "duo-chat-haiku-4-5".cost = {
122 input = 0.8;
123 output = 4;
124 };
125 "duo-chat-sonnet-4-5".cost = {
126 input = 3;
127 output = 15;
128 };
129 "duo-chat-opus-4-5".cost = {
130 input = 15;
131 output = 75;
132 };
133 "duo-chat-sonnet-4-6".cost = {
134 input = 3;
135 output = 15;
136 };
137 "duo-chat-opus-4-6".cost = {
138 input = 15;
139 output = 75;
140 };
141 "duo-chat-gpt-5-2-codex".cost = {
142 input = 2;
143 output = 8;
144 };
145 "duo-chat-gpt-5-mini".cost = {
146 input = 0.4;
147 output = 1.6;
148 };
149 };
150 mcp.glean = {
151 type = "remote";
152 url = "https://gitlab-be.glean.com/mcp/default";
153 };
154 share = "disabled";
155 small_model = "gitlab/duo-chat-haiku-4-5";
156 };
157 };
158
159 programs.peon-ping = {
160 enable = true;
161 settings = {
162 default_pack = "peon";
163 volume = 0.5;
164 enabled = true;
165 desktop_notifications = true;
166 pack_rotation = [
167 "peon"
168 "peasant"
169 "hd2_helldiver"
170 "ocarina_of_time"
171 "protoss"
172 "dota2_witch_doctor"
173 ];
174 pack_rotation_mode = "round-robin";
175 categories = {
176 "session.start" = true;
177 "task.complete" = true;
178 "task.error" = true;
179 "input.required" = true;
180 "resource.limit" = true;
181 "user.spam" = true;
182 };
183 };
184 installPacks = [
185 "peon"
186 "peasant"
187 "hd2_helldiver"
188 "ocarina_of_time"
189 {
190 name = "protoss";
191 src = pkgs.fetchFromGitHub {
192 owner = "codyborn";
193 repo = "protoss-sounds";
194 rev = "v1.0.0";
195 hash = "sha256-uQI7YUobkfAUu/0xhM4EuCq9GQ1d87UmsNCefAg2fGs=";
196 };
197 }
198 {
199 name = "dota2_witch_doctor";
200 src = "${openpeonDota2}/dota2_witch_doctor";
201 }
202 ];
203 enableZshIntegration = true;
204 };
205
206 xdg.configFile = {
207 "opencode/plugins/peon-ping.ts".source =
208 "${peonPingPkg}/share/peon-ping/adapters/opencode/peon-ping.ts";
209 "opencode/plugins/peon-ping-internals.ts".source =
210 "${peonPingPkg}/share/peon-ping/adapters/opencode/peon-ping-internals.ts";
211 "opencode/peon-ping/config.json".source = config.home.file.".openpeon/config.json".source;
212 "opencode/plugins/gitlab-plugin.js".source =
213 "${opencodeGitlabPlugin}/lib/node_modules/opencode-gitlab-plugin/dist/index.js";
214 "opencode/plugins/forge-session-title.js".source =
215 "${opencodeForgeSessionTitle}/lib/node_modules/opencode-forge-session-title/dist/index.js";
216 "opencode/skills/writing-commit-messages".source = ./skills/writing-commit-messages;
217 }
218 // glSkillFiles
219 // ppSkillFiles;
220 }
221
222 # Variant: superpowers (current behavior)
223 (mkIf (cfg.variant == "superpowers") {
224 programs.opencode.settings = {
225 plugin = [ ];
226 agent = {
227 explore.disable = true;
228 general.disable = true;
229 };
230 };
231 xdg.configFile = {
232 "opencode/oh-my-opencode-slim.jsonc".source = ./oh-my-opencode-slim.jsonc;
233 "opencode/plugins/oh-my-opencode-slim.js".source =
234 "${ohMyOpencodeSlim}/lib/node_modules/oh-my-opencode-slim/dist/index.js";
235 "opencode/plugins/superpowers.js".source = "${superpowers}/.opencode/plugins/superpowers.js";
236 "opencode/skills/superpowers".source = "${superpowers}/skills";
237 };
238 })
239
240 # Variant: compound engineering
241 (mkIf (cfg.variant == "compound") {
242 programs.opencode.settings = {
243 plugin = [ ];
244 mcp.context7 = {
245 type = "remote";
246 url = "https://mcp.context7.com/mcp";
247 };
248 };
249 xdg.configFile = ceAgentFiles // ceSkillFiles;
250 })
251
252 # Variant: oh-my-openagent (omo)
253 (mkIf (cfg.variant == "omo") {
254 programs.opencode.settings.plugin = [ ];
255 xdg.configFile = {
256 "opencode/oh-my-opencode.jsonc".source = ./oh-my-opencode.jsonc;
257 "opencode/plugins/oh-my-opencode.js".source =
258 "${ohMyOpencode}/lib/node_modules/oh-my-opencode/dist/index.js";
259 };
260 })
261 ]);
262}