#!/usr/bin/env node import { loadAliases, setAlias } from '../aliases-store.ts' import { formatAliasList, toAliasListItems } from '../alias-list-output.ts' import { parseArgv, resolveCliCommand, type ResolvedCliInput } from '../cli/argv.ts' import { formatCopySuccessLine } from '../copy-summary.ts' import { copyFromGithub } from '../copy-from-github.ts' import { selectAliasInteractively, toAliasMenuItems } from '../interactive-alias-menu.ts' import { CopyFromGithubError } from '../strategy-result.ts' import { readCliVersion } from '../version.ts' const HELP = `gh-cp — copy files or directories from a GitHub repository Usage: gh-cp [options] [destination] gh-cp alias gh-cp alias list gh-cp install [alias-name] [destination] The destination defaults to the current directory. Use --path to override the optional second positional argument. Commands: [destination] Copy a GitHub repo, directory, or file to a destination alias Save a reusable source shortcut alias list List saved aliases install [alias-name] Copy from a saved alias; omit alias-name to choose interactively Examples: gh-cp lirantal/npq/.devcontainer . gh-cp lirantal/npq/.devcontainer --path ./vendor/gh gh-cp lirantal/create-node-lib/blob/main/template/.npmrc gh-cp lirantal/npq#main gh-cp lirantal/npq --ref v1.0.0 gh-cp alias devcontainer github.com/lirantal/create-node-lib/tree/main/template/.devcontainer/ gh-cp alias list gh-cp install devcontainer . gh-cp install Options: -h, --help Show this message -V, --version Show version -v, --verbose Log strategy and progress to stderr -f, --force Overwrite existing files --dry-run Show what would be written without writing --json Print a JSON summary or alias listing to stdout --path DIR Output directory (overrides destination positional) --ref REF Branch, tag, or SHA (overrides #ref in the source spec) Exit codes: 0 Success 1 Usage or validation error 2 All fetch strategies failed ` async function runCopy (input: ResolvedCliInput): Promise { const result = await copyFromGithub({ sourceSpec: input.sourceSpec, destination: input.destination, refOverride: input.ref, force: input.force, dryRun: input.dryRun, verbose: input.verbose }) if (input.json) { process.stdout.write( `${JSON.stringify( { strategy: result.strategy, files: result.written, owner: result.owner, repo: result.repo, path: result.repoPath, ref: result.ref ?? null }, null, input.verbose ? 2 : 0 )}\n` ) } else { process.stdout.write( `${formatCopySuccessLine({ written: result.written, dryRun: input.dryRun })}\n` ) } process.exitCode = 0 } async function main (): Promise { let parsed try { parsed = parseArgv(process.argv) } catch (e) { const msg = e instanceof Error ? e.message : String(e) process.stderr.write(`gh-cp: ${msg}\n`) process.exitCode = 1 return } if (parsed.help) { process.stdout.write(HELP) process.exitCode = 0 return } if (parsed.version) { process.stdout.write(`${readCliVersion()}\n`) process.exitCode = 0 return } let command try { command = resolveCliCommand(parsed) } catch (e) { const msg = e instanceof Error ? e.message : String(e) process.stderr.write(`gh-cp: ${msg}\nTry 'gh-cp --help' for usage.\n`) process.exitCode = 1 return } try { if (command.command === 'alias') { await setAlias(command.aliasName, command.sourceSpec) process.stdout.write( `saved alias "${command.aliasName}" -> ${command.sourceSpec}\n` ) process.exitCode = 0 return } if (command.command === 'alias-list') { const aliases = await loadAliases() if (command.json) { process.stdout.write( `${JSON.stringify({ aliases: toAliasListItems(aliases) })}\n` ) } else { process.stdout.write(formatAliasList(aliases)) } process.exitCode = 0 return } if (command.command === 'install') { const aliases = await loadAliases() let sourceSpec: string | undefined if (command.aliasName !== undefined) { sourceSpec = aliases[command.aliasName] if (sourceSpec === undefined) { throw new Error( `Unknown alias "${command.aliasName}". Run 'gh-cp install' to choose from saved aliases.` ) } } else { if (command.json) { throw new Error( 'Interactive install does not support --json. Pass an alias name, for example: gh-cp install --json.' ) } const items = toAliasMenuItems(aliases) if (items.length === 0) { throw new Error( "No aliases saved. Run 'gh-cp alias ' first." ) } const selected = await selectAliasInteractively(items, command.destination) if (selected === undefined) { process.exitCode = 0 return } sourceSpec = selected.sourceSpec } await runCopy({ sourceSpec, destination: command.destination, ref: command.ref, help: false, version: false, verbose: command.verbose, force: command.force, dryRun: command.dryRun, json: command.json }) return } await runCopy(command) } catch (e) { const version = readCliVersion() if (e instanceof CopyFromGithubError) { process.stderr.write(`gh-cp v${version} — ${e.message}\n`) process.exitCode = e.exitCode return } const msg = e instanceof Error ? e.message : String(e) process.stderr.write(`gh-cp v${version} — ${msg}\n`) process.exitCode = 1 } } main().catch((e: unknown) => { const version = readCliVersion() const msg = e instanceof Error ? e.message : String(e) process.stderr.write(`gh-cp v${version} — ${msg}\n`) process.exit(1) })