#!/usr/bin/env bare
await import('bare-node-runtime/global')
/**
* pgh - Punch Git Hub CLI
*
* A CLI for pear-git, parallel to GitHub's gh.
*
* Usage:
* pgh <command> <subcommand> [args] [--flags]
*/
import { PghClient } from '../lib/cli/client.js'
import { loadConfig, saveConfig, hasDeviceIdentity } from '../lib/cli/config.js'
import { log, error } from '../lib/cli/output.js'
import { repoCommand, repoHelp } from '../lib/cli/commands/repo.js'
import { issueCommand, issueHelp } from '../lib/cli/commands/issue.js'
import { labelCommand, labelHelp } from '../lib/cli/commands/label.js'
import { searchCommand, searchHelp } from '../lib/cli/commands/search.js'
import { authCommand, authHelp } from '../lib/cli/commands/auth.js'
import { tokenCommand, tokenHelp } from '../lib/cli/commands/token.js'
import { remoteCommand, remoteHelp } from '../lib/cli/commands/remote.js'
import { profileCommand, profileHelp } from '../lib/cli/commands/profile.js'
import { serveCommand, serveHelp } from '../lib/cli/commands/serve.js'
import { adminCommand, adminHelp } from '../lib/cli/commands/admin.js'
import { identityCommand, identityHelp } from '../lib/cli/commands/identity.js'
import { peerCommand, peerHelp } from '../lib/cli/commands/peer.js'
import { pushCommand, pushHelp } from '../lib/cli/commands/push.js'
import { pullCommand, pullHelp } from '../lib/cli/commands/pull.js'
import { commitCommand, commitHelp } from '../lib/cli/commands/commit.js'
import { deployCommand, deployHelp } from '../lib/cli/commands/deploy.js'
import { groupCommand, groupHelp } from '../lib/cli/commands/group.js'
import { registryCommand, registryHelp, federationCommand, federationHelp, accessCommand, accessHelp } from '../lib/cli/commands/registry.js'
import { appCommand, appHelp } from '../lib/cli/commands/app.js'
import { serverCommand, serverHelp } from '../lib/cli/commands/server.js'
import { gitSpawn } from '../lib/cli/git.js'
const VERSION = '0.1.0'
// Parse args — globalThis.__pghArgs is set when invoked via `pear run ... pgh`
const rawArgs = globalThis.__pghArgs || process.argv.slice(2)
const flags = {}
const positional = []
// Boolean flags that never take a value argument
const booleanFlags = new Set([
'--json', '--help', '-h', '--version', '-v', '--verbose',
'--force', '--rebase', '--recurse-submodules', '--set-upstream', '-u',
'-a', '--all', '--amend', '--dry-run',
'--mirror', '--peer'
])
for (let i = 0; i < rawArgs.length; i++) {
const arg = rawArgs[i]
if (booleanFlags.has(arg)) {
const key = arg.startsWith('--') ? arg.slice(2) : arg.slice(1)
flags[key] = true
} else if (arg.startsWith('--') && rawArgs[i + 1] && !rawArgs[i + 1].startsWith('--')) {
flags[arg.slice(2)] = rawArgs[++i]
} else if (arg.startsWith('-') && arg.length === 2 && rawArgs[i + 1] && !rawArgs[i + 1].startsWith('-')) {
flags[arg.slice(1)] = rawArgs[++i]
} else if (!arg.startsWith('-')) {
positional.push(arg)
}
}
const command = positional[0]
const subcommand = positional[1]
const args = positional.slice(2)
if (flags.version) {
log(`pgh ${VERSION}`)
process.exit(0)
}
if (!command || flags.help) {
if (command === 'repo') { repoHelp(); process.exit(0) }
if (command === 'issue') { issueHelp(); process.exit(0) }
if (command === 'label') { labelHelp(); process.exit(0) }
if (command === 'search') { searchHelp(); process.exit(0) }
if (command === 'auth') { authHelp(); process.exit(0) }
if (command === 'token') { tokenHelp(); process.exit(0) }
if (command === 'remote') { remoteHelp(); process.exit(0) }
if (command === 'profile') { profileHelp(); process.exit(0) }
if (command === 'serve') { serveHelp(); process.exit(0) }
if (command === 'admin') { adminHelp(); process.exit(0) }
if (command === 'identity') { identityHelp(); process.exit(0) }
if (command === 'peer') { peerHelp(); process.exit(0) }
if (command === 'push') { pushHelp(); process.exit(0) }
if (command === 'pull') { pullHelp(); process.exit(0) }
if (command === 'commit') { commitHelp(); process.exit(0) }
if (command === 'deploy') { deployHelp(); process.exit(0) }
if (command === 'group') { groupHelp(); process.exit(0) }
if (command === 'registry') { registryHelp(); process.exit(0) }
if (command === 'federation') { federationHelp(); process.exit(0) }
if (command === 'access') { accessHelp(); process.exit(0) }
if (command === 'app') { appHelp(); process.exit(0) }
if (command === 'server') { serverHelp(); process.exit(0) }
showHelp()
process.exit(0)
}
// First-run guard — require device identity before most commands
const skipGuardCommands = new Set(['identity', 'config', 'help', 'version', 'install', 'serve', 'app', 'server', 'restart'])
if (command && !skipGuardCommands.has(command) && !hasDeviceIdentity()) {
log('pgh is not configured. Run: pgh identity setup')
process.exit(1)
}
const client = new PghClient(flags)
try {
switch (command) {
case 'repo':
await repoCommand(client, subcommand, args, flags)
break
case 'issue':
await issueCommand(client, subcommand, args, flags)
break
case 'label':
await labelCommand(client, subcommand, args, flags)
break
case 'search':
await searchCommand(client, subcommand, args, flags)
break
case 'auth':
await authCommand(client, subcommand, args, flags)
break
case 'token':
await tokenCommand(client, subcommand, args, flags)
break
case 'remote':
await remoteCommand(client, subcommand, args, flags)
break
case 'profile':
await profileCommand(client, subcommand, args, flags)
break
case 'serve':
await serveCommand(client, subcommand, args, flags)
break
case 'admin':
await adminCommand(client, subcommand, args, flags)
break
case 'identity':
await identityCommand(client, subcommand, args, flags)
break
case 'peer':
await peerCommand(client, subcommand, args, flags)
break
case 'push':
await pushCommand(client, subcommand, args, flags)
break
case 'pull':
await pullCommand(client, subcommand, args, flags)
break
case 'commit':
await commitCommand(client, subcommand, args, flags)
break
case 'deploy':
await deployCommand(client, subcommand, args, flags)
break
case 'group':
await groupCommand(client, subcommand, args, flags)
break
case 'registry':
await registryCommand(client, subcommand, args, flags)
break
case 'federation':
await federationCommand(client, subcommand, args, flags)
break
case 'access':
await accessCommand(client, subcommand, args, flags)
break
case 'app':
await appCommand(client, subcommand, args, flags)
break
case 'server':
await serverCommand(client, subcommand, args, flags)
break
case 'config': {
if (subcommand === 'set' && args[0] && args[1]) {
const cfg = loadConfig()
cfg[args[0]] = args[1]
saveConfig(cfg)
log(`Set ${args[0]} = ${args[1]}`)
} else if (subcommand === 'get' && args[0]) {
const cfg = loadConfig()
log(cfg[args[0]] ?? '(not set)')
} else if (subcommand === 'list' || !subcommand) {
const cfg = loadConfig()
for (const [k, v] of Object.entries(cfg)) {
log(`${k} = ${v ?? '(not set)'}`)
}
} else {
log('Usage: pgh config [set <key> <value> | get <key> | list]')
}
break
}
case 'help':
if (subcommand === 'repo') { repoHelp(); break }
if (subcommand === 'issue') { issueHelp(); break }
if (subcommand === 'label') { labelHelp(); break }
if (subcommand === 'search') { searchHelp(); break }
if (subcommand === 'auth') { authHelp(); break }
if (subcommand === 'token') { tokenHelp(); break }
if (subcommand === 'remote') { remoteHelp(); break }
if (subcommand === 'profile') { profileHelp(); break }
if (subcommand === 'serve') { serveHelp(); break }
if (subcommand === 'admin') { adminHelp(); break }
if (subcommand === 'identity') { identityHelp(); break }
if (subcommand === 'peer') { peerHelp(); break }
if (subcommand === 'push') { pushHelp(); break }
if (subcommand === 'pull') { pullHelp(); break }
if (subcommand === 'commit') { commitHelp(); break }
if (subcommand === 'deploy') { deployHelp(); break }
if (subcommand === 'group') { groupHelp(); break }
if (subcommand === 'registry') { registryHelp(); break }
if (subcommand === 'federation') { federationHelp(); break }
if (subcommand === 'access') { accessHelp(); break }
if (subcommand === 'app') { appHelp(); break }
if (subcommand === 'server') { serverHelp(); break }
showHelp()
break
case 'version':
log(`pgh ${VERSION}`)
break
case 'restart':
await serveCommand(client, 'restart', args, flags)
break
default:
// Pass through to git for any unrecognized command
await gitSpawn(rawArgs, '.')
}
} catch (err) {
if (err.message.includes('exited with code')) {
// Git passthrough error — exit code is in the message
const match = err.message.match(/code (\d+)/)
process.exit(match ? parseInt(match[1], 10) : 1)
}
error(err.message)
}
function showHelp () {
log(`pgh - Punch Git Hub CLI for pear-git
Usage:
pgh <command> <subcommand> [args] [--flags]
Git:
commit Stage and commit changes
push Push to pear remote (auto-detected)
pull Pull from pear remote (auto-detected)
remote Petname remotes (add, rm, list, rename, migrate-origins)
<git cmd> Any unrecognized command is passed through to git
Repos:
repo Manage repositories (list, info, clone, create, scan, import, link)
search Search repos and code (repo, code)
deploy Pear app deployment (setup, trigger, status, log, disable)
Issues:
issue Manage issues (list, create, view, close, reopen, comment)
label Manage labels (list, create, update, delete)
Identity:
identity Device identity (setup, status, show, link, export, change-password)
auth Authentication (status, whoami, test)
token Token management (create, list, revoke, test)
profile User profile (view, set)
Server:
serve Server management (status, p2p, restart, shutdown)
server Server identity (export-mnemonic, link, join, status)
admin Admin tools (users, identities, challenges, merge, stats)
group Manage namespace groups (create, list, info, add-repo, members)
registry P2P namespace registry (share, join, invite, status, peers)
federation Server-to-server linking (invite, join, peers, revoke)
access Access grants (grants, invites, grant-group, grant-repo, join, revoke)
peer Server peering + P2P access (add, join, servers, repair, invite, accept)
config Manage config (set, get, list)
Apps:
app Install and manage Pear apps locally (install, update, list, remove)
Other:
help Show help for a command
version Show version
Global Flags:
--server URL Server URL (default: http://localhost:5005/git)
--token TOKEN Bearer token for auth
--repo NAME Repository name
--namespace NS Namespace
--json Output as JSON
-h, --help Show help
-v, --version Show version
Examples:
pgh commit -a -m "Add feature"
pgh push
pgh pull --rebase
pgh repo list
pgh issue list --repo pear-git
pgh issue create "Fix bug" -d "Description" --repo pear-git
pgh search code "escapeHtml" --repo pear-git`)
}