~ruther/agent-environments

agent-environments/modules/core.nix -rw-r--r-- 2.4 KiB
8a0ec343 — František Boháček Update codex and claude-code inputs a month ago
                                                                                
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
{
  config,
  lib,
  pkgs,
  agentEnvLib,
  ...
}:
let
  inherit (lib) mkOption types;

  sortedInstructions = lib.sort (
    a: b: if a.order == b.order then a.name < b.name else a.order < b.order
  ) (lib.mapAttrsToList (name: v: v // { inherit name; }) config.instructions);

  # Transitive closure over skillDeps, keyed by store path.
  skillClosure = map (e: e.skill) (
    builtins.genericClosure {
      startSet = map (s: {
        key = s.outPath;
        skill = s;
      }) config.skills.packages;
      operator =
        e:
        map (s: {
          key = s.outPath;
          skill = s;
        }) e.skill.skillDeps;
    }
  );
  skillsByName = lib.groupBy (s: s.skillName) skillClosure;
in
{
  options = {
    instructions = mkOption {
      default = { };
      description = "Fragments of the instruction file (CLAUDE.md / AGENTS.md), sorted by order, then name.";
      type = types.attrsOf (
        types.submodule {
          options = {
            text = mkOption { type = types.lines; };
            order = mkOption {
              type = types.int;
              default = 1000;
            };
          };
        }
      );
    };

    skills.packages = mkOption {
      type = types.listOf types.package;
      default = [ ];
      description = "Skills built with lib.mkSkill. Their skill dependencies are added automatically.";
    };

    packages = mkOption {
      type = types.listOf types.package;
      default = [ ];
      description = "Packages put on the PATH of every launcher, e.g. tools that hooks in the agent settings call.";
    };

    # Read-only results used by the agent and launcher modules.
    build = mkOption {
      type = types.attrsOf types.anything;
      default = { };
      internal = true;
    };

    assertions = mkOption {
      type = types.listOf types.attrs;
      default = [ ];
      internal = true;
    };

  };

  config = {
    build = {
      instructionsText =
        lib.concatMapStringsSep "\n\n" (i: lib.removeSuffix "\n" i.text) sortedInstructions + "\n";
      skills = lib.mapAttrsToList (_: ss: lib.head ss) skillsByName;
      runtimeInputs = lib.unique (config.packages ++ lib.concatMap (s: s.runtimeInputs) skillClosure);
    };

    assertions = lib.mapAttrsToList (name: ss: {
      assertion = lib.length ss == 1;
      message = "skill name '${name}' is provided by different packages: ${
        lib.concatMapStringsSep ", " (s: s.outPath) ss
      }";
    }) skillsByName;
  };
}