feat(gitea-axi): install the CLI and wire its agent context #11

Merged
alexion merged 1 commits from install-gitea-axi into main 2026-07-20 21:41:15 -04:00
7 changed files with 79 additions and 65 deletions

View File

@@ -52,10 +52,11 @@ The domain model (Host, Module, Skeleton, Auto-loader, Enable convention, overla
Staging is enough; the file need not be committed. Staging is enough; the file need not be committed.
- chaotic-nyx must **not** follow our `nixpkgs`, and its packages are built against chaotic's own pinned nixpkgs (its overlay defaults to `onTopOf = "flake-nixpkgs"`, the cache-friendly path). - chaotic-nyx must **not** follow our `nixpkgs`, and its packages are built against chaotic's own pinned nixpkgs (its overlay defaults to `onTopOf = "flake-nixpkgs"`, the cache-friendly path).
That is what lets the `nyx-cache.chaotic.cx` binary cache hit instead of compiling the CachyOS kernel from source; the tradeoff is that chaotic packages do not see our `unstable`/`stable` overlays. That is what lets the `nyx-cache.chaotic.cx` binary cache hit instead of compiling the CachyOS kernel from source; the tradeoff is that chaotic packages do not see our `unstable`/`stable` overlays.
- The remote is self-hosted Gitea (`git.alexion.dev`), and the intended CLI is `gitea-axi` rather than `tea`. - The remote is self-hosted Gitea (`git.alexion.dev`), and the forge CLI is `gitea-axi` rather than `tea`.
`gitea-axi` resolves the repository from the `origin` remote and takes credentials from the `axi` tea login, so both are implicit inside a checkout. `gitea-axi` resolves the repository from the `origin` remote and discovers credentials from a `tea` login whose host matches the remote, so both are implicit inside a checkout.
**No forge CLI is installed on the NixOS build.** No `gitea-axi`, no `tea`, no `gh` on `PATH` — the flake names `gitea-axi` only as the claude-code module's `SessionStart` hook command and never packages it, so that hook invokes a binary that is not there. It is installed on `neogaia` by `modules.gitea-axi`, and verified: `gitea-axi` run from this checkout renders the `alexion/dotfiles` dashboard authenticated, so the claude-code `SessionStart` hook that runs it now resolves to a real binary rather than a missing one.
Credentials, however, do exist: `~/.config/tea/config.yml` holds a token-bearing login named `alexion` (not `axi`), so pull requests **can** be opened with `nix run nixpkgs#tea -- pr create --login alexion --repo alexion/dotfiles --base main --head <branch> ...`. The package wraps the binary so `git` and `tea` are reachable without being on `PATH`, while still preferring the operator's own where present.
Credentials: `~/.config/tea/config.yml` holds a token-bearing login named `alexion`, which `gitea-axi` uses and which also opens pull requests directly with `nix run nixpkgs#tea -- pr create --login alexion --repo alexion/dotfiles --base main --head <branch> ...`.
The `--repo` flag is required on that path, since `tea` resolves `origin` only for a login whose SSH host matches. The `--repo` flag is required on that path, since `tea` resolves `origin` only for a login whose SSH host matches.
The same token reads PR discussion, which `tea` itself does poorly: `tea pr <n> --comments` prints only the body, and `-f comments` returns no comments field at all. The same token reads PR discussion, which `tea` itself does poorly: `tea pr <n> --comments` prints only the body, and `-f comments` returns no comments field at all.
Use the API instead, taking the token from `.logins[] | select(.name=="alexion") | .token`. Use the API instead, taking the token from `.logins[] | select(.name=="alexion") | .token`.

45
flake.lock generated
View File

@@ -76,6 +76,27 @@
"url": "https://flakehub.com/f/DeterminateSystems/flake-schemas/%3D0.5.0.tar.gz" "url": "https://flakehub.com/f/DeterminateSystems/flake-schemas/%3D0.5.0.tar.gz"
} }
}, },
"gitea-axi": {
"inputs": {
"home-manager": "home-manager_2",
"nixpkgs": [
"nixpkgs"
]
},
"locked": {
"lastModified": 1784592615,
"narHash": "sha256-AH96vm0yYyS9sk35GnagZoWww8s8NHWYuyZJpSStlMM=",
"ref": "refs/heads/main",
"rev": "1468003f5b63f49fcd3cd456c25a8ad4f25716cc",
"revCount": 82,
"type": "git",
"url": "https://git.alexion.dev/alexion/gitea-axi"
},
"original": {
"type": "git",
"url": "https://git.alexion.dev/alexion/gitea-axi"
}
},
"home-manager": { "home-manager": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
@@ -98,6 +119,27 @@
} }
}, },
"home-manager_2": { "home-manager_2": {
"inputs": {
"nixpkgs": [
"gitea-axi",
"nixpkgs"
]
},
"locked": {
"lastModified": 1784588016,
"narHash": "sha256-ouZe80aWEhMLVMkqICFDN+JUw+0FJtCr/bh+hHtRtMg=",
"owner": "nix-community",
"repo": "home-manager",
"rev": "deeb6b7eb7e0c44ae1819c051ce175bd92a85100",
"type": "github"
},
"original": {
"owner": "nix-community",
"repo": "home-manager",
"type": "github"
}
},
"home-manager_3": {
"inputs": { "inputs": {
"nixpkgs": [ "nixpkgs": [
"nixpkgs" "nixpkgs"
@@ -227,7 +269,8 @@
"inputs": { "inputs": {
"chaotic": "chaotic", "chaotic": "chaotic",
"disko": "disko", "disko": "disko",
"home-manager": "home-manager_2", "gitea-axi": "gitea-axi",
"home-manager": "home-manager_3",
"nixos-hardware": "nixos-hardware", "nixos-hardware": "nixos-hardware",
"nixpkgs": "nixpkgs_2", "nixpkgs": "nixpkgs_2",
"nixpkgs-stable": "nixpkgs-stable", "nixpkgs-stable": "nixpkgs-stable",

View File

@@ -40,6 +40,13 @@
inputs.nixpkgs.follows = "nixpkgs"; inputs.nixpkgs.follows = "nixpkgs";
}; };
# Agent-ergonomic CLI for Gitea, with its home-manager module wiring in the
# Claude Code context where that harness is present.
gitea-axi = {
url = "git+https://git.alexion.dev/alexion/gitea-axi";
inputs.nixpkgs.follows = "nixpkgs";
};
# CachyOS kernel and binary cache. Pins its own nixpkgs so its cache stays # CachyOS kernel and binary cache. Pins its own nixpkgs so its cache stays
# usable and the kernel is fetched from it. # usable and the kernel is fetched from it.
chaotic.url = "github:chaotic-cx/nyx/nyxpkgs-unstable"; chaotic.url = "github:chaotic-cx/nyx/nyxpkgs-unstable";

View File

@@ -49,6 +49,7 @@
modules.tmux.enable = true; modules.tmux.enable = true;
modules.nvim.enable = true; modules.nvim.enable = true;
modules.claude-code.enable = true; modules.claude-code.enable = true;
modules.gitea-axi.enable = true;
time.timeZone = "America/New_York"; time.timeZone = "America/New_York";
i18n.defaultLocale = "en_GB.UTF-8"; i18n.defaultLocale = "en_GB.UTF-8";

View File

@@ -77,18 +77,6 @@ in
]; ];
} }
]; ];
SessionStart = [
{
matcher = "";
hooks = [
{
type = "command";
command = "gitea-axi";
timeout = 10;
}
];
}
];
}; };
}; };
}; };

