axinstall 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Łukasz Jerciński
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,96 @@
1
+ # axinstall
2
+
3
+ Install AI CLI agents (Claude Code, Codex, Gemini, etc.) in any environment.
4
+
5
+ ## Quick Start
6
+
7
+ ```bash
8
+ # Install Claude Code globally
9
+ npx axinstall claude
10
+
11
+ # Install with a specific package manager
12
+ npx axinstall claude --with npm
13
+
14
+ # Preview what would be installed (dry run)
15
+ npx axinstall claude --dry-run
16
+
17
+ # List supported agents
18
+ npx axinstall --list-agents
19
+
20
+ # Check installed agents and available package managers
21
+ npx axinstall --status
22
+ ```
23
+
24
+ ## Requirements
25
+
26
+ axinstall needs at least one supported package manager available:
27
+
28
+ - npm
29
+ - pnpm
30
+ - bun
31
+ - yarn
32
+ - Homebrew (`brew`)
33
+
34
+ ### Custom Paths
35
+
36
+ Override the default binary lookup by setting environment variables:
37
+
38
+ ```bash
39
+ export AXINSTALL_NPM_PATH=/path/to/npm
40
+ export AXINSTALL_PNPM_PATH=/path/to/pnpm
41
+ export AXINSTALL_BUN_PATH=/path/to/bun
42
+ export AXINSTALL_YARN_PATH=/path/to/yarn
43
+ export AXINSTALL_BREW_PATH=/path/to/brew
44
+ ```
45
+
46
+ ## Examples
47
+
48
+ ### Install agents
49
+
50
+ ```bash
51
+ # Install using auto-detected package manager (pnpm > bun > yarn > npm > brew)
52
+ axinstall claude
53
+ axinstall codex
54
+ axinstall gemini
55
+
56
+ # Force a specific package manager
57
+ axinstall claude --with npm
58
+ axinstall opencode --with brew
59
+
60
+ # Install locally (not globally)
61
+ axinstall claude --local
62
+ ```
63
+
64
+ ### Pipeline examples
65
+
66
+ ```bash
67
+ # Get list of agent CLI names
68
+ axinstall --list-agents | tail -n +2 | cut -f1
69
+
70
+ # Find agents from a specific provider
71
+ axinstall --list-agents | tail -n +2 | awk -F'\t' '$4 == "Anthropic" {print $1}'
72
+
73
+ # Get available installers only
74
+ axinstall --status --only installers | tail -n +2 | awk -F'\t' '$4 == "available" {print $2}'
75
+
76
+ # Count installed vs not found agents
77
+ axinstall --status --only agents | tail -n +2 | cut -f4 | sort | uniq -c
78
+ ```
79
+
80
+ ## Agent Rule
81
+
82
+ Add to your `CLAUDE.md` or `AGENTS.md`:
83
+
84
+ ```markdown
85
+ # Rule: `axinstall` Usage
86
+
87
+ Run `npx -y axinstall --help` to learn available options.
88
+
89
+ Use `axinstall` to install AI CLI agents in any environment. It auto-detects
90
+ the best available package manager (pnpm, bun, yarn, npm, brew) and handles
91
+ the installation with a single command.
92
+ ```
93
+
94
+ ## License
95
+
96
+ MIT
package/bin/axinstall ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * CLI entry point that dynamically imports the compiled TypeScript.
4
+ *
5
+ * Uses top-level await to ensure module evaluation errors are handled
6
+ * properly. Without await, errors during import would surface as unhandled
7
+ * rejections instead of clean CLI failures with appropriate exit codes.
8
+ */
9
+ try {
10
+ await import("../dist/cli.js");
11
+ } catch (error) {
12
+ console.error(
13
+ "Failed to start axinstall:",
14
+ error instanceof Error ? error.message : error,
15
+ );
16
+ process.exitCode = 1;
17
+ }
package/dist/cli.d.ts ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * axinstall - AI agent installation CLI.
4
+ *
5
+ * Installs CLI agents (Claude Code, Codex, Gemini, etc.) using
6
+ * the best available package manager in the environment.
7
+ */
8
+ export {};
package/dist/cli.js ADDED
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * axinstall - AI agent installation CLI.
4
+ *
5
+ * Installs CLI agents (Claude Code, Codex, Gemini, etc.) using
6
+ * the best available package manager in the environment.
7
+ */
8
+ import { Command } from "@commander-js/extra-typings";
9
+ import { AGENT_CLIS } from "axshared";
10
+ import packageJson from "../package.json" with { type: "json" };
11
+ import { getInstaller } from "./installer-data.js";
12
+ import { runCommand } from "./run-command.js";
13
+ import { INSTALLER_IDS } from "./types.js";
14
+ // =============================================================================
15
+ // Signal Handling
16
+ // =============================================================================
17
+ process.on("SIGINT", () => {
18
+ console.error("\nInstallation cancelled.");
19
+ process.exit(130); // 128 + SIGINT(2)
20
+ });
21
+ const INSTALLER_NAMES = INSTALLER_IDS.join(", ");
22
+ const INSTALLER_ENV_VARS = INSTALLER_IDS.map((id) => getInstaller(id).envVar).join(", ");
23
+ // =============================================================================
24
+ // CLI Setup
25
+ // =============================================================================
26
+ const program = new Command()
27
+ .name(packageJson.name)
28
+ .description(packageJson.description)
29
+ .version(packageJson.version)
30
+ .showHelpAfterError("(add --help for additional information)")
31
+ .showSuggestionAfterError()
32
+ .option("--list-agents", "List supported agents")
33
+ .option("--status", "Show installed agents and available package managers")
34
+ .option("--only <scope>", "Limit --status output (agents|installers). Default: all.")
35
+ .option("-v, --verbose", "Enable verbose output")
36
+ .addHelpText("after", `
37
+ Examples:
38
+ $ axinstall claude Install Claude Code
39
+ $ axinstall claude --with npm Use specific package manager
40
+ $ axinstall claude --dry-run Preview installation command
41
+ $ axinstall --list-agents | tail -n +2 | cut -f1 List agent CLI names
42
+ $ axinstall --status --only installers | grep available Show available installers
43
+
44
+ Environment:
45
+ AXINSTALL_WITH Default package manager (${INSTALLER_NAMES})
46
+
47
+ Requirements:
48
+ ${INSTALLER_NAMES}
49
+ Override paths: ${INSTALLER_ENV_VARS}`);
50
+ // =============================================================================
51
+ // Install / List / Status (Default)
52
+ // =============================================================================
53
+ program
54
+ .argument("[agent]", `Agent to install (${AGENT_CLIS.join(", ")})`)
55
+ .option("--with <installer>", `Package manager to use (${INSTALLER_IDS.join(", ")})`)
56
+ .option("--dry-run", "Show command without executing", false)
57
+ .option("--local", "Install locally instead of globally", false)
58
+ .action((agentCli, cliOptions) => {
59
+ runCommand(agentCli, cliOptions);
60
+ });
61
+ // =============================================================================
62
+ // Parse and Execute
63
+ // =============================================================================
64
+ program.parse();
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Agent detection logic.
3
+ *
4
+ * Detects which AI agents are installed in the current environment.
5
+ */
6
+ import type { Agent } from "axshared";
7
+ /** Result of checking if an agent is installed */
8
+ interface AgentCheckResult {
9
+ agent: Agent;
10
+ installed: boolean;
11
+ version?: string;
12
+ }
13
+ /**
14
+ * Check if a single agent is installed by running `<cli> --version`.
15
+ */
16
+ declare function checkAgent(agent: Agent): AgentCheckResult;
17
+ /**
18
+ * Check installation status of all agents.
19
+ */
20
+ declare function checkAllAgents(): AgentCheckResult[];
21
+ /**
22
+ * Get all installed agents.
23
+ */
24
+ declare function getInstalledAgents(): Agent[];
25
+ export { checkAgent, checkAllAgents, getInstalledAgents };
26
+ export type { AgentCheckResult };
@@ -0,0 +1,46 @@
1
+ /**
2
+ * Agent detection logic.
3
+ *
4
+ * Detects which AI agents are installed in the current environment.
5
+ */
6
+ import { execSync } from "node:child_process";
7
+ import { getAllAgents } from "axshared";
8
+ /**
9
+ * Check if a single agent is installed by running `<cli> --version`.
10
+ */
11
+ function checkAgent(agent) {
12
+ try {
13
+ const output = execSync(`${agent.cli} --version`, {
14
+ encoding: "utf8",
15
+ stdio: ["pipe", "pipe", "pipe"],
16
+ }).trim();
17
+ // Extract version from first line
18
+ const version = output.split("\n")[0]?.trim();
19
+ return {
20
+ agent,
21
+ installed: true,
22
+ version,
23
+ };
24
+ }
25
+ catch {
26
+ return {
27
+ agent,
28
+ installed: false,
29
+ };
30
+ }
31
+ }
32
+ /**
33
+ * Check installation status of all agents.
34
+ */
35
+ function checkAllAgents() {
36
+ return getAllAgents().map((agent) => checkAgent(agent));
37
+ }
38
+ /**
39
+ * Get all installed agents.
40
+ */
41
+ function getInstalledAgents() {
42
+ return checkAllAgents()
43
+ .filter((result) => result.installed)
44
+ .map((result) => result.agent);
45
+ }
46
+ export { checkAgent, checkAllAgents, getInstalledAgents };
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Installer detection logic.
3
+ *
4
+ * Detects which package managers are available in the current environment.
5
+ */
6
+ import type { Installer, InstallerId, InstallerCheckResult } from "./types.js";
7
+ /**
8
+ * Check if a single installer is available.
9
+ */
10
+ declare function checkInstaller(installer: Installer): InstallerCheckResult;
11
+ /**
12
+ * Check availability of all installers.
13
+ */
14
+ declare function checkAllInstallers(): InstallerCheckResult[];
15
+ /**
16
+ * Get all available installers.
17
+ */
18
+ declare function getAvailableInstallers(): Installer[];
19
+ /**
20
+ * Auto-detect the best available installer based on priority.
21
+ *
22
+ * Priority order: pnpm > bun > yarn > npm > brew
23
+ */
24
+ declare function detectBestInstaller(): Installer | undefined;
25
+ /**
26
+ * Check if a specific installer is available.
27
+ */
28
+ declare function isInstallerAvailable(id: InstallerId): boolean;
29
+ export { checkInstaller, checkAllInstallers, getAvailableInstallers, detectBestInstaller, isInstallerAvailable, };
@@ -0,0 +1,69 @@
1
+ /**
2
+ * Installer detection logic.
3
+ *
4
+ * Detects which package managers are available in the current environment.
5
+ */
6
+ import { execFileSync } from "node:child_process";
7
+ import { getAllInstallers, getInstaller, INSTALLER_PRIORITY, } from "./installer-data.js";
8
+ import { resolveInstallerCommand } from "./resolve-installer-command.js";
9
+ /**
10
+ * Check if a single installer is available.
11
+ */
12
+ function checkInstaller(installer) {
13
+ try {
14
+ const command = resolveInstallerCommand(installer);
15
+ const output = execFileSync(command, [...installer.checkArgs], {
16
+ encoding: "utf8",
17
+ stdio: ["ignore", "pipe", "pipe"],
18
+ }).trim();
19
+ // Extract version from first line
20
+ const version = output.split("\n")[0]?.trim();
21
+ return {
22
+ installer,
23
+ available: true,
24
+ version,
25
+ };
26
+ }
27
+ catch {
28
+ return {
29
+ installer,
30
+ available: false,
31
+ };
32
+ }
33
+ }
34
+ /**
35
+ * Check availability of all installers.
36
+ */
37
+ function checkAllInstallers() {
38
+ return getAllInstallers().map((installer) => checkInstaller(installer));
39
+ }
40
+ /**
41
+ * Get all available installers.
42
+ */
43
+ function getAvailableInstallers() {
44
+ return checkAllInstallers()
45
+ .filter((result) => result.available)
46
+ .map((result) => result.installer);
47
+ }
48
+ /**
49
+ * Auto-detect the best available installer based on priority.
50
+ *
51
+ * Priority order: pnpm > bun > yarn > npm > brew
52
+ */
53
+ function detectBestInstaller() {
54
+ const available = new Set(getAvailableInstallers().map((installer) => installer.id));
55
+ for (const id of INSTALLER_PRIORITY) {
56
+ if (available.has(id)) {
57
+ return getInstaller(id);
58
+ }
59
+ }
60
+ return undefined;
61
+ }
62
+ /**
63
+ * Check if a specific installer is available.
64
+ */
65
+ function isInstallerAvailable(id) {
66
+ const installer = getInstaller(id);
67
+ return checkInstaller(installer).available;
68
+ }
69
+ export { checkInstaller, checkAllInstallers, getAvailableInstallers, detectBestInstaller, isInstallerAvailable, };
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Installation command execution.
3
+ */
4
+ import type { Agent } from "axshared";
5
+ import type { Installer } from "./types.js";
6
+ interface InstallOptions {
7
+ agent: Agent;
8
+ installer: Installer;
9
+ global: boolean;
10
+ dryRun: boolean;
11
+ }
12
+ interface InstallResult {
13
+ ok: true;
14
+ command: string;
15
+ }
16
+ interface InstallError {
17
+ ok: false;
18
+ command: string;
19
+ error: Error;
20
+ }
21
+ type InstallOutcome = InstallResult | InstallError;
22
+ declare function buildInstallCommand(options: InstallOptions): string;
23
+ /**
24
+ * Execute the installation command.
25
+ */
26
+ declare function executeInstall(options: InstallOptions): InstallOutcome;
27
+ export { buildInstallCommand, executeInstall };
28
+ export type { InstallOptions, InstallOutcome };
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Installation command execution.
3
+ */
4
+ import { execFileSync } from "node:child_process";
5
+ import { resolveInstallerCommand } from "./resolve-installer-command.js";
6
+ /**
7
+ * Build the installation arguments for an agent.
8
+ */
9
+ function buildInstallArguments(options) {
10
+ const { agent, installer, global: isGlobal } = options;
11
+ // For brew, use the CLI name as the package name (formula name)
12
+ // For npm-based installers, use the npm package name
13
+ const packageName = installer.id === "brew" ? agent.cli : agent.package;
14
+ const installArguments = isGlobal
15
+ ? installer.globalInstallArgs
16
+ : installer.localInstallArgs;
17
+ return installArguments.map((argument) => argument.replace("{package}", packageName));
18
+ }
19
+ function formatCommand(command, arguments_) {
20
+ const parts = [command, ...arguments_];
21
+ return parts
22
+ .map((part) => (/\s/u.test(part) ? JSON.stringify(part) : part))
23
+ .join(" ");
24
+ }
25
+ function buildInstallCommand(options) {
26
+ const command = resolveInstallerCommand(options.installer);
27
+ const arguments_ = buildInstallArguments(options);
28
+ return formatCommand(command, arguments_);
29
+ }
30
+ /**
31
+ * Execute the installation command.
32
+ */
33
+ function executeInstall(options) {
34
+ const command = resolveInstallerCommand(options.installer);
35
+ const arguments_ = buildInstallArguments(options);
36
+ const displayCommand = formatCommand(command, arguments_);
37
+ if (options.dryRun) {
38
+ return { ok: true, command: displayCommand };
39
+ }
40
+ try {
41
+ execFileSync(command, arguments_, {
42
+ stdio: "inherit",
43
+ encoding: "utf8",
44
+ });
45
+ return { ok: true, command: displayCommand };
46
+ }
47
+ catch (error) {
48
+ return {
49
+ ok: false,
50
+ command: displayCommand,
51
+ error: error instanceof Error ? error : new Error(String(error)),
52
+ };
53
+ }
54
+ }
55
+ export { buildInstallCommand, executeInstall };
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Agent installation handler.
3
+ */
4
+ interface InstallOptions {
5
+ with?: string;
6
+ dryRun: boolean;
7
+ local: boolean;
8
+ verbose?: boolean;
9
+ }
10
+ export declare function handleInstall(agentCli: string, options: InstallOptions): void;
11
+ export {};
@@ -0,0 +1,82 @@
1
+ /**
2
+ * Agent installation handler.
3
+ */
4
+ import { AGENT_CLIS, getAgent, isValidAgentCli } from "axshared";
5
+ import { detectBestInstaller, isInstallerAvailable, } from "./detect-installer.js";
6
+ import { executeInstall } from "./execute-install.js";
7
+ import { getInstaller } from "./installer-data.js";
8
+ import { resolveInstallerCommand } from "./resolve-installer-command.js";
9
+ import { INSTALLER_IDS } from "./types.js";
10
+ const INSTALLER_ID_SET = new Set(INSTALLER_IDS);
11
+ const INSTALLER_NAMES = INSTALLER_IDS.join(", ");
12
+ const INSTALLER_ENV_VARS = INSTALLER_IDS.map((id) => getInstaller(id).envVar).join(", ");
13
+ function isInstallerId(value) {
14
+ return INSTALLER_ID_SET.has(value);
15
+ }
16
+ function reportMissingInstaller(installer) {
17
+ const command = resolveInstallerCommand(installer);
18
+ console.error(`Error: Installer '${installer.id}' not available`);
19
+ console.error(`Looked for: ${command}`);
20
+ console.error(`Install ${installer.name} and try again.`);
21
+ console.error(`Override path: ${installer.envVar}=/path/to/${installer.command}`);
22
+ console.error(`Run 'axinstall --status --only installers' to verify availability.`);
23
+ process.exitCode = 1;
24
+ }
25
+ export function handleInstall(agentCli, options) {
26
+ if (!isValidAgentCli(agentCli)) {
27
+ console.error(`Error: Unknown agent '${agentCli}'`);
28
+ console.error(`Available: ${AGENT_CLIS.join(", ")}`);
29
+ console.error("Try 'axinstall --help' for usage information.");
30
+ process.exitCode = 1;
31
+ return;
32
+ }
33
+ const agent = getAgent(agentCli);
34
+ const requestedInstaller = options.with ?? process.env["AXINSTALL_WITH"];
35
+ let installer;
36
+ if (requestedInstaller) {
37
+ if (!isInstallerId(requestedInstaller)) {
38
+ console.error(`Error: Unknown installer '${requestedInstaller}'`);
39
+ console.error(`Available: ${INSTALLER_IDS.join(", ")}`);
40
+ console.error("Try 'axinstall --help' for usage information.");
41
+ process.exitCode = 1;
42
+ return;
43
+ }
44
+ installer = getInstaller(requestedInstaller);
45
+ if (!isInstallerAvailable(requestedInstaller)) {
46
+ reportMissingInstaller(installer);
47
+ return;
48
+ }
49
+ }
50
+ else {
51
+ installer = detectBestInstaller();
52
+ if (!installer) {
53
+ console.error("Error: No package manager found");
54
+ console.error(`Install ${INSTALLER_NAMES} and try again.`);
55
+ console.error(`Override paths: ${INSTALLER_ENV_VARS}`);
56
+ console.error("Run 'axinstall --status --only installers' to verify availability.");
57
+ process.exitCode = 1;
58
+ return;
59
+ }
60
+ }
61
+ const isGlobal = !options.local;
62
+ const result = executeInstall({
63
+ agent,
64
+ installer,
65
+ global: isGlobal,
66
+ dryRun: options.dryRun,
67
+ });
68
+ if (options.dryRun) {
69
+ console.log(result.command);
70
+ return;
71
+ }
72
+ if (result.ok) {
73
+ if (options.verbose) {
74
+ console.error(`Successfully installed ${agent.name}.`);
75
+ console.error(`Run '${agent.cli}' to get started.`);
76
+ }
77
+ return;
78
+ }
79
+ console.error(`Error: Failed to install ${agent.name}`);
80
+ console.error(`Command: ${result.command}`);
81
+ process.exitCode = 1;
82
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * axinstall - AI agent installation library.
3
+ *
4
+ * Exports programmatic APIs for installing CLI agents.
5
+ */
6
+ export { INSTALLER_IDS } from "./types.js";
7
+ export type { Installer, InstallerId, InstallerCheckResult } from "./types.js";
8
+ export { getInstaller, getAllInstallers, INSTALLERS, INSTALLER_PRIORITY, } from "./installer-data.js";
9
+ export { checkInstaller, checkAllInstallers, getAvailableInstallers, detectBestInstaller, isInstallerAvailable, } from "./detect-installer.js";
10
+ export { checkAgent, checkAllAgents, getInstalledAgents, } from "./detect-agent.js";
11
+ export type { AgentCheckResult } from "./detect-agent.js";
12
+ export { buildInstallCommand, executeInstall } from "./execute-install.js";
13
+ export type { InstallOptions, InstallOutcome } from "./execute-install.js";
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ /**
2
+ * axinstall - AI agent installation library.
3
+ *
4
+ * Exports programmatic APIs for installing CLI agents.
5
+ */
6
+ // Types
7
+ export { INSTALLER_IDS } from "./types.js";
8
+ // Installer data
9
+ export { getInstaller, getAllInstallers, INSTALLERS, INSTALLER_PRIORITY, } from "./installer-data.js";
10
+ // Installer detection
11
+ export { checkInstaller, checkAllInstallers, getAvailableInstallers, detectBestInstaller, isInstallerAvailable, } from "./detect-installer.js";
12
+ // Agent detection
13
+ export { checkAgent, checkAllAgents, getInstalledAgents, } from "./detect-agent.js";
14
+ // Installation
15
+ export { buildInstallCommand, executeInstall } from "./execute-install.js";
@@ -0,0 +1,11 @@
1
+ /**
2
+ * Installer definitions and metadata.
3
+ */
4
+ import type { Installer, InstallerId } from "./types.js";
5
+ /** All installers indexed by ID */
6
+ declare const INSTALLERS: Record<InstallerId, Installer>;
7
+ /** Default priority order for auto-detection */
8
+ declare const INSTALLER_PRIORITY: readonly InstallerId[];
9
+ declare function getInstaller(id: InstallerId): Installer;
10
+ declare function getAllInstallers(): Installer[];
11
+ export { getInstaller, getAllInstallers, INSTALLERS, INSTALLER_PRIORITY };
@@ -0,0 +1,71 @@
1
+ /**
2
+ * Installer definitions and metadata.
3
+ */
4
+ const npm = {
5
+ id: "npm",
6
+ name: "npm",
7
+ command: "npm",
8
+ envVar: "AXINSTALL_NPM_PATH",
9
+ checkArgs: ["--version"],
10
+ globalInstallArgs: ["install", "-g", "{package}"],
11
+ localInstallArgs: ["install", "{package}"],
12
+ };
13
+ const pnpm = {
14
+ id: "pnpm",
15
+ name: "pnpm",
16
+ command: "pnpm",
17
+ envVar: "AXINSTALL_PNPM_PATH",
18
+ checkArgs: ["--version"],
19
+ globalInstallArgs: ["add", "-g", "{package}"],
20
+ localInstallArgs: ["add", "{package}"],
21
+ };
22
+ const bun = {
23
+ id: "bun",
24
+ name: "Bun",
25
+ command: "bun",
26
+ envVar: "AXINSTALL_BUN_PATH",
27
+ checkArgs: ["--version"],
28
+ globalInstallArgs: ["add", "-g", "{package}"],
29
+ localInstallArgs: ["add", "{package}"],
30
+ };
31
+ const yarn = {
32
+ id: "yarn",
33
+ name: "Yarn",
34
+ command: "yarn",
35
+ envVar: "AXINSTALL_YARN_PATH",
36
+ checkArgs: ["--version"],
37
+ globalInstallArgs: ["global", "add", "{package}"],
38
+ localInstallArgs: ["add", "{package}"],
39
+ };
40
+ const brew = {
41
+ id: "brew",
42
+ name: "Homebrew",
43
+ command: "brew",
44
+ envVar: "AXINSTALL_BREW_PATH",
45
+ checkArgs: ["--version"],
46
+ globalInstallArgs: ["install", "{package}"],
47
+ localInstallArgs: ["install", "{package}"],
48
+ };
49
+ /** All installers indexed by ID */
50
+ const INSTALLERS = {
51
+ npm,
52
+ pnpm,
53
+ bun,
54
+ yarn,
55
+ brew,
56
+ };
57
+ /** Default priority order for auto-detection */
58
+ const INSTALLER_PRIORITY = [
59
+ "pnpm",
60
+ "bun",
61
+ "yarn",
62
+ "npm",
63
+ "brew",
64
+ ];
65
+ function getInstaller(id) {
66
+ return INSTALLERS[id];
67
+ }
68
+ function getAllInstallers() {
69
+ return Object.values(INSTALLERS);
70
+ }
71
+ export { getInstaller, getAllInstallers, INSTALLERS, INSTALLER_PRIORITY };
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Status and agent listing output.
3
+ */
4
+ declare const STATUS_SCOPES: readonly ["all", "agents", "installers"];
5
+ type StatusScope = (typeof STATUS_SCOPES)[number];
6
+ export declare function isStatusScope(value: string): value is StatusScope;
7
+ export declare function printSupportedAgents(): void;
8
+ export declare function printStatus(scope: StatusScope): void;
9
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Status and agent listing output.
3
+ */
4
+ import { getAllAgents } from "axshared";
5
+ import { checkAllAgents } from "./detect-agent.js";
6
+ import { checkAllInstallers } from "./detect-installer.js";
7
+ const STATUS_SCOPES = ["all", "agents", "installers"];
8
+ const STATUS_SCOPE_SET = new Set(STATUS_SCOPES);
9
+ export function isStatusScope(value) {
10
+ return STATUS_SCOPE_SET.has(value);
11
+ }
12
+ export function printSupportedAgents() {
13
+ const agents = getAllAgents();
14
+ console.log("CLI\tNAME\tPACKAGE\tPROVIDER");
15
+ for (const agent of agents) {
16
+ console.log(`${agent.cli}\t${agent.name}\t${agent.package}\t${agent.provider}`);
17
+ }
18
+ }
19
+ export function printStatus(scope) {
20
+ const includeAgents = scope === "all" || scope === "agents";
21
+ const includeInstallers = scope === "all" || scope === "installers";
22
+ console.log("TYPE\tID\tNAME\tSTATUS\tVERSION\tPROVIDER\tPACKAGE");
23
+ if (includeAgents) {
24
+ const agentResults = checkAllAgents();
25
+ for (const result of agentResults) {
26
+ const status = result.installed ? "installed" : "not_found";
27
+ const version = result.version ?? "-";
28
+ console.log(`AGENT\t${result.agent.cli}\t${result.agent.name}\t${status}\t${version}\t${result.agent.provider}\t${result.agent.package}`);
29
+ }
30
+ }
31
+ if (includeInstallers) {
32
+ const installerResults = checkAllInstallers();
33
+ for (const result of installerResults) {
34
+ const status = result.available ? "available" : "not_found";
35
+ const version = result.version ?? "-";
36
+ console.log(`INSTALLER\t${result.installer.id}\t${result.installer.name}\t${status}\t${version}\t-\t-`);
37
+ }
38
+ }
39
+ }
@@ -0,0 +1,3 @@
1
+ import type { Installer } from "./types.js";
2
+ declare function resolveInstallerCommand(installer: Installer): string;
3
+ export { resolveInstallerCommand };
@@ -0,0 +1,5 @@
1
+ function resolveInstallerCommand(installer) {
2
+ const override = process.env[installer.envVar];
3
+ return override && override.length > 0 ? override : installer.command;
4
+ }
5
+ export { resolveInstallerCommand };
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Command execution logic for axinstall CLI.
3
+ */
4
+ export interface CliOptions {
5
+ listAgents?: boolean;
6
+ status?: boolean;
7
+ only?: string;
8
+ verbose?: boolean;
9
+ with?: string;
10
+ dryRun: boolean;
11
+ local: boolean;
12
+ }
13
+ export declare function runCommand(agentCli: string | undefined, options: CliOptions): void;
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Command execution logic for axinstall CLI.
3
+ */
4
+ import { handleInstall } from "./handle-install.js";
5
+ import { isStatusScope, printStatus, printSupportedAgents, } from "./print-status.js";
6
+ import { validateOptions } from "./validate-cli-options.js";
7
+ function reportUsageError(message) {
8
+ console.error(`Error: ${message}`);
9
+ console.error("Usage: axinstall [options] <agent>");
10
+ console.error(" axinstall --list-agents");
11
+ console.error(" axinstall --status [--only <scope>]");
12
+ console.error("Try 'axinstall --help' for usage information.");
13
+ process.exitCode = 1;
14
+ }
15
+ export function runCommand(agentCli, options) {
16
+ const validation = validateOptions(agentCli, options);
17
+ if (!validation.valid) {
18
+ reportUsageError(validation.message);
19
+ return;
20
+ }
21
+ if (options.listAgents) {
22
+ printSupportedAgents();
23
+ return;
24
+ }
25
+ if (options.status) {
26
+ const scopeInput = options.only?.toLowerCase() ?? "all";
27
+ if (!isStatusScope(scopeInput)) {
28
+ reportUsageError(`Invalid --only value '${options.only}'. Use agents or installers.`);
29
+ return;
30
+ }
31
+ printStatus(scopeInput);
32
+ return;
33
+ }
34
+ if (!agentCli) {
35
+ reportUsageError("Missing required argument 'agent'.");
36
+ return;
37
+ }
38
+ handleInstall(agentCli, options);
39
+ }
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Type definitions for axinstall.
3
+ */
4
+ /** Supported package manager identifiers */
5
+ declare const INSTALLER_IDS: readonly ["npm", "pnpm", "bun", "yarn", "brew"];
6
+ type InstallerId = (typeof INSTALLER_IDS)[number];
7
+ /** Package manager metadata */
8
+ interface Installer {
9
+ id: InstallerId;
10
+ name: string;
11
+ /** Executable name used when no env override is set. */
12
+ command: string;
13
+ /** Environment variable to override the command path. */
14
+ envVar: string;
15
+ /** Args for a version check. */
16
+ checkArgs: readonly string[];
17
+ /** Args for global install. Use {package} as placeholder. */
18
+ globalInstallArgs: readonly string[];
19
+ /** Args for local install. Use {package} as placeholder. */
20
+ localInstallArgs: readonly string[];
21
+ }
22
+ /** Result of checking installer availability */
23
+ interface InstallerCheckResult {
24
+ installer: Installer;
25
+ available: boolean;
26
+ version?: string;
27
+ }
28
+ export { INSTALLER_IDS };
29
+ export type { Installer, InstallerId, InstallerCheckResult };
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Type definitions for axinstall.
3
+ */
4
+ /** Supported package manager identifiers */
5
+ const INSTALLER_IDS = ["npm", "pnpm", "bun", "yarn", "brew"];
6
+ export { INSTALLER_IDS };
@@ -0,0 +1,14 @@
1
+ /**
2
+ * CLI option validation.
3
+ */
4
+ import type { CliOptions } from "./run-command.js";
5
+ type ValidationError = {
6
+ valid: false;
7
+ message: string;
8
+ };
9
+ type ValidationSuccess = {
10
+ valid: true;
11
+ };
12
+ type ValidationResult = ValidationError | ValidationSuccess;
13
+ export declare function validateOptions(agentCli: string | undefined, options: CliOptions): ValidationResult;
14
+ export {};
@@ -0,0 +1,39 @@
1
+ /**
2
+ * CLI option validation.
3
+ */
4
+ export function validateOptions(agentCli, options) {
5
+ if (options.listAgents && options.status) {
6
+ return {
7
+ valid: false,
8
+ message: "Use either --list-agents or --status, not both.",
9
+ };
10
+ }
11
+ if ((options.listAgents || options.status) && agentCli) {
12
+ return {
13
+ valid: false,
14
+ message: "Do not pass an agent when using --list-agents or --status.",
15
+ };
16
+ }
17
+ if ((options.listAgents || options.status) && options.with) {
18
+ return {
19
+ valid: false,
20
+ message: "--with is only valid when installing an agent.",
21
+ };
22
+ }
23
+ if ((options.listAgents || options.status) && options.local) {
24
+ return {
25
+ valid: false,
26
+ message: "--local is only valid when installing an agent.",
27
+ };
28
+ }
29
+ if ((options.listAgents || options.status) && options.dryRun) {
30
+ return {
31
+ valid: false,
32
+ message: "--dry-run is only valid when installing an agent.",
33
+ };
34
+ }
35
+ if (options.only && !options.status) {
36
+ return { valid: false, message: "--only must be used with --status." };
37
+ }
38
+ return { valid: true };
39
+ }
package/package.json ADDED
@@ -0,0 +1,95 @@
1
+ {
2
+ "name": "axinstall",
3
+ "author": "Łukasz Jerciński",
4
+ "license": "MIT",
5
+ "version": "1.0.0",
6
+ "description": "Universal installer for AI CLI agents with automatic package manager detection",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git+https://github.com/Jercik/axinstall.git"
10
+ },
11
+ "homepage": "https://github.com/Jercik/axinstall#readme",
12
+ "bugs": {
13
+ "url": "https://github.com/Jercik/axinstall/issues"
14
+ },
15
+ "type": "module",
16
+ "main": "./dist/index.js",
17
+ "types": "./dist/index.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./dist/index.d.ts",
21
+ "import": "./dist/index.js"
22
+ }
23
+ },
24
+ "bin": {
25
+ "axinstall": "bin/axinstall"
26
+ },
27
+ "files": [
28
+ "bin/",
29
+ "dist/",
30
+ "README.md",
31
+ "LICENSE"
32
+ ],
33
+ "scripts": {
34
+ "prepare": "git config core.hooksPath .githooks",
35
+ "prepublishOnly": "pnpm run rebuild",
36
+ "build": "tsc -p tsconfig.app.json",
37
+ "clean": "rm -rf dist *.tsbuildinfo",
38
+ "format": "prettier --write .",
39
+ "format:check": "prettier --check .",
40
+ "fta": "fta-check",
41
+ "knip": "knip",
42
+ "lint": "eslint",
43
+ "rebuild": "pnpm run clean && pnpm run build",
44
+ "start": "pnpm -s run rebuild && node bin/axinstall",
45
+ "test": "vitest run --exclude '**/*.integration.test.ts'",
46
+ "test:coverage": "vitest run --coverage --exclude '**/*.integration.test.ts'",
47
+ "test:integration": "vitest run --testTimeout=300000 src/docker-install.integration.test.ts",
48
+ "test:all": "vitest run --testTimeout=300000",
49
+ "test:watch": "vitest --exclude '**/*.integration.test.ts'",
50
+ "typecheck": "tsc -b --noEmit"
51
+ },
52
+ "keywords": [
53
+ "ai",
54
+ "agent",
55
+ "cli",
56
+ "config",
57
+ "configuration",
58
+ "claude-code",
59
+ "codex",
60
+ "gemini",
61
+ "opencode",
62
+ "llm",
63
+ "automation",
64
+ "coding-assistant"
65
+ ],
66
+ "packageManager": "pnpm@10.26.1",
67
+ "engines": {
68
+ "node": ">=22.14.0"
69
+ },
70
+ "dependencies": {
71
+ "@commander-js/extra-typings": "^14.0.0",
72
+ "axshared": "^1.1.0",
73
+ "commander": "^14.0.2"
74
+ },
75
+ "devDependencies": {
76
+ "@eslint/compat": "^2.0.0",
77
+ "@eslint/js": "^9.39.2",
78
+ "@total-typescript/ts-reset": "^0.6.1",
79
+ "@types/node": "^25.0.3",
80
+ "@vitest/coverage-v8": "^4.0.16",
81
+ "@vitest/eslint-plugin": "^1.5.4",
82
+ "eslint": "^9.39.2",
83
+ "eslint-config-prettier": "^10.1.8",
84
+ "eslint-plugin-unicorn": "^62.0.0",
85
+ "fta-check": "^1.5.1",
86
+ "fta-cli": "^3.0.0",
87
+ "globals": "^16.5.0",
88
+ "knip": "^5.76.1",
89
+ "prettier": "3.7.4",
90
+ "semantic-release": "^25.0.2",
91
+ "typescript": "^5.9.3",
92
+ "typescript-eslint": "^8.50.0",
93
+ "vitest": "^4.0.16"
94
+ }
95
+ }