import { readFileSync } from "node:fs"; import { exitCodeForError, runAxiCli, AxiError } from "axi-sdk-js"; import { dashboardCommand } from "./commands/dashboard.js"; import { issueCommand } from "./commands/issue.js"; import { labelCommand } from "./commands/label.js"; import { prCommand } from "./commands/pr.js"; import { searchCommand } from "./commands/search.js"; import { setupCommand } from "./commands/setup.js"; import { updateCommand } from "./commands/update.js"; import type { CliDeps, GlobalFlags } from "./deps.js"; import { consumeFlagValue, splitFlag } from "./flags.js"; import { renderErrorOutput } from "./render.js"; const DESCRIPTION = "Agent-ergonomic CLI for Gitea issues and pull requests"; const TOP_LEVEL_HELP = `usage: gitea-axi [flags] Run with no command to see the repository dashboard (open issues and pull requests); add --full for the rich view (the open-PR table and issue counts by label). commands: issue list List issues in the current repository issue view Show a single issue's details issue create Create an issue issue comment Post a comment on an issue or pull request pr create Create a pull request pr comment Post a comment on a pull request label list List labels in the current repository label create Create a label search issues Full-text search for issues in the current repository search prs Full-text search for pull requests in the current repository setup Install the bundled Agent Skill (setup hooks adds the session-start hook) global flags: -R, --repo Override the repository detected from the git origin remote --login Select a tea login profile by name --help Show help (also available on every command) -v, --version Show version environment: GITEA_AXI_REPO Repository override, as OWNER/NAME GITEA_AXI_LOGIN Login profile override `; const GLOBAL_FLAG_NAMES: Record = { "-R": "repo", "--repo": "repo", "--login": "login", }; interface ExtractedArgv { argv: string[]; globals: GlobalFlags; } /** * Pull the context override flags out of argv before the SDK sees it: they are * accepted anywhere on the command line, while the SDK rejects any flag placed * before the command. */ export function extractGlobalFlags(argv: string[]): ExtractedArgv { const globals: GlobalFlags = {}; const rest: string[] = []; for (let i = 0; i < argv.length; i++) { const flag = splitFlag(argv[i]!); const target = GLOBAL_FLAG_NAMES[flag.name]; if (!target) { rest.push(argv[i]!); continue; } const consumed = consumeFlagValue(argv, i, flag); globals[target] = consumed.value; i = consumed.lastIndex; } return { argv: rest, globals }; } function readVersion(): string { const packageJson = JSON.parse( readFileSync(new URL("../package.json", import.meta.url), "utf8"), ) as { version: string }; return packageJson.version; } export interface RunCliOptions { argv: string[]; env: Record; cwd: string; stdout: { write: (chunk: string) => unknown }; } export async function runCli(options: RunCliOptions): Promise { process.exitCode = 0; let extracted: ExtractedArgv; try { extracted = extractGlobalFlags(options.argv); } catch (error) { const axi = error as AxiError; options.stdout.write(`${renderErrorOutput(axi.message, axi.code, axi.suggestions)}\n`); process.exitCode = exitCodeForError(error); return process.exitCode; } const deps: CliDeps = { env: options.env, cwd: options.cwd, globals: extracted.globals, }; // The dashboard's full tier is `gitea-axi --full`. The SDK rejects any flag // before a command, so `--full` never reaches the home handler on its own; it // is pulled out here when it is the sole remaining argument, leaving an empty // argv for the SDK to dispatch to the home handler in full-tier mode. const full = extracted.argv.length === 1 && extracted.argv[0] === "--full"; const argv = full ? [] : extracted.argv; await runAxiCli({ description: DESCRIPTION, version: readVersion(), argv, topLevelHelp: TOP_LEVEL_HELP, commands: { issue: issueCommand(deps), pr: prCommand(deps), label: labelCommand(deps), search: searchCommand(deps), setup: setupCommand(deps), // Shadow the SDK's built-in `update` self-update command (ADR 0013). update: updateCommand(deps), }, home: dashboardCommand(deps, full), stdout: options.stdout, }); return typeof process.exitCode === "number" ? process.exitCode : 0; }