All checks were successful
CI / test (22) (pull_request) Successful in 52s
CI / test (true, 24) (pull_request) Successful in 1m4s
CI / flake (pull_request) Successful in 3s
CI / test (22) (push) Successful in 48s
CI / test (true, 24) (push) Successful in 1m2s
CI / flake (push) Successful in 3s
Reshape the home-manager module so `programs.gitea-axi.enable` installs the binary always, and the Claude Code context follows the harness. The two per-artefact toggles and their assertion are replaced by one per-harness toggle, `enableClaudeCodeIntegration` (default true); its artefacts land only when `programs.claude-code.enable` is also on, silently absent otherwise. The Agent Skill is now written through home.file into Claude Code's skills directory, rather than contributed to `programs.claude-code.skills`, so it composes with both the attribute-set and whole-directory forms of an operator's own skills option. The Skill write is gated on `claude-code.enable` explicitly (keeping package realisation lazy); the hook keeps its sibling-module gate for free, and the asymmetry is commented. Supersedes three decisions of ADR 0020; recorded in ADR 0021. INSTALL.md is updated to the new option surface and the path-form limitation paragraph removed. Adds the parent spec and the follow-up task 0047 (the flake check proving composition, implemented separately).
119 lines
5.3 KiB
Nix
119 lines
5.3 KiB
Nix
# A home-manager module installing gitea-axi and, when a harness is present,
|
|
# its ambient context — the bundled Agent Skill and the SessionStart hook
|
|
# (ADR 0020, reshaped by ADR 0021).
|
|
#
|
|
# `programs.gitea-axi.enable` installs the CLI, always. The Claude Code context
|
|
# follows the harness: it is declared under one per-harness toggle and lands
|
|
# only when `programs.claude-code.enable` is also on. gitea-axi is a working CLI
|
|
# without a harness, so enabling it on a host with no Claude Code installs the
|
|
# binary and nothing else, with no assertion.
|
|
#
|
|
# The module is a wiring layer and nothing more. Both artefacts come from the
|
|
# package's published attributes, so what a declarative configuration installs
|
|
# and what `gitea-axi setup` writes imperatively are the same two artefacts, and
|
|
# neither is restated here.
|
|
#
|
|
# Importing this module changes nothing until `programs.gitea-axi.enable` is set.
|
|
{
|
|
config,
|
|
lib,
|
|
pkgs,
|
|
...
|
|
}:
|
|
let
|
|
cfg = config.programs.gitea-axi;
|
|
|
|
# The package the declarations are read out of. `package = null` opts out of
|
|
# putting the binary on PATH, not out of the configuration — an operator who
|
|
# installs gitea-axi system-wide still wants the Skill — so the declarations
|
|
# fall back to the default build, which in that arrangement is already in the
|
|
# closure anyway.
|
|
sourcePackage = if cfg.package != null then cfg.package else defaultPackage;
|
|
|
|
defaultPackage = pkgs.callPackage ./package.nix { };
|
|
|
|
claudeCode = config.programs.claude-code;
|
|
in
|
|
{
|
|
options.programs.gitea-axi = {
|
|
enable = lib.mkEnableOption "gitea-axi, an agent-ergonomic CLI for Gitea issues and pull requests";
|
|
|
|
package = lib.mkOption {
|
|
type = lib.types.nullOr lib.types.package;
|
|
default = defaultPackage;
|
|
defaultText = lib.literalExpression "pkgs.callPackage ./package.nix { }";
|
|
description = ''
|
|
The gitea-axi package to install, or `null` to declare the ambient
|
|
context without installing the binary — for an operator who supplies it
|
|
another way, such as `environment.systemPackages`.
|
|
|
|
The SessionStart hook records a name resolved on `PATH`, so a binary
|
|
installed elsewhere satisfies it. With `null` the Agent Skill is still
|
|
taken from the default build.
|
|
'';
|
|
};
|
|
|
|
enableClaudeCodeIntegration = lib.mkOption {
|
|
type = lib.types.bool;
|
|
default = true;
|
|
description = ''
|
|
Whether to declare gitea-axi's Claude Code context — the bundled Agent
|
|
Skill and the SessionStart hook — alongside the CLI.
|
|
|
|
Both artefacts land only when `programs.claude-code.enable` is also on;
|
|
with it off they are silently absent, matching how home-manager's own
|
|
`enableBashIntegration`-style toggles behave against a disabled sibling.
|
|
|
|
Turn this off to install gitea-axi declaratively while writing the
|
|
Claude Code context by hand or with `gitea-axi setup`. The toggle
|
|
generalises: a future harness reads as `enableCodexIntegration`.
|
|
'';
|
|
};
|
|
};
|
|
|
|
config = lib.mkIf cfg.enable (
|
|
lib.mkMerge [
|
|
(lib.mkIf (cfg.package != null) { home.packages = [ cfg.package ]; })
|
|
|
|
# The two Claude Code artefacts move together under one per-harness
|
|
# toggle. They are gated by different mechanisms internally — the Skill by
|
|
# an explicit `claude-code.enable` condition, the hook by the Claude Code
|
|
# module's own gate — because of how each is declared, below.
|
|
(lib.mkIf cfg.enableClaudeCodeIntegration (
|
|
lib.mkMerge [
|
|
# The hook is declared through the Claude Code module's own settings
|
|
# option. That composes it with an operator's own SessionStart hooks
|
|
# instead of colliding, and the module drops the declaration for free
|
|
# when it is disabled — so no explicit `claude-code.enable` gate here.
|
|
{ programs.claude-code.settings.hooks.SessionStart = [ sourcePackage.sessionStartHook ]; }
|
|
|
|
# The Skill is written as an ordinary file into Claude Code's skills
|
|
# directory, rather than contributed to `programs.claude-code.skills`.
|
|
# That composes with both forms of the operator's own skills option —
|
|
# an attribute set and a single path for a whole directory — because
|
|
# it never touches that option's type, so the path-form collision
|
|
# disappears at its root instead of being escaped by a toggle.
|
|
#
|
|
# Writing through home.file does not inherit the Claude Code module's
|
|
# `enable`-gate the way declaring through its options does, so the
|
|
# Skill is gated on `claude-code.enable` explicitly. The gate is also
|
|
# what keeps package realisation lazy: home.file reads the Skill's
|
|
# source directory while evaluating, so an ungated write would realise
|
|
# the package on every host — even one with no Claude Code that
|
|
# installs nothing from it.
|
|
#
|
|
# `configDir` is read from the Claude Code module rather than
|
|
# hardcoded, so the Skill lands beside that module's own skills
|
|
# wherever the operator points it.
|
|
(lib.mkIf claudeCode.enable {
|
|
home.file."${claudeCode.configDir}/skills/gitea-axi" = {
|
|
source = sourcePackage.skill;
|
|
recursive = true;
|
|
};
|
|
})
|
|
]
|
|
))
|
|
]
|
|
);
|
|
}
|