This repository has no description
5.1 kB
144 lines
1{ self, ... }:
2{
3 flake.wrapperModules.pi =
4 {
5 config,
6 lib,
7 ...
8 }:
9 let
10 pkgs = config.pkgs;
11
12 # Extract npm packages with hashes from settings.packages
13 npmPackages = lib.filter (p: builtins.isAttrs p && p ? hash) (config.settings.packages or [ ]);
14 fetchPlugin =
15 pkg:
16 let
17 source = pkg.source or pkg;
18 name = lib.removePrefix "npm:" source;
19 in
20 pkgs.runCommand "pi-plugin-${name}"
21 {
22 nativeBuildInputs = [
23 pkgs.nodejs
24 pkgs.cacert
25 ];
26 outputHashMode = "recursive";
27 outputHashAlgo = "sha256";
28 outputHash = pkg.hash;
29 impureEnvVars = [
30 "http_proxy"
31 "https_proxy"
32 ];
33 SSL_CERT_FILE = "${pkgs.cacert}/etc/ssl/certs/ca-bundle.crt";
34 }
35 ''
36 export HOME=$TMPDIR
37 export npm_config_prefix=$out
38 npm install -g ${name}
39 '';
40
41 fetchedPlugins = map fetchPlugin npmPackages;
42
43 # Strip hash and null-valued attrs from package entries before writing settings.json
44 cleanPackage =
45 p: if builtins.isAttrs p then lib.filterAttrs (k: v: k != "hash" && v != null) p else p;
46
47 cleanSettings = config.settings // {
48 packages = map cleanPackage (config.settings.packages or [ ]);
49 };
50
51 settingsFile = pkgs.writeText "pi-settings.json" (builtins.toJSON cleanSettings);
52 in
53 {
54 _class = "wrapper";
55
56 options = {
57 settings = lib.mkOption {
58 type = lib.types.submodule {
59 freeformType = (pkgs.formats.json { }).type;
60 options.packages = lib.mkOption {
61 type = lib.types.listOf (
62 lib.types.either lib.types.str (
63 lib.types.submodule {
64 freeformType = (pkgs.formats.json { }).type;
65 options.hash = lib.mkOption {
66 type = lib.types.str;
67 description = "Fixed-output hash for fetching this npm package.";
68 };
69 options.source = lib.mkOption {
70 type = lib.types.str;
71 description = "Package source (e.g. npm:package-name).";
72 };
73 options.extensions = lib.mkOption {
74 type = lib.types.nullOr (lib.types.listOf lib.types.str);
75 default = null;
76 description = ''
77 Extension filter patterns following pi's native syntax.
78 When null (default), all extensions from the package are loaded.
79 An empty list explicitly disables all extensions.
80 Patterns use prefixes to control inclusion:
81 - `"!pattern"` — exclude matching extensions
82 - `"+pattern"` — force-include (overrides excludes)
83 - `"-pattern"` — force-exclude (overrides includes)
84 - `"path"` — plain path to include
85 '';
86 example = [
87 "!./extensions/resistance.ts"
88 ];
89 };
90 }
91 )
92 );
93 default = [ ];
94 description = "Pi packages list. Object entries may include a `hash` for Nix fetching.";
95 };
96 };
97 default = { };
98 description = "Pi settings.json content.";
99 };
100
101 pluginOverrides = lib.mkOption {
102 type = lib.types.str;
103 default = "";
104 description = ''
105 Shell commands to run after assembling the plugin prefix.
106 The variable `$out` points to the mutable copy.
107 '';
108 };
109
110 pluginPrefix = lib.mkOption {
111 type = lib.types.package;
112 description = "The final npm prefix with all plugins merged and overrides applied.";
113 default = pkgs.runCommand "pi-plugins" { } ''
114 mkdir -p $out
115 ${lib.concatMapStringsSep "\n" (p: "cp -a --no-preserve=mode ${p}/* $out/") fetchedPlugins}
116 ${lib.optionalString (config.pluginOverrides != "") config.pluginOverrides}
117 '';
118 };
119 };
120
121 config.package = lib.mkDefault (
122 self.legacyPackages.${pkgs.system}.llm.pi.overrideAttrs (old: {
123 postInstall = (old.postInstall or "") + ''
124 patch $out/lib/node_modules/@mariozechner/pi-coding-agent/dist/core/settings-manager.js \
125 ${./settings-env.patch}
126 '';
127 })
128 );
129
130 config.extraPackages = [ pkgs.nodejs ];
131
132 config.env.npm_config_prefix = "${config.pluginPrefix}";
133 config.env.PI_SETTINGS_FILE = "${settingsFile}";
134
135 config.meta.maintainers = [
136 {
137 name = "lassulus";
138 github = "lassulus";
139 githubId = 621375;
140 }
141 ];
142 config.meta.platforms = lib.platforms.all;
143 };
144}