Commit 052a1a49371c
tarball5 files changed+139-16
@@ -31,6 +31,16 @@ claude plugin marketplace add ./result
3131
claude plugin install roundabout@roundabout3232
```3333
34
+or on nixos35
+36
+```nix37
+imports = [ roundabout.nixosModules.default ];38
+programs.roundabout.enable = true;39
+programs.roundabout.settings.budget.ram = "16GiB"; # lands in /etc/roundabout/config.toml40
+```41
+42
+that installs roundabout and a socket-activated systemd user unit for the daemon (`socketActivation = false` to let clients spawn it instead). `programs.roundabout.claude` has read-only `hooks`, `skill` and `plugin` for whatever writes your claude config, e.g. `settings.hooks = config.programs.roundabout.claude.hooks`.43
+3444
the plugin brings a skill and hooks. unwrapped builds get bounced back with instructions, overdue builds get mentioned to the agent that started them, and agents over their artifact cap get told to tidy up before they stop. `rabt` needs to be on PATH for the agent too, so install `.#roundabout` alongside.3545
3646
## config@@ -16,6 +16,8 @@
1616
pkgsFor = system: nixpkgs.legacyPackages.${system};1717
in1818
{19
+ nixosModules.default = import ./nix/nixos-module.nix self;20
+1921
packages = forAllSystems (2022
system:2123
let@@ -0,0 +1,20 @@
1
+{ lib, roundabout }:2
+3
+let4
+ hook = event: {5
+ type = "command";6
+ command = "${lib.getExe roundabout} hook ${event}";7
+ };8
+in9
+{10
+ PreToolUse = [11
+ {12
+ matcher = "Bash";13
+ hooks = [ (hook "pre-tool-use") ];14
+ }15
+ ];16
+ PostToolUse = [ { hooks = [ (hook "post-tool-use") ]; } ];17
+ UserPromptSubmit = [ { hooks = [ (hook "prompt-submit") ]; } ];18
+ Stop = [ { hooks = [ (hook "stop") ]; } ];19
+ SubagentStop = [ { hooks = [ (hook "subagent-stop") ]; } ];20
+}@@ -0,0 +1,106 @@
1
+self:2
+{3
+ config,4
+ lib,5
+ pkgs,6
+ ...7
+}:8
+9
+let10
+ cfg = config.programs.roundabout;11
+ toml = pkgs.formats.toml { };12
+ plugin = pkgs.callPackage ./plugin.nix { roundabout = cfg.package; };13
+in14
+{15
+ options.programs.roundabout = {16
+ enable = lib.mkEnableOption "roundabout, build admission for agents";17
+18
+ package = lib.mkOption {19
+ type = lib.types.package;20
+ default = self.packages.${pkgs.stdenv.hostPlatform.system}.roundabout;21
+ defaultText = lib.literalExpression "roundabout.packages.\${system}.roundabout";22
+ description = "the roundabout package";23
+ };24
+25
+ socketActivation = lib.mkOption {26
+ type = lib.types.bool;27
+ default = true;28
+ description = ''29
+ let a systemd user socket own `$XDG_RUNTIME_DIR/roundabout.sock` and start the30
+ daemon on demand. when disabled, the first client spawns daemon instead31
+ '';32
+ };33
+34
+ settings = lib.mkOption {35
+ inherit (toml) type;36
+ default = { };37
+ example = lib.literalExpression ''38
+ {39
+ budget.ram = "16GiB";40
+ enforce.unwrapped = "wrap";41
+ artifacts.stop = "block";42
+ }43
+ '';44
+ description = ''45
+ written to `/etc/roundabout/config.toml`, which anyone without a46
+ `~/.config/roundabout/config.toml` of their own gets. see readme47
+ '';48
+ };49
+50
+ claude = {51
+ hooks = lib.mkOption {52
+ type = lib.types.attrs;53
+ readOnly = true;54
+ default = import ./hooks.nix {55
+ inherit lib;56
+ roundabout = cfg.package;57
+ };58
+ defaultText = lib.literalMD "roundabout's hooks, keyed by event";59
+ description = ''60
+ hooks in `settings.json` shape, for whatever writes your claude config.61
+ lists concatenate under the json format type, so they sit alongside62
+ hooks you already have63
+ '';64
+ };65
+66
+ skill = lib.mkOption {67
+ type = lib.types.path;68
+ readOnly = true;69
+ default = "${plugin}/plugins/roundabout/skills/roundabout";70
+ defaultText = lib.literalMD "the roundabout skill directory";71
+ description = "the skill directory, for configs that manage `~/.claude/skills` themselves";72
+ };73
+74
+ plugin = lib.mkOption {75
+ type = lib.types.package;76
+ readOnly = true;77
+ default = plugin;78
+ defaultText = lib.literalMD "the roundabout plugin marketplace";79
+ description = "hooks and skill as a marketplace, for `claude plugin marketplace add`";80
+ };81
+ };82
+ };83
+84
+ config = lib.mkIf cfg.enable {85
+ environment.systemPackages = [ cfg.package ];86
+87
+ # a real file rather than the usual /etc/static symlink, so a rebuild88
+ # renames over it where the daemon's inotify watch can see89
+ environment.etc."roundabout/config.toml" = lib.mkIf (cfg.settings != { }) {90
+ source = toml.generate "roundabout-config.toml" cfg.settings;91
+ mode = "0444";92
+ };93
+94
+ systemd.user.sockets.roundabout = lib.mkIf cfg.socketActivation {95
+ description = "roundabout build admission socket";96
+ wantedBy = [ "sockets.target" ];97
+ listenStreams = [ "%t/roundabout.sock" ];98
+ socketConfig.SocketMode = "0600";99
+ };100
+101
+ systemd.user.services.roundabout = lib.mkIf cfg.socketActivation {102
+ description = "roundabout build admission daemon";103
+ serviceConfig.ExecStart = "${lib.getExe cfg.package} daemon";104
+ };105
+ };106
+}@@ -6,24 +6,9 @@
66
}:77
88
let9
- hook = event: {10
- type = "command";11
- command = "${lib.getExe roundabout} hook ${event}";12
- };139
hooks = writeText "hooks.json" (1410
builtins.toJSON {15
- hooks = {16
- PreToolUse = [17
- {18
- matcher = "Bash";19
- hooks = [ (hook "pre-tool-use") ];20
- }21
- ];22
- PostToolUse = [ { hooks = [ (hook "post-tool-use") ]; } ];23
- UserPromptSubmit = [ { hooks = [ (hook "prompt-submit") ]; } ];24
- Stop = [ { hooks = [ (hook "stop") ]; } ];25
- SubagentStop = [ { hooks = [ (hook "subagent-stop") ]; } ];26
- };11
+ hooks = import ./hooks.nix { inherit lib roundabout; };2712
}2813
);2914
plugin = writeText "plugin.json" (