View File

@@ -1,48 +0,0 @@
---
name: gitea-axi
description: Use when working with a Gitea repository's issues, pull requests, labels, reviews, comments, or milestones — listing, viewing, creating, editing, commenting, reviewing, or merging on a Gitea host such as git.alexion.dev. Prefer this over the `tea` CLI, raw Gitea API calls, or improvised `git` commands for issue/PR/label work.
---
# gitea-axi
`gitea-axi` is an agent-ergonomic CLI for a Gitea repository's issues and pull requests.
Its output is compact TOON built for another program to read, and its errors are structured with actionable suggestions.
## When to use it
Reach for `gitea-axi` whenever a task touches a Gitea repository's issues, pull requests, labels, or reviews.
- **Over `tea`:** `gitea-axi` returns structured output and typed errors instead of human-formatted tables, and it defaults the repository and login from the local checkout.
- **Over raw Gitea API calls:** it handles auth, pagination, name-to-ID resolution, and review-decision aggregation for you, so you do not hand-roll HTTP.
- **Over improvised `git`:** for anything about issues or pull requests as entities (state, reviews, labels, comments) rather than local commits and branches.
## Targeting and authentication
Every command resolves two things: which repository to act on, and which credentials to authenticate with.
Get both right on the first call — they are the usual reason a command fails and has to be retried.
- **Repository.** Inside a Gitea checkout it is taken from the `origin` remote automatically.
Outside a checkout you must name it: pass `-R OWNER/NAME` on every command (or set `GITEA_AXI_REPO=OWNER/NAME` once for the session).
- **Credentials.** When the environment is pre-configured — `GITEA_AXI_TOKEN` together with `GITEA_AXI_API_URL` — authentication is automatic and you need nothing more.
Otherwise credentials come from a `tea` login: pass `--login <name>` (or set `GITEA_AXI_LOGIN=<name>`) unless the checkout's remote already selects one.
So outside a checkout with the token in the environment, `gitea-axi <command> -R OWNER/NAME …` is all you need; do not go hunting for a config file or a login profile.
## Command groups
- `issue` — list, view, create, comment on, edit, close/reopen, pin, and link issues.
- `pr` — create, view, comment on, edit, review, merge, check out, diff, and inspect the checks of pull requests.
- `label` — list, create, edit, and delete labels.
- `search` — full-text search; it takes a subcommand, so search issues with `search issues "<query>"` and pull requests with `search prs "<query>"` (a bare `search "<query>"` is not valid).
- `setup` — install this skill (`setup`) and, opt-in, the SessionStart dashboard hook (`setup hooks`).
To read one issue's fields, reach straight for `issue view <number>`: it shows labels and state by default, and takes `--fields assignees,milestone,…` for the rest.
You rarely need `issue list` to answer a question about a single issue.
## Discovery
This skill is a pointer, not a command reference — the CLI is the single source of truth for its own interface.
- Run `gitea-axi` with no arguments for the repository dashboard (open issues and pull requests).
Add `--full` for the open-PR table and issue counts by label.
- Run `gitea-axi <command> --help` (or `gitea-axi <group> <command> --help`) for the exact flags of any command.

22
modules/gitea-axi.nix Normal file
View File

@@ -0,0 +1,22 @@
{
config,
lib,
inputs,
...
}:
# gitea-axi for the primary user, installed through its own home-manager module.
# That module also declares the Claude Code context when that harness is
# enabled on the host; enabling this alone installs the CLI and nothing else.
let
cfg = config.modules.gitea-axi;
user = config.user.name;
in
{
options.modules.gitea-axi.enable =
lib.mkEnableOption "gitea-axi, an agent-ergonomic CLI for Gitea issues and pull requests";
config = lib.mkIf cfg.enable {
home-manager.sharedModules = [ inputs.gitea-axi.homeModules.default ];
home-manager.users.${user}.programs.gitea-axi.enable = true;
};
}